dcsimg

The Template Toolkit, Part Two

In the previous "Perl of Wisdom," I introduced my templating system of choice, the aptly-named Template Toolkit (TT). Continuing from where I left off, let's look at some of TT's other features.

In the previous “Perl of Wisdom,” I introduced my templating system of choice, the aptly-named Template Toolkit (TT). Continuing from where I left off, let’s look at some of TT’s other features.

The FILTER directive

FILTER processes a block through a filter, returning the result. Many standard filters are provided, but you can easily create additional filters with Perl code.

For example, the html filter provides rudimentary HTML entity escaping:


[% FILTER html %]
Now I can use < and >
[% END %]

Like many other directives, a trailing form is also available, to take the output of some operation and filter it, as in:


[% INCLUDE someblock FILTER html %]

TT comes with many standard filters. The format filter provides processing line-by-line through a printf string:


[% FILTER format('<!-- %-40s -->') %]
some more text
[% END %]

Some other filters include upper (uppercase), lower (lowercase), and ucfirst and lcfirst (like their Perl equivalents).

Some filters operate on whitespace.

The trim filter removes all leading and trailing whitespace. The collapse filter replaces all embedded whitespace with a single space, which is handy to shrink random HTML to reduce transfer time. The indent filter adds extra whitespace to the left of each line, taking a parameter for the number of spaces to indent:


[% FILTER indent(8) %]
This text will be indented.
[% END %]

Other web-related filters include html (fixing up the minimum characters); html_entity (fixing up all of Latin-1); html_para (turns blank lines into paragraph breaks); and html_break (turns blank lines into br tags).

The uri filter adds the correct URI-escaping to a string. So, to create a link to a filename that may be arbitrarily nasty, we can simply combine uri and html_entity like so:


<a href=”[% filename | uri | html_entity %]“>
[% filename | html_entity %]</a>

For convenience, FILTER may be spelled as a single | (“pipe”).

remove takes a Perl regular expression and deletes the matching occurrences. replace takes a regular expression and replacement string, but, alas, cannot use references such as $1 and $2 in the replacement.

Some filters alter the output destination of the text. For example, the null filter simply discards the output. And stdout and stderr force the respective filehandles.

For example, I use this for my web site error message:


[% "template error $error on page $url\n" | stderr; %]

The USE directive

The USE directive brings in a plug-in that defines objects that provide method calls to perform actions and return values. Like filters, TT comes with many pre-defined plug-ins, and it’s easy to add your own.

Most standard CPAN libraries can be wrapped with a very thin adaptor to become a TT plug-in, simply returning the object back into TT for method calls.

For example, the CGI plug-in is a thin layer over CGI.pm:


[% USE q = CGI %]

Any parameters to this USE are passed as parameters to the new method of the class. We can save a specific instance if we choose (here as q), or use the default object:


[% USE CGI; # default object is now simply CGI
name = CGI.param('param_name');
CGI.start_form;
CGI.popup_menu(
Name => 'color',
Values => [ 'Green', 'Brown' ]
);
CGI.submit;
CGI.end_form;
%]

Another simple plug-in is the Date plug-in:


[% USE date %]
The time is now [% date.format %].
This file was last modified
[% date.format(template.modtime) %]

The last line uses the meta-information about the template, formatting it appropriately.

One of my favorite plug-ins is Table, which takes a list of values and breaks it into given-sized chunks, a simple but common task in paging the output of an operation:


[% USE table(list, rows=n, cols=n,
overlap=n, pad=0) %]
[% USE table(['a'..'z'], cols=3, pad=0);
FOREACH g = table.cols %]
[ [% g.first %] – [% g.last %]
([% g.size %] letters) ]
[% END %]

which generates:


[ a - i ( 9 letters ) ]
[ j - r ( 9 letters ) ]
[ s - z (8 letters) ]

Other plug-ins include Autoformat, Datafile, DBI, Directory, Dumper, File, Format, GD::*, HTML, Iterator, Pod, String, URL, XML::RSS, XML::Simple, and XML::XPath, providing a wide array of data access and manipulation. Still others can be found in the CPAN.

The PERL and RAWPERL directives (normally disabled) allow direct embedding of Perl code into the translated compiled text. This is handy for quick projects or testing, but such code should be migrated into a plug-in or filter for better maintenance.

The META directive

The META directive provides metadata for a template:


[% META thingy = 'this' %]

This value is available to a wrapper template (explained later) as template.thingy and can be used to define additional information about the remainder of the file. For example, metadata could specify whether a file is an index page or a detail page.

Exception Handling and Variables

TT has reasonable exception handling, with a hierarchical class of errors and a catch/throw syntax. Even die exceptions from Perl code within plug-ins or filters can be caught.

TT variable names are Perl-like, using alphanumerics (including underscore), usually lowercase, because some uppercase names are reserved. A TT variable is like a scalar in Perl, holding a scalar value, or a reference to a complex type.

Unlike Perl, a single dot provides array and hash element access, as well as method calls:


listy.3; # fourth item of a list (0-based)
hashy.foo; # the “foo” item of hashy
q.self_url; # calling $q->self_url

This provides great flexibility and easy of use: a value could be a hash one day, and an object that lazily computes the hash element the next, both being accessed with the same notation.

Even lvalue method calls are permitted: the value to be assigned is passed as an argument to the method, so something.thiskey = new_value also works, either turning in to the equivalent of $something->{thiskey} = $new_value or $something->thiskey($new_value), depending on whether $something is a hash or an object. To pass additional parameters to a method call, or to call a coderef, parentheses are used:


something.thiskey(a, b, c)

Named parameters are supported as a hashref distinguished from the remaining positional args. Keys in hashes that start with an underscore or dot are considered private, and may not be accessed by any means.

Literal Values and Scoping

As seen in examples, literal values are similar to Perl’s, although they may be simplified when unambiguous. For example, hashes can be written either as traditional Perl hashref notations…


d = { Who => ‘First’, What => ‘Second’ };
d.Who; # First

… or as simplified key/value list assignments:


d = { Who = ‘First’ What = ‘Second’ };

Arrays get square brackets, like Perl arrayrefs…


n = [ value1, value2, value3 ];
n.2; # whatever was in value3

… and again can be simplified. Dot-range notations are available.

The INCLUDE and WRAPPER directives create a new local scope. Top-level variables that are changed are restored on exit from the INCLUDEd block or file. However, secondary values within unchanged variables are not changed. A standard global hash is provided to permit shared data across the entire set of templates, although you can create additional values if you choose.

The PROCESS directive does not create a local scope, so you must be careful not to clobber the caller’s variables. Typically, PROCESS is used merely for things which are intentionally setting the values anyway, like configuration constants.

Operations on Scalar Data

A variable holding scalar data can have methods applied using the trailing-dot notation.

For example, foo.length returns the length of the string in foo. These are called scalar vmethods in the TT documentation, and also include such things as defined, repeat, replace, split, and chunk.

One cool scalar vmethod is match:


[% name = 'Larry Wall';
greeting =
IF (matches = name.match('^(\w+)
(\w+)$'));
THEN;
"My name is "; matches.1; "... ";
matches.0; " "; matches.1; ".";
ELSE;
"My name is $name."; # nothing fancy here
END;
%]

This code results in greeting being set to My name is Wall… Larry Wall.

Operations on Hash and List Data

The hash vmethods include keys, values, each, defined, exists, and item, and perform very Perl-like operations on hashes.

Similarly, list vmethods provide traditional Perl-like operations on arrays, such as reverse, join, sort (and numeric sorting with nsort), unshift, push, shift, pop, slice, and splice. Additionally, first and last are provided for convenience, as well as unique and merge for complicated list operations.

What’s Next?

Well, I’m running out of room again, so next time I’ll cover how to use TT from Perl, including how to configure the templating engine in dozens of ways, and how to use the command-line tpage and ttree tools. I’ll also look at Apache::Template and many of the other mod_perl frameworks that use TT2, such as OpenInteract and Bricolage.

Until next time, enjoy!



Randal Schwartz is the chief Perl guru at Stonehenge Consulting and the author of many books on Perl. He can be reached at merlyn@stonehenge.com.

Comments are closed.