E-Commerce Solutions: Template-Driven Pages, Part 2
September 13, 2000
First of all, I feel I should apologize to those readers
who've been awaiting the follow-up to my original
article. Unfortunately, pressures of work on other projects has meant
that I've been unable to switch my attention to any other
projects, including the E-Commerce series. However, I'm now back
on track, so here we go with the follow-up on template-driven pages,
this time using Perl and Python to deliver those SSI templates
straight into CGI documents.
In the last article we examined the different ways in which we
could introduce pages purely using the SSI (Server Side Includes)
features of Apache. The problem with SSI is that it doesn't apply
to pages that have been served by a CGI script. Instead, you need to
manually read and regurgitate the template for yourself within the
script or CGI service that you are using.
You don't actually need to use a different template, you can
continue to use the SSI templates that we've seen before, as long
as you keep to the same rules as you would with a server-parsed HTML
file.
The obvious way of outputting an HTML file is just to open the
file, slurp in the text, and print it out. But, if we're going to
go to all this trouble, why don't we do a little more than just
regurgitate the text?
Using Perl
Perl is ideally suited to reading in entire files, doing a bit of
processing, and then print it out again. I have for years now used the
same basic function of reproducing templates in Perl. The function
looks like this:
sub parse_template
{
my (,%subs) = @_;
open(TEMPLATE,)
or print "I tried to load <br>\n";
{
local $/;
sh = <TEMPLATE>;
}
close(TEMPLATE);
foreach (sort keys %subs)
{
sh =~ s/\%\%\%\%/{}/g;
}
return sh;
}
There are a few points to note about this function before we look
at how best to use it. First and foremost, you'll notice that we
load the entire template file into memory. This is because we want to
process the file in it's entirety. The second point is that we
don't actually print the template from within the function,
instead, we return the translated text to the caller. This is just in
case we want to use the template for something other than an active
HTML page generated by a CGI script. We could use the same function to
introduce templates into a static HTML file, whilst still allowing us
to reproduce and parse the template in the process.
The third point is just a small nicety. If the file that's
been selected doesn't exist, we print a little message to say
that there's been an error. We could equally return nothing, but
I prefer to be able to spot the problem. In production systems,
I've actually used an SSI type error message, and also taken the
time to mail an error message to the webmaster to highlight a possible
problem.
Now for the important part. The second half of the function
actually processes the template so that we can embed elements into the
templates that can be replaced on the fly. We replace strings of the
form %%string%% by using a hash which we supply to the function. The
key of the hash is the string, and the value is the replacement
text. For example, take the simple template:
<title>%%title%%</title>
Using the function
above we can print out the template using:
print parse_template('template','title' => 'This is the title text');
This will produce the desired:
<title>This is the title text</title>
You can create as many templates as you like, and have as many
different replacement strings as you like. It'll also replace the
same string a number of times, useful if you want the page title, and
the title displayed within the page to be the same.
There is of course a little problem with this, in that in order for
this to work, you need to have a different set of templates that
support the %%text%% construct. So, the final trick is to change the
way in which we search for the matching string that we want to
replace. Instead of using %%text%%, you use a standard SSI construct,
using a comment to encapsulate the text to be replaced. For example,
we could have a template with:
<font size=+2><b><!--#include perltext=title --></b></font>
Now if you use the template as an SSI include in another document,
the 'replacement' text will be ignored, because the SSI
system will treat it as a comment. But when parsed by an updated
version of our function, the 'title' gets replaced with the
desired text.
All you have to do is modify the function to replace the quoted
string. I've included the full version of that function
below:
sub parse_template
{
my (,%subs) = @_;
open(TEMPLATE,"")
or print "I tried to load <br>\n";
{
local $/;
sh = <TEMPLATE>;
}
close(TEMPLATE);
foreach (sort keys %subs)
{
= quotemeta '<!--#include perltext=' . . ' -->';
sh =~ s//{}/g;
}
return sh;
}
I've used quotemeta here to make sure that the whole string is
suitable for use as a search string in the regular expression.
Using Python
Although Python lags slightly behind Perl, it's use as a CGI
scripting technology is increasing, especially with the popularity of
the XML tools (which have been completely rewritten in the new Python
2.0 beta) and complete web serving solutions such as ZOPE (Z-Objects
Publishing Environment). The sequences with Python is much the same,
we slurp in the text and then use the re (regular expression) module
to do the replacement on each line. The actual function looks like
this:
import re
def parse_template(file,sub):
try:
tempfile = open(file)
except:
return ''
template = tempfile.read()
for subtext in sub.keys():
template = re.sub('%%'+subtext+'%%',sub[subtext],template)
return template
We call it in much the same way as with Perl, supplying the name of the
template and a dictionary that contains the replacement strings:
print parse_template('t2.html',{ 'title':'This is the new title' })
Now you might wonder we I haven't used readlines to
read in all the lines to a list. It's basically because I want to
be able to output a complete template string without any extra
processing, rather than using a list of strings. I also want to avoid
map and instead just use a single pass to do the
replacement.
Template Principles
All of the template principles I demonstrated in the last article
apply. Providing we continue to use the same templates, and follow the
same basic rules about dividing and splitting the individual elements
that make a page, there's no reason why using a CGI based
template parser should produce any different results than when using
the SSI system.
What's Next?
Next time, I'm going to take a minor diversion from the raw
HTML templates that we've used here, and instead look at using
XML to define a document, and a Perl-based parser for converting that
XML document into an HTML document that can be displayed by your
browser.
About the Author
Martin C. Brown is a full-time writer and consultant
specializing in multi-platform integration and Internet technologies. He is
author of both the Perl and Python Annotated Archives and Perl: The Complete
Reference. He can be contacted at mc@mcwords.com or through the
MCwords website.
|