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

NAME

HCKit::Template - A template system for HTML construction

SYNOPSIS

First you make a template like this, test.tmpl:

 <html>
 <body>
   <h1><* title text="My Friends" *></h1>
   <ul>
     <{ foreach friend as f }>
       <li><* f.name *>, <* f.age *>
     <{ /foreach }>
   </ul>
 </body>
 </html>
 ---RULE---
 <title><h1><* text *></h1></title>
 ---DATA---
 <friend>
   <name>Arthur Beck</name>
   <age>23</age>
 </friend>
 <friend>
   <name>Charles Douglas</name>
   <age>26</age>
 </friend>
 <friend>
   <name>Earl Fairchild</name>
   <age>18</age>
 </friend>
  

Then you convert the template to an HTML page with the following script.

 use HCKit::Template;
 my $tmpl = HCKit::Template->new;
 print $tmpl->rewrite_file("test.tmpl");

The output becomes like this:

 <html>
 <head><title>My Friends</title>
 <body>
   <h1>My Friends</h1>
   <ul>
     <li>Arthur Beck, 23
     <li>Charles Douglas, 26
     <li>Earl Fairchild, 18
   </ul>
 </body>
 </html>

Rewriting process can be controlled by a script. For example, with the following template in a file "test2.tmpl":

 <html>
 <body>
   <h1><* title text="My Friends" *></h1>
   <ul>
     <{ foreach friend as f }>
       <li><* f.name *>, <* f.age *>
     <{ /foreach }>
   </ul>
 </body>
 </html>
 ---RULE---
 <title><h1><* text *></h1></title>

following script produces the same output as the above example.

 use HCKit::Template;
 my $tmpl = HCKit::Template->new;
 $tmpl->set_var( friend =>
                 [ { name => 'Arthur Beck', age => 23 },
                   { name => 'Charles Douglas', age => 26 },
                   { name => 'Earl Fairchild', age => 18 },
                   ]);
 print $tmpl->rewrite_file("test.tmpl");

DESCRIPTION

This module constructs an HTML page from a template. The conversion proceeds with the help of rewrite rules and data sources. Rewrite rules can be specified in the template file itself, or in the Perl script. Data sources can be XML files, or dynamically constructed in the Perl script.

TEMPLATE FILE

A template file consists of three portions: a template itself, rewrite rules, and data sources. A template file starts by specifying a template itself. A line beginning with the string '---RULE---' starts rewrite rules. A line beginning with the string '---DATA---' starts data sources. Sections for rewrite rules and data sources are optional.

Here is an example template file:

 I am <* full-name first=first-name last=last-name *>
 I am <* first-name *> <* last-name *>.
 ---RULE---
 <full-name><* first *> <* last *></full-name>
 ---DATA---
 <first-name>Andy</first-name>
 <last-naem>Davis</last-name>

This template lacks data source, and is converted to:

 I am Andy Davis.

TEMPLATE

Within templates, three kinds of constructs are identified and rewritten by this module: simple constructs, funcall constructs, block constructs;

Following is the syntax of a simple construct.

 <* IDENTIFIER *>

When a simple construct is encountered, the module searches for IDENTIFIER in the rewrite rules and data sources and replaces the construct with its value.

For example, with the following template file:

 <* name *>
 ---RULE---
 <name><b>Harold</b></name>
    

'name' is looked up in the rewrite rules, and the construct is replaced with its value, <b>Harold</b>.

With the following template file:

 <* name *>
 ---DATA---
 <name>Eugene</name>

'name' is looked up in the data source, and the construct is replaced with its value, Eugene.

By concatenating identifiers with ., nested data in the data source can be accessed. For example, the following template

 <* name.first-name *> <* name.last-name *>
 ---DATA---
 <name>
   <first-name>Andy</first-name>
   <last-name>Varmus</last-name>
 </name>

is rewritten to 'Andy Varmus'.

Following is the syntax of a funcall construct.

 <& IDENTIFIER &>
   <key1>val1</key1>
   <key2>val2</key2>
   ...
 <& /IDENTIFIED &>

This construct extends the current environment with the key-value pairs specified in its body and applies the IDENTIFIEER rewrite rule. IDENTIFIER.

For example, the following template

 <& full-name &>
   <first>Andy</first>
   <last>Varmus</last>
 <& /full-name &>
 ---RULE---
 <full-name><* first *> <* last *></full-name>

is rewritten to 'Andy Varmus'.

Following is the syntax of a block construct.

 <{ IDENTIFIER }>
   ...
 <{ /IDENTIFIER }>

This construct invokes a built in block function identified by IDENTIFIER.

For example the following template

 <{ foreach num as n }>
   <* n *>
 <{ /foreach }>
 ---DATA---
 <num>1</num>
 <num>2</num>
 <num>3</num>

is rewritten to 1 2 3

Nested block constructs are supported by supplying a distinctive tag to each construct. Such a tag is appended to IDENTIFIER following the character '@'.

For example, following template

  <{ foreach@1 1..3 as i }>
    <{ foreach@2 1..2 as j }>
      <* i *>-<* j *>
    <{ /foreach@2 }>
  <{ /foreach@1 }>

results in

  1-1
  1-2
  2-1
  2-2
  3-1
  3-2

REWRITE RULE

Rewrite rules appear following a line that begins with '---RULE---'. Each rewrite rule is in the following format:

 <IDENTIFIER>
   BODY
 </IDENTIFIER>

'IDENTIFIER' indicates the name with which this rewrite rule is invoked. BODY indicates the output of this rewrite rule. In BODY, all kinds of constructs can appear as in templates.

For example, with the following rewrite rule and data section,

 ---RULE---
 <greeting>
   Hello, <* guest *>!
 </greeting>
 ---DATA---
 <guest>Andy</guest>

<* greeting *> is rewritten to 'Hello, Andy!' (neglecting the leading and trailing white spaces).

DATA SECTION

The data section consists of data in the XML format.

For example, with the following data section,

 ---DATA---
 <friend>
  <name>Arthur Beck</name>
  <age>23</age>
 </friend>

<* friend.name *> is rewritten to 'Arthur Beck'.

If there are multiple data with the same name, they are grouped as a list of data.

For example, with the following data section,

 ---DATA---
 <friend>
   <name>Arthur Beck</name>
   <age>23</age>
 </friend>
 <friend>
   <name>Charles Douglas</name>
   <age>26</age>
 </friend>
 <friend>
   <name>Earl Fairchild</name>
   <age>18</age>
 </friend>

a block construct of

 <{ foreach friend as f }>
   <* f.name *>, <* f.age *>
 <{ /foreach }>

is rewritten to

  Arthur Beck, 23
  Charles Douglas, 26
  Earl Fiarchild, 18

PRE/POST PROCESSOR

Each construct can have multiple pre/post processors. They are specified following IDENTIFIER in the start tag.

For example in the simple construct, the general syntax is

 <* IDENTIFIER PRE ; POST *>

in which PRE is a space-separated list of pre-processors and POST is a space-separated list of post-procesors.

These processors are applied before and after the invocation of rewrite, respectively.

Each construct has a value stack of its own (construct stack). This stack is used by built in functions to pass and retrieve arguments. Initially, this stack is empty. pre-processors manipulate this stack before invoking the rule/function identified by IDENTIFIER. Then, the stack is reset to empty, and the output of the rule/function is pushed to the stack. The post-processors manipulate this stack and should leave only one element in the stack, which becomes the final result of the rewrite of the construct.

Currently, following pre/post-processors are supported.

name=value

Extends the current environment with name/value pair. It does not alter the construct stack. If value includes a space, it should be quoted by ' or ". This processor is useful only in pre-processing.

Example:

  <* full-name first=Andy last=Varmus *>
  ---RULE---
  <full-name><* first *> <* last *></full-name>

The output becomes: Andy Varmus

FROM..TO

Pushes a list of numbers ranging from FROM to TO (inclusive).

For example, following template

  <{ foreach 1..3 as i }>
    <* i *>
  <{ /foreach }>

results in

  1
  2
  3
trim

Pops an element from the stack, removes leading and trailing space characters from the element, and push the result back to the stack.

For example,

  My name is <* name ; trim *>.
  ---RULE---
  <name>
    Andy
  </name>

is rewritten to

  My name is Andy.

Whereas,

  My name is <* name *>.
  ---RULE---
  <name>
    Andy
  </name>

is rewritten to

  My name is 
    Andy
list-remove-last

Pops an element from the stack, removes the last item from the element (which should be a list), and pushes back the element to the stack.

For example, following template

  <{ foreach fruit list-remove-last as f }>
    <* f *>
  <{ /foreach }>
  ---DATA---
  <fruit>apple</fruit>
  <fruit>banana</fruit>
  <fruit>orange</fruit>
  <fruit>water melon</fruit>

is rewritten to

  apple
  banana
  orange
list-last

Pops an element from the stack, takes the last item from the element (which should be a list), and push back the item to the stack.

For example, following template

  <* fruit ; list-last *>
  ---DATA---
  <fruit>apple</fruit>
  <fruit>banana</fruit>
  <fruit>orange</fruit>
  <fruit>water melon</fruit>

is rewritten to 'water melon'. In this example, the value of 'fruit' is pushed to the construct stack, as the only element, before post-processor is invoked. The post-processor pops the list (apple, banana, orange, and water melon) and pushes back the last item in the list, which becomes the final rewrite of the construct.

not

Pops an element from the stack, negates the value of the element, and pushes back the result to the stack.

For example, following template

  <{ foreach fruit as f }>
    <* f.name *> <{ if f.stock not }>(not available)<{ /if }>
  <{ /foreach }>
  ---DATA---
  <fruit>
    <name>apple</name>
    <stock>0</stock>
  </fruit>
  <fruit>
    <name>banana</name>
    <stock>12</stock>
  </fruit>
  <fruit>
    <name>orange</name>
    <stock>23</stock>
  </fruit>
  <fruit>
    <name>water melon</name>
    <stock>0</stock>
  </fruit>

is rewritten to apple (not available) banana orange water melon (not available)

eq

Pops str2 from the stack, pops str1 from the stack, compares str1 and str2 (string compare), and pushes the boolean result to the stack.

For example, following template

  <{ foreach friend as f }>
    <{ if f.gender 'female' eq }><* f.name *><{ /if }>
  <{ /foreach }>
  ---DATA---
  <friend>
    <name>Alice</name>
    <gender>female</gender>
  </friend>
  <friend>
    <name>Bob</name>
    <gender>male</gender>
  </friend>
  <friend>
    <name>Eve</name>
    <gender>female</gender>
  </friend>

is rewritten to

  Alice
  Eve
or

Pops a boolean value b2 from the stack, pops a boolean value b1 from the stack, and pushes (b1 || b2) to the stack.

  <{ foreach friend as f }>
    <{ if f.hair 'black' eq f.hair 'red' eq or }><* f.name *><{ /if }>
  <{ /foreach }>
  ---DATA---
  <friend>
    <name>Alice</name>
    <hair>black</hair>
  </friend>
  <friend>
    <name>Bob</name>
    <hair>red</hair>
  </friend>
  <friend>
    <name>Eve</name>
    <hair>blonde</hair>
  </friend>
  

is rewritten to

  Alice
  Bob
and

Pops a boolean value b2 from the stack, pops a boolean value b1 from the stack, and pushes (b1 && b2) to the stack.

  <{ foreach friend as f }>
    <{ if f.hair 'black' eq f.eye 'brown' eq and }><* f.name *><{ /if }>
  <{ /foreach }>
  ---DATA---
  <friend>
    <name>Alice</name>
    <hair>black</hair>
    <eye>brown</eye>
  </friend>
  <friend>
    <name>Bob</name>
    <hair>red</hair>
    <eye>blue</eye>
  </friend>
  <friend>
    <name>Eve</name>
    <hair>blonde</hair>
    <eye>black</eye>
  </friend>
  

is rewritten to

  Alice
concat

Pops a string value str1 from the stack, pops another string value str2 from the stack, and pushes a concatenated string "$str1$str2" to the stack.

For example, following template

  <* name ; ', Jr' concat *>
  ---DATA---
  <name>Davis</name>

is rewritten to 'Davis, Jr'.

lookup

Pops an attribute name from the stack, pops a data from the stack, and pushes back the attribute value of the data to the stack.

For example, following template

  <* friend ; 'name.first' lookup *>
  ---DATA---
  <friend>
    <name>
      <first>Michael</first>
      <last>Stuart</last>
    </name>
  </friend>

is rewritten to 'Michael'.

REWRITE RULE OPTIONS

The definition of a rewrite rule can have options after its IDENTIFIER. Options are separated by spaces with each other.

For example, in the following rule,

  ---RULE---
  <full-name trim>
    <* first *> <* last *>
  </full-name>

'trim' option removes leading and trailing white spaces from rewrite rule BODY. Therefore, the above rule is equivalent to

  ---RULE---
  <full-name><* first *> <* last *></full-name>

Currently available options are as follows.

trim

Removes leading and trailing white spaces from rewrite rule BODY.

+

Appends rewrite rule BODY to the previously defined rule with the same name. Normally, when multiple rewrite rules with the same name are defined, the last definition overwrites the others.

For exmaple, the following template

  <style type="text/css">
  <* stylesheet *>
  </style>
  ---RULE---
  <stylesheet>
    body { background-color: #fff }
  </stylesheet>
  <stylesheet>
    frame { border:thin solid #f00 }
  </stylesheet>

is equivalent to

  <style type="text/css">
    frame { border:thin solid #f00 }
  </style>

However, with + option (note the '+' in the second stylesheet rule),

  <style type="text/css">
  <* stylesheet *>
  </style>
  ---RULE---
  <stylesheet>
    body { background-color: #fff }
  </stylesheet>
  <stylesheet +>
    frame { border:thin solid #f00 }
  </stylesheet>

the above template is equivalent to

  <style type="text/css">
    body { background-color: #fff }
    frame { border:thin solid #f00 }
  </style>
  

DATA SECTION

Data section appears following a line that begins '---DATA---'. Data section comprises of data described in XML format.

For example, following template

  <* friend.first *>
  ---DATA---
  <friend>
    <first>John</first>
    <last>Williams</last>
  </friend>

results in 'John'.

CDATA section can be included in the data.

For example, following template

  <* header *>
  ---DATA---
  <header>
    <![[CDATA[
      <h1>Header</h1>
    ]]>
  </header>

results in '<h1>Header</h1>'.

BUILTIN FUNCTIONS

Followings are the list of builtin functions.

foreach
  <{ foreach LIST [AS NAME] }>
    BODY
  <{ /foreach }>

For each item in LIST, it is bound to NAME and then BODY is rewritten. If NAME is omitted, each item is bound to the name 'iter'.

For example:

  <{ foreach num }>
    <* iter *>
  <{ /foreach }>
  ---DATA---
  <num>1</num>
  <num>2</num>
  <num>3</num>

This results in:

    1
    2
    3

Another example:

  <{ foreach site as s }>
    <a href="<* s.url *>"><* s.label *></a><br>
  <{ /foreach }>
  ---DATA---
  <site>
    <href>http://www.yahoo.com</href>
    <label>Yahoo!</label>
  </site>
  <site>
    <href>http://www.google.com</href>
    <label>Google</label>
  </site>

This results in:

  <a href="http://www.yahoo.com">Yahoo!</a>
  <a href="http://www.google.com">Google</a>

Following preprocessors are effective for foreach.

foreach:sep=SEP proprocessor specifies separator between outputs of iterations.

For example,

  <{ foreach num foreach:sep=' | '}>
    <* iter *>
  <{ /foreach }>
  ---DATA---
  <num>1</num>
  <num>2</num>
  <num>3</num>

This results in:

    1 | 
    2 |
    3

foreach:toggle=INIT preprocessor introduces a new variable named 'toggle' that has initial value of INIT. After each iteration, the variable 'toggle' is toggled between 0 and 1.

For example,

  <{ foreach num as n foreach:toggle=0 }>
    <div class="style-<* toggle *>"><* n *></div>
  <{ /foreach }>
  ---DATA---
  <num>1</num>
  <num>2</num>
  <num>3</num>

Results in:

    <div class="style-0">1</div>
    <div class="style-1">1</div>
    <div class="style-0">1</div>
  
if
  <{ if EXPR }>
    BODY
  <{ /if }>

If EXPR evaluates to true, BODY is rewritten; otherwise, nothing is output.

For example, following template

  <{ if link }>
    <* link.label *>
  <{ /if }>
  ---DATA---
  <link>
    <label>Yahoo!</label>
    <url>http://www.yahoo.com</url>
  </link>

is rewritten to 'Yahoo!'.

include
  <* include FILE *>

Reads FILE and processes its rewrite rule and data sections. The template section, if any, is ignored.

For example, following template

  <* include './friends.xml' *>
  <{ foreach friend as f }>
    <* f.name *>, <* f.age *><br>
  <{ /foreach }>

with accompanying file ./friends.data, whose content is

  ---DATA---
  <friend>
    <name>Arthur</name>
    <age>23</age>
  </friend>
  <friend>
    <name>Charles</name>
    <age>26</age>
  </friend>
  <friend>
    <name>Earl</name>
    <age>18</age>
  </friend>

results in

  Arthur, 23<br>
  Charles, 26<br>
  Earl, 18<br>
set
  <* set IDENTIFIER VALUE *>

sets the value of a variable with name IDENTIFIER to VALUE.

For example, following template

  <* set f friend *>
  <* f.name *>
  <* f.age *>
  ---DATA---
  <friend>
    <name>Arthur</name>
    <age>23</age>
  </friend>

results in

  Arthur
  23
default
  <* default IDENTIFIER VALUE *>

sets the value of a variable with name IDENTIFIER to VALUE, if the variable value is not already defined.

For example, following template

  <* background *>
  ---RULE---
  <background>
    <* default color "#ff0000" *>
    <* color *>
  </background>

results in

  #ff0000

However, following template

  <* background color="#000000"*>
  ---RULE---
  <background>
    <* default color "#ff0000" *>
    <* color *>
  </background>

results in

  #000000  

METHODS

new

Creates a template object.

  my $tmpl = HCKit::Template->new;
rewrite_file

Rewrites the template contained in a file and returns the string.

  my $tmpl = HCKit::Template->new;
  print $tmpl->rewrite_file("main.tmpl");
rewrite_string

Rewrites the template contained in a string and returns the rewritten string.

  my $tmpl = HCKit::Template->new;
  print $tmpl->rewrite_string(<<EOI);
  <* friend.name *>
  ---DATA---
  <friend><name>John</name></friend>
  EOI
process_file

Reads files and processes rewrite rule sections and data sections. Template sections in these files are neglected.

  my $tmpl = HCKit::Template->new;
  $tmpl->process_file("frame.tlib", "box.tlib");
get_var

Gets the value of a variable.

  my $tmpl = HCKit::Template->new;
  $tmpl->get_var('width');
set_var

Sets the value of a variable.

  my $tmpl = HCKit::Template->new;
  $tmpl->set_var('width', 780);

SEE ALSO

There are other excellent and mature Perl modules with similar purposes, but with different concepts. For example, Sam Tregar's HTML::Template, http://theoryx5.uwinnipeg.ca/mod_perl/cpan-search?modinfo=8997 and Andy Wardley's Template http://theoryx5.uwinnipeg.ca/mod_perl/cpan-search?modinfo=18155 are famous ones.

AUTHOR

Hangil Chang, <hangil@chang.jp>

COPYRIGHT AND LICENSE

Copyright 2003 by Hangil Chang

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.