Sunday, August 19, 2007

Wasp Templating: An Introduction

Templating involves putting a text document (the template) though a series of transformations to produce a finished document. In word processing this is handy for mail merge operations or the insertion of boilerplate text. For the web, it is often used to automate the creation of HTML files. Templating provides several advantages, the most obvious being a decrease in repetitive markup and enhanced organisation.

There are many different approaches to web templating, as illustrated by the variety of Python tools devoted to the task.

Wasp follows the following design precepts:
  • Editing XML-based templates is too much work

  • Adding descriptors to existing HTML tags is a muddle

  • Use your usual HTML editor for the job

  • Be friendly to web designers

  • Use Python syntax so there's nothing new to learn

  • Minimal clean implementation

  • No performance bottlenecks

Wasp 1.x took a minimal approach that did not allow arbitrary Python code in HTML files, though it did provide an execute tag for running custom functions. While I still believe that a separation of markup and programme code is the best way, I acknowledge that it is almost impossible to write a web app without mixing the two in some fashion. Either your Python writes HTML or your HTML embeds Python. Either can result in a code base that is difficult to maintain.

The upcoming Wasp 2.0 does away with execute, instead allowing arbitrary Python code in embedded tags. This is much more powerful, and proved to be easy to implement.

You can now have markup that defines and uses functions, like so:
<?w def row(arg):
y = arg + 1
prn('* hello %d' % y) ?>

Here is a list:
<?w for j in range(x):
row(j) ?>

If this template is compiled with x=4 the result is:
Here is a list:
* hello 1
* hello 2
* hello 3
* hello 4

(This illustrates that Wasp templating need not be used only for HTML.)

The additional Wasp tags have been implemented anew as Python functions. Thus their syntax has changed. Where before you included a separate file with the include tag, like so:
<?w i:header('title') ?>
some stuff in between
<?w i:footer ?>

You now do it this way, the name of the file becoming the first argument to the function:
<?w i('header', 'title') ?>
some stuff in between
<?w i('footer') ?>

I have kept two useful features: the arbitrary nesting (include a file that includes a file that...) and the fact that arguments can be passed to the included bits. So in the previous example the header include file would receive an argument title which would get inserted where you use the tag <?w 1 ?>. If there was a second argument it would be interpolated where the code said <?w 2 ?> and so on up to 9. (I must say that if you are passing nine arguments there is likely a better way to do things!)

Finally, to expand a macro into its full text (as defined in the configuration file), use this function:
My name is: <?w m('my_name') ?>

If you were a famous surrealist, this might output:
My name is: Max Ernst

That's it. I've done away with all the other tags. The special tags that worked only in batch mode (seq and index) will be zapped. If people need them I am sure they can be created through plugins. The conditionals (if, endif) will go too, since I think that with full Python code in place they'll be irrelevant.

There is one other major change that you might have noticed. In Wasp 1.x you output text to the web page from within your code by simply using the print command. In Wasp 2.x you must use functions for this task as well. prnz(s) outputs only the string whereas prn(s) adds a line break in accordance with how print works.

The reason for this change has to do with the architecture of the server code. Previously I was redirecting standard output in order to capture the print statements. This works very badly in a multi-threaded application!

If you want more than what Wasp templating provides, I recommend Cheetah. I hope to provide hooks to slot in external templating packages in a version after 2.0.

Does Wasp templating do anything radical or new? No. Does it do things simpler than any other tool? Yes. For me that was a good enough reason to roll my own solution to this much-solved problem.


No comments:

Post a Comment