Copyright Notice
This text is copyright by InfoStrada Communications, Inc., and is used with their permission. Further distribution or use is not permitted.This text has appeared in an edited form in Linux Magazine magazine. However, the version you are reading here is as the author originally submitted the article for publication, not after their editors applied their creativity.
Please read all the information in the table of contents before using this article.
Linux Magazine Column 61 (Jul 2004)
[suggested title: ``Introduction to Template Toolkit (part 2)'']
In the previous column, I introduced my templating system of choice, Template Toolkit. Continuing from where I left off, let's look at some of the other features of the Template Toolkit (abbreviated to TT because I'm lazy).
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:
[% INCLUDE someblock FILTER html %]
For convenience, FILTER
may be spelled as a single |
character:
[% INCLUDE someblock | html %]
Some standard filters
The format
filter provides processing line-by-line through
a printf
string:
[% FILTER format('<!-- %-40s -->') %] some text 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: handy for 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. As will this. [% END %]
The truncate
filter reduces a string to a specified length. If the
string exceeds the length, the string is truncated, and the last three
characters changed to three dots to indicate truncation.
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
:
<a href="[% filename | uri | html_entity %]"> [% filename | html_entity %]</a>
The repeat
filter makes copies of the source material. remove
takes a Perl regular expression, deleting 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. I use this for my website
error message:
[% "template error $error on page $url\n" | stderr; %]
The USE directive
The USE
directive brings in a plugin that defines objects against
which method calls are triggered to perform actions and return values.
Like filters, TT comes with many pre-defined plugins, and it's easy to
add your own. Most standard CPAN libraries can be wrapped with a very
thin adaptor to become a TT plugin, simply returning the object back
into TT for method calls. For example, the CGI
plugin 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 plugin is the Date
plugin:
[% 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 favorites is the Table
plugin, 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 plugins include: Autoformat
, Datafile
, DBI
,
Directory
, Dumper
, File
, Filter
, Format
, GD::*
,
HTML
, Iterator
, Pod
, String
, URL
, Wrap
, XML::RSS
,
XML::Simple
, XML::Style
, and XML::XPath
, providing a wide
array of data access and manipulation. Still others are found in the
CPAN as well.
The PERL and RAWPERL directives
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 probably be
migrated into a plugin or filter for better maintenance.
Exception handling
TT has reasonable exception handling, with a hierarchical class of
errors and a catch/throw syntax. Even die
exceptions from Perl
code within plugins or filters can be caught.
The META directive
The META
directive provides meta-data 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, whether a file is an
index page or a detail page.
The TAGS directive
The default [%
and %]
tags marking the beginning and ending
of embedded directives can be changed somewhat arbitrarily. For
example, to use an HTML style:
[% TAGS html %] <!-- INCLUDE somefile -->
The directive remains in effect until changed back.
Variables
TT variable names are Perl-like: 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 day, both being accessed with the same notation:
result = something.thiskey;
Even lvalue method calls are permitted: the value to be assigned is passed as an argument to the method, so this also works:
something.thiskey = new_value;
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 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 underscore or dot are considered private, and may not be accessed by any means.
Literal values
As seen in examples, the literal values are similar to Perl, although they may be simplified when unambiguous. For example, hashes can be written either as traditional Perl hashref notations:
d = { Fred => 'Flintstone', Barney => 'Rubble' }; d.Fred; # Flintstone
or as simplified key/value list assignments:
d = { fred = 'Flintstone' Barney = 'Rubble' };
Arrays get square brackets, like Perl arrayrefs:
n = [ value1, value2, value3 ]; n.2; # whaver was in value3
and again can be simplified. Dot-range notations are available.
Scoping
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; %]
which results greeting
being set to:
My name is Wall... Larry Wall.
Operations on hash data
The hash vmethods include keys
, values
, each
, defined
,
exists
, and item
, performing very Perl-like operations on
hashes.
Operations on list data
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!