Fork me on GitHub

How to process templates

A collection of best practices on rendering output with Snippetory templates.

It's a common and well-known practice in software design to do recurring in a similar fashion. This helps to read and understand the code fast and reliable. As Snippetory works a little bit different to other technologies, I think it might help to have some additional conventions.

Variable naming

When binding a business object to a template you have typically a sub-template for this business object. I.e. you have to deal with an object that holds the data and an object of type Template to sink the data provided. Thus the naming should express the relationship of those references and has to distinguish between the different tasks. I recommend to do this by appending 'Tpl' to the name of the business object.

productTpl.set("name", product.getName());

With name of the business object I refer to the real world name, not to the name of the field. In turn other suffixes are skipped, of course.

productTpl.set("name", productDto.getName());

On the other hand abbreviation rules are not affected.

prodTpl.set("name", prod.getName());

Where the relationship to another object doesn't apply or it's not necessary to express it `template` and 'out' nice options, too.

Method Names

There are three different patterns to be modeled by methods. These patterns are distinguished by the naming.

getTemplate
Acquires a template. Such a method abstracts the source of the template and initializes the template. To acquire a template it may need to have access to a TemplateContext. Typically such a method is used acquire a complete template rather than a sub-template of a template has already in access.
Those methods typically takes one or more arguments.
render...
This is the most common type of method. It binds some data to a template. This template is handed over as a method argument. After binding the data the template is rendered. As rendering ends the live cycle of a template, the method typically returns void. However, if necessary it may return some outcome of the rendering logic.
bind...
Sometimes it's not desired to render, but it's necessary to have the option to build a piece of template by several methods, that can be combined in different ways. Those methods are bind methods. They create a little bit more overhead for their users. In order to keep this overhead as small as possible, they return the template. This allows chained calls.

  public Template bindProductLabels(Template out, ResourceBundle msg) {
    out.set("name-label", msg.getString("product.name");
    out.set("desc-label", msg.getString("product.description");
    out.set("price-label", msg.getString("product.price");
    return out;
  }
  
  public Template bindProduct(Temllate prodTpl, Product prod, User user) {
    prodTpl.set("name", prod.getName()).set("desc", prod.getDescription());
    Price p = PriceFactory.getInstance().calculate(prod, user);
    // Price is rendered using a custom format. Template might look like this:
    // <td>{v:price price="amount"}</td><td>{v:price price="currency"}</td>
    prodTpl.set("price", p); 
    return prodTpl;
  }

Template instanciation

Snippetory provides a number of convenient ways to create templates. However, the landscape changed over time. Starting with 0.9.4 I redesigned the API to be clearer structured and more configurable. This creates the need to remove some of those old methods. I already removed some of the more exotic methods, that are simple to replace with remaining ones. Meanwhile I decided to deprecate entire org.jproggy.snippetory.Repo object. In fact the only ways to instantiate templates are now:
Syntaxes.xx.parse
As a syntax is mandatory for parsing and a default syntax leads to problems while evolving, all methods depending on the default syntax will be skipped in future. Hence this is the only method for ad hoc parsing of strings, that makes sense.
TemplateContext.getTemplate(String uri)
The TemplateContext is perfect for maintaining your own standards and configurations. It can be injected, extended, and handled in any way. Building up your own Repo is simple and will definitely better fit your needs. In fact the TemplateContext is the best way for everything, that's too complicated for Syntaxes.parse.

Handling the TemplateContext

The TemplateContext is desinged to be lightweight and thus having a low creation overhead. And it`s mutable. This makes caching difficult and not too efficient. The problem with re-use of an instance in a multithreaded environment is, that an instance might be accessable for multiple threads at the same time. And if one of these threads alters the TemplateContext the state gets unpredictable. Thus it`s a bad idea to configure a TemplateContext as a singleton object in your injection environment.

When using methods to provide a TemplateContext those should create a new instance per call, töo. However, it's ok to an instance of TemplateContext for several Template creation one afer anothe.

Bernd Ebertz

Head, Founder and chief technology evangelist of
jproggy.org
Java, and all Java-based marks are trademarks or registered trademarks of Oracle.
This site is not affiliated in any way with Oracle.