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

NAME

Basset::Template - my templating system

AUTHOR

Jim Thomason, jim@jimandkoka.com

DESCRIPTION

Yes yes, I am horribly horribly villified for even considering doing such a thing. But this is actually a pretty damn powerful AND flexible templating system. It embeds perl into the template and doesn't worry about extra syntax or tokens or such of its own. Theoretically, it'd be really easy to subclass the thing and actually create your own template syntax, if you so desire. Personally, I don't. At least, not yet...

Templates live in their own namespaces, so you won't need to worry about things colliding. At all. Ever. Magic! The only variable that's imported into a template's namespace is '$self', the template object being processed.

QUICK START

Okay, so you want to write a template. It's going to need a few things. Code, values, and passed variables. Try this example:

 in: /path/to/template.tpl

 %% foreach my $age (1..5) {
        <% $name %> is now <% $age %><% "\n" %>
 %% };

Then, your code can be:

 use Basset::Template;
 my $template = Basset::Template->new(
        'template' => '/path/to/template.tpl'
 ) || die Basset::Template->error();

 print $template->process(
        {
                'name' => \'Jim'
        }
 ) || $template->error;

Voila. All done. Note that %% starts a code line which goes to the end. <% %> delimits a variable to be inserted. Also be aware that any white space between code blocks or variable insertion blocks will be stripped. That's why we have that "\n" in a variable insertion block - it puts in a new line. We don't end up with 2 newlines because the actual newline is stripped.

The process method returns the processed template. You may pass in a hashref containing the values to be inserted. Note that values should be passed by reference. In this case, we pass in 'name', which is a scalar containing 'Jim'. If you don't pass a value by reference, it will be assumed that you meant to pass in a scalar reference and be alterred as such. Note that this has no effect on you.

 {'name' => \'Jim'} == {'name' => 'Jim'}

And that includes the read-only nature of the ref to the literal. Both values are accessed in your template via '$name'

You can also skip creating an object, if you'd prefer:

 print Basset::Template->process('/path/to/template.tpl', {'name' => \'Jim'}) || die Basset::Template->error;

Damn near everything is configurable. Read on for more information.

ATTRIBUTES

Note that all attributes should be set in the conf file

open_return_tag, close_return_tag

In a template, the simplest thing that you're going to want to do is embed a value. Say you have $x = 7 and want to display that. Your template could be:

 $x = <% $x %>

Which, when processed, would print:

 $x = 7

In this case, <% is your open_return_tag and %> is your close_return_tag. These should be specified in your conf file, but may be alterred on a per-object basis (if you're a real masochist).

Also note that side effects are handled correctly:

 $x is now : <% ++$x %>, and is still <% $x %>;

 evaluates to :
 $x is now : 8, and is still 8

And that you may do extra processing here, if you'd like. The final value is the one returned.

 <% $x++; $x = 18; $x %>

 evaluates to :
 18

Defaults are <% and %>

open_eval_tag, close_eval_tag

Sometimes, though, it gets a little more complicated, and you actually want to put code in your template. That's where the eval tags come into play. The defaults are "%%" and "\n".

For example:

 %% foreach my $x (1..5) {
        <% $x %>
 %% };

evalutes to:

 12345

(recall that the whitespace is stripped)

Voila. You may insert any perl code you'd like in there, as long as it's valid. If you want to output something into the template instead of using the eval tags, use the special filehandle OUT.

 %% foreach my $x (1..5) {
        %% print OUT $x;
 %% };

is the same thing.

Note that you may put comments in this way.

%% # this is a comment.

Comments will be stripped before the template is displayed. See also open_comment_tag and close_comment_tag

big_open_eval_tag, big_close_eval_tag

By default, our code is line delimited. That's nice for not needing closing tags for one-liner things, like if statements or for loops. But sometimes you need to do a lot of processing. That's a mess.

 %% my $x = some_function();
 %% $x + 1;
 %% if ($x > 18) {
 %%             $x = other_function;
 %% } elsif ($x > 14) {
        $x = 12;
 %% };
 %% $x = process($x);
 %% # etc.

So, we have our big tags, defaulting to <code> and </code>, which are a synonym, just with a closing tag.

 <code>
          my $x = some_function();
          $x + 1;
          if ($x > 18) {
                        $x = other_function;
          } elsif ($x > 14) {
                $x = 12;
          };
          $x = process($x);
          # etc.
 </code>

Much cleaner.

open_comment_tag, close_comment_tag

You're a bad, bad developer if you're not commenting your code. And your templates are no exception. While you can embed comments via the eval_tags, it's less than ideal.

 %% # this is a comment
 <code> #this is a comment </code>

So we have our comment tags, which default to <# and #>

 <# this is a comment that will be stripped out well before you see the processed template #>
open_include_tag, close_include_tag

You may want to include another template inside your current template. That's accomplished with include tags, which default to <& and &>

 This is my template.
 Here is a subtemplate : <& /path/to/subtemplate.tpl &>

There are two ways to include a subtemplate - with passed variables and without. Passing without variables is easy - we just did that up above.

 <& /path/to/subtemplate.tpl &>

Passing with variables is also easy, just give it a hashref.

<& /path/to/subtemplate.tpl {'var1' => \$var1, 'var2' => \$var2, 'var3' => \$var3} &>

And voila. All set. Same rules apply for passing in variables as applies for the process method. You may break the include statement over multiple lines, if so desired.

The major difference between the two is that if a subtemplate is included without variables, then it is evaluted in the template's package. So it has access to all variables, etc. within the template and vice-versa. If a subtemplate is included with variables, then it is evaluated in it's own package. So it does not have access to any variables of the supertemplate, nor does the supertemplate have access to the subtemplate's values.

document_root

For included files, this is the document root. Say you're running a webserver, and you want to include a file. Your webserver doc root is: /home/users/me/public_html/mysite.com/

Now, when you include files, you don't want to have to do:

 <& /home/users/me/public_html/mysite.com/someplace/mysubtemplate.tpl &>

because that's messy and very non-portable. So just set a document_root.

 $tpl->document_root('/home/users/me/public_html/mysite.com/');

and voila:

 <& /someplace/mysubtemplate.tpl &>

Note that this only affects subtemplates set with an absolute path. So even with that doc root, these includes are unaffected:

 <& someplace/mysubtemplate.tpl &>
 <& ~/someplace/mysubtemplate.tpl &>
allows_debugging

Boolean flag. 1/0. If 1, then the debug tags will execute, if 0, then they won't.

open_debug_tag, close_debug_tag

Debugging information can be a very good thing. The debug tags are the equivalent of return tags, but they go to STDERR. You may do additional processing, manipulation, etc., but the last value always go to STDERR.

 <debug>
        "Now at line 15, and x is $x"
 </debug>

 <debug>
        my $z = $x;
        $z *= 2;
        "Now at line 15, and twice x is $z";
 </debug>

Debug tags will only be executed if allows_debugging is 1.

pipe_flags

This is a trickled class attribute hashref.

The pipe_flags allow you to deviate from standard perl and send the output of your value to a processor before displaying.

Built in flags include 'h' to escape the output for HTML and 'u' to escape the output for URLs.

New flags should have the flag as the key and the processor as the value.

 Some::Template::Subclass->pipe_flags->{'blogger_flag'} = 'blogger_processor';

Will get one argument - the value to be processed.

template

This is the actual template on which you are operating.

 $tpl->template('/path/to/template');   #for a file
 $tpl->template(\$template);                    #for a value in memory

 my $tpl = Basset::Template->new('template' => '/path/to/template');

Hand in a literal string for a file to open, or a scalarref if it's already in memory.

Note that if you had in a template with an absolute path starting with /, the template will automatically be assumed to be sitting off of the document root. Relative paths are unaffected.

preprocessed_template

This stores the value of the template after it's been run through the preprocessor. You probably don't want to touch this unless you really know what you're doing.

Still, it's sometimes useful to look at for debugging purposes, if your template isn't displaying properly. Be warned - it's a bitch to read.

caching

Boolean flag. 1/0.

templates are obviously not executable code. Nothing can be done with them, they're nonsensical. So, before they can be used, they must be preprocessed into something useful. That preprocessing step is reasonably fast, but it's still effectively overhead. You don't care about the transformations happening, you just want it to work!

Besides most templates are modified very rarely - it's normally the same thing being re-displayed. So constantly re-preprocessing it is inefficient. So, you may turn on caching.

If caching is on, during preprocessing the template looks in your cache_dir. If it finds a preprocessed version that is current, it grabs that version and returns it. No more additional processing. If it finds a preprocessed version that is out of date (i.e., the actual template was modified after the cached version was created) then it looks to the new template and re-caches it. If no cached value is found, then one is cached for future use.

cache_dir

This is your cache directory, used if caching is on.

 $template->cache_dir('/path/to/my/cache/dir/');
compress_whitespace

Boolean flag. 1/0.

Sometimes whitespace in your template doesn't matter. an HTML file, for example. So, you can compress it. That way you're sending less data to a web browser, for instance.

compress_whitespace turns runs of spaces or tabs into a single space, and runs of newlines into a single newline.

full_file_path

given a file (usually a template), full_file_path returns the absolute path of the file, relative to the file system root

METHODS

preprocess

preprocess is called internally, so you'll never need to worry about it. It takes the template and translates it into an executable form. Only call preprocess if you really know what you're doing (for example, if you want to look at the preprocessed_template without actually calling process).

process

Ahh, the magic method that finally does what we want - turns our template into the populated thing we want to work with.

Takes 0, 1, or 2 arguments, and may be called as a class or an object method. If called as an class method, a new template object is created internally.

With 0 arguments, you must call it as an object method. The 0 argument form is equivalent to:

 $tpl->process($tpl->template);

With 1 argument, you may either pass a template OR a hashref of values.

 $tpl->process($template);
 $tpl->process($hashref);

The second form is equivalent to:

 $tpl->process($tpl->template, $hashref);

With 2 arguments, you pass a template AND a hashref of values.

 $tpl->process($template, $hashref);

process returns the completed, processed, done template.

 my $page = $tpl->process($hashref) || die $tpl->error();
 print $page;

The hashref contains the values to be populated into the template. Assume your template is:

 Hello, <% $name %>

Then you may process it as:

 $tpl->process();                                               # prints "Hello, "
 $tpl->process({'name' => \'Billy'});   #prints "Hello, Billy"
 $name = 'Jack';
 $tpl->process({'name' => \$name});             #prints "Hello, Jack"
 $tpl->process({'$name' => \$name});    #prints "Hello, Jack"

You may pass different types of variables with the same name, if you specify their types.

 $tpl->process( {
        '$name' => \$name,
        '@name' => \@name,
        '%name' => \%name
 } );

If no type is specified in the key, it is assumed to be a scalar.

Also be warned that while you may pass in a reference to a constant, just like any other constant reference, you may not then alter its value in your template. Even if you pass in the constant itself, it internally becomes a reference and you can't change it.

escape_for_html

class method, all it does is turn &, ", ', <, and > into their respective HTML entities. This is here for simplicity of all the subclasses to display things in templates

escape_for_url

URL escapes the key/value pair passed. This is here for simplicity of all the subclasses to display things in templates.

 my $escaped = $class->escape_for_url('foo', 'this&that'); #$escape is foo=this%26that

Also, you may pass an arrayref of values

 my $escaped = $class->escape_for_url('foo', ['this&that', 'me', '***'); #$escape is foo=this%26that&foo=me&foo=%2A%2A%2A

EXAMPLES

 These are some example templates, with scripts to populate them.

 template
 --------

 %% my $old_age = 0;
 %% foreach my $age (1..5) {
        I was <% $old_age %>, but now I am <% $age %>.
        %% $old_age = $age;
 %% };

 script
 --------

 print Basset::Template->process('/path/to/template');

 result
 --------

 I was 0, but now I am 1.
 I was 1, but now I am 2.
 I was 2, but now I am 3.
 I was 3, but now I am 4.
 I was 4, but now I am 5.

 ========

 template
 --------

 Hello there, <% $name %>.
 I see that you are <% $admin ? '' : 'not' %> an admin.
 %% if ($admin) {
        You may do administrative things.
 %% } else {
        You may not do administrative things.
 %% }; 

 script
 --------

 my $template = Basset::Template->new('template' => '/path/to/template');
 print $template->process(
        {
                'name' => \'Jack',
                'admin' => \'0'
        }
 );

 result
 --------

 Hello there, Jack.
 I see that you are not an admin.
 You may not do administrative things.

 ========

 template
 --------

 <select name = "foo">
        %% while (my ($key, $val) = each %foo) {
                <option value = "<% $val %>"><% $key %></option>
        %% };
 </select>

 script
 --------

 {
        local $/ = undef;
        $data = <DATA>;
 };

 my $template = Basset::Template->new();
 $template->template(\$data);
 print $template->process(
        {
                'foo' => {
                        'one' => '1',
                        'two' => '2',
                        'three' => '3'
                }
        }
 );

 __DATA__
 <select name = "foo">
        %% while (my ($key, $val) = each %foo) {
                <option value = "<% $val %>"><% $key %></option>
        %% };
 </select>

 result
 --------

 <select name = "foo">
        <option value = "1">one</option>
        <option value = "3">three</option>
        <option value = "2">two</option>
 </select>