Building the ideal web application template engine


Building the ideal web application template engine

A month and a half ago I had put out a call for what I was calling the ‘ideal web application template engine’ along with a list of requirements that I thought would be present in such a system. Since then I looked a bunch of them and decided that I like the simple markup that mustache defined but none of the implementations were up to doing what I wanted. This led me to embark on building a new engine with for that markup for my chosen platform, Java, which I called creatively, mustache.java. Though it claims to be ‘logic-less’ I would say that it has some amount of logic. It will loop over a set of objects and it will check booleans, but I think it is about as close to logic-less as you would want to be in a template language. So, let’s look at each of the requirements and how I ended up implementing them:

  • Works well with HTML5/CSS3 progressive enhancement

This one is straight-forward. Because I put no limitations on the generated text you can actually generate almost anything with mustach.java, in fact, I am using it in another project to generate Java source for Avro objects. It is really overkill for that project but it is so easy to use that I used it anyway.

  • Allows mock data within the template that is replaced at runtime
  • Client-side version that leverages the mock data for shift-reload debugging

These I implemented somewhat differently from the requirement because the mustache template language is not tag based. I have actually thought about whether it might make sense to make it tag-based and get the full requirement, but I digress. What I did get to is a system that is easy for a frontend developer or designer to use without having the whole system running on their machine. Included with mustache.java is a mini-server (called handlebar, thanks for the name Luke) that combines mock json data with mustache.java templates. This lets them view a fully rendered version of the page they are working on with the required templating information that will be used in production but without running a big production system. In fact, because mustache.java dynamically generates and compiles Java code, they can fire up handlebar in directory along with their mock data and just keep reloading the page to see the effect of changes they make.

  • Composable components, not monolithic pages
  • Very little or no business logic in the templates

The choice of mustache as a base template language ensured that this would be the case. Including a partial within a template is easy and makes pages very easy to compose. I still miss the Django template style extends/block system and may extend mustache to support it. Having to remember to include things like the header and footer on every page doesn’t seem necessary. As I said above, I think that mustache has the minimum reasonable logic.

  • Concurrent evaluation possible

This one was very important to me from a performance perspective. My implementation makes every effort to be as low latency as possible for the end user. At every possible point in template evaluation things are actually executed in separate threads or purely asynchronously in the case of external I/O like HTTP requests to other services. The one last thing I am going to do here is to allow not only a straight-line evaluation of the template but the possibility of specifying a timeout so that you can return different markup if evaluation doesn’t return in time. It may be unnecessary to have this in the base template engine though, still looking at the design of the feature.

So how easy is using the template system? I think I can answer that question by looking at the relevant portions of the implementation of handlebar:

final MustacheCompiler mc = new MustacheCompiler(new File("."));
....
Mustache mustache = mc.compile(new BufferedReader(new FileReader(filename)));
FutureWriter fw = new FutureWriter(res.getWriter());
File file = new File(mocks, base + ".json");
if (file.exists()) {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
JsonParser parser = jf.createJsonParser(bis);
JsonNode json = parser.readValueAsTree();
mustache.execute(fw, new Scope(json));
} else {
mustache.execute(fw, new Scope());
}
fw.flush();

The most interesting part of this is the FutureWriter. The future writer lets mustache write evaluated templates completed out of order in template order. The scope is essentially a sophisticated context object that knows how to pull data from Java objects of various types, including raw object fields and methods, Maps and Json objects. Conveniently, you can also store values in it directly. There are some things I would like to extend at some point, however, for all practical purposes the template system is finished.