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!


Randal L. Schwartz is a renowned expert on the Perl programming language (the lifeblood of the Internet), having contributed to a dozen top-selling books on the subject, and over 200 magazine articles. Schwartz runs a Perl training and consulting company (Stonehenge Consulting Services, Inc of Portland, Oregon), and is a highly sought-after speaker for his masterful stage combination of technical skill, comedic timing, and crowd rapport. And he's a pretty good Karaoke singer, winning contests regularly.

Schwartz can be reached for comment at merlyn@stonehenge.com or +1 503 777-0095, and welcomes questions on Perl and other related topics.