A couple of comments on Simple Attribute-Based Template Exporting have asked for an example. I'll show off more of this code in my YAPC::NA 2012 and Open Source Bridge 2012 talk about how to write the wrong code (along with a handful of other techniques).
(I assume some knowledge of Template Toolkit (besides far too many books about finance, accounting, and investing, the Template Toolkit book is always within reach these days); I've set up a wrapper template which provides the standard look and feel of my application and I include/process other templates liberally. If you understand that much, you'll be able to follow along.)
One of the interesting templates in the system displays a list of chapters of a book in progress. A cron job rebuilds a static page from this template once a day. The template looks something much like:
[% USE Bootstrap -%]
[%- canonical_url = 'http://sitename.example.com/book/' _ link -%]
[%- add_og_properties({
'fb:admins' => '436500086365356',
'og:title' => title _ ' | sitename.example.com',
'og:type' => 'article',
'og:image' => 'http://static.sitename.example.com/images/logo.png',
'og:url' => canonical_url,
'og:description' => text.chunk(300).0,
'og:site_name' => 'Sitename: site tag line',
})
-%]
[%- add_meta(
'pagetitle' => title _ ' | sitename.example.com',
'feed_url' => 'http://static.sitename.example.com/book/atom.xml'
'canonical_url' => canonical_url
) -%]
[% article_text = BLOCK -%]
<article>
<h2>[% title | html %]</h2>
<p>Published: <time datetime="[% date %]">[% nice_date %]</time></p>
[% text %]
</article>
<ul class="pager">
[%- IF prev -%]
<li><a href="[% prev.link %].html">← [% prev.title | html %]</a></li>
[%- END -%]
<li><a href="/onehourinvestor">index</a></li>
[%- IF next -%]
<li><a href="[% next.link %].html">[% next.title | html %] →</a></li>
[%- END -%]
</ul>
[% INCLUDE 'components/social_links.tt', title => title %]
[%- END -%]
[%- row(
maincontent( article_text ),
sidebar(
sideblock( process( 'components/cached/book_latest_chapters.tt' ) ),
sideblock( process( 'components/cached/book_drafts.tt' ) )
)
) -%]
The emboldened lines are most important; they put all of the
content produced or assembled by this template in the HTML structure
the site needs. That is to say, everything on the site needs to fit into
something I call a row
. A row
can contain multiple
elements, such as maincontent
and a sidebar
, or
fullcontent
by itself with no sidebar
. A
sidebar
can contain multiple sideblock
s.
(You can ignore the other functions; they put metadata in the right places to pass to wrapper templates.)
Within my template plugin (called Bootstrap
), each of these
elements is a simple Perl function which takes one or more arguments and
interpolates it into some HTML:
sub row :Export
{
return <<END_HTML;
<div class="row">
@_
</div>
END_HTML
}
sub sidebar :Export
{
return <<END_HTML;
<div class="span4">
@_
</div>
END_HTML
}
(I initially tried to write these functions as templates within Template Toolkit itself, but there comes a point at which you want a real language. That point came very early for me.)
I lose no love over the varname = BLOCK
pattern necessary to
populate variables to pass to these plugin functions, but it works for now. In
some of my templates—usually those with lots of text I might end up
changing later—I extract that text into a separate template under
components/content/ to make it easy to edit. (This idea came up during
a client project where the client wanted to edit the legal clickthrough
arrangement after users create accounts. I didn't want lawyers or anyone to
have the ability to mess up the templating language, so I said "Edit this
single file as plain HTML and you'll be fine." It worked great.)
While my programmer brain says "This is ugly, and you're a horrible person for committing this hack upon the world—you're calling Perl from your template system to generate HTML you're stuffing into a template and that puts your presentation elements in Perl code, you awful human being!", it keeps the presentation code in a single place where I can update it infrequently (being that I don't change the layout of the site dramatically) without having to change the divs and classes of multiple templates.
I'm not arguing that this technique as expressed here is right. It's probably not optimal; there may be easier approaches to achieve the same effects.
I am saying that this currently works very well for me. I'm not typing the same HTML over and over and over again, and I can tweak it much more easily than I did before when I was refining the look and feel. In fact, I've even forgotten the exact details of the layout, from the HTML/CSS point of view, and now think only in terms of rows, maincontent, and sidebars.
Working abstractions are very nice.
Template::Semantic gives a good separation of html from perl code, which is useful in team developement.
Hey,
As one of the people asking for a code sample, THANKS!
I understand your 'no love lost' over the named block thing. I took a similar approach last time I wrote a layout system for TT (I guess I should have published it since it was quite similar to bootstrap). Lately I've played with using filters instead. I did a toy app using Catalyst that took this approach, and this is a very early prototype, but the View is defined here: https://github.com/jjn1056/Shutterstock-Exchange/blob/master/lib/Shutterstock/Exchange/Web/View/HTML.pm and the actual code is pulled over from a application model ( here: "https://github.com/jjn1056/Shutterstock-Exchange/blob/master/lib/Shutterstock/Exchange/ListQuestions.pm")
The Template is here: https://github.com/jjn1056/Shutterstock-Exchange/blob/master/share/html/index.tt#L30
This is probably almost exactly not what I really but, but the tiny part that is, is some progress for me :)
Thanks!