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

Calling Components with Content Blocks

As you saw earlier, <%filter> blocks can be quite handy. The example we showed in Chapter 3 altered the src attribute of <img> tags in order to point them to a different server.

In Chapter 8 we will show an example that filters a link menu of <a href> tags to find the link for the current page and changes it to a <b> tag instead, in order to highlight the current page.

Both of these examples work just fine as long as we are willing to filter the output of an entire component, but sometimes we’d like to limit the filtering to just one section of the component. Consider the following autohandler:

<html>
<head>
<title><& SELF:title &></title>
</head>
<body>

<& SELF:top_menu, %ARGS &>

% $m->call_next;

</body>
</html>

This component calls the top_menu method, expecting it to produce some sort of menu of links. We’d like to use the menu-filtering trick just mentioned, but using a regular <%filter> block in this component would filter not only the menu of links but also the entire page. That’s a waste of processing, not to mention a potential source of bugs — and we hate bugs.

Another option would be to insert a <%filter> block directly into the source of the top_menu method. However, the method may be defined in many different places; the whole point of using a method instead of a regular component call is that any component may redefine the method as it chooses. So we’d end up adding the same filter block to every definition of the top_menu method. That’s a pretty poor solution.

What we really want is a solution that allows us to write the code once but apply it to only the portion of the output that we choose. Of course, there is such a thing called a “component call with content,” introduced in Mason Version 1.10. It looks just like a regular <& &> component call, except that there’s an extra pipe (|) character to distinguish it and a corresponding end tag, </&>. Using a component call with content, we can apply the desired filter to just the menu of links:

<html>
<head>
<title><& SELF:title &></title>
</head>
<body>

<&| .top_menu_filter &>
 <& SELF:top_menu, %ARGS &>
</&>

% $m->call_next;

</body>
</html>

So the .top_menu_filter component — presumably a subcomponent defined in the same file — is somehow being passed the output from the call to <& SELF:top_menu, %ARGS &>. The .top_menu_filter component would look something like this:

<%def .top_menu_filter>
% my $text = $m->content;
% my $uri = $r->uri;
% $text =~ s,<a href="\Q$uri\E[^"]*">([^<]+)</a>,<b>$1</b>,;
<% $text %>
</%def>

This looks more or less like any other <%filter> block, but with two main differences. First, the body of a <%filter> block contains plain Perl code, but since .top_menu_filter is a subcomponent, it contains Mason code. Second, we access the text to filter via a call to $m->content instead of in the $_ variable. The $m->content() method returns the evaluated output of the content block, which in this case is the output of the SELF:top_menu component.

Mason goes through some contortions in order to trick the wrapped portion of the component into thinking that it is still in the original component. If we had a component named bob.html, as shown in the example below:

<&| .uc &>
I am in <% $m->current_comp->name %>
</&>

<%def .uc>
<% uc $m->content %>
</%def>

we would expect the output to be:

I AM IN BOB.HTML

And indeed, that is what will happen. You can also nest these sorts of calls:

<&| .ucfirst &>
  <&| .reverse &>
I am in <% $m->current_comp->name %>
  </&>
</&>

<%def .reverse>
<% scalar reverse $m->content %>
</%def>
<%def .ucfirst>
<% join ' ', map {ucfirst} split / /, $m->content %>
</%def>

This produces:

Lmth.bob Ni Ma I

As you can see, the filtering components are called from innermost to outermost.

It may have already occurred to you, but this can actually be used to implement something in Mason that looks a lot like Java Server Page taglibs. Without commenting on whether the taglib concept is conducive to effective site management or not, we’ll show you how to create a similar effect in Mason. Here’s a simple SQL select expressed in something like a taglib style:

<table>
 <tr>
  <th>Name</th>
  <th>Age</th>
 </tr>
<&| /sql/select, query => 'SELECT name, age FROM User' &>
 <tr>
  <td>%name</td>
  <td>%age</td>
 </tr>
</&>
</table>

The idea is that the query argument specifies the SQL query to run, and the content block dictates how each row returned should be displayed. Fields are indicated here by a % and then the name of the field.

Now let’s write the /sql/select component.

<%args>
 $query
</%args>
<%init>
 my $sth = $dbh->prepare($query);

 while ( my $row = $sth->fetchrow_hashref ) {
   my $content = $m->content;
   $content =~ s/%(\w+)/$row->{$1}/g;
   $m->print($content);
 }
</%init>

Obviously, this example is grossly simplified (it doesn’t handle things like bound SQL variables, and it doesn’t handle extra embedded % characters very well), but it demonstrates the basic technique.

Seeing all this, you may wonder if you can somehow use this feature to implement control structures, again a taglib-esque idea. The answer is yes, with some caveats. We say “with caveats” because due to the way this feature is implemented, with closures, you have to jump through a few hoops. Here is something that will not work:

<&| /loop, items => ['one', 'two', 'three'] &>
<% $item %>
</&>

And in /loop:

<%args>
 @items
</%args>
% foreach my $item (@items) {
<% $m->content %>
% }

Remember, the previous example will not work. The reason should be obvious. At no time is the variable $item declared in the calling component, either as a global or lexical variable, so a syntax error will occur when the component is compiled.

So how can this idea be made to work? Here is one way. Rewrite the calling component first:

% my $item;
<&| /loop, items => ['one', 'two', 'three'], item => \$item &>
<% $item %>
</&>

Then rewrite /loop:

<%args>
 $item
 @items
</%args>
% foreach (@items) {
%   $$item = $_;
<% $m->content %>
% }

This takes advantages of how Perl treats lexical variables inside closures, but explaining this in detail is way beyond the scope of this book.

You can also achieve this same thing with a global variable. This next version assumes that $item has been declared using allow_globals:

<&| /loop, items => ['one', 'two', 'three'] &>
<% $item %>
</&>

And /loop becomes this:

<%args>
 @items
</%args>
% foreach $item (@items) {
<% $m->content %>
% }

This version is perhaps a little less funky, but it could lead to having more globals than you’d really like.

An in-between solution using Perl’s special $_ variable can solve many of these problems. This variable is a global but is automatically localized by loop controls like foreach or while. So we can now write:

<&| /loop, items => ['one', 'two', 'three'] &>
<% $_ %>
</&>

And for /loop:

<%args>
 @items
</%args>
% foreach (@items) {
<% $m->content %>
% }

Magic. It isn’t perfect, but it looks kind of neat.

In any case, Mason was designed to use Perl’s built-in control structures, so we don’t feel too bad that it’s awkward to build your own.

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