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!