NAME
HTML::YaTmpl - Yet Another Template Processor
SYNOPSIS
use HTML::YaTmpl;
my $t=HTML::YaTmpl->new( file=>'template.tmpl' );
$t->evaluate( key1=>$value1,
key2=>[$val21, $val22, ...]
... );
$t->evaluate_to_file( $outputfilename,
key1=>$value1,
key2=>[$val21, $val22, ...]
... );
ABSTRACT
"HTML::YaTmpl" aims mainly to provide a HTML template processor that
saves the template writer typing.
There are general template processors like "Text::Template" and tools to
embed perl in HTML like "HTML::Embperl" or HTML template processors like
"HTML::Template". Why have I decided to start yet another? Well,
"Text::Template" is not really convenient when it comes to process
repeating data records like HTML tables. With "HTML::Embperl" no
professional "WEB Designer" will be able to "enhance" the pages. And
"HTML::Template" enforces a strict division of design and programming.
Thus, it enforces changes to the programming logic even if you only want
to exchange a long number like 2835067264068365493 with a more human
readable 2,835,067,264,068,365,493.
"HTML::YaTmpl" attempts to make simple things easy but complexity
feasible.
DESCRIPTION
"HTML::YaTmpl" follows the object oriented paradigm, i.e. you have to
create a template processor prior to using it. A template processor is
not bound to any particular template. You can use one processor to
evaluate various templates. But other properties like error handling are
bound to the processor.
Constructor
new( attrname=>attrval, ... )
creates a new "HTML::YaTmpl" object. These attributes can be set:
template [optional]
is the actual template text. It is used for programmatically
generated templates. It is overridden by template() and open()
methods.
file [optional]
sets a file to read the template from. Setting this attribute via
new() causes the open() method to be called. If open() fails new()
will return undef instead of an object.
path [optional]
is an ARRAY (passed as reference) to be used as template search
path. If omitted the environment variable "HTML_TMPL_SEARCH_PATH" is
split by your path separator (see the Config module documentation)
(on UNIX a ':' on Windows ';'). The "path" attribute is used by
open().
package [optional]
specify a package to evaluate template code fragments. If omitted
the caller package of the constructor is used. Thus, the package can
be used to define convenience functions to be called from within a
template.
no_eval_cache [optional]
"HTML::YaTmpl" wraps all code fragments from templates into
subroutines and calls then these subroutines. Normally the
subroutines are cached to avoid multiple calls of the perl
interpreter. If "no_eval_cache" is set the cache is turned off. This
can be useful for long running applications that process user
provided templates. On the other hand there is the method
clear_cache() to clear that cache from time to time.
onerror [optional]
eprefix [optional]
errors [optional]
see ERROR HANDLING below
compress [optional]
This attribute let you choose a compression method given by a file
name suffix. By now only "gz" is defined.
"evaluate()" returns compressed output. "evaluate_to_file()" writes
compressed output. The output file name is extented with a suffix
indicating the compression method.
Only the tailing characters of the given string are evaluated to
select the compression method but the complete string is used as
file name suffix. Thus:
$t->compress='.gz' or
$t->compress='-gz' or
$t->compress='gz'
all select the "gzip" compression method. But "$t->evaluate_to_file(
"out.html" )" will write "out.html.gz" or "out.html-gz" or
"out.htmlgz" respectivly.
If the constructor is called as instance method rather than as class
method the new object inherits all attributes from the old instance.
Naturally, explicitely named attributes are overridden.
Attribute Access Methods
These methods have got the ":lvalue" attribute. They can be assigned
using "$self->method=$new_value" syntax. Called without parameter they
return the actual attribute value. Calling with parameter sets the
attribute value.
template
file
path
package
no_eval_cache
compress
see new() above
onerror
eprefix
errors
see ERROR HANDLING below
Template Evaluation Methods
evaluate($private_data, key1=>$val1, key2=>$val2, ...)
This function evaluates the current template (set via template() or
open()) and returns the result. Note, simply setting the current file
via "$self->file=$newfile" will not change the current template. Only
after a call to the open() method the template is changed.
$private_data is an optional argument that can be used to pass
additional data to and from the template. From within the template it
is accessible as $p.
All other parameters are key=>value pairs that provide variables to
the template. Internally they are gathered in a hash that is
accessible from within the template as $h.
evaluate_to_file($file, \%private_data, key1=>$val1, ...)
This function calls evaluate() and writes the result to $file. If
$file is a GLOB reference the evaluation result is written to that
file handle. If it is a CODE reference the referenced function is
called with the evaluation result as $_[0]. The return value is
returned. If $file is an object and the object UNIVERSAL::can print()
then that print method is called. Otherwise $file is interpreted as
the name of a file where the evaluation result is to be written. If
the template evaluation throws an exception $file remains untouched.
evaluate_to_file() returns false if something went wrong, e.g. no
space left on device or no permission to write the file.
Configuration File Evaluation Methods
evaluate_as_config($private_data, key1=>$val1, key2=>$val2, ...)
This function is similar to evaluate(). However, it treats the
template as some kind of configuration file. Here is an example how it
works. Given the template:
<=key1>Value1=key1>
<=key2>Value2=key2>
evaluate_as_config() will return an hash reference:
{ key1=>'Value1', key2=>'Value2' }
Note, this function is experimental.
Cache Management Methods
"HTML::YaTmpl" uses a cache to avoid multiple compilation of the same
code fragment or multiple parsing of the same template. Simply hashing
each compiled code or template fragment can lead to memory leaks for
long running processes that use user provided templates. To prevent that
the cache is assigned a high and a low water mark. If the number of
cache elements reaches the high water mark elements are deleted on a LRU
basis so that it drops to the low water mark. The cache is not bound to
a "HTML::YaTmpl" object but is shared by all objects.
The cache is implemented as 2 independent HASHes for compiled code and
template fragments respectively. The high and low water marks are use
for both of them.
clear_cache
deletes all cache entries.
cache_highwatermark
returns and sets the high water mark. It is set by default to 10000.
This function is assigned the :lvalue attribute. It can thus be called
"HTML::YaTmpl-"cache_highwatermark=$newvalue>.
cache_lowwatermark
returns and sets the low water mark. It is set by default to 5000.
This function is assigned the :lvalue attribute. It can thus be called
"HTML::YaTmpl-"cache_lowwatermark=$newvalue>.
cache_sizes
returns the actual number of cached code fragments.
Other Methods
open
opens the current file and sets it's content as the current template.
You can construct a "HTML::YaTmpl" object without any template and
later set the "file" and possibly the "path" attribute and call open()
to set the template from the file's content.
open() can be called with arguments: $self->open( file=>$filename,
path=>$path ); to set the current file and template path in one call.
Both parameters are optional.
If the current file cannot be read for any reason open() returns
undef. $! will indicate the reason. If a template file was read
successfully $self is returned.
clear_errors
clears the objects error list returning it's content.
TEMPLATE SYNTAX AND EVALUATION
As for other templare processors a template is a file containing normal
content and some special template sequences that will be exchanged with
computed values during template evaluation. Since this is mainly a HTML
template engine these sequences are chosen similar to HTML tags. There
are 3 kinds of sequences:
Sequences starting with <=
are used for variable expansion.
Sequences starting with <:
are used to control further evaluation, e.g. including other template
files, evaluate only parts of the template depending on some
variables, ...
Sequences starting with <#
are used as comments
Template sequences as other HTML/XML sequences are opened with an
opening tag and closed with a closing tag, e.g.
<=variable_name parameter_list> body =variable_name>
But if "body" is empty this sequence can be abbreviated to
<=variable_name parameter_list/>
where the trailing "/>" instead of the simple ">" is essential. Thus,
here come some valid template sequences:
<=name/>
this is probably the most used form. It simply inserts the value of
the key "name" provided to evaluate() instead of "<=name/>".
<=name type="array"/>
now the parameter "type" is set. It specifies that this sequence
should be expanded only if the value provided to evaluate as "name" is
a reference to a non-empty array. In that case the string consisting
of all elements of the array concatenated is inserted. Otherwise the
template sequence is simply deleted from the output.
<:include other_template.tmpl inherit a=b x=y/>
this is an example of a control sequence. It reads and evaluates the
template file "other_template.tmpl". This new evaluation inherits all
variables from the current evaluation and adds 2 new variables "a" and
"x" with the values "b" and "y".
<:include other_template.tmpl><:set a>b
just the same but with closing tags. The only variable
"other_template.tmpl" sees is "a".
Simple Variable Substitution
Basics
The basic form of variable substitution is "<=name/>" or
"<=name>=name>". Given the template:
some text '<=var/>' other text
If evaluate is called as
$t->evaluate( var=>'computed text' );
it will return
some text 'computed text' other text
But you can provide also an ARRAY reference to evaluate instead of the
scalar to achieve the same result.
$t->evaluate( var=>['com', 'pu', 'ted', ' ', 'text'] );
Scalar, Array and Empty Processing
How does this work? If evaluate receives an ARRAY ref as a variable's
value it sort of evaluates "<=var/>" for each array element and
concatenates the results. But you can specify that a variable
substitution should be done only if the provided value is of a
particular type. 4 such types are available
scalar
only if the provided value is a non-empty scalar (ref() does not
return "ARRAY" and length() returns something not equal zero) it is
substituted. Otherwise an empty string is substitued.
array
only if the provided value is a non-empty array (ref() returns "ARRAY"
and it consists of at least one element) it is substituted. Otherwise
an empty string is substitued.
empty
a value is empty if it is not a scalar nor an array as described
above, i.e. if it is either an empty string or an array without any
element.
given
a value is given if it is a non-empty array or string. If this type is
specified special list handling is turned off. It is usefull if you
want for example generate a link list to array items that are
processed in detail elsewhere. For example a table should be printed
somewhere on a page and on top of that page a shortcut link to that
table should be printed but only if the table is not empty.
The template
some text '<=var type=scalar/>' other text
will show the result seen above only if evaluate() is called as
$t->evaluate( var=>'computed text' );
The other case will produce
some text '' other text
If a variable substitution is valid for multiple types they can be
concatenated with a comma (,):
<=var type=scalar,array/>
is evaluated if "var" is an array or a scalar but not if it's empty.
Further,
<=var type=given>Link=var>
evaluates to "Link" if "var" is a non-empty list or a non-empty string.
Even if "var" is a list containing multiple values only one occurence of
"Link" is substituted.
Modifying the substituted value on the fly
There are cases when it would be really useful to modify the substituted
value a little from within the template. Imagine you want to make big
numbers more readable (52345635476 should be displayed as
52,345,635,476) or you want to substitute a list (passed as array
reference, see above) and don't want the elements simply be concatenated
but displayed as HTML list elements ("
element
"). "HTML::YaTmpl"
provides 2 ways for doing this.
Using "<:/>"
Given the template
fruits comprise
<=fruits>
<:/>
=fruits>
the call
$t->evaluate( fruits=>[qw{apples pears plums cherries}] );
will generate
fruits comprise
apples
pears
plums
cherries
Now we are using the long form of variable substitution (<=var>=var>).
The tag body describes the actual substitution. Within the tag body the
control sequence "<:/>" stands for the actual value. Of course "<:/>"
can be given several times within a substitution body:
<=fruits><:/> and <:/> give <:/>
=fruits>but
men and women give children
generates
apples and apples give apples
pears and pears give pears
plums and plums give plums
cherries and cherries give cherries
but
men and women give children
Using Perl code fragments
The same result can be achieved using Perl inside the "<:/>" control
sequence:
<=fruits><:"$v and $v give $v\n"/>=fruits>but
men and women give children
Now we see the "<:/>" control sequence in action. It can contain perl
code fragments that change the substituted value. The code fragment is
called with $v set to the actual variable. If the evaluate() call was
passed the optional $private_data parameter it is available as $p from
within these code fragments. Otherwise $p points to a HASH that is
created once for each evaluate() call. Thus, code fragments can
communicate with each other using this hash:
<=fruits><: ++$p->{fruitcounter} />. <:/>
=fruits>total: <:$p->{fruitcounter}/> fruits
produces
1. apples
2. pears
3. plums
4. cherries
total: 4 fruits
Maybe you have noticed the total counter in the last line was generated
without any surrounding "<=var>=var>" sequence. Yes, that works too.
In this case $v is "undef".
Supplying Perl fragments using "code=..."
By now the most examples have used the long variable substitution form
("<=var>...=var>"). In most cases this is probably the *right* thing
but you can use the short form even when modifying the substitution
value:
<=fruits code="<: ++$p->{fruitcounter} />. <:/>
"/>total: <:$p->{fruitcounter}/> fruits
will produce exactly the same result as above but it's almost not
readable. Whereas:
<=fruits code="
<: ++$p->{counter} />
<:/>
"/>
total number of fruits
<:$p->{counter}/>
generate perfectly valid (but not easy human readable) HTML text.
You see, the "code=..." parameter to a "<=var .../>" sequence does the
trick. It can comprehend any text but should in most cases be surrounded
by double quote characters ("). Within this surrounding double quote and
backslash characters must be quoted with backslashes, all other can.
Thus:
<=fruits code="
<:\"\\u$v\"/>
"/>
generates a HTML list of capitalized fruits:
Apples
Pears
Plums
Cherries
However, I believe, this kind of code fragments is the wrong way. But it
can be even worse. You can omit the surrounding double quotes but then
you must quote almost anything except characters matching "\w" with
backslashes. The previous example without surrounding quotes looks like:
<=fruits code=\
\<:\"\\u$v\"/\>\
/>
Special list processings
When a list is to be substituted often special treatment is required for
the first and the last list element or some text should be prepended or
appended to the substitution result:
<=fruits first="<: ucfirst $v/>" last=" and <:/>" code=", <:/>"/>
are fruits.
generates
Apples, pears, plums and cherries
are fruits.
You see, if there are "first=..." and "last=..." parameters to a
variable substitution they affect the first respectively last list
element.
On the other side there are "pre=..." and "post=..." parameters. They
are evaluated before the first respectivly after the last list element:
<=fruits pre="<:\"\\n\"/>"/>
produces
Using the long substitution form these examples would look like:
<=fruits>
<:first><:"\u$v"/>
<:last> and <:/>
<:code>, <:/>
=fruits>
are fruits.
respectively:
<=fruits>
<:pre>
<:code>
=fruits>
I think, the "<:first>", "<:last>", "<:prev>" and "<:post>" semantics
are intuitively clear. "<:code>" needs some explanation. In the previous
examples using the long form the text between "<=var>" and "=var>" has
described what to substitute. That will remain to work. If no "<:code>"
section is found all text between "<=var>" and "=var>" save the
control sequences will be used. But this results in many unnecessary
newlines since the previous example without "<:prev>" and "<:post>" look
like:
<=fruits>
# empty line
# empty line
=fruits>
Thus, "<:code>" can be used for convenience.
More special list processings
Perl knows list operations such as "map", "grep" and "sort". These are
also useful in templates. Why the program logic should know about the
order in which a list is displayed? It must supply the list. That's it.
Just to arrange our fruits alphabetically we can write:
<=fruits sort="$a cmp $b">
<:code><:/>,
<:last><:/>
=fruits>
and get
apples, cherries, pears, plums
But can we order reverse fruits, i.e. selppa instead of apples?
<=fruits map="scalar reverse $_" sort="$a cmp $b">
<:code><:/>,
<:last><:/>
=fruits>
give
seirrehc, selppa, smulp, sraep
But I want:
cherries, apples, plums, pears
ok, here comes the template:
<=fruits map="scalar reverse $_"
sort="$a cmp $b"
map="scalar reverse $_">
<:code><:/>,
<:last><:/>
=fruits>
But I don't like plums! Ok then:
<=fruits grep="!/plum/i"
map="scalar reverse $_"
sort="$a cmp $b"
map="scalar reverse $_">
<:code><:/>,
<:last><:/>
=fruits>
results in
cherries, apples, pears
Of course these parameters can also be written in long form and even
mixed:
<=fruits grep="!/plum/i">
<:map>scalar reverse $_
<:sort>$a cmp $b
<:code><:/>,
<:last><:/>
=fruits>
produces
seirrehc, selppa, sraep
There can be as many as you like grep/sort/map fragments. The source
list is first processed by the fragments passed as parameters ("<=var
grep=... map=... sort=... ...>") in left to right order and then by the
fragments given in long form ("<:sort>...") in top down order.
Real World Example
Suppose you want to create a WEB application with an input field to put
the name of a town. The user would first see a simple "" field. He types in some characters and submits the form.
Now your program matches the user input with a database. There can be 3
results. The user input can non-ambiguously match a database record or
there are several matches or no match at all. In the first case your
program should answer the user with a page simply showing the matching
record. In the second case it should display a select field and in the
3rd the "" field. Your template would look like:
<=town type=empty>=town>
<=town type=array pre="">
=town>
<=town type=scalar><:/>=town>
and you get a text field "town" is passed as "undef", an empty string or
an empty array. A selection field is generated if "town" is passed as a
non-empty array and the town set in bold is produced if it is passed as
non-empty string.
Control Statements
Many control sequences have been shown in the previous chapter. Here the
are listed again:
<:/> or <:>
is used to execute perl code. "<: some perl code />" and "<:> some
perl code " are equivalent. Within the perl code the variables $v,
$p and $h can be used. $v contains the current variable to be
substituted. $p holds the "private_data" parameter. $h points at a
HASH containing all parameters passed to the current scope (see
"<:eval>" for an example of using it).
<:code>
is used to mark the actual template code within variable substitution
or "<:eval>" or "<:for>" blocks. This can be used for convenience.
<:pre>
<:post>
<:first>
<:last>
<:map>
<:grep>
<:sort>
see previous chapter.
<:for ...>...
The "<:for>" statement is used to evaluate a part of the template with
changed parameters. For example you want to make up a HTML table that
is passed as list of lists:
$t->evaluate( fruit_colors=>[[qw{apples red/green}],
[qw{pears green}],
[qw{plums blue/yellow}],
[qw{cherries red}]] );
with the template
<=fruit_colors><:for f="<:/>">
<:code>
<=f>
<:/>
=f>
=fruit_colors>
generates
apples
red/green
pears
green
plums
blue/yellow
cherries
red
Here the opening "<=fruit_colors>" begins a scope of substituting
"fruit_colors". As "fruit_colors" is an array the tag body is
evaluated for each element. The tag body contains a single "<:for>"
statement used to assign "f" temporarily the current value of
"fruit_colors", i.e. "f" is assigned in turn each element of the
"fruit_colors" list. Within to "<:for>" statement we see the
evaluation of "<=f>" surrounded by "
". As "f" is also an array it
generated "
"'s for each element.
The "<:for>" statement features a template within a template. The
inner template can be given as a "<:code>" statement or if omitted the
body of the tag is used. Thus, the following template is almost
equivalent to the previous one:
<=fruit_colors><:for f="<:/>">
<=f>
<:/>
=f>
=fruit_colors>
In fact it generates an empty line before each table row.
The parameter list for the inner template (the one that is passed to
the evaluate() function) is completely made anew. In our example the
inner template receives only one variable: "f". But there is a way to
bequeath all current variables to the inner template. Just put the
reserved word ":inherit" or ":inheritparms" in the parameter list of
the "<:for>" statement. With
<:for f="<:/>" :inherit>
the inner template would see also "fruit_colors" and all other outer
variables.
All parameters containing an unquoted equal sign (=) are used to set
up the parameter list for the include template. The simplest form of a
parameter is a string like
<:for some_fruit="plum">
Slightly more complex is passing an outer variable with an other name:
<:for inner_fruits="<=fruits/>">
This evaluates "fruits" and stores the result as "inner_fruits". If
"fruits" is an array so does "inner_fruits". If the substitution
statement of "fruits" contains "pre" or "post" components
"inner_fruits" will contain them as first / last list element. You
also can surround the variable substitution with plain text. In this
case each element of "inner_fruits" gets surrounded by this text. And
you can include in the definition of one inner variable multiple outer
variables. In this case each outer list variable is expanded and the
resulting number of list elements is the mathematical product of the
numbers of elements of all outer lists. The template (Note: The
sequence "<#.../>" is a comment and used in this example to hide the
newline in the template. For more details on comments see below.):
<:for inner_fruits="PRE <=fruits pre=pre1
post=post1
grep=\"/l/\"/> <#
/>BETWEEN <=fruits pre=pre2
post=post2
grep=\"/r/\"/> POST">
<:code><=inner_fruits last="(<:/>)">(<:/>)
=inner_fruits>
generates a total of 16 lines:
(PRE pre1 BETWEEN pre2 POST)
(PRE pre1 BETWEEN pears POST)
(PRE pre1 BETWEEN cherries POST)
(PRE pre1 BETWEEN post2 POST)
(PRE apples BETWEEN pre2 POST)
(PRE apples BETWEEN pears POST)
(PRE apples BETWEEN cherries POST)
(PRE apples BETWEEN post2 POST)
(PRE plums BETWEEN pre2 POST)
(PRE plums BETWEEN pears POST)
(PRE plums BETWEEN cherries POST)
(PRE plums BETWEEN post2 POST)
(PRE post1 BETWEEN pre2 POST)
(PRE post1 BETWEEN pears POST)
(PRE post1 BETWEEN cherries POST)
(PRE post1 BETWEEN post2 POST)
Due to the "grep" statements the first expansion of "fruits" contains
only elements with an "l" letter, i.e. "apples" and "plums", whereas
the second expansion consists of elements with an "r" letter, i.e
"pears" and "cherries". As shown each "fruits" list is expanded with a
"pre" and "post" elements. Thus, the total number of elements of the
"inner_fruits" list is 4*4=16.
By now we have seen in this chapter assigning of simple strings and
expanded arrays to inner variables. But what happens if "<:for>" is
placed within a variable substitution scope like in the prefacing
example to this chapter? The template (provided with line numbers)
1 <:>
2 sub fac {
3 use Math::BigInt;
4 my $x=shift;
5 my $res=Math::BigInt->new(1);
6 for( my $i=1; $i<=$x; $i++ ) {
7 $res*=$i;
8 }
9 return $res;
10 }
11
12
29
generates a HTML page containing a table of the first 30 square and
cube numbers and factorials. The first control sequence (lines 1-11)
is substituted with nothing as the value of this code fragment is
"undef". It just defines a "fac" function. Lines 13 and 14 opens a
"<:for>" scope that is closed at line 28. Within this scope the
variables "x" and "h" are valid. Both are list variables since the
"<:/>" control sequences return ARRAY references. Within that scope at
line 17 "h" is evaluated to a list of "
" statements. That is more
or less what we have seen before. But now in line 19 a variable
substitution scope is opened. That means for each element of the "x"
list lines 21 to 25 are evaluated repeatedly. Since "x" consists of
arrays the evaluation of "<:/>" produces an array. That is used in a
nested "<:for>" scope at lines 22 to 24 to subsequently assign to a
variable "y" each of them. Now the body of our nested "<:for>" can
evaluate "y" and create a list of "
" statements.
By now we have seen parameter lists formed like
<:for a="b" c="d" ... />
This can lead to a lot of quoting backslashes within strings. A little
foreboding give the last "inner_fruits" example. There we had to write
"grep=\"/l/\"" to quote the double quotes. This can be avoided using
the "<:set>" control sequence.
<:for>
<:set inner_fruits>PRE <=fruits pre=pre1
post=post1
grep="/l/"/> <#
/>BETWEEN <=fruits pre=pre2
post=post2
grep="/r/"/> POST
<:code><=inner_fruits last="(<:/>)">(<:/>)
=inner_fruits>
generates exactly the same result without quoting any character.
Well, I believe, these examples are enough to show what can be done
with "<:for>". BTW, all these examples are contained in the file
t/5_fruits.t of the distribution. The module is tested against them.
<:eval ...>...
works exactly the same as the "<:for>" statement but resulting text is
evaluated again. This can be useful in some rare cases, e.g.:
1 <:for fruits="<:[qw/apple pear/]/>"
2 books="<:['The Silmarillion',
3 'The Lord of the Rings',
4 'The Hobbit or There And Back Again']/>"><#
5 /><:eval what="<:[qw/book fruit/]/>"><#
6 /><=what><#
7 /><=<:/>s>
8 <<#/>:pre>Select a <: ucfirst $v />:
9
11 <<#/>/:post>
12 <<#/>:code>
13 <<#/>/:code>
14 =<:/>s><#
15 />=what><#
16 /><#
17 />
Consider you get a lot of specialized variables that need to be
treated all the same way. In the example above the outer "<:for>"
scope built with lines 1-4 and 17 creates 2 variables. For both of
them a "