9.1 The Template Toolkit


Full of built-in features and optional extensions, Andy Wardley's Template Toolkit has proven a popular choice for web templating. Although TT2 is useful for generating practically any format, it is especially suited for generating and transforming XML content.

The Template Toolkit implements a modest set of simple directives that provide access to common templating system features: inline variable interpolation, inclusion of component templates and flat files, conditional blocks, loop constructs, exception handling, and more. Directives are combined and mixed with literal output using [% . . . %] as default delimiters. We will not cover TT2's complete syntax and feature set in any detail here (it would likely fill a book), so if you are not familiar with it, visit the project home page at http://template-toolkit.org/. A simple TT2 template looks like this:

 <html> <head>     [% INCLUDE common_meta title="My Latest Rant" %]  </head>   <body>     [% INSERT static_header %]          <p>         Pleurodonts! Crinoids! Wildebeests! Lend me your, um, ears . . .      </p>          [% INSERT static_footer %]       </body> </html> 

This simple HTML template includes and processes a separate common_meta template, setting that template's local title variable. It then inserts static page header and footer components around the document's main content. The directive syntax is not Perl, though it is rather Perl-minded (and you can embed raw Perl into your templates by setting the proper configuration options, if you want), nor is it really like XML.

Part of what makes the Template Toolkit a good fit with an XML environment is precisely that its directive and default delimiter syntax is so markedly different from the syntax of both Perl and XML. For example, XSP and other XML grammars that use Perl as the embedded language often require logic blocks to be wrapped in CDATA sections to avoid potential XML well- formedness errors caused by common Perl operators and "here" document syntax. Similarly, documents created for templating systems that use angle brackets as delimiters often cannot be processed with XML tools. Those that can often require the use of XML namespaces to distinguish between markup that is part of the application grammar and markup meant to be part of the output. The Template Toolkit's syntax deftly avoids these potential annoyances by limiting its operators to a handful of markup-friendly characters and letting different things (embedded directives) be and look substantially different from the literal output.

When considering how the Template Toolkit can be used within the context of AxKit, it is natural to first think of creating a Provider class that simply returns the content generated from the template expansion; it would be easy enough to do so. However, a closer look at the Template Toolkit's features shows that it can be useful for transforming XML content, as well. This means that it is probably best implemented as an AxKit Language module, rather than a Provider. In fact, integrating the Template Toolkit via the Language interface creates a unique medium that offers an alternative to both XSP (for generating content), and XSLT and XPathScript (for transforming it).

Based on what you already know from the details covered in Section 8.3, creating an AxKit Language interface for the Template Toolkit is pretty straightforwardyou simply store the result of the template process in the appropriate Apache pnotes field. But to pull double duty as both a generative and transformative language, the Template Toolkit Language module needs to be a bit smarter . That is, in cases in which you would use the language to generate content, the source XML itself contains the template to be processed. There is no external stylesheet. When you want to use the Template Toolkit's powers to transform XML, then an external template is required. To be truly useful, the AxKit Template Toolkit Language module needs to be able to distinguish between these two cases. Example 9-1 is an excerpt from a simplified version of the Apache::AxKit::Language::TemplateToolkit2 class.

Example 9-1. Language::TemplateToolkit2
 package Language::TemplateToolkit2;          use strict; use vars qw( @ISA ); use Apache::AxKit::Language; use Template; @ISA = qw( Apache::AxKit::Language );        sub handler {     my $class = shift;     my ($r, $xml, $style, $last_in_chain) = @_;           AxKit::Debug( 5, 'Language::TT2 called');        my $input_string = undef;     my $xml_string = undef;     my $result_string = '';              my $params = { Apache => $r };                if (my $dom = $r->pnotes('dom_tree')) {            $xml_string = $dom->toString;    }     else {        $xml_string = $r->pnotes('xml_string');         delete $r->pnotes( )->{'xml_string'};     }          $xml_string = ${$xml->get_strref( )};              # process the source as the template, or a param passed to     # an external template     if ( $style->{file} =~ /NULL$/ ) {          $input_string = $xml_string;     }     else {         $params->{xmlstring} = $xml_string;         $input_string = ${$style->get_strref( )};     }          my $tt = Template->new( get_tt_config($r) );     $tt->process($input_string, $params, $result_string )          throw Apache::AxKit::Exception::Error( -text => "Error processing TT2  template: " . $tt->error( ) );                  delete $r->pnotes( )->{'dom_tree'};     $r->pnotes('xml_string', $result_string);     return Apache::Constants::OK; } # helper functions, etc. 1; 

Essentially, this module's main handler( ) method examines the path to the stylesheet for the current transformation. If that option contains the literal value NULL as the final step, then the source content is expected to contain the template document to be processed, much the same way that XSP works. Otherwise, that path is used as the location of the external template to which the source content is passed as a parameter, named xmlstring , for further processing. This allows you to use the Template Toolkit in both generative and transformative contexts while using AxKit's configuration directive syntax and conventions to differentiate between the two cases. The following configuration snippet illustrates both uses:

 # Add the Language module AxAddStyleMap application/x-tt2 Apache::AxKit::Language::TemplateToolkit2 # TT2 as a generative Language such as XSP (source defines the template) AxAddProcessor application/x-tt2 NULL # TT2 as a transformative Language such as XSLT (external template applied to source) AxAddProcessor application/x-tt2 /styles/mytemplate.tt2 

9.1.1 Generating Content

Generating content using the Language::TemplateToolkit2 module follows the most common uses of the Template Toolkit, in general. It combines literal markup with special directives to construct a complete result when processed by the Template engine. Used in this way, the top-level template acts like an XSP page; that is, no external stylesheet is applied. The source document itself is what gets processed. Because the template is the source content, it must be a well- formed XML document, since AxKit will extract the root element name , document-type information, and any xml-stylesheet processing instructions that may be contained there for use with the style processor configuration directives (as it does with all content sources).

 <?xml version="1.0"?> <products>     [% USE DBI( 'dbi:driver:dbname', 'user', 'passwd' ) %]     [% FOREACH product DBI.query( 'SELECT * FROM products' ) %]     <product id="[% product.id %]">       <name>[% product.name %]</name>       <description>[% product.description %]</description>       <price>[% product.price %]</price>       <stock>[% product.stock %]</stock>     </product>     [% END %] </products> 

This simple template uses the Template Toolkit's DBI plug-in to select product information from a relational database and generate an appropriate product element for each row returned. To configure AxKit to process this template as expected, you only need to add the correct directives to the host's httpd.conf or .htaccess file:

 <Location /products.xml>  AxAddProcessor application/x-tt2 NULL</emphasis>  AxAddProcessor text/xsl /styles/generic.xsl </Location> 

Or, if you do not mind hardcoding styling information into the document itself, you can achieve the same effect by adding xml-stylesheet processing instructions to the template.

 <?xml version="1.0"?>  <?xml-stylesheet type="application/x-tt2" href="NULL"?>  <?xml-stylesheet type="text/xsl" href="/styles/generic.xsl"?>]]><products>     [% USE DBI( 'dbi:driver:dbname', 'user', 'passwd' ) %]     [% FOREACH product DBI.query( 'SELECT * FROM products' ) %]     <product id="[% product.id %]">       <name>[% product.name %]</name>       <description>[% product.description %]</description>       <price>[% product.price %]</price>       <stock>[% product.stock %]</stock>     </product>     [% END %] </products> 

9.1.2 Transforming Content

While XSP is solely concerned with generating content, TT2's filtering capabilities make it useful as a transformative language as well:

 <html>  <head><title>Our Products</title></head> [% USE xpath = XML.XPath(xmlstring) %] <body>   <h1>Our Products</h1>   [% PROCESS productlist %] </body> </html> [% BLOCK productlist %]   <table>     <tr>      <th>ID</th><th>Name</th><th>Description</th><th>Price</th><th>In Stock</th>     </tr>      [% FOREACH product = xpath.findnodes('/products/product') %]        [% PROCESS product item = product %]     [% END %]   </table>    [% END %]  [% BLOCK product %]   <tr>     <td>[% item.getAttribute('id') %]</td>     <td>[% item.findvalue('name') %]</td>      <td>[% item.findvalue('description') %]</td>     <td>[% item.findvalue('price') %]</td>     <td>[% item.findvalue('stock') %]</td>   </tr> [% END %] 

This is how the same transformation can be done with XSLT:

 <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/">   <html>   <head><title>Our Products</title></head>   <body>      <h1>Our Products</h1>      <xsl:call-template name="productlist"/>   </body> </html>   </xsl:template> <xsl:template name="productlist">   <table>     <tr>       <th>ID</th><th>Name</th><th>Description</th><th>Price</th><th>In Stock</th>     </tr>     <xsl:for-each select="/products/product">        <xsl:apply-templates select="."/>     </xsl:for-each>   </table> </xsl:template> <xsl:template match="product">   <tr>     <td><xsl:value-of select="@id"/></td>     <td><xsl:value-of select="name"/></td>     <td><xsl:value-of select="description"/></td>     <td><xsl:value-of select="price"/></td>     <td><xsl:value-of select="stock"/></td>   </tr> </xsl:template> </xsl:stylesheet> 

If you favor another templating system and want to make it available as an AxKit Language module, please see Section 8.4 for more detail about creating your own custom AxKit Language module.

XML Publishing with AxKit
XML Publishing with Axkit
ISBN: 0596002165
EAN: 2147483647
Year: 2003
Pages: 109
Authors: Kip Hampton

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net