Building Sites with Mason

This month, Mr. Lerner introduces us to a Mod-perl module to aid in building large, dynamic web sites.
Mason Components

In the Mason universe, a “component” can return either HTML or a value. The former usually consists of HTML templates or template fragments, while the latter consists of subroutines and other code which are invoked by templates. All components share the same syntax, which should be familiar to anyone who has used a template system.

Perl code can be placed inside a component, bracketed by <% and %>. Any returned value is inserted into the component, replacing the Perl code that created it. For example, the following component (output.html) will display the current time of day each time it is invoked:

<Head><Title>Current time</Title></Head>
<H1>Current time</H1>
<P>The current time is: <% scalar localtime %></P>

I put the above into the file time.html and placed it in the component root directory. Immediately after doing so, I was able to go to the URL /mason/time.html and get the current time.

Mason supports two other types of Perl sections, which can be useful in different contexts. A % in the first column of a Mason component forces the entire line to be interpreted as Perl, rather than literally. This is best used for control structures (such as loops and if-then statements) that produce text strings, as in the following:

<Head><Title>Current time</Title></Head>
% foreach my $month (qw(Jan Feb Mar Apr May Jun
% Jul Aug Sep Oct Nov Dec))
% {
<P><% $month %></P>
% }

As you can see, the <% %> construct works in all contexts. In addition, lexicals declared at the top level of one Perl segment can be used within any other Perl segment.

Finally, long runs of Perl can be placed inside %perl blocks. This is best for doing heavy-duty computation, rather than simply retrieving variable values. For example:

<Head><Title>Current time</Title></Head>
my @months = qw(January February March April May
June July August September October November December);
<P>The current month is <% $months[(localtime)[4]]

Once again, notice how the lexical (my) variable declared in the <%perl> section is available in the following <% %> section.

Calling Components

Experienced users of Text::Template and other Perl templating modules are probably not very impressed at this point. After all, there are dozens of ways to create templates of this sort, and many work with mod_perl for extra speed.

However, Mason's template syntax includes provisions for invoking other components, much as one subroutine might invoke another. (Indeed, since the Mason parser turns each component into a subroutine, this is not an incorrect analogy.) In some ways, this is like having a heavy-duty server-side include system, allowing you to standardize headers and footers. However, because components can return values as well as HTML output, and because Mason makes it possible to pass arguments to a component, things can get far more interesting.

One component can invoke another component with the special <& &> syntax. For example, the following invokes the component subcomp:

<& subcomp &>

Any HTML produced by subcomp is placed at the point where it was invoked, much like a server-side include. Each HTML page generated by a Mason site can consist of one, five, 10, 20 or more components. In this way, it is possible to assemble a page from individual elements—beginning with headers and footers and moving on to tables and pull-down menus. For example, here is a header component:

<!-- begin component: header.comp -->
<Body bgcolor="#FFFFFF">
<H1>This is a header</H1>
<!-- end component: header.comp -->
And here is a footer component:
<!-- begin component: footer.comp -->
<a href=""></a>
<!-- end component: footer.comp -->
Finally, here is a top-level component in which the header and footer come from the above components:
<& header.comp &>
<P>This is the body</P>
<& footer.comp &>
Notice, I gave file extensions of “comp” rather than “html” to the header and footer. This is simply a convention that enables me to differentiate between top-level components (which have .html extensions) and lower-level fragments.

Also, notice how I begin and end each lower-level component with HTML comments that indicate where it begins and ends. This provides a primitive type of debugging (expanded by the Mason previewer/debugger component) that lets me see where things are happening, simply by viewing a component's HTML source code.