But first, let’s complete our portal by writing the RSSBox library that all these sources use. First, we want a ONCE block to load up the modules we need:
<%ONCE>
use XML::RSS;
use LWP::Simple;
</%ONCE>
Next we take our arguments, setting appropriate defaults:
<%ARGS>
$URL
$Color => "#0000aa"
$Max => 5
$Full => 1
$Title => undef
</%ARGS>
Before we start outputting any content, we load up the feed in question and parse it with the XML::RSS module. We call Mason’s cache_self method to have this component handle caching its output; if the same URL is accessed within 10 minutes, the cached copy will be presented instead:
<%INIT>
return if $m->cache_self(key => $URL, expires_in => ’10 minutes’);
my $rss = new XML::RSS;
eval { $rss->parse(get($URL));};
my $title = $Title || $rss->channel(‘title’);
</%INIT>
And now we are ready to go. So lets look at this altogether in Example 3-15.
Example 3-15. RSSBox
<%ONCE>
use XML::RSS;
use LWP::Simple;
</%ONCE>
<%ARGS>
$URL
$Color => "#0000aa"
$Max => 5
$Full => 1
$Title => undef
</%ARGS>
<%INIT>
my $rss = new XML::RSS;
eval { $rss->parse(get($URL));};
my $title = $Title || $rss->channel(‘title’);
my $site = $rss->channel(‘link’);
</%INIT>
<BR>
<& BoxTop, color => $Color, title => $title, title_href => $site &>
<dl class="rss">
% my $count = 0;
% for (@{$rss->{items}}) {
<dt class="rss">
<a href="<% $_->{link} %>"> <% $_->{title} %> </a>
</dt>
% if ($Full) {
<dd> <% $_->{description} %> </dd>
% }
% last if ++$count >= $Max;
% }
</dl>
<& /BoxEnd &>
There isn’t much to it; for each item in the feed, we want to provide a link, the item’s title, and, optionally, the description. We stop if we have more items than we want.
This demonstrates how powerful Mason can be; as I said, the total development time for this site was a couple of hours at most. The entire site takes considerably fewer than 200 lines of code. And, as we mentioned, we have the flexibility to include components that are not RSS. For instance, we don’t actually have an RSS feed of the Oxford weather. However, there is a web page that spits out a weather report in a well-known format. This means that Weather/01-Oxford does not call RSSBox at all, but is in fact the following:
<%INIT>
use LWP::Simple;
my @lines = grep /Temperature|Pressure|humidity|^Sun|Rain/,
split /n/,
get(‘’);
</%INIT>
<br>
<& /BoxTop, title => "Oxford Weather", color => "#dd00dd" &>
<ul>
% for (@lines) {
<li> <% $_ %> </li>
% }
</ul>
<& /BoxEnd &>
And that sums up Mason–simple, extensible, and highly powerful.
Of course, there are many other Mason tricks for you to learn–too many to cover here. Dave Rolsky and Ken Williams’s fantastic book Embedding Perl in HTML with Mason () covers many of them, including more details about getting Mason up and running in your web server. Also check out the Mason home page ().
{mospagebreak title=Template Toolkit}
While the solutions we’ve seen so far have been primarily for Perl programmers–embedding Perl code in some other medium–Andy Wardley’s Template Toolkit () is slightly different. It uses its own templating language to express components, loops, method calls, data structure elements, and more; it’s therefore useful for teaching to designers who have no knowledge of the Perl side of your application* but who need to work on the presentation. As the documentation puts it, you should think of the Template Toolkit language as a set of layout directives for displaying data, not calculating it.
Like Mason, it seamlessly handles compiling, caching, and delivering your templates. However, unlike Mason, it’s designed to provide general-purpose display and formatting capabilities in a very extensible way. As an example, you can use Template Toolkit to dynamically serve up PDF documents containing graphs based on data from a database–and all this using nothing other than the standard plugins and filters and all within the Template Toolkit mini language.
But before we look at the clever stuff, let’s look at the very simple uses of Template Toolkit. In the simplest cases, it behaves a lot like Text::Template. We take a template object, feed it some values, and give it a template to process:
use Template;
my $template = Template->new();
my $variables = {
who => "Andy Wardley",
modulename => "Template Toolkit",
hours => 30,
games => int(30*2.4)
};
$template->process("thankyou.txt", $variables);
This time, our template looks like the following:
Dear [% who %],
Thank you for the [% modulename %] Perl module, which has saved me
[% hours %] hours of work this year. This would have left me free to play
[% games %] games of go, which I would have greatly appreciated
had I not spent the time goofing off on IRC instead.
Love,
Simon
Lo and behold, the templated text appears on standard output. Notice, however, that our variables inside the [% and %] delimiters aren’t Perl variables with the usual type sign in front of them; instead, they’re now Template Toolkit variables. Template Toolkit variables can be more than just simple scalars, though; complex data structures and even Perl objects are available to Template Toolkit through a simple, consistent syntax. Let’s go back to our design work invoices, but with a slightly different data structure:
my $invoice = {
client => "Acme Motorhomes and Eugenics Ltd.",
jobs => [
{ cost => 450.00, description => "Designing the new logo" },
{ cost => 300.00, description => "Letterheads and complements slips" },
{ cost => 900.00, description => "Web site redesign" },
{ cost => 33.75, description => "Miscellaneous Expenses" }
],
total => 0
};
$invoice->{total} += $_->{cost} for @{$invoice->{jobs}};
How would we design a template to fit that data? Obviously, we’re going to need to loop over the jobs in the anonymous array and extract various hash values. Here’s how it’s done:
To [% client %]:
Thank you for consulting the services of Fungly Foobar Design
Associates. Here is our invoice in accordance with the work we have
carried out for you:
[% FOREACH job = jobs %]
[% job.description %] : [% job.cost %]
[% END %]
Total $[% total %]
Payment terms 30 days.
Many thanks,
Fungly Foobar
As you can see, the syntax is inspired by Perl–we can foreach over a list and use a local variable job to represent each element of the iterator. The dot operator is equivalent to Perl’s ->– it dereferences array and hash reference elements and can also call methods on objects.
However, there’s something slightly wrong with this example; since we can expect our descriptions to be of variable width, our costs aren’t going to line up nicely at the end.* What can we do about this? This is where a nice, extensible feature of the Template Toolkit called filters comes in.
{mospagebreak title=Filters}
Template Toolkit filters are a little like Unix filters–they’re little routines that take an input, transform it, and spit it back out again. And just like Unix filters, they’re connected to our template output with a pipe symbol (|).
In this case, the filter we want is the oddly named format filter, which performs printf-like formatting on its input:
[% job.description | format("%60s") %] : [% job.cost %]
This fixes the case where the data is being produced by our template processor–job.description is turned into a real description, and then filtered. But we can also filter whole blocks of template content. For example, if we wanted to format the output as HTML, we could apply the html_entity filter to replace entities with their HTML encoding:
[% FILTER html_entity %]
Payment terms: < 30 days.
[% END %]
This turns into: Payment terms: < 30 days.
This is another example of a Template Toolkit block; we’ve seen FOREACH blocks and FILTER blocks. There’s also the IF/ ELSIF/ELSE block:
[% IF delinquent %]
Our records indicate that this is the second issuing of this
invoice. Please pay IMMEDIATELY.
[% ELSE %]
Payment terms: <30 days.
[% END %]
Other interesting filters include the upper, lower, ucfirst, and lcfirst filters to change the casing of the text; uri to URI-escape any special characters; eval to treat the text to another level of template processing, and perl_eval to treat the output as Perl, eval it, and then add the output to the template. For a more complete list of filters with examples, see the Template::Manual::Filters documentation.
{mospagebreak title=Plugins}
While filters are an interface to simple Perl functionality–built-in functions like eval, uc, and sprintf, or simple text substitutions–plugins are used to interface to more complex functions. Typically, they’re used to expose the functionality of a Perl module to the format language.
For instance, the Template::Plugin::Autoformat plugin allows one to use Text:: Autoformat’s autoformatting functionality. Just as with the Perl module, use the USE directive to tell the format processor to load the plugin. This then exports the autoformat subroutine and a corresponding autoformat filter:
[% USE autoformat(right=78) %]
[% address | autoformat %]
This assures that the address is printed in a nice block on the right-hand side of the page.
A particularly neat plugin is the Template::Plugin::XML::Simple module, which allows you to parse an XML data file using XML::Simple and manipulate the resulting data structure from inside a template. Here we use USE to return a value:
[% USE document = XML.Simple("app2ed.xml") %]
And now we have a data structure created from the structure and text of an XML document. We can explore this data structure by entering the elements, just as we did in "XML Parsing" in Chapter 2:
The author of this book is
[% document.bookinfo.authorgroup.author.firstname # ‘Simon’ %]
[% document.bookinfo.authorgroup.author.surname # ‘Cozens’ %]
Actually writing a plugin module like this is surprisingly easy–and, in fact, something we’re going to need to do for our RSS example. First, we create a new module called Template::Plugin::Whatever, where Whatever is what we want our plugin to be known as inside the template language. This module will load up whatever module we want to interface to. We’ll also need it to inherit from Template::Plugin. Let’s go ahead and write an interface to Tony Bowden’s Data::BT::PhoneBill, a module for querying UK telephone bills.
package Template::Plugin::PhoneBill;
use base ‘Template::Plugin';
use Data::BT::PhoneBill;
Now we want to receive a filename when the plugin is USEd and turn that into the appropriate object. Therefore we write a new method to do just that:
sub new {
my ($class, $context, $filename) = @_;
return Data::BT::PhoneBill->new($filename);
}
$context is an object passed by Template Toolkit to represent the context we’re being evaluated in. And that’s basically it–you can add error checking to make sure the filename exists and that the module can parse the phone bill properly, but the guts of a plugin are as we’ve shown.
Now that we’ve created the plugin, we can access the phone bill just like we did with the XML::Simple data structure:
[% USE bill = PhoneBill("mybill.txt") %]
[% WHILE call = bill.next_call %]
Call made on [% call.date %] to [% call.number %]…
[% END %]
An interesting thing to notice is that when we were using the XML.Simple plugin, we accessed elements of the data structure with the dot operator: document.bookinfo and so on. In that case, we were navigating hash references; the Perl code would have looked like $document->{bookinfo}->{authorgroup}->{author}…. In this example, we’re using precisely the same dot operator syntax, but, instead of navigating hash references, we’re calling methods: call.date would translate to $call->date. However, it all looks the same to the template writer. This abstraction of the underlying data structure is one of the big strengths of Template Toolkit.
{mospagebreak title=Components and Macros}
When we looked at HTML::Mason, one of the things we praised was the ability to split template functionality up into multiple components, then include those components with particular parameters. It shouldn’t be a surprise that we can do precisely the same in Template Toolkit.
The mechanism through which we pull in components is the INCLUDE directive. For instance, we can specify our box drawing library in a way very similar to the HTML::Mason method, as in Example 3-16.
Example 3-16. BoxTop
<table bgcolor="#777777" cellspacing=0 border=0 cellpadding=0>
<tr>
<td rowspan=2></td>
<td valign=middle align=left
<font size=-1
<b>
[% IF title_href %]
<a href="[% title_href %]"> [% title %] </a>
[% ELSE %]
[% title %]
[% END %]
</b>
</font>
</td>
<td rowspan=2> </td>
</tr>
<tr>
<td colspan=2 bgcolor="#eeeeee" valign=top align=left width=100%>
<table cellpadding=2 width=100%>
<tr><td>
And in the same way as HTML::Mason, we can use local parameters when we include these components:
[% INCLUDE boxtop
title = "Login"
…
%]
However, Template Toolkit provides another method of abstracting out common components, the MACRO directive. We can define a MACRO to expand to any Template Toolkit code; let’s start by defining it to simply INCLUDE the drawing component:
[% MACRO boxtop INCLUDE boxtop %]
[% MACRO boxend INCLUDE boxend %]
With this, we can draw boxes with a little less syntax:
[% boxtop(title="My Box") %]
<P> Hello, people! </P>
[% boxend %]
Instead of using a component file and INCLUDE, we can also associate a block of Template Toolkit directives with a macro name.
[% MACRO boxtop BLOCK %]
<table bgcolor="#777777" cellspacing=0 border=0 cellpadding=0>
<tr>
…
[% END %]
[% MACRO boxend BLOCK %]
</td></tr></table>
</td></tr>
<tr><td colspan=4> </td></tr>
</table>
[% END %]
Eventually, we can build up a library of useful macros and then INCLUDE that, instead of having a bunch of component files hanging around.
Let’s assume we’ve created such a library and it contains these two box-drawing macros, and now we’ll move on to putting together our RSS aggregator.
The RSS Aggregator
When it comes to writing the aggregator, we first look at the list of Template Toolkit plugins and notice with some delight that there’s already a Template::Plugin::XML::RSS, which talks to XML::RSS. Unfortunately, our delight is short-lived, as we soon discover that this expects to get a filename rather than a URL or a string of XML data. We don’t really want to be writing out files and then parsing them in again.
Template Toolkit
So let’s create our own subclass of Template::Plugin::XML::RSS that fetches URLs and parses those instead:
package Template::Plugin::XML::RSS::URL;
use base ‘Template::Plugin::XML::RSS';
use LWP::Simple;
sub new {
my ($class, $context, $url) = @_;
return $class->fail(‘No URL specified’) unless $url;
my $url_data = get($url)
or return $class->fail("Couldn’t fetch $url");
my $rss = XML::RSS->new
or return $class->fail(‘failed to create XML::RSS’);
eval { $rss->parse($url_data) } and not $@
or return $class->fail("failed to parse $url: $@");
return $rss;
}
1;
Now we can build up the equivalent of the RSSBox component we made in Mason:
[% MACRO RSSBox(url) USE rss = XML.RSS.URL(url) %]
[% box_top(title = rss.channel.title, title_href = rss.channel.link) %]
<dl class="rss">
[% FOREACH item = news.items %]
<dt class="rss">
<a href="[% item.link %]"> [% item.title %] </a>
[% IF full %]
<dd> [% item.description %] </dd>
[% END ]
</dt>
[% END %]
</dl>
[% box_end %]
[% END %]
The important difference between this and the Mason example is that this piece of code handles everything itself–the whole process of obtaining and parsing the RSS feed is available to the template designer. There’s no Perl code here to be seen at all. It’s also considerably more concise and easier to read and understand. Now that we have this macro, we can produce an HTML box full of RSS stories with a simple call to it:
[% RSSBox("") %]
From here on, constructing an RSS aggregator is a simple matter of templating; all of the Perl work has been abstracted away.
{mospagebreak title=AxKit}
Although we include it in our list of templating systems, AxKit () is a slightly different kettle of fish from the modules we’ve seen so far; this is no mere templating system, it’s a fully fledged XML application server for Apache. The most common use of AxKit is to transform XML to HTML on-the-fly for delivery over the web.
However, thanks to XSP (Extensible Server Pages), developed by the Apache Cocoon project, AxKit can be used as an extraordinarily extensible templating system. The basic idea behind XSP is that certain XML tags trigger the execution of given Perl routines. At a very basic level, you can use tags to delimit raw Perl code:
<p>
Good
<xsp:logic>
if ((localtime)[2] >= 12) {
<i>Afternoon</i>
}
else {
<i>Morning</i>
}
</xsp:logic>
</p>
Notice that AxKit is quite happy for you to intersperse XML marked-up data with your Perl code. Because AxKit parses the XML, it knows that <i>Afternoon</i> is data, not Perl code, and treats it appropriately. This also means that if you have an XML guru handy, he can find a way of validating your HTML-with-embedded-XSP. In fact, since AxKit parses everything as XML, your HTML must be well-formed and valid or you won’t get anything out of AxKit at all.
However, AxKit does not stop at this basic level; XSP allows you to create tag libraries with frontend Perl code. For instance, the AxKit::XSP::ESQL taglib provides a wrapper around the DBI libraries. These tag libraries define their own XML namespaces and place tags inside them. So your XML would use a namespace declaration to import the tag library:
<xsp:page
language="perl"
xmlns:xsp= core/v1
xmlns:esql= SQL/v2
>
and this would allow you to use <esql:…> tags in your page:
<esql:driver>Pg</esql:driver>
<esql:dburl>dbname=rss</esql:dburl>
<esql:username>www</esql:username>
<esql:password></esql:password>
<esql:execute-query>
<esql:query>
select description, url, title from feeds
</esql:query>
<esql:results>
<ul>
<esql:row-results>
<li>
<a>
<xsp:attribute
<esql:get-string
</xsp:attribute>
<esql:get-string
</a> – <esql-get-string
</li>
</esql:row-results>
</ul>
</esql:results>
<esql:no-results> <p> Couldn’t get any results! </p> </esql:no-results>
</esql:execute-query>
</esql:connection>
This executes the SQL query near the top of the XML and turns it into an HTML list. The only potentially non-obvious part is where we use <xsp:attribute>. The key to understanding this is that a document processed by AxKit has to be 100% valid, well-formed XML. On the other hand, with HTML::Template and HTML::Mason we could get away with things like
<a href="<TMPL_VAR URL>"> or
<a href="<% $url |n%>">–in a sense, putting tags inside tags.
But with AxKit, the whole document is parsed as XML, and then transformations are applied. With the above examples, AxKit would parse the tag as having the perfectly valid (but nonsensical) attribute values <TMPL_VAR URL> and <% $url|n> and do no more processing on them. Worse still, we can’t get away with anything like <a href=<esql:get-string> as thats not even well-formed XML.
So we play a slight trick. We ask the XSP layer to rewrite the <a> tag, after everything has been parsed, with the appropriate href attribute. This keeps everything well-formed and parsable.
There are many other tag libraries that perform the same function as Template Toolkit’s plugins and give the XML author access to high-level Perl functionality; my own AxKit::XSP::ObjectTaglib allows the programmer to easily wrap any object-oriented module into a tag library.
We’re not going to implement our RSS aggregator in AxKit, as it turns out, because AxKit is a fully featured XML processor. All of the heavy lifting can be done in XSLT stylesheets, and there’s almost no Perl content involved.
Instead, for more on AxKit, we’ll refer you to Perl and XML (O’Reilly) and http://, the AxKit home page.
{mospagebreak title=Conclusion}
In this chapter, we’ve looked at a few of the available templating tools that are commonly used in Perl; from simple formats–sprintf, and the like–on through Text::Template and HTML::Template, and then up to the more sophisticated solutions of HTML::Mason and Template Toolkit.
But we’ve missed out on one quite important question: which one should you use? As usual, the answer depends partly on what you need and partly on your tastes.
First, consider the distinction between Perl-based systems like Text::Template and Text::Autoformat, and inside-out modules like HTML::Mason. If the main purpose of your program is to provide some templated output, as in the case of a web-based application, then you probably want to gravitate toward the HTML::Mason and Template Toolkit end of the spectrum.
You also need to consider who’s going to be writing the templates and whether you want to expose them to Perl code. Template Toolkit, AxKit, and HTML::Template all tend to keep the templater away from Perl, whereas HTML::Mason forces the templater to get down and dirty with it.
Second, there’s the element of personal taste. I’m not a great fan of HTML::Template, preferring the way Mason does things; I find AxKit very powerful but at times very frustrating because of its insistence on clean XML; and I’m beginning to like Template Toolkit the more I use it, but prefer Mason basically because I’m more used to it.
Your tastes may differ. It’s just as well, that as with so many things in Perl, there’s more than one way to do it.
* As it happens, I didn’t actually use formats in my code, because I wanted to have a variable-width instead of a fixed-width display. But for cases where a fixed-width output is acceptable, this solution is perfect.
* Allowing a user-defined argument is a great way to make a callback extremely extensible.
* Feel free, of course, to implement all these things as an exercise in HTML::Mason programming.
* If you need the actual Apache request object in Mason, it’s available as $r.
* Therefore, as it happens, all requests will go through index.html, and we could have put our header and footer code in there, but using an autohandler is cleaner and actually more conventional.
* And probably no desire to find out!
* We completely glossed over this in the Text::Template example; did you notice?