O'Reilly logo

Embedding Perl in HTML with Mason by Ken Williams, Dave Rolsky

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Methods and Attributes

The ability to use Mason’s component-level object-oriented methods and attributes can give you powerful techniques for managing your site.

As explained in Chapter 3, one of the major benefits of object-oriented techniques is that they help you reduce redundancy in your site. Site redundancy is a much bigger problem than most people realize. How many times have you forgone a site revision because performing the revision would be “too intrusive,” and you can’t afford the downtime? How many Internet web sites have you seen that look promising at first, but fail to fix problems and don’t adapt to usage patterns over the long run? Nobody likes to be stuck with an unmaintainable site, and the only way to avoid it is to design the site to be adaptable and extensible in the first place. Eliminating redundancy goes a long way toward this goal.

Methods

Methods in Mason are actually quite simple. A method is just like a subcomponent, but instead of defining it with a <%def> section, you use a <%method> section:

<%method .my_method>
 Any regular component syntax here...
</%method>

The difference between subcomponents and methods is primarily in how they can be invoked from other components. A method can only be invoked using special method syntax. We present three ways of doing this here:

# Fetch the bottommost child of the current component
my $self = $m->base_comp;
$self->call_method('.my_method');

# Shortcut for the above two lines
$m->comp('SELF:.my_method');

# Same thing, using <& &> syntax
<& SELF:.my_method &>

Let’s think about what happens when you invoke a method. Suppose there is a component called /staff/flintoff.mas, whose parent is /staff/autohandler, whose parent is in turn /autohandler. While any of these components are executing (which might be when a top-level request comes in for /staff/flintoff.mas or when /staff/flintoff.mas is called from another component), calling $m->base_comp from within any of these three components will return a component object representing /staff/flintoff.mas. In the example, that component object is stored in $self. Invoking call_method('.my_method') will search $self and its hierarchy of parents for a method called .my_method, starting the search at $self and proceeding upward. If such a method is found, it gets executed. If no such method is found, a fatal error occurs. You may want to call $self->method_exists('.my_method') first if you’re not sure whether the method exists.

Remember that methods are full-blown subcomponents, so you may also pass them arguments when you invoke them. Example 5-3 and Example 5-4 demonstrate a more sophisticated example of method invocation.

Example 5-3. /autohandler

<html>
<& $m->call_next &>
</html>
<%method .body_tag>
 <%args>
  $bgcolor => 'white'
  $textcolor => 'black'
 </%args>
 <body onLoad="prepare_images( )" bgcolor="<% $bgcolor %>" text="<% $textcolor %>">
</%method>

Example 5-4. /important_advice.mas

<head><title>A Blue Page With Red Text</title></head>

<& SELF:.body_tag, bgcolor=>'blue', textcolor=>'red' &>
 Never put anything bigger than your elbow into your ear.
</body>

The central thing to note about this example is the way the main component and the autohandler cooperate to produce the <body> tag. The designer of this site has chosen to make the bgcolor and textcolor page attributes configurable by each page, and the autohandler will generate the rest, including the call to the JavaScript function prepare_images( ) .

Incidentally, note that the autohandler took responsibility for the <html> and </html> tags, while the main component generated everything in the <head> and <body> sections. This is not necessarily good design — you must determine the right factorization for each site you create — but it made the example straightforward.

Now that you know what methods are and how they work, we can explore some ways that you can use them to design your site to be flexible and maintainable.

Using Methods for Titles and Headers

The most familiar example of commonality within a site’s structure is probably the overall design of pages. Most web sites want to have a common design structure across multiple pages, including common colors and fonts, common navigational elements and headers, common keywords in <META> tags, and so on. In this section, we explore how you can use methods for the specific problem of generating commonly styled headers and titles for your pages.

Generating titles and headers was the major motivation behind developing Mason’s method capabilities in the first place. Consider for a moment the “title and header problem”: it is often desirable to control the top and bottom of an HTML page centrally, for all the reasons we’ve tried to drum into your skull throughout this chapter. However, while large portions of the top and bottom of the page may be the same for all pages on your site, certain small pieces may be different on every page — titles and headers often fall into this category. So, you would like a way to generate the large common portions of your pages centrally but insert the noncommon titles and headers where they belong.

Mason’s methods provide a perfect answer. Each title and header can be specified using a method. Then an autohandler can generate the common headers and footers, calling the base component’s title and header methods to insert the page-specific information in its proper place (Example 5-5 and Example 5-6).

Example 5-5. autohandler

<html>
<head><title><& SELF:title &></title></head>
<body>
 <center><h3><& SELF:header &></h3></center>
% $m->call_next;
 <center><a href="/">-home-</a></center>
</body>
</html>
<%method title>
 www.Example.com
</%method>
<%method header>
 Welcome to Example.com
</%method>

Example 5-6. fancy_page.html

<p>This page isn't all <i>that</i> fancy, but it might be the
fanciest one we've seen yet.</p>
<%method title>
 Fancy Page
</%method>

<%method header>
 A Very Fancy Page
</%method>

The autohandler provides a default title and header, so if the base component fancy_page.html didn’t provide a title or header method, the autohandler would use its default values. If none of the components in the parent hierarchy (autohandler and fancy_page.html in this case) defines a certain method and that method is invoked, a fatal exception will be thrown. If you don’t want to have a default title and header, ensuring that each page sets its own, you can simply omit the default methods in the autohandler. If a page fails to set its title or header, you will know it pretty quickly in the development cycle.

Remember that methods are Mason components, so they can contain more than just static text. You might compute a page’s title or header based on information determined at runtime, for example.

Methods with Dynamic Content

As you know, methods and inheritance may be used to let a page and its autohandler share the responsibility for generating page elements like headers and titles. Since these elements may often depend on user input or other environmental conditions (e.g., “Welcome, Jon Swartz!” or “Information about your 9/13/2001 order”), you’ll need a way to set these properties (like “Jon Swartz” or “9/13/2001”) at run-time. Why is this an issue? Well, the following won’t work:

<%method title>
 <!-- this method is invoked in the autohandler -->
 Information about your <% $order_date %> order
</%method>

Your order included the following items:
 ...generate item listing here...

<%init>
 my $order_date = $session{user}->last_order_date;
</%init>

The reason that won’t work is that variables set in the <%init> block won’t be visible inside the <%method title> block. Even if the scope of $order_date included the <%method title> block (it doesn’t), the sequence of events at runtime wouldn’t allow its value to be seen:

  1. A request for /your_order.html is received. Mason constructs the runtime inheritance hierarchy, assigning /autohandler as /your_order.html’s parent.

  2. Mason executes the /autohandler component, which invokes its SELF:title method. The title method invoked is the one contained in /your_order.html.

  3. The /your_order.html:title method runs, and the value of the $order_date is still unset — in fact, the variable is undeclared, so Perl will complain that the Global symbol "$order_date" requires explicit package name. Let’s suppose you trapped this error with eval {}, so that we can continue tracing the sequence of events.

  4. Control returns to /autohandler, which eventually calls $m->call_next and passes control to /your_order.html.

  5. /your_order.html runs its <%init> section and then its main body. Note that it would set $order_date much too late to affect the title method back in step 3.

  6. /your_order.html finishes and passes control back to /autohandler, and the request ends.

What’s a Mason designer to do? The solution is simple: use a <%shared> block instead of an <%init> block to set the $order_date variable. This way, the variable can be shared among all the methods of the /your_order.html component, and it will be set at the proper time (right before step 2 in the previous listing) for it to be useful when the methods are invoked.

The proper code is remarkably similar to the improper code; the only difference is the name of the block in which the $order_date variable is set:

<%method title>
 <!-- this method is invoked in the autohandler -->
 Information about your <% $order_date %> order
</%method>

Your order included the following items:
 ...generate item listing here...

<%shared>
 my $order_date = $session{user}->last_order_date;
</%shared>

<%shared> blocks are executed only once per request, whenever the first component sharing the block needs it. Its scope lasts only to the end of the request. Because of this, <%shared> blocks are ideal for sharing scoped variables or performing component-specific initialization code that needs to happen only once per request.

Now imagine another scenario, one in which the method needs to examine the incoming arguments in order to generate its output. For instance, suppose you request /view_user.html?id=2982, and you want the title of the page to display some information about user 2982. You’ll have to make sure that the user ID is available to the method, because under normal conditions it isn’t. The two most common ways to get this information in the method are either for the method to call $m->request_args( ) or for the autohandler to pass its %ARGS to the method when calling it. The method could then either declare $id in an <%args> block or examine the incoming %ARGS hash directly. An example using request_args( ) follows:

<%method title>
 <!-- this method is invoked in the autohandler -->
 User page for <% $user->name %>
</%method>

 ... display information about $user ...

<%shared>
 my $user = MyApp::User->new(id => $m->request_args->{id});
</%shared>

Note that we cached the $user object with a shared variable so that we didn’t have to create a new user object twice.

Attributes

Sometimes you want to take advantage of Mason’s inheritance system, but you don’t necessarily need to inherit the full components. For instance, in our first title and header example, the title and header methods contained just plain text and didn’t use any of the dynamic capabilities of components. You might therefore consider it wasteful in this case to bring the full component-processing system to bear on the generation of headers and footers.

If you find yourself in this situation, Mason’s component attributes may be of interest. An attribute is like a method in the way its inheritance works, but the value of an attribute is a Perl scalar variable, not a Mason component.

Example 5-7 and Example 5-8 rewrite our previous autohandler example using attributes instead of methods.

Example 5-7. autohandler

<html>
<head><title><% $m->base_comp->attr('title') %></title></head>
<body>
 <center><h3><% $m->base_comp->attr('header') %></h3></center>
% $m->call_next;
 <center><a href="/">-home-</a></center>
</body>
</html>

<%attr>
 title  => "FancyMasonSite.Example.com"
 header => "Welcome to FancyMasonSite.Example.com"
</%attr>

Example 5-8. fancy_page.html

<p>This page isn't all <i>that</i> fancy, but it might be the
fanciest one we've seen yet.</p>

<%attr>
 title  => "Fancy Page"
 header => "A Very Fancy Page"
</%attr>

Attributes can be used like this, for small bits of text that become part of every page, but whose values vary from page to page. They can also be used to set properties of the component, such as whether to display a certain navigation bar or to omit it, whether the user must have certain characteristics in order to view this page,[13] and so on.

In the current version of Mason, each attribute in an <%attr> block must be on a single line. This means that you cannot use multiple lines for clarity or to specify multiline values. Future versions of Mason may provide additional syntax options for multiline attributes. If you run up against this limitation, you may want to use a method instead of an attribute anyway, since methods can more easily deal with more complex definitions.

Another limitation of attributes in the current version of Mason is that their values are completely static properties of the component and can’t change from one request to the next. This may or may not be addressed by a future version of Mason. In any case, if you think you need dynamic attributes, you probably actually need to use methods instead.

Top-Down Versus Bottom-Up Inheritance

The attentive reader will have noticed that there are two distinct facets to Mason’s inheritance — the first is the "content wrapping” behavior, by which a parent component can output some content, then call its child component, then output some more content. The typical example of this is an autohandler that generates headers and footers, wrapped around the output of its base component. The second facet of inheritance is the method and attribute system, by which designers can define general behavior and properties in the parent components and specific behavior in the children.

A major difference between these two facets is the direction of inheritance. Mason will begin the search for methods and attributes by starting with the bottommost child and working its way toward the parent, but it will begin the component’s content generation by starting at the topmost parent and working its way toward the bottommost child (assuming each parent calls $m->call_next).

Although this behavior may seem odd if this is the first time you’ve encountered it, the inheritance system will seem like second nature after you’ve worked with it for a while. To help form an intuitive notion of what’s happening, simply remember that autohandlers (or other parent components) specify general behavior, whereas top-level (i.e., child; i.e., “regular”) components dictate specific behavior, overriding the parents.



[13] In a mod_perl setting, authentication and authorization often happen before the content generation phase (i.e., before Mason even steps into the picture). However, you may wish to bypass the auth control phases and do your own authorization in the autohandler, just so that you can use Mason’s attributes to control the behavior. For instance, you might give an unauthenticated user a different view of a certain page, rather than denying access outright.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required