You want to separate presentation (HTML formatting) from logic (Perl code) in your program. You want designers and other people who don't speak Perl to be able to edit the templates.
Use the Template Toolkit and Apache::Template.
The Template Toolkit (TT2) is a general templating system that can be used not just for web pages, but for any kind of templated text. The Apache::Template module is an Apache content handler that uses TT2 to build the returned page. The biggest benefit of TT2 is that it has a simple language for variables, loops, and data structures, which can be used instead of Perl for presentation logic. This simple language can be read and written by people who don't know Perl.
This recipe documents Version 2 of the Template Toolkit. As with HTML::Mason, there's far more to TT2 that we can possibly cover here. This recipe is just a tour of some of the highlights of TT2's syntax and functionality. The Template Toolkit is well documented at http://www.template-toolkit.org, and in the upcoming book Perl Template Toolkit, by Darren Chamberlain, Dave Cross, and Andy Wardley (O'Reilly).
Install the Template and Apache::Template modules from CPAN. Add this to your httpd.conf file:
PerlModule Apache::Template TT2EvalPerl On TT2Params all TT2IncludePath /usr/local/apache/htdocs/tt2 <Location /tt2> SetHandler perl-script PerlHandler Apache::Template DefaultType text/html </Location>
The TT2EvalPerl directive lets us embed Perl code in our templates as well as the TT2 language. TT2Params tells Apache::Template to give our templates access to form parameters, Apache environment variables, notes, cookies, and more. TT2IncludePath tells the Template Toolkit where to look for templates that our templates include. Finally, we designate the /tt2 area of our server for pages generated by the Template Toolkit.
Templates are regular HTML files with directives embedded in [% ... %] tags. The tag delimiters are customizable, but in practice they're rarely changed. The use of square brackets rather than angle brackets means that templates can be edited in HTML editors without fear that a templating directive will be confused for an HTML tag.
This is a simple template:
<b>This is how you count to three:</b> [% FOREACH i = [1 .. 3] %] [% i %] ... [% END %] Wasn't that easy?
When TT2 executes this template, it will produce:
<b>This is how you count to three:</b> 1 ... 2 ... 3 ... Wasn't that easy?
Store that in the file tt2/count and point your browser at the equivalent URL.
The FOREACH loop is an example of a TT2 directive. The i variable is the loop iterator, and it takes each value in turn from the list on the righthand side of the =. Loops, like every TT2 block, are terminated by an END directive. Variables in TT2 code have no type sigil like $, @, or %.
To display the value of a variable or expression, simply enclose it in the [% ... %] tags. You can't put arbitrary Perl code there, though, only TT2 syntax.
If you want to execute Perl, use a PERL directive:
[% PERL %] my @numbers = (1 .. 3); print join(" ... ", @numbers); [% END %]
Anything printed from within a PERL block becomes part of the final document. PERL blocks execute under use strict, so it pays to use lexical variables.
These lexical variables are separate from the TT2 variables like i, the loop iterator in the earlier example. To make a Perl value accessible to TT2 code, or vice versa, you must use the stash. This is the TT2 symbol table, and is accessible through the $stash variable automatically present in PERL blocks:
[% PERL %] my @numbers = (1 .. 3); my $text = join(" ... ", @numbers); $stash->set(counting => $text); [% END %] Here's how you count to three: [% counting %]. Wasn't that easy?
Normally you use Perl code for business logic (e.g., fetching values from databases) and TT2 code for presentation logic (e.g., building tables). The Perl code sets TT2 variables with the results of the business logic (e.g., the values from the database) so that the presentation logic has values to put into the template. In practice, most people prefer to disable TT2EvalPerl and keep Perl code out of their templates. this strict separation of business from presentation logic means a customized version of Apache::Template is needed to load the Perl code and place data in the stash.
You can initialize TT2 variables from TT2 as well:
[% text = "1 ... 2 ... 3" %] <!-- string --> [% names = [ "Larry", "Tom", "Tim" ] %] <!-- array --> [% language = { Larry => "Perl 6", <!-- hash --> Tom => "Perl 5", Tim => "Latin" } %] [% people = { Larry => { Language => "Perl 6", <!-- nested structure --> Town => "Mountain View" }, Tom => { Language => "Perl 5", Town => "Boulder" } } %]
Similarly, you can fetch TT2 values from the stash:
[% FOREACH i = [1 .. 3] %] [% PERL %] my $number = $stash->get("i"); $stash->set(doubled => 2*$number); [% END %] [% doubled %] ... [% END %] 2 ... 4 ... 6 ...
From within a PERL block, you can also use modules. It's more efficient, however, to load the modules when Apache starts up by replacing the PERL block's use Some::Thing with a PerlModule Some::Thing in httpd.conf.
The stash lets you put scalars, arrays, hashes, even subroutines into the world of TT2 code. Here's an array definition and access:
[% names = [ "Nat", "Jenine", "William", "Raley" ] %] The first person is [% names.0 %]. The first person is Nat.
The period (.) separates the structure name from the field you want to access. This works for hashes as well:
[% age = { Nat => 30, Jenine => 36, William => 3, Raley => 1.5 } %] Nat is [% age.Nat %] (and he feels it!) Nat is 30 (and feels it!)
Unlike Perl, TT2 code doesn't put [ ] or { } around the array position or hash key whose value you're accessing. This is part of the simplicity of TT2 code, and why non-programmers can easily modify it. It also hides the implementation—age.1 could just as easily be implemented through an array, a hash, or an object, without requiring changes in the template.
If your index is stored in another variable, use a $:
[% age = { Nat => 30, Jenine => 36, William => 3, Raley => 1.5 } %] [% name = "Nat" %] Nat is [% age.$name %] (and he feels it) Nat is 30 (and feels it!)
Loop over an array or hash with FOREACH:
[% FOREACH name = names %] Hi, [% name %]! [% END %] Hi, Nat! Hi, Jenine! Hi, William! Hi, Raley! [% FOREACH person = age %] [% person.key %] = [% person.value %]. [% END %] Nat is 30. Jenine is 36. William is 3. Raley is 1.5.
The key and person methods can be called on a hash loop iterator to get the current key and value, respectively. TT2 also makes a loop variable available in loops, from which you can access the current position, find out whether the current position is the first or last, and more. Table 21-1 lists the loop variable methods and their meanings.
Method |
Meaning |
---|---|
size |
Number of elements in the list |
max |
Index number of last element (size - 1) |
index |
Index of current iteration from 0 to max |
count |
Iteration counter from 1 to size (i.e., index + 1) |
first |
True if the current iteration is the first |
last |
True if the current iteration is the last |
prev |
Return the previous item in the list |
next |
Return the next item in the list |
To build a table with alternating row colors, do the following:
[% folks = [ [ "Larry", "Mountain View" ], [ "Tom", "Boulder" ], [ "Jarkko", "Helsinki" ], [ "Nat", "Fort Collins" ] ] %] <table> [% FOREACH row = folks %] <tr [% IF loop.index % 2 %] bgcolor="#ffff00" [% ELSE %] bgcolor="#ffff80" [% END %] > [% FOREACH col = row %] <td>[% col %]</td> [% END %] </tr> [% END %] </table>
If you build a lot of tables like this, you should abstract out the code into a subroutine. In TT2 syntax, a subroutine is a block. Here's a simple block that takes no parameters:
[% BLOCK greet %] Hello, world! [% END %]
To call it, use the INCLUDE directive:
[% INCLUDE greet %]
Here's how you'd write a generic HTML table routine:
[% BLOCK table %] <table> [% FOREACH row = array %] <tr [% IF loop.index % 2 %] bgcolor="#ffff00" [% ELSE %] bgcolor="#ffff80" [% END %] > [% FOREACH col = row %] <td>[% col %]</td> [% END %] </tr> [% END %] </table> [% END %]
To call this table block and tell it to print the array folks, you'd say:
[% INCLUDE table array=folks %]
The same syntax that you used to call a block defined within a template can be used to load and execute another file:
[% INCLUDE "header.tt2" %]
An INCLUDEd file is treated as a TT2 template. To insert a file that doesn't contain TT2 directives, it's faster to use INSERT:
[% INSERT "header.html" %]
INSERTed files are not processed by TT2 in any way. Their contents are simply inserted verbatim into the document being built.
Apache::Template provides you with several TT2 variables corresponding to various parts of your web environment. Table 21-2 lists these variables and what they contain.
Variable |
Contains |
---|---|
uri |
String containing URI of current page |
env |
Hash of environment variables |
params |
Hash of form parameters |
pnotes |
Hash of Apache request's pnotes |
cookies |
Hash of cookies |
uploads |
Array of Apache::Upload objects |
Here's a form:
<form action="consult"> Whose city do you want to look up? <select name="person"> <option value="larry">Larry</option> <option value="tom">Tom</option> <option value="nat">Nat</option> </select><p> <input type="submit"> </form>
The person form parameter contains the person's name. Here's the consult template:
[% cities = { larry => "Mountain View", tom => "Boulder", nat => "Fort Collins" } %] [% name = params.person %] [% name %] lives in [% cities.$name %]
The Template Toolkit comes with many plug-ins. The most useful is probably the DBI plug-in:
[% USE DBI('dbi:mysql:library', 'user', 'pass') %] [% FOREACH book = DBI.query( 'SELECT title,authors FROM books' ) %] [% book.authors %] wrote [% book.title %]<br> [% END %]
Once the plug-in is loaded with the USE directive, you can use the TT2 variable DBI to issue SQL queries. The query method returns an array of rows, each row is a hash mapping column name to value.
The HTML plug-in is also useful. It offers methods to HTML-escape strings:
[% USE HTML %] [% string = 'Over -----> Here' %] Look [% HTML.escape(string) %] Look Over -----> Here
The documentation for the Template and Apache::Template modules from CPAN; http://www.template-toolkit.org; Perl Template Toolkit; Recipe 15.9 in mod_perl Developer's Cookbook
Copyright © 2003 O'Reilly & Associates. All rights reserved.