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

NAME

TemplateRex - A Template toolkit that partitions code from text and uses nestable sections.

SYNOPIS

 # Assuming you have the following:
 # 1. Template file - "my_template.html"
 # 2. Hash consisting of the data to merge with the template - %data_hsh

 use TemplateRex;

 $args{'file'} = "my_template.html";

 $t_rex = new TemplateRex( %args );            # Arguments can be either a hash or hash reference

 $t_rex->render(\%data_hsh);                   # Prints to standard out
 $t_rex->render(\%data_hsh, "rendered.html")   # Prints to a file

 # Or a functional interface
 render_this(\%args, \%data_hsh);
 render_this(\%args, \%data_hsh, "rendered.html");

DESCRIPTION

The objective of TemplateRex is to achieve complete separation between code and presentation. While this module was developed with html generation in mind it works equally well with any text based files (such as gnuplot scripts).

Most CGI web based application start off with placing all the html text within print statements in the code or generate html via functions such as with CGI.pm. For applications of any size or sophistication this approach quickly develops maintenance issues such as

  • Code becomes bloated with embedded html.

  • Cannot leaverage the use of wysiwyg html generators (Dreamweaver, Frontpage).

  • The html is within the domain of the code programmer and not the html designers.

Templates solve this problem by outsourcing the presentation or html outside the code. The next step of evolution is then to place code within the html (asp, php, jsp) to handle things like generating rows of a table or repeated sections or chunks of html. The problem with appoach are

  • HTML becomes bloated with embedded code

  • If you are using several 'skins' or templates sets for a different look-and-feel for an application, pieces of code tend to be replicated in different templates sets.

  • Cannot leaverage the use of wysiwyg html generators (Dreamweaver, Frontpage).

  • Security issues with templates being able to execute code. That is you need to be able to 'trust' your template designers.

It is the opinion of the author that both extremes present their own sets of problems and that partitioning of code from presentation into their own separate realms is the best approach for long term maintenance of large and/or sophisticated web applications.

Variable Replacement

At the most basic level this module replaces "$variables" within your template with values of a data hash. For example, if you have a hash with a key of "time" and some value then that value will replace each $time in your template.

$data{time} = "Fri Jul 19 17:30:20 PDT 2002";

Template file:

   It is now '$time'

Rendered html:

   It is now 'Fri Jul 19 17:30:20 PDT 2002'

Triggered Function Calls

In addition the template processor can run user definded Perl functions. For example if a function exists in your code such as:

sub _get_time { return scaler localtime }

Template file

   It is now &get_time()

Rendered html

   It is now Fri Jul 19 17:30:20 PDT 2002

Note the use of the underscore prefix is to prevent a template author from running any function within your code. Therefore all functions that you want to trigger from the html template should be defined with a prefix. The underscore is the default prefix but this default can redefined.

You can even pass arguments to the triggered function. For example if you have a defined function such as:

sub _get_time { $arg = shift @_; return scalar localtime($arg) }

Template file:

   Last modified on &get_time(1042133373)

Rendered html:

   Last modified on Thu Jan  9 09:32:45 2003

There is one reserved function call &include_file('my_header.html') that will include the specified file at the location of were the function is specified.

Template Section Parsing

However the unique and most useful part of TemplateRex is the ability to parse a template based on sections. A template can be sectioned up using html comment delimiters such as

  <!-- BEGIN NAME=error_code  -->

    You Must Enter Your Credit Card Number !

  <!-- END NAME=error_code  -->

This defines a section with the name "error_code" which could be optionally rendered or ignored. For example if you had the above section in your template then the following:

 $t_rex = new TemplateRex( { 'file'=>"my_template.html" }  );

 unless ($cc_num) {  $t_rex->render_sec('error_code') }
 $t_rex->render();

Would insert the error string in the rendered template if $cc_num was not defined. If $cc_num was defined then the error message would not appear in the rendered template.

Sections can be reused and nested. If a template section is called more than once then the rendered section is automatically appended or accumulated. If a section is nested than the lower level accumulation is not rendered until the parent section is rendered. This is best demonstrated with a simple example.

If we have a template as shown below that consists of a "tbl" section with a nested "row" section. This template will be used to render a two column table with a header showing the keys and values of the global %ENV hash.

Template file:

     Current Environment Settings <br>
     <!-- BEGIN NAME=tbl  -->
        <table>
          <tr>
            <th> Key    </th>
            <th> Value  </th>
          </tr>

          <!-- BEGIN NAME=row -->
          <tr>
            <td> $key   </td>
            <td> "$value" </td>
          </tr>
          <!-- END NAME=row -->

        </table>
     <!-- END NAME=tbl  -->

Now assume you have some code such as:

 $t_rex = new TemplateRex( { 'file'=>"my_template.html" }  );

 foreach $key (keys %ENV)
 {
   $data_hsh{'key'}   = $key;
   $data_hsh{'value'} = $value;

   $t_rex->render_sec('row', \%data_hsh);   # Render and accumulate rows
 }

 $t_rex->render_sec('tbl');                 # Render the table with the accumulated rows

 $t_rex->render();                          # Render the complete template

The code and template would render something like:

Rendered html

   Key       Value
   HOME      "/home/httpd"
   HOST      "webdev"
   ...
   SHELL     "/bin/tcsh"
   TERM      "xterm"

The power in this is that the table can be generated and previewed in any HTML editor before the data is rendered so that changes to the table can be made completely independent of the Perl coding and data rendering process.

Template Where Art Thou

Templates are expected by default to be in either the current working directory '.' or './templates'. If the requested template cannot be found in the current directory then it will look in a templates sub-directory. This search path can be modified or appended to by using the 'inc_dir_lst' parameter and the set_defaults() class method.

Default Parameters

 * inc_dir_lst       - A reference to a list of directories where templates reside.  The list
 is searched recursively until the a template is found.  Default [ '.', './templates' ]

 * cmnt_verbose      - A flag signalling the template processor to embed the location or source
 of the underlying templates.  Default 1

 * cmnt_prefix_char  - The prefix comment character used if cmnt_verbose flag is set.
 Default '<!--'

 * cmnt_postfix_char - The postfix comment character used if cmnt_verbose flag is set
 Default  '-->'

 * func_prefix       - The prefix added to an embedded function in a temlate.  A prefix is
 used to prevent a template from running any user or native function (such as unlink('*')).
 Default '_'

 * func_package      - The default package where embedded function are called.  This allows
 an application to restrict all template triggered functions to a specific package. Default
 "" which translates to the main package.

The default parameters can be retrieved and set using the class methods

 %config_hsh = TemplateRex->get_defaults();

 TemplateRex->set_defaults(%config_hsh);

The set_defaults class method sets the defaults for all subsequent TemplateRex instances for a session. Also, the set_defaults methods merges with the existing defaults so that you can change one default without overwriting the other defaults.

  $hsh{'func_prefix'} = "my_callbacks_";

  TemplateRex->set_defaults(%hsh);

Will only set the 'func_prefix' parameter leaving the others as they were. The defaults can also be set at object creation. See METHOD below for more infomation.

METHODS

new

synopsis: $trex_obj = new( 'file'=>"my_template.html", %config )

The input to the new() method requires a hash with at least a 'file' or 'string' parameter defined. If 'file' is provided then the contructor will search the include path for the template and then read and preprocess the template. If 'string' is provided then the contructor will preprocess the string template.

render_sec

synopsis: $str = $trex_obj->render_sec( 'section_name', \%data_hsh )

The render_sec() will render the given 'section_name' using the provided %data_hsh for replacement values. Note: The data hash must be passed down as a reference. The render_sec() function will maintain a buffer that is appended to on each subsequent call. This section buffer will automatically be rendered upon a call to render().

If the section is nested (i.e.) within the delimiters of another section then the parent buffer will be appended to when that section is rendered and child buffer will be reset.

If this sounds confusing than see the example provided in the description above.

Also the rendered section is return on a successful render_sec() call.

render

synopsis: $str = $trex_obj->render( \%data_hsh, 'file_out_spec' )

This renders the entire template. If the second arguement is provided than a file will be written, if not than output will be to standard out (or to the client in a cgi environment). Also if desired the output will be returned.

render_this

synopsis: $str = render_this( 'my_template', 'file_out_spec' )

This is a function-oriented call that renders the entire template in one fell swoop. As in the case with the render() OO method this function outputs to a file if provided or to standard out if no file_out_spec is provided. If you do not need sectional processing then this is the only function call you.

Also the rendered section is return on a successful render_sec() call.

AUTHOR

Steve Troxel troxelso@nswccd.navy.mil

BUGS

None known. Make reports to troxelso@nswccd.navy.mil

SEE ALSO

COPYRIGHT

2002/2003 Steve Troxel