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 62 (Aug 2004)

[suggested title: ``Introduction to Template Toolkit (part 3)'']

In the previous two columns, 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), including how to configure TT and use it from Perl, from the command line, and embedded in Apache.

Using TT from Perl

The simplest code looks like:

    use Template;
    my $template = Template->new(\%CONFIG);
    $template->process($filename,\%VARS);

This sets up a $template object using the configuration parameters in %CONFIG (described later), and then executes $filename as a template, sending the result to standard output, passing the pre-defined variables of %VARS. $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;
    Template->new->process(
      \*DATA, # process DATA
      { a => 1, b => 2, # passing these constants
        argv => \@ARGV, # and the arg list
        env => \%ENV,   # and the environment
      },
    );
    __END__
    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(\ $intput, \%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 through small adjustments. I'll try to hit the ones I use the most often here.

Template filenames are located along an INCLUDE_PATH. This path is given similar to 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> will look for templates in those directories in that order. A template can also be specified using an absolute path if the ABSOLUTE configuration parameter is true (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 or after the requested template. 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 website). For example:

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

In this case, 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 the Perl 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 are invoked, this is a big savings. You will need to clean this directory out from time to time though, because TT itself won't delete anything.

Locally defined plugins 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 plugins 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 a true value (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 of 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, to define a filter that lowercases everything (if it didn't already exist as the lower filter) is as simple as:

  FILTERS => [
    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 manpage for a quick overview. In general, if you need to configure it, 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 is a simple invocation of TT on a source file producing 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 website or documentation package) with minimal specification.

Using tpage

The tpage tool is a simple TT invocation on one or more source files, producing the results on standard output:

  tpage input_file >output_file

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

  tpage --define style=green --define margin=3 input >output

In this case, the variables style and margin will have their corresponding values.

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

Using ttree

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 the make). The synopsis line of the manpage is a 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, and act similar to the Makefile 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 will 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 more 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 sourcefiles 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 which contains a template. If you specify a COMPILE_DIR, this template is translated into Perl code which is 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.

Apache::Template configuration

Configuration for Apache::Template is similar to ttree, in that a series of command directives map into the configuration options given earlier. In the httpd.conf file, we'll 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
    </Location>

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 to 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 %]
  your 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, as I've been repeatedly reprimanded for my current Splash-based-website by professionals for my severe lack of sensible web design. But hey, it works.

Powerful higher layers

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

The OpenInteract package uses TT under mod_perl to create a web applications server with reusable and editable components. Common widgets like a ``login box'' are provided, and applications can be ``installed'' and ``upgraded'' relatively independently.

The Slashcode engine is the guts behind the famous slashdot site, as well as hundreds of other discussion areas, like 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, tracking 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.

In Summary

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 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.