The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Template::Recall - "Reverse callback" templating system

SYNOPSIS

        use Template::Recall;

        # Load template sections from file

        my $tr = Template::Recall->new( template_path => '/path/to/template/sections' );

        # Or, use single file, with sections marked
        # my $tr = Template::Recall->new( template_path => '/path/to/template_file.html' );


        my @prods = (
                'soda,sugary goodness,$.99', 
                'energy drink,jittery goodness,$1.99',
                'green tea,wholesome goodness,$1.59'
                );

        $tr->render('header');

        # Load template into memory

        $tr->preload('prodrow');
                                                        
        for (@prods) 
        {
                my %h;
                my @a = split(/,/, $_);

                $h{'product'} = $a[0];
                $h{'description'} = $a[1];
                $h{'price'} = $a[2];

                print $tr->render('prodrow', \%h);
        }

        # Remove template from memory

        $tr->unload('prodrows');

        print $tr->render('footer');

DESCRIPTION

Template::Recall works using what I call a "reverse callback" approach. A "callback" templating system (i.e. Mason, Apache::ASP) generally includes template markup and code in the same file. The template "calls" out to the code where needed. Template::Recall works in reverse. Rather than inserting code inside the template, the template remains separate, but broken into sections. The sections are called from within the code at the appropriate times.

A template section is merely a file on disk (or a "marked" section in a single file). For instance, 'prodrow' above (actually prodrow.html in the template directory), might look like

        <tr>
                <td>[' product ']</td>
                <td>[' description ']</td>
                <td>['price']</td>
        </tr>

The render() method is used to "call" back to the template sections. Simply create a hash of name/value pairs that represent the template tags you wish to replace, and pass a reference of it along with the template section, i.e.

        $tr->render('prodrow', \%h);

METHODS

new( [ template_path => $path, flavor => $template_flavor, secpat => $section_pattern, delims => ['opening', 'closing'] ] )

Instantiates the object. If you do not specify template_path, it will assume templates are in the directory that the script lives in. If template_path points to a file rather than a directory, it loads all the template sections from this file. The file must be sectioned using the "section pattern", which can be adjusted via the secpat parameter.

flavor is a pattern to specify what type of template to load. This is /html$|htm$/i by default, which picks up HTML file extensions. You could set it to /xml$/i, for instance, to get *.xml files.

secpat, by default, is [\s*=+\s*\w+\s*=+\s*]/. So if you put all your template sections in one file, the way Template::Recall knows where to get the sections is via this pattern, e.g.

        [ ==================== header ==================== ]
        <html
                <head><title>Untitled</title></head>
        <body>

        <table>

        [ ==================== prodrow ==================== ]
        <tr>
                <td>[' product ']</td>
                <td>[' description ']</td>
                <td>[' price ']</td>
        </tr>
        
        [==================== footer ==================== ]
        
        </table>

        </body>
        </html>

You may set secpat to any pattern you wish. Note that if you use delimiters (i.e. opening and closing symbols) for the section pattern, you will also need to set the secpat_delims parameter to those delimiters. So if you had set secpat to that above, you would need also need to set secpat_delims => [ '[\s*=+\s*', '\s*=+\s*]' ]. If you decide to not use delimiters, and use something like secpat => qr/MYTEMPLATE_SECTION_\w+/, then you must set secpat_delims => 'no'.

The default delimeters for variables in Template::Recall are [' (opening) and '] (closing). This tells Template::Recall that [' price '] is different from "price" in the same template, e.g.

        What is the price? It's [' price ']

You can change delims by passing a two element array to new() representing the opening and closing delimiters, such as delims => [ '<%', '%>' ]. If you don't want to use delimiters at all, simply set delims => 'none'.

The template_str parameter allows you to pass in a string that contains the template data, instead of reading it from disk:

new( template_str => $str )

For example, this enables you to store templates in the __DATA__ section of the calling script

render( $template_pattern [, $store, $reference_to_hash ] );

You must specify $template_pattern, which tells render() what template "section" to load. $reference_to_hash is optional. Sometimes you just want to return a template section without any variables. Usually, $reference_to_hash will be used, and render() iterates through the hash, replacing the key found in the template with the value associated to key. A reference was chosen for efficiency. The hash may be large, so either pass it using a backslash like in the synopsis, or do something like $hash_ref = { 'name' => 'value' } and pass $hash_ref.

The other optional argument $store must either be the string "a" or the string "s", for "append" or "store", respectively. This tells render() that you want the rendered section to internal storage, until the call to assemble() is made (see below). It's not always expedient to render a section immediately when you invoke render(). Here's how to save the rows from the synopsis loop above:

        for (@prods) 
        {
            # ... etc ...

            $tr->render('prodrow', 'a', \%h);
        }

To replace an internally rendered section instead of appending to it, change "a" to "s":

        $tr->render('section', 's', \%h);

Use assemble() below to arrange the sections you want and output.

preload( $template_pattern );

In the loop over @prods in the synopsis, the 'prodrow' template is being accessed multiple times. If the section is stored in a file, i.e. prodrow.html, you have to read from the disk every time render() is called. preload() allows you to load a template section file into memory. Then, every time render() is called, it pulls the template from memory rather than disk. This does not work for single file templates, since they are already loaded into memory.

unload( $template_pattern );

When you are finished with the template, free up the memory.

trim( 'off|left|right|both' );

You may want to control whitespace in your section output. You could use s/// on the returned text, of course, but trim()is included for convenience and clarity. Simply pass the directive you want when you call it, e.g.

        $tr->trim('right');
        print $tr->render('sec1', \%values);
        $tr->trim('both')
        print $tr->render('sec2', \%values2);
        $tr->trim('off');
        # ...etc...

If you just do

        $tr->trim();

it will default to trimming both ends of the template. Note that you can also use abbreviations, i.e. $tr->trim( 'o|l|r|b' ) to save a few keystrokes.

assemble( $array_ref [, 'clear'] )

If you have stored any sections internally using the "a" or "s" directives to render(), this method is used to return those sections. Pass in a reference to an array that contains the section names you want returned, e.g.

        print $tr->assemble( [ 'section1', 'section2', 'section3' ] );

This also gives you some flexibility, for instance, you could just as easily reverse the sections like

        print $tr->assemble( [ 'section3', 'section2', 'section1' ] );

Since internal storage causes memory to be used, you will probably at some point want to remove all the stored sections. Do this with the 'clear' parameter. For instance,

        print $tr->assemble( [ 'section1', 'section2' ], 'clear' );

will return the sections, and immediately remove them from internal storage.

AUTHOR

James Robson <info AT arbingersys DOT com>

SEE ALSO

http://perl.apache.org/docs/tutorials/tmpl/comparison/comparison.html