NAME

Piffle::Template - Perlish templating language

SYNOPSIS

    use Piffle::Template;
    use Piffle::Template qw{template_to_perl expand_template};

    # OO syntax, with output stored and returned:
    print Piffle::Template->expand(source_file => 'foo/fish.xml',
                                   include_path => ['foo/inc','foo']);

    # Immediate: OO syntax: output goes directly to STDOUT:
    Piffle::Template->expand(source_file => 'foo/fish.xml',
                             output_file => \*STDOUT);

    # Procedural syntax, data from a string
    $string = <<'__END__';
      <?perl for $world (1 .. 10) { ?>
       Hello world number {$world}.
      <?perl     print random_greeting(); ?>
      <?perl } ?>
      <?include std_disclaimer.txt?>
    __END__
    expand_template(source => $string,
                    output_file => \*FILE);

DESCRIPTION

This is a simple Perl-embedding syntax for template code which is geared towards allowing authors to validate their templates directly against schemas or DTDs. The embedded language is Perl itself, which allows great flexibility at the expense of having to be disciplined about the barrier between template code and module code.

In operation, the source template is transformed to an in-memory Perl script which is then run using Perl's eval operator. Errors can be redirected to files or subroutines, and the output can be either caught in a variable or written to a file or open filehandle.

expand_template

    # Generic syntax
    expand_template(%OPT);

    # -either-
    my $result = expand_template(source_file => '...');

    # -or-
    expand_template(include_path => [qw{/tmp/foo /usr/local/lib/foo}],
                    source_file => '/usr/local/lib/foo/foo.xml',
                    output_file => \*FH,
                    errors_to => sub {},
    # -or-
    expand_template(source => $long_string);

Expands a template stored somewhere, and writes the output somewhere else. The generated script is executed in a uniquely-named package, without any enforced use strict preceding it. This call normally stores the printed output from the template-script in a temporary scalar variable, and returns this scalar. You can redirect the output somewhere else though.

With versions of Perl prior to 5.8, you must redirect output using the output_file option. Your program will fail ungracefully if you don't. This is because the internal store-and-return mechanism relies on the ability to open a filehandle to a scalar ref, which was introduced with Perl 5.8.

The options are:

source

A string containing the template to convert to Perl code and run. Either this or source_file must be specified.

source_file

A source file containing the template to execute. This can be either a string containing the name of a file, or an open filehandle passed by reference.

output_file

Redirects output to a specific file. Like source_file, this may be either a filename or a filehandle.

errors_to

Defines how errors raised by the template code are handled. This can be either a subroutine (which will be called with $@ as its single argument), or a filehandle (to which the error message will be printed). The default is to propagate them back via die.

include_path

An array containing a number of directories to search when processing the <?include...?> directive.

reported_filename

Overrides the filename associated with error messages.

expand

    Piffle::Template->expand(%opt);

Identical arguments, and return values to expand_template(), but with object-oriented syntax.

template_to_perl

    my $perl = template_to_perl($template_txt, $filename, @inc);

This is what expand() and expand_template() use to generate the Perl script that gets eval()ed. Useful for debugging. By the way, <?include?> is processed in this pass.

The Templating Language

There are only three constructs to worry about, the rest is just Perl, and the language you're embedding it in.

<?perl...?>

    <p>
      <?perl  print $polite_greeting;  ?>
    </p>
    <!-- You'll have to do your own escaping here. -->

    <ul>
      <?perl for (1 .. 10) { ?>
      <li>Hello</li>
      <?perl } ?>
    </ul>

The tokens <?perl and the immediately following ?> denote a fragment of embedded Perl. Any calls to print go to the currently selected output. The syntax is supposed to look like an XML processing instruction. If you use it where an XML PI could be used, your document should validate.

<?include...?>

    <?include sidebars/easter_bunny.pl.html ?>

This is a simple textual inclusion. The named file is searched for in the include paths passed to template_to_perl(), and is opened, transformed to a Perl script, and included into the Perl script being generated.

If the file doesn't exist, you get a warning about the problem; your template code doesn't fail before compilation if an inclusion can't be found. This is the right behaviour for template code; if you want to import vital constants and subroutines, you should be using use or require instead, and writing a proper Perl module.

Textual inclusions happen before compilation, i.e. when template_to_perl() is called.

{$varname} etc.

     <p>{$polite_greeting}<p>

     <p>{@arrayvar}</p>

     <a title="{$title}" href="foo.cgi?t={$title,uri}&amp;p=1">
       {$title}</a>

Variables with simple names (/^[\$\@\%]\w+$/)can be interpolated into plain text by writing their names inside curly brackets. The resulting code contains a print statement wrapped around something which escapes the contents of the variable.

You can optionally use a nonstandard character escaping style by appending a comma after the variable name, followed by the name of an escaping style. the escaping styles are:

xml
    {$varname}          ;; typical
    {$varname,xml}      ;; canonical

This is the default, and is also what you'll get if the escaping style you use isn't known. Characters in the set [&<>"'] are turned into decimal XML character references suitable for all XML dialects and hopefully all parsers.

uri
    {%varname,uri}

Bytes (not characters) which aren't safe for use as an unparsed token in a URI are escaped into their hexadecimal representation. It's pretty much what CGI::escape() does.

raw
    {@varname,raw}

Interpolates a variable without doing any escaping on its value whatsoever. Make sure you know what you're doing before trying this.

The syntax for variable interpolation is intended to look something like the way Expressions can be interpolated into attributes in XSLT. The default escape style was chosen because in most template CGI template work, I've tended to use this style most often.

Character escaping styles are intended to be extensible by module users. They just aren't yet.

Order of Execution

When your wrapper script calls:

    Piffle::Template->expand(source_file => 'foo/bar.pl.html',
                             output_file => \*STDOUT);

The file foo/bar.pl.html is slurped in, and transformed to a Perl script using template_to_perl(). The resulting script is studded with #line directives, so errors will be reported from the right place. Any textual inclusions happen at this point too.

The script is then run using Perl's eval-string operator. Despite the fact that the data came from an external file, this operation is considered taint-safe due to the way the template language parse works. Do not use this module if there are doubts about whether the Perl code in your template files is trustworthy.

Any print statement in the template code, or in library code called from it, is sent to STDOUT.

Worked Examples, Tricks and Tips

Database Table Splats

This charming term comes from my workplace, and describes dumping the results of a SQL query into an HTML table for display and viewing. A common approach to doing this in a Perlish templating language is:

    <table>
      <?perl
        while (my ($id,$name,$colour,$partnum) = each_part($manuf))
        {
      ?>
      <tr>
        <td>{$id}</td>
        <td>{$name}</td>
        <td>{$colour}</td>
        <td>{$partnum}</td>
      </tr>
      <?perl
        }  #each_part
      ?>
    </table>

The corresponding definition of each_part() follows. It has to do a little state management so that it can behave a little like Perl's builtin each operator.

    use DBI;
    our $dbh = DBI->connect('some_dsn', 'user', 'secret') or die;
    our $each_part_sth;

    sub each_part
    {
            my $manuf = shift;
            if (! defined $each_part_sth)
            {
                    $each_part_sth = $dbh->prepare(q{
                            SELECT p.id,p.name,s.colour,s.partnum
                              FROM  Parts p, Styles s
                             WHERE p.id = s.part
                               AND p.manufacturer = ?
                    });
                    $each_part_sth->execute($manuf);
            }
            my @ret = $each_part_sth->fetchrow_array;
            unless (@ret)
            {
                    $each_part_sth->finish;
                    undef $each_part_sth;
            }
    }

Optional Attributes

Consider the HTML select box:

    <select name="colour" multiple="multiple">
      <option value="r" selected="selected">Red</option>
      <option value="g">Green</option>
      <option value="b">Blue</option>
    </select>

The option elements may or may not be selected. Sometimes you want to make template code that prints the above multiselect using information about the selection state from a database. Unfortunately, you can't just write

    XXX Incorrect Code XXX
    <option value="{$col_s}" {$is_sel}>{$col_l}</option>
    XXX Incorrect Code XXX

because your template wouldn't validate. The fix is to use the following messy workaround:

    <select name="colour" multiple="multiple">
      <?perl
        my $sel_hack_on = '" selected="selected';
        while (my ($col_s, $is_sel, $col_l) = each_col())
        {
             my $sel_hack = $is_sel ? $sel_hack_on : '';
      ?>
      <option value="{$col_s}{$sel_hack}">{$col_l}</option>
      <?perl
        }  #each_col
      ?>
    </select>

It's ugly, but it works.

Messing with Execution Order

Special blocks such as BEGIN and END can be used to change the order in which output is generated, which is useful for generating HTTP headers in your code while retaining good XML syntax:

   <?xml version="1.0"?>
   <foo>
     <?perl
       # All of the above turns into prints. But we want the header to be
       # printed before that.
       use CGI;
       BEGIN {
               # ... do something with the POSTdata ...
               print CGI->header(-type => 'application/xml',
                                 -charset => 'UTF/8',
                                 -status => "201 Created");
       }
     ?>
     <bar name="Cheers" location="East West 13th St, NY, USA" />
     <pub name="Eagle &amp; Child" location="Oxford, Oxon, UK" />
   </foo>

You can't put a lump of Perl before an XML declaration because that would break XML's syntax. Sticking the header print inside a BEGIN block causes the HTTP header to be emitted in a place that keeps web servers happy. END is slightly less useful than BEGIN, but could be used to clean up resources after you've finished executing. If you try to use INIT or CHECK, you'll get an error looking like:

    Too late to run INIT block at DATA line 5, <DATA> line 1.

This is because the Perl runtime has already started up, and you can't catch the transition between the compilation phase(s) and normal execution. See "BEGIN, CHECK, INIT and END" in perlmod for more on this.

Similar Modules and Related Software

You may wish to consider a number of other modules which do a similar thing to Piffle::Template. They're almost certainly far better.

Text::Template - Mark-Jason Dominus

Good and simple, with more support than this thing. Perl code can be written between curly brackets: '{' and '}'. Each pair of brackets corresponds to an eval"", and the resulting value of the block gets interpolated into the output, raw. Loops are done inside the brackets, and if you want each pass to generate output, you have to concatenate onto a variable. The delimiter syntax can be varied from the default.

Embperl - G. Richter

Uses a rather complicated syntax for its embedding glue, and features a pluggable framework, but does essentially the same thing as Piffle::Template. Automatically HTML-escapes its interpolations.

ePerl - Ralf S. Engelschall

A perl-in-plaintext dialect similar to Embperl and Piffle::Template which comes with its own Perl interpreter and an Apache plugin. Allows shorthand for interpolation. Bound to be much faster than my effort, at the expense of some extra weight and dependencies.

mmm-mode

This is an Emacs mode for writing blocks of one language inside another language, using delimiters to separate the two. It can be used with any of the above, and features delimiter definitions for some of them.

BUGS

The embedding syntax cannot be changed, and it's somewhat monopurpose in intent: write Perl in XML, but make it validate.

You need to escape the embedding tokens to stop Piffle::Template from choking on them.

Error reports can catch some very strange code when the generated script is syntactically invalid, mostly around interpolations with escapes. You have to know what you're doing in order to debug this sometimes, despite the #line directives. Experiment with template_to_perl() to see the kinds of horrors that get generated.

However, if you think this wheel is rounder than others out there, please tell me. I might make it into a more formal release. However, there are plenty of wheels out there to choose from. I think this one has the nicest syntax, however.

AUTHOR

Andrew Chadwick, <andrewc-ptemplate200402@piffle.org>.

This software may be distributed under the same terms as Perl itself.