Greytower Technologies

If the navigation is not visible, this link will take you to it.

 

PetiteCGI::Template

Last update Friday, 17th of November, 2006, 20:04 (CET)

Please note that this documentation applies to version 2.0b10, 24th of August 2003. The module is an Beta release.

The documentation is under revision.

Background

One of the most common problems in developing web-based applications is how to separate content, structure and layout. The Template library provide methods for doing just that.

Table of Contents


Usage

Using PetiteCGI::Template is a matter of replacing some parts in your HTML - or anything else for that matter1 - with module specific hooks, and doing some Perlish mumbo-jumbo to get the two bits working in concert.

... in templates

The general syntax for use in the template files look like this:

<!-- template FUNCTION
       ATTRIBUTES
-->
    

Whitespace within the template declaration is, for the most part, not significant. The exception is the first line which identifies the comment as important to the template system. The characters double quote (") and single quote (') work as one might expect - though nothing will prevent a template variable to be expanded, even inside a string encased in single quotes (').

Since, as you'll shortly see, a variable is never expanded unless it is registered with the Perl script using the module, this is not a problem.

Case - outside of values - is never significant. The FUNCTION may, from version 2.0b10, be one of either:

... in Perl scripts

PetiteCGI::Template is an object-oriented module. As such each template (physically a file) is an object. The Perl syntax for creating these is, of course:

my($templateRef) = new Template({}) ;
    

$templateRef you can, of course, replace by something of your own choice. The new() method takes an associative array - hash - as its only argument. This example uses the default values to illustrate:

my($templateRef) = new Template({
                          'sourceFile'            => '',
                           'sourcePath'            => $ENV{'PATH_TRANSLATED'},
                            'outputPath'            => \*STDOUT,
                             'expandEmptyItems'      => 'no',
                              'matchDefaultSubstring' => 'no',
                               'debug'                 => 0,
                                }) ;
    

where

sourceFile
... is the name of the file that is the template. If this file does not start with a '/', then it is looked for in (a) ./sourceFile and (b) sourcePath/sourceFile as one would expect.
sourcePath
... sets the path to the physical location where that file might be found IF the file does not start with a slash ('/').
outputPath
... is the filehandle where the template is written.
expandEmptyItems
If this is set to yes, then startTag and endTag will be output even if the callback generates an empty ("") or undefined value.
matchDefaultSubstring
If set to yes, then the default value specified will match a generated value even if it is a substring of same. If no, then only exact matches will generate a selectedItem.
debug
... takes a 0 or a 1, and controls the verbosity of the module.

Variables

Using variables is a well-known way to manage - or mangle - code. It is only fair that PetiteCGI::Template should have these contructs, and only natural that it does it in a non-common way.

Defined

Defining a variable in a template is done with the template set construct:

<!-- template set
       name = "uri"
        value = "http://www.greytower.net/"
-->
    

When processing templates the PetiteCGI::Template module will gather up these variables, and store them.

Once a variable has been set, it can be retrieved in the Perl program by something along these lines:

my($uri) = $templateRef->getValue("uri") ;
    

Note that a variable set in one template will not become available in another unless the former is include'ed in the latter. See Content Inclusion

You could also define variables in the Perl code for later expansion in the template - this might be the most common method:

$templateRef->register({
                        'name'              => "uri",
                         'callback'          => \"http://www.greytower.net/",
                          'callbackArguments' => []
                       }) ;
    

The register() method takes three arguments: the variable name, the data structure - typically a scalar - it is associated with, and a list of arguments. The latter is used if the data structure is actually a function:

$templateRef->register({
                        'name'              => "uri",
                         'callback'          => \&getHostName,
                          'callbackArguments' => ["localhost"]
                       }) ;
    

Expanded

Expanding a variable - either one defined in the template code itself or one defined in the Perl code - can be done in one of two ways. Either you'd use the callback syntax explained later:

<!-- template callback
       name      = "calculatePi"
        startTag  = ""
         endTag    = ""
          item      = "%%pi%%"
            
-->
    

Or, since replacing a variable using the syntax above can be tedious, you could use the alternative which is exactly2 equivalent in functionality:

<p>
 The value of PI is approximately %%pi%%
</p>
    

Callbacks

A callback in the world of PetiteCGI::Template is a Perl subroutine - though I persist in calling them functions - which is associated with a structure in the template. This allow you to construct more complex constructs.

A callback is defined with the syntax:

<!-- template callback
       name      = "generateFruitList"
        startTag  = "<ul>"
         endTag    = "</ul>"
          item      = "<li>%%fruitName%%>/li>"
-->
  

Basic Operation

In short, the above will make PetiteCGI::Template call the routine associated with the name generateFruitList, use the return values to create one, or more, copies of what is in item, wrap the whole thing in the content of startTag and endTag, and replace the above with the result.

What you write in startTag and endTag is your business - the template parser will not touch it in any way what so ever. Naturally there are exceptions - namely that it will expand variables and macros as one would expect.

You could, therefore, write:

<!-- template callback
       name      = "generateFruitList"
        startTag  = '<ol start="%%startIndex%%">'
         endTag    = "</ol>"
          item      = "<li>%%fruitName%%>/li>"
-->
    

But since the start attribute to OL is deprecated you wouldn't do that. Of course.

Return values

A function used as a callback can, as is normal with Perl, return absolutely anything. PetiteCGI::Template is however only able to grasp a limited set of anythings. There are the classic anythings:

scalar
The value returned will be used to expand the first template variable in an ITEM. If the scalar is a reference to another scalar, then it is de-referenced first. If the scalar is a reference to an array, or a hash, then it is de-referenced and treated as suggested below.
array
Each value in the list will be used to create one copy of the structure in ITEM, replacing the first template variable found. If the value is a reference to a scalar, then it will be de-referenced before use. If the value is a reference to an array or a hash, then it'll be de-referenced and treated as suggested below.
hash
Each key/value pair will produce one copy of the ITEM structure with the first variable replaced by the key, the second by the value.

... and then there are the rather more complex anythings that the module will - try to - recognise:

array of array
Each item in the list will produce one copy of the structure in the ITEM construct. Variables are replaced left to right with values from the nested lists.
array of hash
Each item in the list will produce one copy of the structure in the ITEM construct. Variables are replaced with the value associated with each named key.

At each point in the above where the value could be a reference, that reference will be de-referenced before use - this also applies if it points to a piece of code, anonymous or otherwise. Some caution is suggested in using this feature.

PetiteCGI::Template will not make any effort to avoid circular references or code refs calling other code refs reductio ad absurdum - it's a template library, not a miracle-worker (or a Doctor).

The treatment of return values allow you to do things like:

<table>
 <thead>
  <tr>
   <th>Name</th>
   <th>Age</th>
  </tr>
 </thead>

 <tbody>
 <!-- loop begins here -->
 <!-- template callback
                name="getAgeList"
                 startTag="<tr>"
                  endTag="</tr>"
                   item="<td>%%name%%</td><td>%%age%%</td>"
 -->
 </tbody>
</table>
    

Then, in the Perl code:

$templateRef->register({
                        'name'              => "getAgeList",
                         'callback'          => \&getSortedAgeList,
                          'callbackArguments' => []
                       }) ;

    

If getSortedAgeList() now return either an array of arrays with exactly two entries in each 2nd level array:

[['Donald', 60], ['Daisy', 59] ]
    

or an array of hash with names chosen to match:

[{'name' => 'Donald', 'age' => '60'}, {'name' => 'Daisy', 'age' => '59'}]
    

... the above construct will produce:

<table>
 <thead>
  <tr>
   <th>Name</th>
   <th>Age</th>
  </tr>
 </thead>

 <tbody>
 <!-- loop begins here -->
<tr>
<td>Donald</td><td>60</td>
</tr>

<tr>
<td>Daisy</td><td>50</td>
</tr>

 </tbody>
</table>
    

Content Inclusion

Odd as it might seem, the very first thing done by the template parser is to include external content. The reason for that becomes obvious when you think back to how variables are handled.

To include content from a truly external source - such as a file or an URI - you can use the include construct:

<!-- template include
       src = "http://www.greytower.net/"
-->
   

Inclusion defaults to file://, and what was said about paths in the description of Perl syntax holds here as well.

It is worth your while to note what the above does with variables. This construct:

<!-- template set
       name = "uri"
        value = "http://www.greytower.net/"
-->

<!-- template include
       src = "%%uri%%"
-->
   

... will not work. However, put the following in your Perl code prior to the template code being parsed:

$templateRef->register({
                        'name'              => "uri",
                         'callback'          => \"http://www.greytower.net/",
                          'callbackArguments' => []
                       }) ;
   

And this in the template:

<!-- template include
       src = "%%uri%%"
-->
   

... and all is well.

Default Values

Many people seem to think it's a good thing to present defaults to the user - in particular through the use of selected in the HTML OPTION element.

This does, however, present somewhat of a problem when the aim is to strictly separate template and code. It means that there need to be a syntax with which the template author can communicate to the Perl author that something should be marked as default. I also need to add code in the Perl module to ensure that PetiteCGI::Template understands this.

The first idea - trusting the template parser to understand where to put a random-language specific phrase or word indicating default - was pure insanity. Luckily I surround myself with more or less sane people, and Jörgen came up with the idea of redundancy:

<!-- template callback
       name         = "generateFruitList"
        startTag     = '<ol start="%%startIndex%%">'
         endTag       = "</ol>"
          item         = "<li>%%fruitName%%</li>"
           selectedItem = "<li><img src="default.png" alt="">%%fruitName%%</li>"
-->
   

The idea, of course, is that the selectedItem code should be used to represent a defaulted item. The next example makes somewhat more sense:

<!-- template callback
 name        = "getFruitList"
  startTag    = '<select name="fruit">'
   endTag      = "</select>"
    item        = '<option value="%%fruitName%%">%%fruitName%%</option>'
     selectedItem = '<option value="%%fruitName%%" selected>%%fruitName%%</option>'
-->
   

Of course, we're still left with the Perl pieces. However, by adding yet another argument to the register() method:

$templateRef->register({
                        'name'              => "getFruitList",
                         'callback'          => \&getSortedFruitList,
                          'callbackArguments' => [],
                           'default'           => \"Apple",
                            'defaultKey'        => '',
                             'defaultArguments'  => [],
                       }) ;
   

where

name
... is the name associating the callback with the template
callback
... is a reference to a data structure containing the data to be used in the replacement, or a reference to a routine/method that returns such a data structure
callbackArguments
... is an anonymous array listing arguments for a routine used as a callback
default
... is a reference to either a data structure or a routine that represents the default value to be used with the selectedItem construct in a template
defaultKey
... is a scalar variable representing the name of a field in which to check for the default values. This is typically used when the callback results in an array of references to hashes.
defaultArguments
... is an anonymous array listing arguments for a routine used as a default source.

If the callback produces a value which matches the default key, then the selectedItem code is used as the template. If it does not, then item is used.


1 Come to think of it, I don't think this would work very well with any type of binary code, but you're welcome to try. Any time. Any where. Just don't blaim me if your PNG files come out as neatly coded markup ...

2 Well. Almost.

Return to the top of the document