Moulder in action

In a previous blog post, I’ve talked about templating in general and showed a quick example of how Moulder does templating.

Moulder is an Open source templating library that exists in 2 versions: Scala and Java. Both versions are hosted on Github and are available under the MIT Licence.

Unlike many other templating libraries, Moulder keeps the template data and the templating rules in separate files. The template data can (only) be any XML-like format, including HTML.
The templating rules are written in Scala or in Java (depending on the version you choose) and mimics jQuery’s usage patterns, i.e. a rule is expressed as a CSS selector and a list of operations to perform on the elements returned by that selector.

Moulder’s internals

At its core, Moulder is very simple. You start by registering a number of transformations chains. A transformation chain is composed of a CSS selector and a list of moulders. When the rendering is triggered on a Document, Moulder applies the following for each chain:

  1. For every element E returned by the selector, do the following:
  2. Apply the first moulder on E, and collect the produced nodes in a list L1
  3. Apply the second moulder in the chain on the nodes produced by the first moulder L1, and collect the produced nodes in a list, L2
  4. Do the same for the remaining moulders in the chain, i.e. applying a moulder on the result of the one before it
  5. When all the moulders of the chain have been applied, replace the element E in the document with the list of nodes produced by the last moulder in the chain
  6. Go back to step 2 with the next element returned by the CSS selector

Moulder in action

The goal of this post is to demonstrate moulder’s usage on a real world although simple use case. This use case will involve repetition, injection of text, modification of attributes and conditional rendering of markup.

I’ve chosen to reuse the same example I used in a previous post of mine (in french) which consists in a min bug tracking system. More precisely, I’ll limit this post to the rendering of of tasks. A task is defined as follow:

  • Type: a task can be a bug, an enhancement or a new feature
  • Status: a task can be either open or closed
  • Title: well, a title
  • Description: …
  • Urgent: a task can be urgent or not

Or in java terms:

public class Task {
    public static enum TaskType {
        BUG, ENHANCEMENT, NEW_FEATURE;
    }

    public static enum TaskStatus {
        OPEN, CLOSED;
    }

    public final String title;
    public final String description;
    public final TaskType type;
    public final TaskStatus status;
    public final boolean urgent;

    public Task(String title, String description, TaskType type,
                TaskStatus status, boolean urgent) {
        this.title = title;
        this.description = description;
        this.type = type;
        this.status = status;
        this.urgent = urgent;
    }
}

And I’ll use this bit of markup to render a task:

<ul id="tasks">
    <li>
        <img src="/images/circle_red.png"/>

        <h2>[title]</h2>

        <p>[description]</p>
        <span>urgent</span>
    </li>
</ul>

According to the markup above, each task will be rendered as a list item, with an image to denote its type, a h2 element to hold its title and a p for its description, and finally, a span with the mention “urgent” will be rendered for the urgent tasks.

First, a bit of setup:

List<Task> tasks = tasks();

MoulderShop m = new MoulderShop();

[...]

Document doc = m.process(getClass().getResourceAsStream("tasks.html"));
return doc.toString();

We start by retrieving the tasks list and storing it in a variable named tasks.
We then create an instance of MoulderShop, which can be thought of as an entry point to Moulder’s API. It’s simply a class that you configure a set of transformations rules in it and then instruct it to apply them on a HTML stream.
The latter is achieved by calling the process method on the MoulderShop instance and passing the HTML source as an argument, in the form of an InputStream or an already parsed Document.

Now to the transformation chains. Our first requirement is to repeat the li element once for every task. This is achieved by attaching the repeat transformer (or moulder) to the li element:

m.register("#tasks li", repeat(tasks));

register takes a CSS selector and a list of moulders as its arguments and defines a chain of transformations. The repeat moulder is created using a factory method by statically importing all of moulder.moulds.Moulds methods. It takes an Iterable as its argument. On rendering, the repeat moulder repeats its associated markup once for every item of the Iterable it was instanciated with.

Next, we need to modify each generated li‘s content to dispay the task it represents.
This can be achieved in two ways:

  1. Start a new chain of transformations, which will run after the repetition has occured (and thus the li element would have been already repeated in the document) by selecting the li‘s children and modifying them
  2. Continue in the same chain by using the sub moulder

1. New chains

As stated earlier, if a new chain is started, its selector will be executed against the document after it was modified by the previous chains. If we had 3 tasks for example, and after the repetition chain, the following chains will work on a document with 3 li elements in it.

Starting with the images, what we’d like to do is modify their src attribute to point to the right image depending on the task’s type.
Modifying an element’s attribute with Moulder is as easy as applying the attr moulder on it, which takes the name and the value of the attribute as its arguments.
What’s tricky here is that the img selector will return multiple elements and each one of them would need a different value.
Moulder provides a solution for such a use case via the notion of Value. Value is defined as an interface with a method that returns the concrete value.
The attr moulder can take a Value as its second argument (besides a String). We can then provide an implementation of Value that returns the right image for the first task, then for the second task, etc. But Moulder already provides a couple of useful and generic implementations of Value that can be used to solve this problem.

One such implementation is Values, which takes a list of concrete values and returns them one by one. If we could manage to create a list of Strings called srcs containing the src attribute’s values that correspond to the tasks list, defining the img transformation would be as simple as :

m.register("#tasks img", attr("src", new Values<String>(srcs));

But still, building the srcs list is still a PITA, especially without something like map and lambdas. So, this can be further improved by using another implementation of Value: ValueTransformer.
ValueTransformer is an abstract class that takes another value as an argument and lets you define a transform method that works on its delegate Value‘s value and produce a new value.
In this case, we can reuse the tasks list by passing it to the Values constructor, and wrapping the latter in a ValueTransformer that transforms the Task to the correct image:

m.register("#tasks img", attr("src", new ValueTransformer<Task, String>(new Values<Task>(tasks)) {
    @Override
    protected String transform(Task s) {
        "/images/" + (s.type == Task.TaskType.BUG ? "circle_red.png" : s.type == Task.TaskType.ENHANCEMENT ? "circle_green.png" : "circle_blue.png");
    }
}));

The same approach can be taken to manipulate the other items of the markup (title, description, urgent).

2. Same chain

The same processing shown above can be achieved while remaining in the same transformation chain by using the sub moulder. The sub moulder let’s you select parts of the element being worked on and attach new moulders that operate on them.
The first moulder in the first chain is the repeater. The repeat produces a list of li elements. The sub moulder can be added after it and filtered to only select the img element, and apply its own list of moulders on it:

m.register("#tasks li", repeat(tasks), sub("img", attr("src", new ValueTransformer<Task, String>(new Values<Task>(tasks)) {
    @Override
    protected String transform(Task s) {
        "/images/" + (s.type == Task.TaskType.BUG ? "circle_red.png" : s.type == Task.TaskType.ENHANCEMENT ? "circle_green.png" : "circle_blue.png");
    }
})));

This can be further improved by using the notion of an element’s data. As said earlier, a moulder takes an node as an argument and produces a list of nodes. But what more, it actually takes a node and a piece of data attached to it, and produces a list of nodes, each with its data. This data can be anything, and its semantics vary from a moulder to another.
In the case of the repeat moulder, which repeats its argument as many times as the items of a list, each produced node is paired with the element it corresponds to in the list.
Thus, the first li produced is associated with the first task, the second with the second task, etc. This piece of data can be accessed in the following moulder by using the ElementDataValue implementation of Value. The previous code snippet can then be rewritten as follow (by replacing Values with ElementDataValue):

m.register("#tasks li", repeat(tasks), sub("img", attr("src", new ValueTransformer<Task, String>(new ElementDataValue<Task>()) {
    @Override
    protected String transform(Task s) {
        "/images/" + (s.type == Task.TaskType.BUG ? "circle_red.png" : s.type == Task.TaskType.ENHANCEMENT ? "circle_green.png" : "circle_blue.png");
    }
})));

sub can also be used to fill in the tasks’s title, description:

m.register("#tasks li", repeat(tasks),        
        sub("img", attr("src", new ValueTransformer<Task, String>(new ElementDataValue<Task>()) {
            @Override
            protected String transform(Task s) {
                return "/images/" + (s.type == Task.TaskType.BUG ? "circle_red.png" : s.type == Task.TaskType.ENHANCEMENT ? "circle_green.png" : "circle_blue.png");
            }
        })), sub("h2", text(new ValueTransformer<Task, String>(new ElementDataValue<Task>()) {
    @Override
    protected String transform(Task s) {
        return s.title;
    }
})), sub("p", text(new ValueTransformer<Task, String>(new ElementDataValue<Task>()) {
    @Override
    protected String transform(Task s) {
        return s.description;
    }
})));

For the title and description, the text moulder was used. As its name implies, this moulder takes a text (as a String or a Value) as an argument and sets its element’s textual content with it.

To handle the urgency, and since the urgency marker (the span) is present by default in the markup, we need to remove it for the non-urgent tasks. This can be achieved with the remove moulder, which takes a boolean as its argument to indicate whether the elements it processes are to be removed or not:

m.register("#tasks li", repeat(tasks),
        sub("span", remove(new ValueTransformer<Task, Boolean>(new ElementDataValue<Task>()) {
            @Override
            protected Boolean transform(Task s) {
                return !s.urgent;
            }
        })), 
    ...);

And finally, to handle the task status (open or closed), we need to add the closed class to the li’s of closed tasks, which can be achieved by the attr moulder:

m.register("#tasks li", repeat(tasks),
        attr("class", new ValueTransformer<Task, String>(new ElementDataValue<Task>()) {
            @Override
            protected String transform(Task s) {
                return s.status == Task.TaskStatus.CLOSED ? "closed" : null;
            }
        }),
        ...));

Here’s how the complete rendering code looks like:

List<Task> tasks = tasks();
MoulderShop m = new MoulderShop();

m.register("#tasks li", repeat(tasks),
        attr("class", new ValueTransformer<Task, String>(new ElementDataValue<Task>()) {
            @Override
            protected String transform(Task s) {
                return s.status == Task.TaskStatus.CLOSED ? "closed" : null;
            }
        }),
        sub("span", remove(new ValueTransformer<Task, Boolean>(new ElementDataValue<Task>()) {
            @Override
            protected Boolean transform(Task s) {
                return !s.urgent;
            }
        })),
        sub("img", attr("src", new ValueTransformer<Task, String>(new ElementDataValue<Task>()) {
            @Override
            protected String transform(Task s) {
                return "/images/" + (s.type == Task.TaskType.BUG ? "circle_red.png" : s.type == Task.TaskType.ENHANCEMENT ? "circle_green.png" : "circle_blue.png");
            }
        })), sub("h2", text(new ValueTransformer<Task, String>(new ElementDataValue<Task>()) {
    @Override
    protected String transform(Task s) {
        return s.title;
    }
})), sub("p", text(new ValueTransformer<Task, String>(new ElementDataValue<Task>()) {
    @Override
    protected String transform(Task s) {
        return s.description;
    }
})));

Document doc = m.process(getClass().getResourceAsStream("tasks.html"));
return doc.toString();

And here’s how the rendered result looks like:

result

Gosh but that was long !

I’ll admit it, the amount of code required to achieve this rather simple rendering is a lot, but :

  • It’s mostly Java’s fault: most of the code you see there is due to ValueTransformer. if there were lambdas in Java, a lot of lines would go away
  • This ‘a lot of code’ is Java code, can be written in your favorite IDE with autocompletion, syntax highlighting, amd on the fly error checking. It can be refactored, extracted in methods or other classes
  • This way of doing things is very robust against markup changes. It’s not immune, meaning that after changing the markup you may need to change the selectors or maybe even the moulders, but at least you won’t have to rewrite everything from scratch, or reinject the templating instructions in the new markup files in the correct places.

To prove the first point, here’s how the previous code looks like when written using the Scala version of Moulder:

val tasks = tasks();
val m = MoulderShop();

m.register("#tasks li", repeat(tasks),
  attr("class", tr(eData[Task](), (t: Task) => if (t.status == CLOSED) "closed" else "")),
  sub().register("span", remove(tr(eData[Task](), (t: Task) => !t.urgent))),
  sub().register("img", attr("src", tr(eData[Task](), (t: Task) => "/images/" + (t.typ match {
    case BUG => "circle_red.png"
    case ENHANCEMENT => "circle_green.png"
    case _ => "circle_blue.png"
  })))),
  sub().register("h2", text(tr(eData[Task](), (t: Task) => t.title))),
  sub().register("p", text(tr(eData[Task](), (t: Task) => t.description))))

val doc = m.process(getClass().getResourceAsStream("tasks.html"));
doc.toString()
About these ads

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: