Peter Stuifzand

Executing code from templates

Until today I couldn’t use variables in my template that are pieces of code. I added one piece of code that executes the piece of code in a the stash and returns its value. In the template it looks like this.

[% FOR p IN products %]
    <p>[% p.name %]
[% END %]

There are two places in this piece of code that could contain code references. The first is products. This could be implemented as follows.

my $stash = {
    products => sub { my $db=shift; return $db->ProductList(); },
};

Here I show the implementation of the template evaluation code.

sub find_value_in_stash {
    my ($db, $stash, $name) = @_;

    my $it = $stash;

    for my $p (split /\./, $name) {
        $it = $it->{$p};

        if (ref($it) eq 'CODE') {
            $it = $it->($db);
        }
    }
    return $it;
}

This code doesn’t contain the error-checking code that’s necessary for a production environment. This code allows us to add variables to the stash without knowing the value when we add it. The nice thing is, we don’t need to execute the potentially expensive code, for retrieving all the products from the database.

By adding a simple two-line feature like this to a the templating system, we can write simpler controller code. The controllers don’t need to retrieve all the information from the database if it isn’t used. If the variables are used in the template, then the values will automatically be loaded by the templating engine.

The second place in the template where we can use code, is in the second line, where we get the name field. This field could be an value in a hash. On the other hand it could be a method in the object p. By added another line to the find_value method we can use objects, as well as, simple hash values in templates.

The line that move to the next value in the stash needs to be changed to the following. The line

        $it = $it->{$p};

becomes

        if (my $meth = $it->can($p)) {
            $it = $it->$meth();
        }
        else {
            $it = $it->{$p};
        }

This change allows us to use methods on objects. It enables us to write code in classes, that is executed when needed, instead of when the controller was written to build the parameter hash.

To be clear, Template::Toolkit provides both these features. I have written and seen a few web applications and most of them didn’t use and create many objects, because there was a tendency to think of objects as being slow and using much memory. I do think we should watch out for creating many unused objects or loading many rows from a database, because it can slow down your web application a lot. I consider not using method calls and sub references here a form of premature optimisation.

© 2023 Peter Stuifzand