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

NAME

Text::FastTemplate - Class that compiles text templates into subroutines.

GETTING STARTED

  # It can be as simple as ...

  Text::FastTemplate->new( file => 'template')
        ->print( \%data);

  # or as complex as ...

  Text::FastTemplate->defaults(
    path => [
      '/apps/sales/heads_n_feet',
      '/apps/sales/content'
      ],
    );

  Text::FastTemplate->preload( [
    { file => 'template.txt', key => 'the_page' }
    ]);

  $report= Text::FastTemplate->new( key => 'the_page');
  $output= $report->output( \%data );
  print $output;

NEW FEATURES

    These features were added for the most recent releases.

    *

    For preload(), the programmer now can use an array to pass the list of templates to be loaded; an ARRAY-REF is no longer required and is deprecated.

    *

    Defaults for more than one group can be created with a single call to the defaults() class method, versus a call for each group as was previously required.

    *

    In addition to the template list, the preload() class method accepts a list of common attributes for each of the specified templates.

    *

    Templates which have been modified since being loaded can now be dynamically reloaded via the 'reload' constructor attribute.

    *

    The programmer can organize template objects and defaults into groups via the 'group' and 'key' constructor attributes.

DESCRIPTION

Text::FastTemplate compiles templates that are written in a line-oriented syntax that resembles the C-preprocessor syntax into Perl subroutines. As much as possible, it is designed to be:

Simple

the API and the template syntax are very simple.

Fast

the generation of the template output is very fast, as fast as perl can print anyway.

Maintainable

the application and the presentation are completely separated.

As a template processor, its core purpose is to provide macro-substition into a text template that is provided by the user. However, simple macro substitution hardly comprises a useful template processor.

In order to be truly useful, Text::FastTemplate implements two simple flow-control mechanisms:

        loops or repetitive text:
                #for / #endfor

        conditions or optional text:
                #if / #elsif / #else / #endif

and a mechanism for including additional templates

        external template inclusion:
                #include

In the end, Text::FastTemplate provides simple yet powerful interface between the application and the presentation layer that provides both the programmer and the presentation designer excellent control over their respective components.

One of my common applications of this module is to derive a Page class from it. The Page class overrides the output() method with a method that adds some macroes to every message passed to the object, such as a DATE or USER_ID string. The Page class can also have an import() function that set defaults and preloads templates.

USAGE

This POD has been written as a quick reference to help the programmer start using Text::FastTemplate quickly. Comprehensive documentation with examples and references will be available at the Text::FastTemplate web site.

        http://bozzio.the-lehrs.com/Text::FastTemplate/

TEMPLATE METHODS

To start using Text::FastTemplate immediately, one needs only two methods, new() and output().

The constructor: new()

As the constructor, new() expects a hash and returns a Text::FastTemplate object. Several attributes provide the programmer with useful parameters for organizing templates. However, each call to new() requires only that either the 'file' or the 'key' attribute be provided.

  + file        the name of the file that contains the template
  + key         a unique name given by the user, used for caching
  + path        the search path to find the template file
  + group       the template's group, used for defaults and caching
  + reload      specifies whether templates should be reloaded
  + debug       debug flag, increase execution verbosity
file

This is the name of the template to be loaded. It can be an absolute or relative pathname. If a relative pathname is used then the directories specified in 'path' are searched. If 'path' is not specified then the current working directory for the process is searched.

key

This is a name that the programmer associates with Text::FastTemplate object. If groups are being used then the key needs to be unique within the template's assigned group. It was to be used with the preload() class method.

path

This is a list of directories to be searched when a file is specified with a relative pathname. The list of paths is passed in an ARRAY-REF.

group

This designates the group for which to use defaults and object cache. Each group has its own object cache which enables the caller to use the same KEY in different groups.

reload

By setting this to true, Text::FastTemplate will reload all of the template files that are used by this template that have been modified. It checks the mtime of the files. So all that is necessary to force a reload is to 'touch' the file that should be reloaded.

debug

setting this to true prints some debugging information. This is only partially useful.

Another constructor: preload()

This class method simply loads a list of templates by creating a Text::FastTemplate object [ new() ] for each template in the list that is passed to it. This purpose of this method is to bypass the latency associated with reading and compiling a template the first time. This method can be called in several ways. But the most basic call requires an ARRAY-REF to a list of HASH-REFs. These hashes are passed to new() iteratively.

    e.g.,

    Text::FastTemplate->preload( [
        { file => 'file1.txt', key => 'file1' },
        { file => 'file2.txt', key => 'file2' },
        { file => 'file3.txt', key => 'file3' },
    ]);

preload() and new() differ significantl in that both the 'file' and 'key' attributes are required by the preload() constructor. That is because it uses each template's 'key' as its index when it caches them.

Grouping defaults: defaults()

This is used to set default values for constructor attributes, 'path', 'reload' and 'debug'. These defaults values are used whenever a new object is instantiated. It is useful during a preload phase of a program. The For example,

    Text::FastTemplate->defaults( path => [ '/apps/sales/pages' ]);
    Text::FastTemplate->preload( [
        { file => 'page1.txt', key => 'page1' },
        { file => 'page2.txt', key => 'page2' },
        ]);

If the 'group' attribute is sent to defaults() then these default values are assigned to the specified group so that, whenever that group is used in a succeeding constructor, the defaults that are assigned to that group are used to instantiate that object. For example,

    Text::FastTemplate->defaults(
        group => 'example',
        path => [ '/apps/mfg/pages', ],
        reload => 1
        );
    Text::FastTemplate->new(
        key => 'a', file => 'a.tpl', group => 'example');
    Text::FastTemplate->new(
        key => 'b', file => 'b.tpl', group => 'example');
    Text::FastTemplate->new(
        key => 'c', file => 'c.tpl', group => 'example');

These calls assign the example 'path' and 'reload' values to the 'example' group. During the calls to new() in which the 'example' group is specified, those default values are used to instantiate the template objects.

The 'group' attribute is not necessary since a default group, '_default', will be automatically assigned to the object without the caller's knowledge.

Object methods with action: output(), print()

These are identical:

    print $template->output( \%data);
    $template->print( \%data);
output()

These methods take the data provided by the user in a hash and plug it into the compiled template. output() returns a scalar that contains the resultant text.

print()

This method actually sends the the output of output() to STDOUT. This method might or might not be deleted in a future version unless a lot of people use it. It might be better to save this for a derived class which can send the output to a customized file-handle.

TEMPLATE SYNTAX

The syntax of Text::FastTemplate is simple. Those who are familiar with the C-prepocessor will recognize the similarity. Here is an example of a template that uses everything Text::FastTemplate offers.

        This is a ##A_SIMPLE_MACRO##

        #include 'another_file.txt'

        #if ##A_FACT##
          It is true.  See?  ##A_FACT##
        #elsif ! ##A_FACT##
          It is false.  See?  ##A_FACT##
        #else
          What is it then?
        #endif

        #for ##A_FOR_MACRO##
          ##A_FOR_MACRO_LOOP_ID## : survey says, "##SOME_TEXT##"
        #endfor

Templates are processed in two ways.

  • macroes are substituted into the text.

  • statements are processed.

Macro substition is performed anywhere in the text of the template. They are case-sensitive.

Statements are line-oriented. That means that a statement must exist on any lines by itself; a statement cannot be embedded in the actual content.

However, statements can be continued on separate lines by using the backslash. Also, the statement doesn't need to start at the left margin.

A statement is comprised of a keyword and a macro argument, if one is required. Whereas the macroes are case-sensitive in all contexts, keywords are case-insensitive. The exception to this are #if and #elsif which accept any perl expressions that is accepted in perl's if and elsif statements.

Text::FastTemplate offers the following keywords:

        + #include
        + #if, #elsif, #else and #endif
        + #for and #endfor

They are described in detail below.

Macro Syntax

Very simply, Text::FastTemplate identifies a macro as a word bounded by double-hashes, '##'. Macroes are case-sensitive. The regular expression looks like this,

        $macro =~ m/##\w+?##/

If a template uses a macro named 'A_SIMPLE_MACRO' then it will refer to that macro in its text as '##A_SIMPLE_MACRO##'. In the program that uses this template, this macro will be referred to in a hash by its real name, 'A_SIMPLE_MACRO'.

Here is an example.

        This is a ##A_SIMPLE_MACRO##

If we now assign some text, "lousy example", to the macro 'A_SIMPLE_MACRO' in our data structure that we pass to this template then the output will look like this.

        This is a lousy example

[ The ability to specify a delimiter different than '##' might be provided in a future version. ]

#include

Other templates can be included by a template.

        #include 'filename' | "filename" | filename

The name of the file can be a relative or absolute pathname, just as with the 'file' constructor attribute. If a relative pathname is used then the same rules apply as during object instanatation. The 'path' of the including object, if provided, is searched; otherwise, the current working directory of the script is searched.

The filename can be enclosed in single quotes, double quotes or not at all. All are legal. Currently, a macro cannot be used; this feature is in the queue.

Beware of #include loops!!! This will cause infinite recursion in your program. Currently, Text::FastTemplate does not check for infinite recursion; this, too, is in the queue.

#if / #elsif / #else / #endif

The condition statements should be obvious. They correspond directly with the Perl statements. The #if and #elsif statements require arguments. They don't need to be macroes; but they are not otherwise very useful.

        #if ##A_FACT##
          It is true.  See?  ##A_FACT##
        #elsif ! ##A_FACT##
          It is false.  See?  ##A_FACT##
        #else
          What is it then?
        #endif

The condition statements are the exception to the separation of the presentation and the application.

That is because the argument given in a condition statement can be a full-fledged Perl expression. The only difference is that only scalars are available and only via the macro syntax. You might use something like this:

        #if ##PAGE## eq 'home'
                highlight the home tab
        #elsif ##PAGE## eq 'search'
                highlight the search tab
        #endif

or

        #if ##GROUP_ID## =~ /^dba-.*/
                you are a group member
        #endif

This is really not a design flaw; it was just easier to implement it this way.

#for / #endfor

The repitition or looping construct used by Text::FastTemplate is the #for / #endfor statement. This is not part of the C-preprocessor syntax; but the resemblance is still there.

        #for ##A_FOR_MACRO##
          some text; survey says, "##SOME_TEXT##"
        #endfor

#for LOOP_ID

There is one special feature about the #for loop that needs to be mentioned. Every #for loop has a special macro that corresponds to the number of times that the loop has iterated. This is called the LOOP_ID. To access the LOOP_ID inside of the #for loop, simply concatenate the #for macro and '_LOOP_ID'. For example,

        #for ##A_FOR_MACRO##

uses a macro named 'A_FOR_MACRO'. Concatentate this with '_LOOP_ID' and the result is

        A_FOR_MACRO_LOOP_ID

This special macro can be used to access to number of iterations of the #for loop. For example, if the following loop iterates three times

        #for ##A_FOR_MACRO##
          iteration ##A_FOR_MACRO_LOOP_ID##
        #endfor

then the result will be

        iteration 1
        iteration 2
        iteration 3

Clear?

TEMPLATE DATA STRUCTURE

A data structure that might be used for the above examples might look like this. It is a hash-ref and it is the basic data structure used by Text::FastTemplate. The keys of this hash-ref correspond with the template's macroes. The values for the hash-ref are scalars except for the #for macro.

The #for macro uses a array-ref that contains a list of these hash-refs.

        { A SIMPLE_MACRO => 'fact',
          A_FACT => 1,
          A_FOR_MACRO => [ # a #for loop
            { SOME_TEXT => "Iteration #1"},
            { SOME_TEXT => "Iteration #2"}
            ]
        }

BUGS

These bugs were deemed to be acceptable for release since they are currently in production on several web sites. They have been targeted for elimination.

  • Macro scopes are poorly handled. Consider a #for loop around some HTML tags that refer to a ##BGCOLOR## macro that is commonly used. The #for loop will not access the ##BGCOLOR## macro used outside of the loop. The ##BGCOLOR## macro needs to be included in the data that is passed to the #for loop.

  • Infinite recursion with the #include statement is not checked. Care should be taken to avoid it. One of the easiest cases in which this might happen is with circular inclusions. This is the same problem as with Perl's circular references and garbage collection. It shouldn't be new to anyone.

  • If the any line of the actual content of a template starts with a keyword then it will be processed by Text::FastTemplate.

FUTURE ENHANCEMENTS

  • Currently, files and templates have a 1-to-1 relationship. In the future, they might have a many-to-1 relationship by using real "templates" instead of "files". It will be necessary to do this before the following enhancement can be accomplished.

  • Add "#template" to be able to define several templates in a single file, thus enabling a designer to consolidate them into a template library. This would approach Template::Toolkit's widget plug-in feature but without the ability to pass arguments to them. That is a very cool feature because the designer can control more parameters.

  • Just as #include inserts a body of text from another template, it would be nice if a block of text could be "wrapped" in a text from another template.

  • Add "#define" and "#undef" to template syntax; will require that handling of macro scope be improved.

  • Add support for fixed-width macro substitution, similar to printf().

  • Error-handling _needs_ to be improved.

  • Add "source" attribute so that strings and arrays can be used for templates, maybe.

AUTHOR

Robert Lehr, bozzio@the-lehrs.com

I certainly would appreciate any feedback from people that use it, including complaints, suggestions or patches. Even people that don't use it are welcome to send comments.

COPYRIGHT & DISCLAIMER

Copyright (c) 2001 Robert Lehr. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

Caveat emptor. Use this module at your own risk. I will accept no responsibility any loss of any kind that is the direct or indirect result of the use of this module.

DEPENDENCIES

Perl modules: Cwd, Carp

SEE ALSO

perl(1), perlref(1).

1 POD Error

The following errors were encountered while parsing the POD:

Around line 31:

You can't have =items (as at line 35) unless the first thing after the =over is an =item