Template expander module

This is a module to expand a file or string in the template syntax described below using supplied token values and handler functions. The primary motivation for this tool was to allow different template files to be used for the same underlying data so that the look of some web pages could be changed, but another good reason for using this is to make the form of such pages more easily editable without changing the content.

The intent is general purpose expansion of text files, rather than targetting a particular output format.

Template syntax

The template expander understands the following constructions:

Note that all constructs which take a <text> argument may contain other constructs as part of the text. Eg

To set a default value:

    [ifndef(limit):using default limit value[set(limit):40]]

To output some text when a clump is empty:

    [set(playerText):[clump()players]$player$[ifndef(last):, ]]
    [ifdef(playerText):Players this week are $playerText$]
    [ifndef(playerText):No one wants to play!]

Constructs and text arguments may stretch across an arbitrary number of lines, as long as the closing square bracket is present.

Usage

Template is an OO module which has the following methods which are of use to the caller:

  1. new - class level method taking a hash of clump name to clump handler and returning a Template object.

  2. expandFile - taking a filename and a hash of token name to token value. The return value is the expanded content of the file.

  3. expandLines - taking an array of strings and a hash of token name to token value. The return value is the expanded content of the passed lines.

A typical code fragment using the Template module might be as follows (where gameClump and moveClump are both clump handlers):

    $gameFile = shift;
    $templateFile = shift;
    %tokens = ("game" => $gameFile);
    %clump_handlers =
        ( "game" => \&gameClump,
          "move" => \&moveClump
          );
    $tplexp = Template->new(\%clump_handlers);
    $output = $tplexp->expandFile($templateFile, \%tokens);

Important points to note in this sample are:


Functions defined

new

Constructor for Template. Takes an optional single argument of a reference to a hash of clump name to clump handler function reference, and returns a suitably blessed object reference.

Clump handlers themselves must behave as follows:

The details of how a clump populates the row data are not specifiable here, and are highly individual to the application. As an example of a simple clump handler, here is a possible definition of a clump handler for daysOfWeek mentioned earlier:

    sub daysOfWeekClump()
    {
        my ($tokens, $count) = (shift, shift);
        my @rows = ();
        @daysOfWeek = ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");
        $i = 0;
        foreach $c (@daysOfWeek)
        {
            # abort if loop limit exceeded (unlikely in this case, but hey...)
            if ($count ne "" && $i >= $count)
            {
                return @rows;
            }
            # need to declare this local to ensure that we get a fresh hash
            # object for each row.
            my %h = ("day" => $c);
            push @rows, \%h;
            $i++;
        }
        return @rows;
    }

If row data can be precomputed, it may be easier to not use clump handlers and to insert row data directly into the token context passed into the appropriate expand function.

Template::ClumpIterator

Interface for clump iterator which may be substituted for a fully defined clump set in a token hash. This allows iteration across data sets which are either not yet fully defined, or which are not reasonable to hold entirely in memory.

Note that the usual properties of clumps should be observed (eg first and last tokens should be set, the row number should be defined), and that there is support in this interface for those properties, but it is the responsibility of the implementing iterator to provide the actual implementation.

Methods with default implementations are:

next
Calls nextRow to retrieve the actual row data, then lastRow to determine if that was the last of it.

Methods which must be defined are:

nextRow
Returns the next record in the clump, or null if there are no more.

lastRow
Returns true if the most recently retrieved row was the last one in the set, false otherwise.

expandFile

Function to expand the text in the given file. Takes arguments of the filename to read from, and the token context within which the file's text is to be expanded (a reference to a token hash). An optional final argument is a file handle where the expanded text is to be written; if this is supplied then the text will be output as it is expanded.

Returns a result of the expanded text (undef if the file handle argument was supplied). =cut

sub expandFile { my $self = shift; my $filename = shift; my $tokens = shift(); my $fh = shift; my $text = $self->readFile($filename); my $result = $self->expandLines($text, $tokens, $fh); pop @{$self->{FILE_STACK}}; return $result; }

expandLines

Function to expand the text in the passed lines. Takes arguments of a reference to an array of strings to expand, and the token context within which the text is to be expanded (a reference to a token hash). An optional final argument is a file handle where the expanded text is to be written; if this is supplied then the text will be output as it is expanded.

Returns a result of the expanded text (undef if file handle supplied).

parseFile

Function to parse the text in the given file. Takes argument of the filename to read from.

Returns a result of the parse tree. The file path relative to the initial template file location is on the top of the file stack.

parseLines

Function to parse the text in the passed lines. Takes arguments of a reference to an array of strings to parse.

Returns a result of the parse tree.