Taglib TMTOWTDI
by Barrie Slaymaker
|
Pages: 1, 2
Upstream Filters good for?
So what is XSLT upstream of an XSP processor good for? You can do many things with it other than implementing LogicSheets. One use is to implement branding: altering things like logos, site name, and perhaps colors, or other customization, like administrator's mail addresses on a login page that is shared by several sub-sites.
A key advantage of doing transformations upstream of the XSP processor is that the XSP process caches the results of upstream transformations. XSP converts whatever document it receives in to Perl bytecode in memory and then just runs that bytecode if none of the upstream documents have changed.
Another use is to convert source documents that declare what should be on a page to XSP documents that implement the machinery of a page. For instance, a survey site might have the source documents declare what questions to ask:
<survey>
<question>
<text>Have you ever eaten a Balut</text>
<response>Yes</response>
<response>No</response>
<response>Eeeewww</response>
</question>
<question>
<text>Ok, then, well how about a nice haggis</text>
<response>Yes</response>
<response>No</response>
<response>Now that's more like it!</response>
</question>
...
</survey>
XSLT can be used to transform the survey definition in to an XSP page that uses the PerForm taglib to automate form filling, etc. This approach allows pages to be defined in terms of what they are instead of how they should work.
You can also use XSLT upstream of the XSP processor to do other things, like translate from a limited or simpler domain-specific tagset to a more complex or general purpose taglib written as a taglib module. This can allow you to define taglibs that are easier to use in terms of more powerful (but scary!) taglibs that are loaded in to the XSP processor.
My::SimpleWeatherTaglib
A new-ish taglib helper module has been bundled in recent AxKit releases: Jörg Walter's SimpleTaglib (the full module name is Apache::AxKit::Language::XSP::SimpleTaglib). This module performs roughly the same function as Steve Willer's TaglibHelper, but supports namespaces and uses a feature new to Perl, subroutine attributes, to specify the parameters and result formatting instead of a string.
Here is My::SimpleWeatherTaglib:
package My::SimpleWeatherTaglib;
use Apache::AxKit::Language::XSP::SimpleTaglib;
$NS = "http://slaysys.com/axkit_articles/weather/";
package My::SimpleWeatherTaglib::Handlers;
use strict;
require Geo::Weather;
## Return the whole report for fixup later in the processing pipeline
sub report : child(zip) struct({}) {
return 'Geo::Weather->new->get_weather( $attr_zip );'
}
1;
The $NS variable defines the namespace for this taglib. This
module uses the same namespace as my_weather_taglib.xsl and
My::WeatherTaglib, because all three implement the same taglib (this
repetetiveness is to demonstrate the differences between the approaches). See
the Mixing and Matching Taglibs section to see how My::WeatherTaglib and
My::SimpleWeatherTaglib can both be used in the same server instance.
My::SimpleWeatherTaglib then shifts gears in to a new package,
My::SimpleWeatherTaglib::Handlers to define the subroutines for the taglib
tags. Using a virgin package like this provides a clean place with which to declare
the tag handlers. SimpleTaglib looks for the modules in the Foo::Handlers
package if it's use()d in the Foo package (don't use
require for this!).
My::SimpleWeatherTaglib requires Geo::Weather and declares a
single tag, which handles the <weather:report> tag in
weather1.xsp (which we'll show in a moment).
The require Geo::Weather; instead of use
Geo::Weather; is to avoid importing subroutines in to our otherwise
...::Handlers namespace which might look like a handler.
There's something new afoot in the declaration for sub report:
subroutine attributes. Subroutine attributes are a new feature of Perl (as of
perl5.6) that allow us to hang additional little bits of information on the
subroutine declaration that describe it a bit more. perldoc
perlsub for the details of this syntax. Some attributes are
predefined by Perl, but modules may define others for their own purposes. In
this case, the SimpleTaglib module defines a handful of attributes, some of
which describe what parameters the taglib tag can take and others which
describe how to convert the result value from the taglib implementation into
XML output.
The child(zip) subroutine attribute tells the SimpleTaglib
module that this handler expects a single child element named zip
in the taglib's namespace. In weather1.xsp, this ends up looking
like:
<weather:report>
<!-- Get the ?zip=12345 from the URI and pass it
to the weather:report tag as a parameter -->
<weather:zip><param:zip/></weather:zip>
</weather:report>
The text from the <weather:zip> element (which will be
filled in from the URI query string using the param: taglib) will
be made available in a variable named $attr_zip at request time.
The fact that the text from an element shows up in a variable beginning with
$attr_ is confusing, but it does actually work that way.
The struct({}) attribute specifies that the result of this tag
will be returned as a Perl data structure that will be converted into XML.
Geo::Weather->new->get_weather( $zip ) returns a HASH reference that looks like:
{
'city' => 'Pittsburgh',
'state' => 'PA',
'cond' => 'Sunny',
'temp' => '77',
...
};
The struct attribute tells SimpleTaglib to turn this in to XML
like:
<city>Pittsburgh</city>
<state>PA</state>
<cond>Sunny</cond>
<temp>77</temp>
....
The {} in the struct({}) attribute specifies that
the result nodes should be not be in a namespace (and thus have no namespace
prefix), just like the static portions of our weather1.xsp
document. This is one of the advantages that SimpleTaglib has over other
methods: It's easier to emit nodes in different namespaces. To emit nodes in a
specific namespace, put the namespace URI for that namespace inside the
curlies: struct({http://my.namespace.com/foo/bar}). The
{} notation is referred to as James Clark (or jclark)
notation.
Now, the tricky bit. Harkening back to our discussion of how XSP is implemented, remember that
the XSP processor compiles the XSP document into Perl code that is executed
to build the output document. As XSP compiles the page, it keeps a lookout
for tags in namespaces handled by taglib modules that have been configured in
with AxAddXSPTaglib. When XSP sees one of these tags, it calls in
to the taglib module--My::SimpleWeatherTaglib here--for that namespace and
requests a chunk of Perl source code to compile in place of the tag.
Taglibs implemented with the SimpleTaglib module covered here declare
handlers for each taglib tag (sub report, for instance). That
handler subroutine is called at parse time, not at request time. Its job is to
return the chunk of code that will be compiled and then run
later, at request time, to generate the output. So report()
returns a string containing a snippet of Perl code that calls
into Geo::Weather. This Perl code will be compiled once, then
run for each request.
This is a key difference between the TaglibHelper module that
My::WeatherTaglib used in the previous article and the SimpleTaglib module used
here. SimpleTaglib calls My::SimpleWeatherTaglib's report()
subroutine at compile time whereas TaglibHelper quietly, automatically arranges
to call My::WeatherTaglib's report() subroutine at request time.
This difference makes SimpleTaglib not so simple unless you are used to writing code that generates code that will be compiled and run later. On the other hand, "Programs that write programs are the happiest programs in the world" (Andrew Hume, according to a few places on the net). This is true here because we are able to return whatever code is appropriate for the task at hand. In this case, the code is so simple that we can return it directly. If the work to be done was more complicated, then we could also return a call to a subroutine of our own devising. So, while a good deal less simple than the approach taken by TaglibHelper, this approach does offer a bit more flexibility.
SimpleTaglib's author does promise that a new version of SimpleTaglib will offer the "call this subroutine at request time" API which I (and I suspect most others) would prefer most of the time.
I will warn you that the documentation for SimpleTaglib does not stand on its own, so you need to have the source code for an example module or two to put it all together. Beyond the overly simple example presented here, the documentation refers you to a couple of others. Mind you, I'm casting stones while in my glass house here, because nobody has ever accused me of fully documenting my own modules.
For reference, here is the weather1.xsp from the previous
article, which we are reusing verbatim for this example:
<?xml-stylesheet href="NULL" type="application/x-xsp"?>
<?xml-stylesheet href="weather.xsl" type="text/xsl"?>
<?xml-stylesheet href="as_html.xsl" type="text/xsl"?>
<xsp:page
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:util="http://apache.org/xsp/util/v1"
xmlns:param="http://axkit.org/NS/xsp/param/v1"
xmlns:weather="http://slaysys.com/axkit_articles/weather/"
>
<data>
<title><a name="title"/>My Weather Report</title>
<time>
<util:time format="%H:%M:%S" />
</time>
<weather>
<weather:report>
<!-- Get the ?zip=12345 from the URI and pass it
to the weather:report tag as a parameter -->
<weather:zip><param:zip/></weather:zip>
</weather:report>
</weather>
</data>
</xsp:page>
The processing pipeline and intermediate files are also identical to those from the previous article, so we won't repeat them here.
Mixing and Matching Taglibs using
httpd.conf
As detailed in the first article in this series, AxKit integrates tightly with Apache and Apache's configuration engine. Apache allows different files and directories to have different configurations applied, including what taglibs are used. In the real world, for instance, it is sometimes necessary to have part of a site to use a new version of a taglib that might break an old portion.
In the server I used to build the examples for this article, for instance,
the 02/ directory still uses My::WeatherTaglib from the last
article, while the 03/ directory uses the
my_weather_taglib.xsl for one of this article's examples and
My::SimpleWeatherTaglib for the other. This is done by combining Apache's
<Directory> sections with the AxAddXSPTaglib
directive:
##
## Init the httpd to use our "private install" libraries
##
PerlRequire startup.pl
##
## AxKit Configuration
##
PerlModule AxKit
<Directory "/home/me/htdocs">
Options -All +Indexes +FollowSymLinks
# Tell mod_dir to translate / to /index.xml or /index.xsp
DirectoryIndex index.xml index.xsp
AddHandler axkit .xml .xsp
AxDebugLevel 10
AxTraceIntermediate /home/me/axtrace
AxGzipOutput Off
AxAddXSPTaglib AxKit::XSP::Util
AxAddXSPTaglib AxKit::XSP::Param
AxAddStyleMap application/x-xsp \\
Apache::AxKit::Language::XSP
AxAddStyleMap text/xsl \\
Apache::AxKit::Language::LibXSLT
</Directory>
<Directory "/home/me/htdocs/02">
AxAddXSPTaglib My::WeatherTaglib
</Directory>
<Directory "/home/me/htdocs/03">
AxAddXSPTaglib My::SimpleWeatherTaglib
</Directory>
See How
Directory, Location and Files sections work from the apache
httpd documentation (v1.3 or 2.0) for the details of how to use
<Directory> and other
httpd.conf sections to do this sort of thing.
Help and thanks
Jörg Walter as well as Matt Sergeant were of great help in writing this
article, especially since I don't do LogicSheets. Jörg also fixed a bug
in absolutely no time and wrote the SimpleTaglib module and the AxTraceIntermediate feature.
In case of trouble, have a look at some of the helpful resources we listed in the first article.
Copyright 2002, Robert Barrie Slaymaker, Jr. All Rights Reserved.
![]()

