I finally took an hour to revisit the templating code mentioned back here, using minidom instead of rparsexml. As expected, refactoring produces better code, but ‘better’ is a relative term — it’s still a major hack-job. However, in this case, I’m not bothered as long as it proves a point with as little effort as possible.
So, to recap my prior thoughts:
- I don’t like the complexity, and general inelegance, of the templates required to produce dynamic content with facelets & JSF. Faces on its own is bad enough; facelets (while admittedly helping to fix a lot of the nastiness inherent in JSF) adds its own ugliness factor.
- I don’t think it would be hard to design something better. Actually, let me rephrase that:
An electric charge applied to a few single-celled organisms contained in a small bucket of slime could design something better.
My intention is, therefore, to hack together a quick prototype — hopefully coming up with something a little more elegant in the process. And since (I hope) I have a few brain cells to rub together, I might just provide reasonable competition to the bucket of slime.
For demo purposes, I’ve come up with a basic template copying the main sections of the Amazon front page:
I’m gradually adding dynamic elements and enhancing the templating as I go. At the moment, the template looks like this. Note I’m not bothering to include style and images from the Amazon site — focussing, rather, on the content and basic layout.
Below you can see an example of the code used to inject dynamic content. Basically, I’ve defined 2 classes: Localiser - a component to localise the text in an element, and ValueLocaliser - a preset value that will be localised and replace the text in an element. The dummy ‘localise’ function merely converts the text to upper case; just to prove it has processed the text. Once the template is loaded, dynamic content is added using the xhtml element ID as the reference.
For example a template with:
<p id="someid">default value</p>
would be injected with dynamic content using the code:
template['someid'] = 'somevalue'
Similarly, a template with:
<a id="myanchor" href="">link text</p>
Could be repeated in the output by applying:
template["myanchor"] = [ template2.get('Amazon', href='http://www.amazon.com'),
template2.get('Google', value='http://www.google.com') ]
It’s worth emphasising that all I’m talking about here is templating; not lifecycle, event handling and other guff that goes into your average web ‘framework’. Thus the code below is all about applying the content of a model (albeit just dummy data) to a view. The advantage of this approaching to templating xhtml content is that you can view the template in-browser — what-you-see-is-what-you-get (well almost).
The (unfinished) result of running the code against the aforementioned template can be found here. You can hopefully see that the ‘localiser’ has converted text to uppercase where specified. The template ‘engine’, as it currently stands, is here.
More to come…
#! /usr/bin/python
import template2
# dummy localiser function
# just returns upper value of string to prove it has
# processed
def localise(val):
return val.upper()
class Localiser(template2.PluginAttributes):
'''
a subclass of a template plugin that 'localises' the content
of an element (nodeValue) and any children. Note that the
localise is just a dummy function that capitalises the value
'''
def process(self, dom, node):
template2.PluginAttributes.process(self, dom, node)
if node.nodeValue:
node.nodeValue = localise(node.nodeValue)
if node.hasChildNodes():
for n in node.childNodes:
self.process(dom, n)
class ValueLocaliser(template2.PluginAttributes):
'''
a subclass of a template plugin which 'localises' a preset
value
'''
def __init__(self, content, **kwargs):
template2.PluginAttributes.__init__(self, **kwargs)
self.content = content
def process(self, dom, node):
template2.PluginAttributes.process(self, dom, node)
if node.firstChild:
node.firstChild.nodeValue = localise(self.content)
else:
node.nodeValue = localise(self.content)
# create an instance of the template engine, then load
# a template
engine = template2.Engine()
template = engine.load('amazon_template.html')
# create a localiser
localiser = Localiser()
# inject banner1 content
template['b1:home'] = template2.get(href='http://www.amazon.com')
template['b1:user-store'] = template2.get('Jason\'s Store', href='http://localhost/amazon/store/jason')
template['b1:categories'] = template2.get(href='http://localhost/amazon/categories')
template['b1:account'] = template2.get(href='http://localhost/amazon/account/jason')
template['b1:cart'] = template2.get(href='http://localhost/amazon/cart/jason')
template['b1:help'] = Localiser(href='http://localhost/amazon/help')
template['b1:wish-list'] = template2.get(href='http://localhost/amazon/wish-list/jason')
# inject banner 2 content
template['b2:specials'] = [ ValueLocaliser('Gift Ideas', href='http://localhost/amazon/gifts'),
ValueLocaliser('International', href='http://localhost/amazon/international'),
ValueLocaliser('New Releases', href='http://localhost/amazon/new-releases'),
ValueLocaliser('Top Sellers', href='http://localhost/amazon/top-sellers'),
ValueLocaliser('Today\'s Deals', href='http://localhost/amazon/todays-deals'),
ValueLocaliser('Sell your stuff', href='http://localhost/amazon/sell') ]
template['b2:searchlabel'] = Localiser()
template['b2:searchwhat'] = [ template2.get('Amazon.com', value='amazon'),
template2.get('Books', value='books'),
template2.get('Popular Music', value='popular-music'),
template2.get('Music Downloads', value='music-downloads') ]
template['b2:searchtext'] = template2.get(None, value='some search term I entered last')
# inject banner 3 content
template['b3:welcome1'] = localiser
template['b3:user'] = 'Jason R Briggs'
template['b3:welcome2'] = localiser
template['b3:recommendations'] = template2.get(None, href='http://localhost/amazon/recommendations/jason')
# inject subnav content
template['subnav:browse-heading'] = localiser
template['subnav:favourites-heading'] = localiser
template['subnav:favourites'] = [ ValueLocaliser('Books', href='http://localhost/amazon/favourites/books/jason'),
ValueLocaliser('Music', href='http://localhost/amazon/favourites/music/jason') ]
template['subnav:stores-heading'] = localiser
template['subnav:stores'] = [ ValueLocaliser('Apparel & Accessories', href='http://localhost/amazon/stores/apparel'),
ValueLocaliser('Beauty', href='http://localhost/amazon/stores/beauty'),
ValueLocaliser('DVD\'s TV Central', href='http://localhost/amazon/stores/dvd'),
ValueLocaliser('Electronics', href='http://localhost/amazon/stores/electronics'),
ValueLocaliser('Gourmet Food', href='http://localhost/amazon/stores/food') ]
# inject content for the books/music/dvd subsection
template['subnav:bmd'] = [ ValueLocaliser('Books', href='http://localhost/amazon/bmd/books'),
ValueLocaliser('DVD', href='http://localhost/amazon/bmd/dvd'),
ValueLocaliser('Magazine Subscriptions', href='http://localhost/amazon/bmd/subscriptions'),
ValueLocaliser('Music', href='http://localhost/amazon/bmd/music'),
ValueLocaliser('Video', href='http://localhost/amazon/bmd/video'),
ValueLocaliser('Amazon Shorts', href='http://localhost/amazon/bmd/shorts'),
ValueLocaliser('Textbooks', href='http://localhost/amazon/bmd/textbooks') ]
# print out the result
print str(template)

No comments yet.