SYNOPSIS

 use OODoc::Template;
 my $t = OODoc::Template->new;

 my $template = ".....";  # usually read from file
 my %values   = ( a => 3 );

 # list context keeps parse tree as well
 my ($output, $tree) = $t->process($template, \%values);

 # scalar context catches output in string
 my $output = $t->process($template, \%values);
 my $output = $t->process($tree, \%values);
 # void context output to selected file-handle
 select OUTPUT;
 $t->process($template, \%values);
 $t->process($tree, \%values);

DESCRIPTION

The OODoc::Template module is a light-weight but powerful template system, only providing display needs for applications, not behavior. Let's start with a promise: this module will never grow into a new programming language, as all the other template systems did over time.

There are at least a zillion comparible modules on CPAN, but this one is mine ;-)

Short introduction

If you are used to template systems, then you should read this to get a rapid overview on the features implemented by this module.

DETAILS

This module works as simple as possible: pass a string to process() with some values to be inserted in the string, and the result is printed to STDOUT.

Getting started

context

There are three ways to produce output via the template system. It depends in which context you call process(), where the output goes to.

prepare for performance

When used in a website, you may want to produce the various templates once, before the processes get forked. Just select the output to the null device, and then call all templates once.

   my %top;
   foreach my $lang ( qw/en nl de/ )
   {   my ($output, $parsed) = $t->process($template, lang => $lang);
       $top{$lang} = $parsed;
   }

   print $t->process($top{nl}, a => 42);

Some processing tricks will seriously hinder the caching of the parsed templates. If you use DYNAMIC, then you are on your own. If you use variables in the filenames for included templates, then you may miss the cache.

Expanding variables

The $template string contains HTML with special comment blocks. These special comment blocks are replaced by the specified values. The block can appear in two shapes (which may provided different output):

   <!--{TAG ATTRIBUTES}-->
      some text
   <!--{/TAG}-->

or

   <!--{TAG ATTRIBUTES}-->

The first example shows a container, the second a terminal tag. The TAG is one of the specified values. ATTRIBUTES are used when the TAG is not a constant value, but dynamically produced.

Containers are used to enclose a region where additional values as set. The TAG is related to an ARRAY of HASHes, which are effeciated one after the other, as such creating a loop around this block

Conditionals

The standard conditional structure, which is used everywhere, is the simple container. When the container has values attached to is (always a HASH or ARRAY-of-HASHES filled with key-value pairs), the content is displayed. So, a simple if-then looks like this:

 <!--{want_something ATTRIBUTES}-->
   ...
 <!--{/want_something}-->

The optional ATTRIBUTES are extra values set when processing the container. The pre-defined tag defined can be used to only set attributes: it's a no-op.

You may decide to be more explicit in the if-then, by using the optional IF keyword:

 <!--{IF want_something ATTRIBUTES}-->
   ...
 <!--{/want_something}-->

When the TAG starts with <NOT > or <NOT_>, it is used to negate the boolean interpretation of the values returned by evaluating the tag:

 <!--{NOT want_something ATTRIBUTES}-->
   ...
 <!--{/want_something}-->

An if-then-else looks like this:

 <!--{want_something ATTRIBUTES}-->
   ...
 <!--{ELSE want_something}-->
   ...
 <!--{/want_something}-->

The want_something tag must produce either a HASH or an ARRAY-of-HASHes or undef, because that is what containers do. Because of parser limitations, the

Definition

tags

Tags are barewords (may only contain [0-9a-zA-Z_]), which are looked-up in the < %values >, which are passed with new() and process() to produce a value.

attributes

Attibutes are values which are used when the text which is to be inserted is produced dynamically. Their syntax is like this:

 # attributes are optionally separated by comma's
 attrs:  attr , attrs
       | attr attrs

 # a hash initiation syntax may be used, but single
 # barewords as well
 attr:   bareword
       | bareword => " string " | bareword = " string "
       | bareword => ' char* '  | bareword = ' char* '
       | bareword => bareword   | bareword = bareword
       | bareword => variable   | bareword = variable

 string: ( char | variable ) *

 # pass value produced by other tag
 variable:
         '$' tag
       | '${' tag attrs '}'

A string may contain variables, which are stringified. This means that tags which produce hashes or arrays are not usuable to interpolate.

» Example:
 <!--{section nr => 2, show_number, a => "monkey", chapter => $cnr}-->
 <!--{section nr=2 show_number a=monkey chapter=$cnr}-->

The attributes result (internally) in a hash (of ARGS) which contains the keys nr, show_number, a, and chapter with respecively values 2, true, monkey, and the looked-up value for cnr.

values

The values which are related to the tags are rather powerful. When a certain tag can not be found, the value is undef.

the DYNAMIC value

The procedure of a value lookup is quite straight forward: start with the values defined by the innermost block (container) which defined a HASH or ARRAY of HASHes, and work the way back through the enclosing blocks until the initial values have been reached.

If the tag was not found as key, undef is used. If the key was found, than the related value is treated as described in the previous section.

Working through the list of blocks, a miss on a value HASH will cause a second lookup: for the key DYNAMIC. If a block's set of values contains this key, the related CODE reference is called to produce a value. If the produced value is undef, the search will continue to outer blocks. Other results will be treated as any other value.

The DYNAMIC keys may be used like AUTOLOAD: to handle unexpected keys. For instance, used in the initial hash of values (passed to the parse method) it can be used to produce warnings on use of undefined tags.

Pre-defined tags

Tags can as well represent procedures, which are executed to produce data when filling in templates (via CODE references), or represent constants.

Pre-defined values:

Pre-defined procedures:

» Example: change the markers locally

In this example, the content of the container uses other markup symbols than the container itself.

  <!--{define markers="<{,}>" }-->\
    value of c: <{c}>\
  <!--{/define}-->
» Example: use of macro

A macro is used to define a piece of template, but apply it later.

 <!--{macro name="chapter"}-->
    <h2><!--{title}--></h2>
 <!--{/macro}-->

 <!--{template macro="chapter" title="hi there!"}-->
» Example: use of template file
 <!--{template file=$lang/header.txt alt=en/header.txt}->

White-space removal

The template tags are usually quite large with respect to the output that they produce. Therefore, you often wish to use more lines in the template file, than will be present in the output. However, you have to help the output processor.

A backslash (followed by any number of invisible blanks) before a new-line will have that new-line, and all following (visually) blank lines removed. When the first line with (visual) content starts with a (start or end) marker, then the blanks before that are removed as well. In other cases, the blanks are left intact.

» Example: of white-space removal

The template looks like this:

 The follow\
 ing error was\

  produced:
     <!--{error}-->, \
     <!--{errno}-->

The output is:

 The following error was produced:
    No such file or directory, 2