The Template Toolkit, Part Three

In the previous two columns, I introduced my templating system of choice, the Template Toolkit. Continuing from where I left off, let's look at some of the other features of the Template Toolkit (TT), including how to configure TT and use it from Perl, from the command line, and embedded in Apache.

In the previous two columns, I introduced my templating system of choice, the Template Toolkit. Continuing from where I left off, let’s look at some of the other features of the Template Toolkit (TT), including how to configure TT and use it from Perl, from the command line, and embedded in Apache.

Using the Template Toolkit from Perl

The simplest TT code looks like this:

use Template;
my $template = Template-new(\%CONFIG);

This sets up a $template object using the configuration parameters in %CONFIG (described later), and then executes $filename as a template, passing the pre-defined variables of %VARS, sending the result to standard output. $filename might be found along a search path (see INCLUDE_PATH later), or might be a filehandle. For example, my favorite trick is using DATA, as in:

use Template;
\*DATA, # process DATA
{ a = 1, b = 2, # passing these constants
argv = \@ARGV, # and the arg list
env = \%ENV, # and the environment
a is [% a %] and my home is [% env.HOME %].
[% FOREACH arg = argv;
"The command line args are: " IF loop.first;
arg; ", " UNLESS loop.last;
".\n" IF loop.last;

Rather than sending the output to stdout, I can capture the output into another variable:

my $output;
$template-process($filename, \%VARS, \$output);

I can also provide the template directly as a scalar reference:

my $input = “a is [% a %]“;
$template-process(\$input, \%VARS, \$output);

Configuration Parameters

One of the key things that puts the toolkit in Template Toolkit is its flexibility. In particular, TT has dozens of configuration parameters to tweak its operation to fit nearly any need. I’ll try to hit the ones I use the most often here.

Template filenames are located along an INCLUDE_PATH. This path is defined like a search path in the shell, as a colon-separated list of directories.

For example, an INCLUDE_PATH of /home/merlyn/lib/tt2:/usr/ local/tt2 looks for templates in /home/merlyn/lib/tt2 and /usr/ local/tt2, in that order.

A template can also be specified using an absolute path if the ABSOLUTE configuration parameter is true (it’s false by default), or upwardly relative to the INCLUDE_PATH directories if RELATIVE is true (also false by default). The default INCLUDE _PATH is only the current directory.

The PRE_PROCESS and POST_PROCESS configuration directives provide the names of one or more templates that are always processed before and after the requested template, respectively. These can be used to set variables and define blocks accessible to each template, or to provide consistent header and footer text (such as for a web site). For example, in this code…

my %CONFIG = (
PRE_PROCESS = [qw(config header)],
POST_PROCESS = ‘footer’,
my $tt = Template-new(\%CONFIG);
$tt-process($_) for @ARGV; # process
command-line arguments

… the files config (presumably configuration parameters) and header (probably header text) are processed before each file, and footer is processed afterwards.

Another method to achieve similar results is to specify a PROCESS configuration that can be “in charge” instead of the original template. The original template object is available in the template variable…

my $tt = Template-new({PROCESS = ‘wrapper’});
$tt-process($_) for @ARGV;

… and in wrapper:

[% PROCESS config; PROCESS header; -%]
[% PROCESS $template -%]
[% PROCESS footer; %]

This achieves the same results as the previous configuration, but I can now shuffle the order a bit:

[% PROCESS config; content = PROCESS $template; -%]
[% PROCESS header; content; PROCESS footer; -%]

The template can now alter some of the configuration parameters to provide specific overrides before the header is generated. It’s a very flexible approach, and I use this strategy frequently.

TT translates templates into Perl code, then compiles and runs that code. Compiled Perl code can be cached in memory as subroutines. By default, all Perl subroutines are cached, but you can limit this (for a mod_perl environment, for example) with CACHE_SIZE.

In a multi-process environment such as mod_perl, a much more important cache is the disk cache, defined by COMPILE_DIR. As each template is translated to Perl code, the resulting Perl code is saved to disk and made available for other processes to skip the translation step.

Since most templates change relatively infrequently compared to the number of times they’re invoked, this is a big savings. You’ll need to clean this directory out from time to time though, because TT itself won’t delete anything.

Locally defined plug-ins for USE directives are simply Perl modules located along the Perl @INC path, prefixed by Template::Plugin:: by default. You can add additional prefixes with PLUGIN_BASE.

For example, my local plug-ins are defined as Stonehenge: :Template::Plugin::name, using a configuration directive of PLUGIN_BASE=’Stonehenge::Template::Plugin’.

The default setting of Template::Plugin is always added to the end of the list. In this case, USE MyThing will search all of Perl’s @INC directories twice: first for Stonehenge::Template::Plugin::MyThing and then for Template::Plugin: :MyThing.

As an added option, LOAD_PERL can be set to true (default false), which causes USE MyPlugin to further search for a simple MyPlugin class in the @INC path, sending it a new method to instantiate an object. This gives a simple means to access most traditional Perl objects without writing additional code.

Similarly, additional filters can be created using the FILTERS configuration parameter. Rather than specify a path to a Perl module defining the filter, the FILTERS configuration defines the filters directly as coderefs.

For example, defining a filter that lowercases everything (if it didn’t already exist as the lower filter) is as simple as:

to_lowercase = sub { return lc shift },

And now in TT code I can say:

[% FILTER to_lowercase %] Some Stuff Here
[% END %]

There are many other configuration parameters that I don’t have space to list here, so check out the Template man page for a quick overview. In general, if you need to configure TT, there’s probably already a knob to twiddle somewhere.

Using TT Command-line tools

The TT distribution comes with two command-line tools. The tpage tool invokes TT on a source file and produces output to stdout. The ttree tool is like make for a tree of inputs and outputs, and can be used to manage complete projects (like a web site or documentation package) with minimal specification.

tpage is simple to use:

% tpage input_file output_file

You can specify variable definitions with additional command-line operations.

% tpage –define style=green \
–define margin=3 \
input_file output_file

In this case, the variables style and margin are assigned their corresponding values.

I find tpage is a great way to quickly test things from the command line. Because tpage defaults to stdin, I can simply type tpage and a few lines of TT code, and then hit CONTROL-D to see how it will be processed.

In stark contrast to the sheer simplicity of tpage, the ttree utility is a full-blown application that can render hundreds of template files into their respective outputs, managing dependencies to minimize the amount of work performed (similar to make). The synopsis line of its man page is bit deceptive:

% ttree [options] [files]

First, ttree reads .ttreerc from your home directory (overridden by the TTREERC environment variable). Then, any additional arguments configuration files specified by -f in the options are also read. These configuration directives drive the rest of the process, as Makefile does for make.

For example, the configuration directives might say:

src = /my/website/src
dest = /my/website/htdocs
lib = /my/website/tt-lib
ignore = \b(CVS|RCS)\b
ignore = ^#
ignore = ~$
copy = \.(gif|png|jpg|pdf)$
pre_process = config header
post_process = footer

With just these minimal directives, every file below the src directory is copied or processed into the same relative location below the dest directory, unless it matches the ignore patterns. Files that match copy are copied literally, but any other files are processed through TT, using the given pre_process and post_process templates, and an include path specified in lib.

Dependency checking is automatic. On the first invocation, the entire tree is processed, but subsequent invocations perform the minimal work needed. Files are never deleted, however, so you might need to completely blast the output directory and start over occasionally.

As with the rest of TT, the ttree tool has a zillion configuration directives. See ttree –help for all the gory details.

Using TT from Apache::Template

The Apache::Template module (found in the CPAN, but distributed separately from the TT distribution) permits mod_ perl to process TT source files on each request to generate dynamic content, similar to the way Apache::Registry processes pseudo-CGI files for dynamic content.

Like Apache::Registry, the URL maps into a filename that contains a template. If you specify a COMPILE_DIR, this template is translated into Perl code that’s cached on disk, and the resulting Perl compilation is also cached in memory (subject to CACHE_SIZE). Thus, after the first hit to a given page, mod_ perl is merely invoking an in-memory subroutine to return the content for the page, and is thus quite fast. Also like Apache: :Registry, the modification time of the source file is noted, triggering a recompile if needed when the source changes.

Because the results are dynamic, a given page can provide changing content, including respond to parameter data resulting from form submission. Thus, a page can act like a traditional static page, or like a CGI script, without needing the “CGI” pages to be in separate directory.

Configuration for Apache::Template is similar to ttree, in that a series of command directives map into the configuration options given earlier. The httpd.conf file will typically have something like:

PerlModule Apache::Template
TT2PostChomp On
TT2IncludePath /usr/local/tt2/templates
TT2CompileDir /var/tt2/cache
TT2PreProcess config header
TT2PostProcess footer
<Location /tt2>
SetHandler perl-script
PerlHandler Apache::Template

The PerlModule and PerlHandler directives are mod_perl directives. The first line also enables Apache::Template-specific directives, all beginning with TT2 here. The last few lines specify that all URLs beginning with /tt2 are to be processed by Apache: :Template. Be careful that you don’t accidentally try to process your images and other text data through TT, though!

I should note that as of this writing, Apache::Template is not yet mod_perl 2.0 compatible, and therefore not Apache2 compatible, but I’m still running Apache 1.x in production on a number of sites, so this is not yet a problem.

Web Widget Libraries

As a head start for basic web design, TT comes with templates designed to generate basic well-formed HTML without typing a lot of less-thans and greater-thans. By installing the HTML library and including it in your INCLUDE_PATH, you can generate an entire web page with proper headers and footers as:

[% WRAPPER html/page %] content here
[% END %]

The Splash! library extends on this by adding colorful web widgets (like buttons and bars and menus) to provide us programmers with at least a little splash to our webpages. It’s no substitute for a true web designer’s input, but, hey, it works.

Powerful Higher Layers

TT is also an important component in some larger applications available on the CPAN (and elsewhere).

The Slashcode engine is the guts behind the famous Slashdot site, as well as hundreds of other discussion areas, like http://use.perl.org. The Slashcode engine is built using TT as well, pulling templates from DBI-based data instead of disk files. Like OpenInteract, Slashcode provides common widgets for login boxes and discussions, and other reusable shapes.

The Bricolage package is a large-scale professional content management system that tracks work flow from writer to reviewers, editors, and publishers. Although Mason is used for the actual Bricolage interface, components can be written using TT for presentation and delivery.

TT is AOK!

Template Toolkit is a powerful templating system that can be tailored to many applications (not just web things). So, the next time you start thinking about rolling your own templating system, please don’t. Just grab TT, and make it do what you need.

Until next time, enjoy!

Randal Schwartz can be reached at merlyn@stonehenge.com.

Comments are closed.