Programming with HTML::Mason Part 3

By: Jonathan Swartz

Last month we continued our exploration of Mason with form handling, sessions, and data caching. In this final month I'll introduce techniques for building sites with template-driven design and navigation.

I've been asked at various times to show the backend of Mason HQ, the official Mason site, and this seemed like a good opportunity. I'll use selected pieces of Mason HQ to motivate each technique.

Note: Some of the newest features mentioned here are part of version 0.82, which is not yet available at time of writing but should be soon.

Templates

Like many sites, Mason HQ sports a consistent look-and-feel in the form of a common header and footer on each page. As we saw in the first two articles, you can add a header and footer to a specific page with the <& &> tag, but this doesn't scale for sites with more than a handful of pages.

Mason's answer to automatic templating is the autohandler. If we place a component named autohandler at the top of a directory hierarchy, all requests in that hierarchy are routed through autohandler. Here's a truncated version of the top-level autohandler on Mason HQ:

autohandler:
    <html>
    <head>
    <& metatags &>
    <title>Mason HQ</title>
    <link rel="stylesheet" href="/s.css">
    </head>
    
    <body bgcolor="#f5f5f5" background=/i/back.gif ...>
    
    ...

    <!-- top table of contents -->
    <& toc &>
    
    ...
    
    <table border="0" cellpadding="4" cellspacing="0" width="96%">
    <tr valign="top">
    <td>
    
    <!-- Call the page component here -->
    <% $m->call_next %>
    
    </td>
    </tr>
    </table>
    
    ...
    
    <!-- bottom table of contents -->
    <& toc &>
    
    ...
    
    </body>
    </html>
    
    <%init>
    $m->comp('setup_globals');
    </%init>
    

This autohandler runs initialization code, outputs a header, calls the actual page (via $m->call_next), and outputs a footer. It will take effect for every page unless overriden.

When autohandlers were first introduced into Mason, at most one autohandler would run per page. Starting in version 0.82 all applicable autohandlers get a chance to run. Control starts with the top-most autohandler; each $m->call_next calls the next autohandler in the chain, until finally the page itself is called. This allows you to define a general template for the site and more specific templates for particular sections.

For example, the code archive section (/arch) of Mason HQ has its own left table of contents. This is implemented by /arch/autohandler, which need only include the parts of the template specific to its section.

Methods and Inheritance

Our current autohandler lacks flexibility. Although in general we want the same template to appear on every page, we want each page to specify certain details like the title. We also want to allow pages to override or eliminate portions of the template. Finally, some pages may need to completely reject the autohandler in lieu of their own unique design.

We can address these issues using the object-oriented style features introduced in 0.82. Every component can define methods (mini components) and attributes (scalars/ref values) to be accessed from other components. Components also inherit methods and attributes from their parent, typically the nearest autohandler.

We modify our Mason HQ autohandler to call two methods on the current page component: title, containing the page title, and headline, containing the piece of HTML between the table of contents and the body. We also define defaults for these methods at the top.

autohandler:
    <%method title></%method>
    <%method headline>
    <span class="hed12"><font size="3"><b>
    <& SELF:title &></b></font></span><br>
    </%method>

    <html>
    <head>
    <& metatags &>
    <title>Mason HQ: <& SELF:title &></title>
    <link rel="stylesheet" href="/s.css">
    </head>
    
    <body bgcolor="#f5f5f5" background=/i/back.gif ...>
    <& SELF:headline &>
    .
    .
    .
    <%init>
    $m->comp('setup_globals');
    </%init>
    

The syntax comp_path:method is used to call a component method. The special path SELF denotes the component for the current page. Thus,

    <& SELF:title &>
calls the title method on the component for the current page.

Now each page can specify its title. The changes page (/changes.html), for example, starts with

    <%method title>Change Log</%method>
Most Mason HQ pages don't bother defining the headline method, choosing instead to inherit the default from autohandler. One exception is the download page, which for design reasons doesn't have a headline. So /download/index.html starts with:
    <%method title>Download</%method>
    <%method headline></%method>
Components that need to change their default parent (or simply not inherit from anyone) can do so with the inherit component flag. For example, the patches section of the site (/bugs/patches) contains plain text patches for which the template is inappropriate. So /bugs/patches/autohandler contains:
    <%flags>
    inherit=>undef
    </%flags>
The <%flags> section in general contains parameters that affect the way components behave. This autohandler effectively states that pages under /bugs/patches inherit from no one and are responsible for their own output.

That gives you a taste of Mason's flexible templating system. Although we made only limited use of methods and inheritance, you can imagine a complex site relying on dozens of distinct methods and attributes for display characteristics, behavior, access control, and so on.

Navigation

Let's turn now to creating navigation that reacts to the current page. Mason HQ has a table of contents component (toc) on the top and bottom of the page. A static version of the component looks like:

toc:
    <table border="0" cellpadding="4" cellspacing="0" width="100%">
    <tr bgcolor="#ffcc00">
    <td align="center" class="nav">
    
    <a href="/">Home</a> | 
    <a href="/features.html">Features</a> | 
    <a href="/sites/">Mason-Powered Sites</a> | 
    <a href="/download/">Download</a> |
    <a href="/arch/">Contributions</a>
    <br>
    
    <a href="/changes.html">Change Log</a> |
    <a href="/docs/manual/">Documentation</a> |
    <a href="/docs/faq/">FAQ</a> | 
    <a href="/bugs/">Known Bugs & Patches</a> |
    <a href="/maillist.html">Mailing List</a>
    <br>
    
    </td></tr></table>
    

As you click around the table of contents on the live site, the navigation link for the current page gets unlinked and highlighted with bold. Designers often request this kind of effect and I've spent some time thinking about ways to implement it. Let's look at a few of these solutions and their pros/cons.

Each solution uses $r->uri to determine the current page, filtering out the redundant index.html suffix.

1. Surround each link with a conditional expression.

toc (1):
    % (my $uri = $r->uri) =~ s/index\.html$//;
    % if ($uri eq '/') {
    <b>Home</b> |
    % } else {
    <a href="/">Home</a> | 
    % }
    % if ($uri eq '/features.html') {
    <b>Features</b> |
    % } else {
    <a href="/features.html">Features</a> | 
    % }
    ...
    

This is the straightforward brute force method. It makes the toc very unreadable, and fails to encapsulate the highlighting effect (making it difficult to change, say, from bold to italics).

2. Produce links by looping through a data structure.

toc (2):
    % (my $uri = $r->uri) =~ s/index\.html$//;
    % my @links = ('/','Home','/features.html','Features',
    %              '/sites/','Mason-Powered Sites', ...);
    % while (my ($link,$name) = shift(@links)) {
    %   if ($uri eq $link) {
    <b><% $name %></b>
    %   } else {
    <a href="<% $link %>"><% $name %></a>
    %   }
    %   if (@links) {
     | 
    %   }
    % }
    

Here at least the highlighting effect is encapsulated in one place, so we can easily change it. However the data structure and loop may make it harder for non-developers to read and modify.

3. Create a subcomponent.

toc (3):
    <%def .link>
    % my ($link,$name) = @_;
    % (my $uri = $r->uri) =~ s/index\.html$//;
    % if ($uri eq $link) {
    <b><% $name %></b>
    % } else {
    <a href="<% $link %>"><% $name %></a>
    % }
    </%def>
    
    <& .link, '/','Home' &> |
    <& .link, '/features.html','Features' &> |
    <& .link, '/sites/','Mason-Powered Sites' &> |
    <& .link, '/download/','Download' &> |
    <& .link, '/arch/','Contributions' &>
    ...
    

The <%def> tag creates an embedded subcomponent that can only be accessed from toc. It is useful for small, reusable bits of HTML and Perl that don't merit a separate file. Here we use it to encapsulate link highlighting in a moderately readable way.

Notice that we call .link with two unnamed ordered arguments, rather than bothering with name/value pairs. You can do this with any component although it is often most appropriate for small subcomponents.

4. Use a filter.

toc (4):
    <a href="/">Home</a> | 
    <a href="/features.html">Features</a> | 
    <a href="/sites/">Mason-Powered Sites</a> | 
    <a href="/download/">Download</a>
    .
    .
    .

    <%filter>
    (my $uri = $r->uri) =~ s/index\.html$//;
    s{<a href="$uri">([^<]+)</a>} {<b>$1</b>};
    </%filter>
    

The <%filter> tag specifies a block of code to filter the output of the component through. $_ contains the component output upon entrance to the <%filter> block, which is expected to alter $_ in place. In this case we use a regexp substitution to replace the current page link with the highlighted name.

This is the solution I actually use on the site. It leaves the HTML untouched and readable, separating the code complexity out to an easily ignored section. On the other hand it does require a certain conformity in the HTML being filtered.

Summary

In this article we've covered That concludes our three article tour of Mason. By now you know where to find more information, so happy site developing!