Discussions

News: Frank Sommers: What Do You Look For in a Template Engine?

  1. Frank Sommers of Artima has started a discussion about template engines and asks what you find important in them. Template engines seem to be one of the most stagnant technologies in Java, many adopt the design that mixes content and logic but implement it differently (PHP, JSP, Velocity, Freemarker, ...). It's a good thing that Terrence Parr (of ANTLR fame) created StringTemplate which seems to move in a similar direction as what we've been doing with our template engine in RIFE. He acknowledges the push model that injects values and text into a template instance instead of pulling them in with an expression language. While I prefer our approach where there really is no logic in the template at all, I really appreciates what Terrence says in his docs:
    Language theory supports my premise that even a minimal StringTemplate engine with only these features is very powerful--such an engine can generate the context-free languages (see Enforcing Strict Model-View Separation in Template Engines); e.g., most programming languages are context-free as are any XML pages whose form can be expressed with a DTD.
    This goes back to a less-is-more philosophy where you build what is needed to comfortably use a technology in trivial and advanced situations, and nothing more. RIFE's template engine does the same. Instead of including a whole collection of additional features, we rely on you making a mental shift to adapt your development habits towards the new capabilities and characteristics of our template engine. In my case its not language theory, but rather lots of very complex HTML layouts and other uses of our template engine that gets me to say that our template engine is powerful enough to allow you to comfortably build anything you want, without compromising on context separation and reusability. Have you ever tried out another template engine besides the classic pull model in anger? If so, what did you think of it? If not, do you think the classic pull model is enough?
  2. Logic-less templates are a good thing. And like Parr says, enforcement it much better than hoping on good behavior. I would prefer RIFE's aproach over StringTemplate, as the latter really only solves a tiny problem when you build web applications, whereas RIFE offers a complete solution. State management being an important factor in that imho.
  3. HTML Tables are the problem[ Go to top ]

    Logic-less/loop-less templating langs/expressions would be nice in theory, but business apps are about data. MVC really means in business: Database - Data Routing/Transportation - Data presentation Nothing revolutionary about that. Data presentation occurs in HTML tables. And tables require looping mechanisms. Dependent presentation always needs if/then/else control flow statements to preferentially display different fields for different items. control flow and looping is 95-100% of the way of a Turing Complete Language (I'm rusty on my Theory of Computation, sue me). In the early pre-struts days (2000 or so) I had a web framework with templates using simple key substitution. (Keys are substituted by a hashmap of strings - $key in the template was replaced by what was the key's value in the hashmap) It was logic-less templates. Of course, I had to generate the HTML of the tables in other places, which was ultimately uglier and more inconvenient. The display/structure/column layout of the tables (presentation data) then bled into other parts where the data was being accumulated to substitute into the template. Active, near-turing-complete template languages are just necessary folks. (For the unacademized, "Turing Complete" refers to a class of programming languages that can compute the known set of solveable problems by a Turing Machine i.e. the modern computer. Fundamentally C++ == Ruby == Assembly == Java == Visual Basic == XSLT, all can be used to technically solve the same set of mathematical problems. Practically speaking though, obviously there are speed differences. Assembly is closest to hardware optimized, then compiled languages such as C++, then bytecode compiled such as Java, then purely intepreted like Ruby, although bytecode compiliation, optimizing JIT runtime interpreters, and cached evaluations all muck with this rule)
  4. Logic-less/loop-less templating langs/expressions would be nice in theory, but business apps are about data.
    I'm not sure whether I can follow you. But what I mean with logic less templates looks something like this:

    1/1/2004
    Comment text goes here.

    or, as a table" 1/1/2004
    Comment text goes here. the Java code could be: add(new PropertyListView("comments", commentList) { public void populateItem(final ListItem listItem) { listItem.add(new Label("date")); listItem.add(new MultiLineLabel("text")); } });
  5. For your table example, I assume the data inside the is a macro-expansion/per-row formatting rule. To render a list of data in that fashion is an implicit foreach. Your template is still looping. What if: The list is a heterogeneous list (not ideal but common in the real world of integrated apps). That would require branching control logic. That always happens. But I think I see what you are going for. Since it is essentially a macro expansion, the logic of the loop is greatly restricted, which helps separation. However, JSP's JSTL and EL did the same thing, and I frequently ran into situations where the looping facility was inadequate. I haven't done JSPs in about three years so I don't remember them. Syntax that looks elegant like that unfortunately is only minimally flexible for the real world of complicated, ugly stuff. All I ever wanted from template langs was a much more concise, generic data access language for: Maps, Lists, Sets, Collections, Arrays, and Beans. Since Maps == Beans and Arrays == Lists from a data access perspective, I wanted the syntax the same. Aside from that, basic looping and conditionals. Velocity was pretty good as I recall, but I haven't used it in a while.
  6. For your table example, I assume the data inside the is a macro-expansion/per-row formatting rule.
    I don't know if I would call it that. The framework pics up that piece of markup and repeats it for a number of times.
    To render a list of data in that fashion is an implicit foreach. Your template is still looping.
    That's the end-result yes. But there is no way you can just add a new loop to that page out-of-the-blue. There has to be a Java component counterpart that directs the dynamic behavior. So in that sense, there is a strict separation of presentation and logic.


    What if:

    The list is a heterogeneous list (not ideal but common in the real world of integrated apps). That would require branching control logic. That always happens.
    Sure, that happens all the time. add(new PropertyListView("foolist", listOfFoo) { public void populateItem(final ListItem listItem) { final Foo foo = (Foo)listItem.getModelObject(); if (foo.bar()) { listItem.add(new BarPanel("row", foo)); } else { listItem.add(new OtherPanel("row", foo)); } } }); Something like that would be one option of handling such a case. If you use Wicket, you'd typically use panels when you don't want to be bound by the exact markup structure upfront. So foolist would look like: sample content


    But I think I see what you are going for. Since it is essentially a macro expansion, the logic of the loop is greatly restricted, which helps separation.

    However, JSP's JSTL and EL did the same thing, and I frequently ran into situations where the looping facility was inadequate. I haven't done JSPs in about three years so I don't remember them.

    Syntax that looks elegant like that unfortunately is only minimally flexible for the real world of complicated, ugly stuff.
    There really are no such limitations with Wicket. Some things will be easier to achieve then others, but you can implement any complex scenario you can imagine.
    All I ever wanted from template langs was a much more concise, generic data access language for: Maps, Lists, Sets, Collections, Arrays, and Beans.

    Since Maps == Beans and Arrays == Lists from a data access perspective, I wanted the syntax the same. Aside from that, basic looping and conditionals. Velocity was pretty good as I recall, but I haven't used it in a while.
    I still like Velocity. It's easy to set up and use and just works.
  7. Hi Carl, logic-less templates don't mean that there's no logic operating on the templates. It means that the templates don't have their own execution model, but are only contain plain old textual content that is augmented with some semantic meaning. An external language can then operate on or with the template, which is merely a collection of textual snippets and placeholders, and use it to create the final result. Using RIFE's templates, building a table is easy, for instance rows (people.html): <!--V people/--> <!--B person--> <!--V firstname/--> <!--V lastname/--> <!--/B--> then, given a Person bean class and a getPeople() method in Java: Template t = TemplateFactory.HTML.get("people"); for (Person person : getPeople()) { t.setBean(person); // fills in the columns from the bean properties t.appendBlock("people", "person"); } String result = t.getContent(); As you see, you can create any complex layout this way, it's just a matter of identifying content block and value placeholders. You can fully benefit of what the Java language has to offer you, and you can easily change templates and maintain them. Best regards, Geert
  8. Re: HTML Tables are the problem[ Go to top ]

    Just continuing a bit, to try to demonstrate one part of the usefulness of this approach. I can re-use this logic and create another template to for example send out an email with the list of people (people.txt): These are the people on the list: <!V people/> <!B person>* <!V firstname/> <!V lastname/> <!/B> or have a RESTful service that provides the list of people over the web as XML (people.xml): <!--V people/--> <!--B person--> <!--V firstname/--> <!--V lastname/--> <!--/B--> The only thing that changes in the Java code, is that instead of specifically obtaining a template of particular type and name, you can for instance use IoC to inject it into your code.