Choosing a Templating System
by Perrin Harkins
|
Pages: 1, 2, 3, 4
Languages
Here's the big issue with templating systems. This is the one that always cranks up the flame on Web development mailing lists.
Some systems use in-line Perl statements. They may provide some extra
semantics, like Embperl's operators for specifying whether the code's
output should be displayed or Mason's <%init> sections for specifying
when the code gets run, but at the end of the day your templates are
written in Perl.
Other systems provide a specialized mini-language instead of (or in addition to) in-line Perl. These will typically have just enough syntax to handle variable substitution, conditionals and looping. HTML::Template and Template Toolkit are popular systems using this approach. AxKit straddles the fence, providing both a (not-so-) mini-language - XSLT - and an in-line Perl approach - XPathScript.
Here's how a typical discussion of the merits of these approaches might go:
IN-LINE: Mini-languages are stupid. I already know Perl and it's easy enough. Why would you want to use something different?
MINI-LANG: Because my HTML coder doesn't know Perl, and this is easier for him.
IN-LINE: Maybe he should learn some Perl. He'd get paid more.
MINI-LANG: Whatever. You just want to use in-line Perl so you can handle change requests by putting little hacks in the template instead of changing your modules. That's sloppy coding.
IN-LINE: That's efficient coding. I can knock out data editing screens in half the time it takes you, and then I can go back through, putting all the in-line code into modules and just have the templates call them.
MINI-LANG: You could, but you won't.
IN-LINE: Is it chilly up there in that ivory tower?
MINI-LANG: Go write some VBScript, weenie.
etc.
Most people pick a side in this war and stay there. If you are one of the few who hasn't decided yet, you should take a moment to think about who will be building and maintaining your templates, what skills those people have and what will allow them to work most efficiently.
Here's an example of a simple chunk of template using first an in-line style (Apache::ASP in this case) and then a mini-language style (Template Toolkit). This code fetches an object and displays some properties of it. The data structures used are identical in both examples. First Apache::ASP:
<% my $product = Product->load('sku' => 'bar1234'); %>
<% if ($product->isbn) { %>
It's a book!
<% } else { %>
It's NOT a book!
<% } %>
<% foreach my $item (@{$product->related}) { %>
You might also enjoy <% $item->name %>.
<% } %>
And now Template Toolkit:
[% USE product(sku=bar1234) %]
[% IF product.isbn %]
It's a book!
[% ELSE %]
It's NOT a book!
[% END %]
[% FOREACH item = product.related %]
You might also enjoy [% item.name %].
[% END %]
There is a third approach, based on parsing an HTML document into a DOM tree and then manipulating the contents of the nodes. The only module using this approach is HTML_Tree. The idea is similar to using a mini-language, but it doesn't require any non-standard HTML tags and it doesn't embed any logic about loops or conditionals in the template itself. This is nice because it means your templates are valid HTML documents that can be viewed in a browser and worked with in most standard HTML tools. It also means people working with the templates can put placeholder data in them for testing and it will simply be replaced when the template is used. This preview ability only breaks down when you need an if/else type construct in the template. In that situation, both the ``if'' and ``else'' chunks of HTML would show up when previewing.
Parsers and Caching
The parsers for these templating systems are implemented in one of three ways: They parse the template every time (``repeated parse''), they parse it and cache the resulting parse tree (``cached parse tree''), or they parse it, convert it to Perl code and compile it (``compiled'').
Systems that compile templates to Perl take advantage of Perl's
powerful run-time code evaluation capabilities. They examine the
template, generate a chunk of Perl code from it and eval the
generated code. After that, subsequent requests for the template can
be handled by running the compiled bytecode in memory. The complexity
of the parsing and code generation steps varies based on the number of
bells and whistles the system provides beyond straight in-line Perl
statements.
Compiling to Perl and then to Perl bytecode is slow on the first hit but provides excellent performance once the template has been compiled, since the template becomes a Perl subroutine call. This is the same approach used by systems like JSP (Java ServerPages). It is most effective in environments with a long-running Perl interpreter, like mod_perl.
HTML::Template, HTML_Tree, and the 2.0 beta release of Embperl all use
a cached parse tree approach. They parse templates into their
respective internal data structures and then keep the parsed structure
for each processed template in memory. This is similar to the
compiled Perl approach in terms of performance and memory
requirements, but does not actually involve Perl code generation and
thus doesn't require an eval step. Which way is faster: caching
the parse tree or compiling? It's hard to objectively measure, but
anecdotal evidence seems to support compilation. Template Toolkit
used a cached parse tree approach for version 1, but switched to a
compilation approach for version 2 after tests showed it to offer a
significant speed increase. However, as will be discussed later,
either approach is more than fast enough.
In contrast to this, a repeated parse approach may sound slow. However, it can be pretty fast if the tokens being parsed for are simple enough. Systems using this approach generally use simple tokens, which allows them to use fast and simple parsers.
Why would you ever use a system with this approach if compilation has better performance? Well, in an environment without a persistent Perl interpreter like vanilla CGI this can actually be faster than a compiled approach since the startup cost is lower. The caching of Perl bytecode done by compilation systems is useless when the Perl interpreter doesn't stick around for more than one request.
There are other reasons, too. Compiled Perl code takes up a lot of
memory. If you have many unique templates, they can add up fast.
Imagine how much RAM it would take up if each page that used
server-side includes (SSI) had to stay in memory after they had been
accessed. (Don't worry, the Apache::SSI module doesn't use
compilation so it doesn't have this problem.)
Application Frameworks vs. Just Templates
Some of the templating tools try to offer a comprehensive solution to the problems of Web development. Others offer just a templating solution and assume you will fit this together with other modules to build a complete system.
Some common features offered in the frameworks include:
All of the frameworks offer a way to map a URL to a template file. In addition to simple mappings similar to the handling of static documents, some offer ways to intercept all requests within a certain directory for pre-processing, or to create an object inheritance scheme out of the directory structure of a site.
Most interactive sites need to use some kind of session tracking to associate application state data with a user. Some tools make this simple by handling all the cookies or URL-munging for you and allowing you simply to read and write from an object or hash that contains the current user's session data. A common approach is to use the Apache::Session module for storage.
Caching is the key to good performance in many Web systems, and some of these tools provide user-controlled caching of output. This is one of the major features of both Mason and AxKit. AxKit can cache at the page level, while Mason also offers fine-grained caching of components within the page.
How will you live without CGI.pm to parse incoming form data? Many of these tools will do it for you, making it available in a convenient data structure. Some also validate form input, and even provide ``sticky'' form widgets that keep their selected values when re-displayed or set up default values based on data you provide.
Everyone knows how painful it can be to debug a CGI script. Templating systems can make it worse, by screwing up Perl's line numbers with generated code. To help fix the problem they've created, some offer built-in debugging support, including extra logging, or integration with the Perl debugger.
If you want to use a system that just does templates but you need some of these other features and don't feel like implementing them yourself, there are some tools on CPAN that provide a framework you can build upon. The libservlet distribution, which provides an interface similar to the Java servlet API, is independent of any particular templating system. Apache::PageKit and CGI::Application are other options in this vein, but both of these are currently tied to HTML::Template. OpenInteract is another framework, this time tied to Template Toolkit. All of these could be adapted for the ``just templates'' module of your choice with fairly minimal effort.

