TinyTemplate – An inline perl Template system in < 30 lines of code

For me, templating systems are the itch that never stops. I’ve written templating systems is C++, Java, Perl and even Javascript (though I gave up the javascript one when I started using TrimPath’s amazing javascript template library).
The latest incarnation is TinyTemplate. This is the 4th templating system I’ve written in Perl and I think this is the one ;-). It will either make me famous or ridiculed for my naievety. I’ve got this thing about writing as little code as possible so each templating system I’ve written has had a progressively smaller codebase. This time I think I’ve hit on something: A templating system in < 30 lines of non-idiomatic Perl code. The code is a little rough around the edges (it was just written this evening). I don’t have the patience (or time) to jump through all the necessary hoops to get this thing on CPAN (as I did with TinyMake – you see a pattern here ?), so I’m presenting TinyTemplate here in all it’s glory…

TinyTemplate.pm


package TinyTemplate;
our $VERSION = '0.01';

=head1 NAME

TinyTemplate - A Tiny Inline-Perl Template module with an execution model similar to JSP's.

Templates are strings which are converted to Perl code for evaluation.

=head1 SYNOPSIS

use TinyTemplate ':all';
print "Content-type: text/html\n\n";
#
# parse will convert a template (in the form of a string)
# into equivalent perl code. The following sample uses Perl's here document syntax...
#
eval parse <<'END';
<html>
<body>
<table>
[% foreach my $row ({name=>"Walter Higgins", address=>"Cork,Ireland"},
{name=>"John Doe", address=>"Surrey,England"})
{ %]
<tr>
<td>$row->{name}</td>
<td>$row->{address}</td>
</tr>
[% } %]
</table>
</body>
</html>
END

=head1 DESCRIPTION

TinyTemplate is a tiny template system that parses a string and converts it into Perl code.

Inline perl code must be enclosed in [% %] markers.

For example:
[% if ($age > 65) { %]
Please avail of our special offer for Senior Citizens
[% } %]

You can send directives to the parser using [%! %] markers.

For example:
[%!include "header.html" %]
will include (and parse) the content of header.html.

Currently there is only one directive (include) supported.

Everything that isn't enclosed in [% %] will be printed in double-quotes (interpolated),
so to print a variable name ...

You are connected to $hostname

...This means that if you want to include an email address in your template you should
escape the @ character like so...

<a href="mailto:walterh\@rocketmail.com">Contact</a>

=head1 SAMPLE CODE

#!/usr/bin/perl
#
# A simple CGI program
#
use strict;
use TinyMake ':all';
print "Content-Type: text/html\n\n";
eval include "my_template.html";
exit;

Contents of my_template.html...

<html>
[%!include "header.html" %]
<body>
<table>
[% foreach my $person ({name=>"Jane Malone",
age=> 64,
address=>"Cork,Ireland"},

{name=>"John Doe",
age=> 68,
address=>"Surrey,England"}) { %]
<tr>
<td>$person->{name}</td>
<td>$person->{address}</td>
<td>
[% if ($person->{age} > 65) { %]
Please avail of our Senior Citizens Offers
[% } %]
</td>
</tr>
[% } %]
</table>
</body>
</html>

Contents of header.html...

<head>
<title>People List</title>
</head>

=cut

use strict;
require Exporter;
our @ISA = ("Exporter");
our @EXPORT_OK = qw(parse include);
our %EXPORT_TAGS = (all => \@EXPORT_OK,);

#
# Parse a string splitting it into tokens then
# joining it back to form a string which can be evaluated
# as perl code.
#
sub parse
{
my $perl = join "\n",map {
my ($enclosed, $directive, $text) = $_ =~ /(\[%)*(!)*(.+)/s;
if ($directive){
my $evald = eval $text ;
die $@ if $@;
$_ = $evald;
}elsif ($enclosed){
$_ = $text;
}else{
$text =~ s/\n/\\n/g;
$text =~ s/"/\\"/g;
$_ = "print \"$text\";";
}
} split /(\[%.*?)%\]/s, $_[0];
}
#
# Include content from another file for evaluation
#
sub include
{
my ($filename) = @_;
open FILE, $filename or die "COULDN'T OPEN FILE $filename because $!\n";
my $contents = join '', <FILE>;
close FILE;
parse $contents;
}

1;

P.S. I’m sure a Perl Wizard could trim this down to a one-liner. A one-liner templating system that supported inline perl would be cool. Any takers ?

Advertisements

One thought on “TinyTemplate – An inline perl Template system in < 30 lines of code

  1. Pedro Borges says:

    Hi, could you share the tinytemplate again.

    TIA

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: