Programming with HTML::Mason Part 1

By: Jonathan Swartz

In this article I describe Mason, a Perl-based web development platform. Leveraging the reliability and performance of Apache/mod_perl, Mason focuses on providing powerful tools for dynamic content generation. Mason's well-defined component model allows pages to be built from shared, reusable pieces of code and HTML. Its wide set of features address the common problems of web development such as caching, debugging, and templating.

Background

Like many open source systems, Mason was born out of specific needs and a frustration with available tools. In 1997 at CMP Media, a $500 million publishing company, we were building sites with a combination of CGI and Netscape's NSAPI. We needed to build a large number of sites with both common and distinct template elements. Most pages were dynamic, drawing data from the database or file system.

We suffered from the usual CGI problems: slowness, no good way to share templates or HTML snippets, and scripts consisting mostly of "print" statements inaccessible to designers. NSAPI didn't fully meet our needs and entailed developing against an obscure C API and recompiling for each change.

Switching to Apache/mod_perl was our first important step. mod_perl vastly improved performance and gave us a comprehensive server API. But mod_perl alone doesn't improve much on the CGI development model, relegating this task instead to third-party modules.

Mason began as a set of improvements to the CGI model: embedded Perl in HTML, modularized pages, automatic handling of GET/POST parameters. As we built sites, each web development frustration spawned a new feature. By spring of 1998 we found ourselves with a system useful enough for public release. In August 1998, with CMP's blessing, we released Mason to CPAN as "HTML::Mason" under the standard Perl Artistic license.

In the year and a half since launch we've continued to upgrade Mason regularly and provided support in the form of a web site and discussion community.

Of course Mason is not alone in addressing these problems. Related solutions range from other Perl templating modules (Embperl, Apache::ASP, HTML::Template), to custom languages (PHP), to full-blown application servers both open source (Zope) and commercial (StoryServer, Cold Fusion, ASP).

Installation

I'll leave the detailed discussion of Mason installation to the documentation and FAQ. Mason should work on any Perl-enabled system, and is generally used in conjunction with Apache and mod_perl. Once the latter are installed, it should take an hour or less to get Mason going.

If you have troubles, first check the FAQ and mailing list archives and determine if your problem is Mason-specific (as opposed to, say, a mod_perl problem). Finally, send a description of your problem to the mailing list.

A First Example

Here's a Mason component called showenv.html that prints the current system environment in a table.

showenv.html:

   <table border=1>
   % foreach my $key (sort keys %ENV) {
   <tr>
   <td><b><% $key %></b></td>
   <td><% $ENV{$key} %></td>
   </tr>
   % }
   </table>

Lines beginning with a '%' character are treated as Perl. The <% %> tag evaluates a Perl expression and displays the result. All other text is treated as HTML and printed. This component might represent an entire page or just one part of a larger page. [View sample output.]

If you're used to CGI programming, you'll see that the main thing we've done is turned the usual "print-print-print" model inside out. Instead of embedding lots of HTML into Perl print statements, we embed a little Perl into an HTML page. This model has become fairly standard in the application server space. It proves more visually intuitive and readable than the CGI "print" model, especially for non-programmers.

Next, we add a standard header and footer to our component:

showenv.html (2):

   <& header &>
   <table border=1>
   % foreach my $key (sort keys %ENV) {
   <tr>
   <td><b><% $key %></b></td>
   <td><% $ENV{$key} %></td>
   </tr>
   % }
   </table>
   <& footer &>

The <& &> tag tells Mason to call another component and insert its output in place. In this case, header and footer are other components (files) residing in the same directory. The advantage of separate header and footer components is that many pages can share them -- you can then modify the look and feel of your site by changing one file.

header:

   <html>
   <head>
   <title>Current environment</title>
   </head>
   <body bgcolor="white">
   <h2>Current environment</h2>

footer:

   <hr>
   Copyright 2000 by me.
   </body>
   </html>

The header and footer need not be in the current directory; you can supply relative or absolute paths in <& &>. Like URLs, absolute paths are interpreted in relation to a component root. So an absolute path looks something like

   <& /shared/header &> 
instead of
   <& /usr/local/www/htdocs/shared/header &>
Since header is potentially used by many components, let's parameterize its title and background color. We add the following to the bottom of header:
   <%args>
   $title
   $bgcolor=>'white'
   </%args>
This declares two arguments, $title and $bgcolor. Both are scalars (you can also declare list and hash arguments). $bgcolor is an optional argument with a default value of 'white'. In contrast, $title is a required argument; a runtime error will occur if header is called without a title value.

Unlike many systems which provide arguments through an API or hash, Mason makes arguments available directly as lexical ("my") variables, local to the component. We modify header to use its new arguments:

header (2):

   <html>
   <head>
   <title><% $title %></title>
   </head>
   <body bgcolor="<% $bgcolor %>">
   <h2><% $title %></h2>

   <%args>
   $title
   $bgcolor=>'white'
   </%args>

Now, when showenv calls header, it passes parameter values like so:

   <& header, title=>'Current environment', bgcolor=>'lightblue' &>
Consistency mavens will be pleased to discover that the same <%args> mechanism is used to declare GET/POST parameters in a top-level component. To illustrate, let's allow users to specify the maximum number of environment fields displayed in showenv.html, via a GET parameter max_fields. We add to showenv the familiar <%args> section and a line that makes use of $max_fields:

showenv.html (3):

   <& header, title=>'Current environment', bgcolor=>'lightblue' &>
   <table border=1>
   % foreach my $key (sort keys %ENV) {
   <tr>
   <td><b><% $key %></b></td>
   <td><% $ENV{$key} %></td>
   </tr>
   %   last if (--$max_fields <= 0);
   % }
   </table>
   <& footer &>

   <%args>
   $max_fields=>1000  # default is show everything
   </%args>

Now, the URL

   /showenv.html
has the same effect as before, but
   /showenv.html?max_fields=5
shows only five fields. [View sample output.] As we'll see in the next article, POST data is handled in the same way.

Summary

In this article we've covered This alone gives you a powerful framework for building modular sites, but for Mason it is just the beginning. Next article we'll show form handling, initialization sections, persistent sessions with cookies, redirects, and caching. In the third article we'll discuss cutting-edge features: templates, filters, embedded components, and a bit of object-oriented site generation.

But you don't have to wait a month to find out more. Download HTML::Mason from your local CPAN site and start playing. Check out the mailing list, documentation, FAQ, and other goodies at http://www.masonhq.com.