devel(3)
NAME
Devel.pod - Mason Developer's Manual
DESCRIPTION
This manual is written for content developers who know
HTML and at least a little Perl. The goal is to write,
run, and debug Mason components.
If you are the webmaster (or otherwise responsible for the
Mason installation), you should also read
HTML::Mason::Admin. There you will find information about
virtual site configuration, performance tuning, component
caching, and so on.
We strongly suggest that you have a working Mason to play
with as you work through these examples. Other component
examples can be found in the "samples/" directory.
HOW TO USE THIS MANUAL
If you are just learning Mason and want to get started
quickly, we recommend the following sections:
o the What Are Components? entry elsewhere in this docu
ment
o the In-Line Perl Sections entry elsewhere in this docu
ment
o the Calling Components entry elsewhere in this document
o the Passing Parameters entry elsewhere in this document
o the Initialization and Cleanup entry elsewhere in this
document (mainly "<%init>")
o the Data Caching entry elsewhere in this document
o the Sending HTTP Headers entry elsewhere in this docu
ment
o the Common Traps entry elsewhere in this document
WHAT ARE COMPONENTS?
- The component - a mix of Perl and HTML - is Mason's basic
building block and computational unit. Under Mason, web
pages are formed by combining the output from multiple
components. An article page for a news publication, for
example, might call separate components for the company
masthead, ad banner, left table of contents, and article
body. Consider this layout sketch: - +---------+------------------+
|Masthead | Banner Ad
+---------+------------------+ - |+-------+|Text of Article ..
- ||Related||Text of Article ..
||Stories
|| ||Text of Article ..
|+-------+
| +------------------+
| | Footer
+---------+------------------+ - The top level component decides the overall page layout,
perhaps with HTML tables. Individual cells are then filled
by the output of subordinate components, one for the Mast
head, one for the Footer, etc. In practice pages are built
up from as few as one, to as many as twenty or more compo
nents. - This component approach reaps many benefits in a web envi
ronment. The first benefit is consistency: by embedding standard design elements in components, you ensure a con
sistent look and make it possible to update the entire
site with just a few edits. The second benefit is concur_
rency: in a multi-person environment, one person can edit
the masthead while another edits the table of contents. A
last benefit is reuseability: a component produced for one site might be useful on another. You can develop a library
of generally useful components to employ on your sites and
to share with others. - Most components emit chunks of HTML. "Top level" compo
nents, invoked from a URL, represent an entire web page.
Other, subordinate components emit smaller bits of HTML
destined for inclusion in top level components. - Components receive form and query data from HTTP requests.
When called from another component, they can accept arbi
trary parameter lists just like a subroutine, and option
ally return values. This enables a type of component that
does not print any HTML, but simply serves as a function,
computing and returning a result. - Mason actually compiles components down to Perl subrou
tines, so you can debug and profile component-based web
pages with standard Perl tools that understand the subrou
tine concept, e.g. you can use the Perl debugger to step
through components, and Devel::DProf to profile their per formance.
IN-LINE PERL SECTIONS
- Here is a simple component example:
- <%perl>
my $noun = 'World';
my @time = split /[]/, localtime;
</%perl>
Hello <% $noun %>,
% if ( $time[3] < 12 ) {
good morning.
% } else {
good afternoon.
% } - After 12 pm, the output of this component is:
Hello world, good afternoon.- This short example demonstrates the three primary "inline" Perl sections. In-line sections are generally embed
ded within HTML and execute in the order they appear.
Other sections ("<%init>", "<%args>", etc.) are tied to
component events like initialization, cleanup, and argu
ment definition. - The parsing rules for these Perl sections are as follows:
- 1. Blocks of the form <% xxx %> are replaced with the
result of evaluating xxx as a single Perl expression.
These are often used for variable replacement. such as
'Hello, <% $name %>!'. - 2. Lines beginning with a '%' character are treated as
Perl. - 3. Multiline blocks of Perl code can be inserted with the
"<%perl>" .. "</%perl>" tag. The enclosed text is exe
cuted as Perl and the return value, if any, is dis
carded. - The "<%perl>" tag is case-insensitive. It may appear
anywhere in the text, and may span any number of
lines. "<%perl>" blocks cannot be nested inside one
another. - Examples and Recommended Usage
- % lines
- Most useful for conditional and loop structures - if,
while, foreach, , etc. - as well as side-effect commands
like assignments. Examples: - o Conditional code
% my $ua = $r->header_in('User-Agent');
% if ($ua =~ /msie/i) {
Welcome, Internet Explorer users
...
% } elsif ($ua =~ /mozilla/i) {
Welcome, Netscape users
...
% }- o HTML list formed from array
<ul>
% foreach $item (@list) {
<li><% $item %>
% }
</ul>o HTML list formed from hash
<ul>
% while (my ($key,$value) = each(%ENV)) {
<li>
<b><% $key %></b>: <% $value %>
% }
</ul>o HTML table formed from list of hashes
<table>
<tr>
% foreach my $h (@loh) {
<td><% $h->{foo} %></td>
<td bgcolor=#ee0000><% $h->{bar} %></td>
<td><% $h->{baz} %></td>
% }
</tr>
</table><% xxx %>Most useful for printing out variables, as well as more
complex expressions. Examples:
Dear <% $name %>: We will come to your house at <% $address %> in the
fair city of <% $city %> to deliver your $<% $amount %>dollar prize!The answer is <% ($y+8) % 2 %>.You are <% $age<18 ? 'not' : '' %> permitted to enterthis site.<%perl> xxx <%/perl>Useful for Perl blocks of more than a few lines.
MASON OBJECTS
This section describes the various objects in the Mason
universe. If you're just starting out, all you need to
worry about initially are the request objects.
Request Objects
Two global per-request objects are available to all compo
nents: $r and $m.
- $r, the mod_perl request object, provides a Perl API to
the current Apache request. It is fully described in
Apache.pod. Here is a sampling of methods useful to compo
nent developers: - $r->uri # the HTTP request URI
$r->header_in(..) # get the named HTTP header line
$r->content_type # set or retrieve content-type
$r->header_out(..) # set or retrieve an outgoing - header
- $r->content # don't use this one! (see Tips
- and Traps)
- $m, the Mason request object, provides an analogous API
for Mason. All Mason features not activated by syntactic
tags are accessed via $m methods. You'll be introduced to
these methods throughout this document as they are needed.
For a description of all methods see the
HTML::Mason::Request manpage. - Note to pre-0.8 users: The $m API replaces both the old
"mc_" command set and the $REQ variable. See the
HTML::Mason::Commands manpage for a conversion guide. There is also a utility provided with the distribution,
bin/convert0.8.pl, that performs appropriate conversions on your existing component tree. - System Objects
- Three system objects share the work of serving requests in
Mason: Parser, Interp, and ApacheHandler. The administra
tor creates these objects and provides parameters that
shape Mason's behavior. As a pure component developer you
shouldn't need to worry about or access these objects, but
occasionally we'll mention a relevant parameter. - Component Objects
- Mason provides an object API for components, allowing you
to query a component's various asociated files, arguments,
etc. For a description of all methods see the
HTML::Mason::Component manpage. Typically you get a han dle on a component object from request methods like
"$m->current_comp" and "$m->fetch_comp". - Note that for many basic applications all you'll want to
do with components is call them, for which no object
method is needed. See next section.
CALLING COMPONENTS
Mason pages often are built not from a single component,
but from multiple components that call each other in a
hierarchical fashion.
Components that output HTML
- To call one component from another, use the <& &> tag:
- <& comp_path, [name=>value, ...] &>
- comp_path:
The component path. With a leading '/', the path is
relative to the component root ("comp_root"). Other
wise, it is relative to the location of the calling
component. - name=>value pairs:
Parameters are passed as one or more name=>value
pairs, e.g. "player=>'M. Jordan'". - comp_path may be a literal string (quotes optional) or a
Perl expression that evaluates to a string. To eliminate
the need for quotes in most cases, Mason employs some
magic parsing: If the first character is one of
"[A-Za-z0-9/_.]", comp_path is assumed to be a literal
string running up to the first comma or &>. Otherwise,
comp_path is evaluated as an expression. - Here are some examples:
# relative component paths
<& topimage &>
<& tools/searchbox &># absolute component path
<& /shared/masthead, color=>'salmon' &># this component path MUST have quotes because it con - tains a comma
<& "sugar,eggs", mix=>1 &> - # variable component path
<& $comp &> - # variable component and arguments
<& $comp, %args &> - # you can use arbitrary expression for component path,
- but it cannot
# begin with a letter or number; delimit with () to - remedy this
<& (int(rand(2)) ? 'thiscomp' : 'thatcomp'), id=>123 - &>
- Several request methods also exist for calling components.
"$m->comp" performs the equivalent action to <& &>:
$m->comp('/shared/masthead', color=>'salmon');- "$m->scomp" is like the sprintf version of "$m->comp": it
returns the component output, allowing the caller to exam
ine and modify it before printing:
my $masthead = $m->scomp('/shared/masthead', col - or=>'salmon');
$masthead =~ ...;
$m->out($masthead); - Components that compute values
- So far you have seen components used solely to output
HTML. However, components may also be used to compute a
value. For example, you might have a component
"is_netscape" that analyzes the user agent to determine
whether it is a Netscape browser:
<%perl>
my $ua = $r->header_in('User-Agent');
return ($ua =~ /Mozilla/i && $ua !~ /MSIE/i) ? 1 : 0;
</%perl>Because components are implemented underneath with Perl
subroutines, they can return values and even understand
scalar/list context.The <& &> notation only calls a component for its sideeffect, and discards its return value, if any. To get at
the return value of a component, use the "$m->comp" com
mand:
% if ($m->comp('is_netscape')) {
Welcome, Netscape user!
% }Mason adds a "return undef" to the bottom of each compo
nent to provide an empty default return value. To return
your own value from a component, you must use an explicit
"return" statement. You cannot rely on the usual Perl
trick of letting return values "fall through".Generally components are divided into two types: those
that output HTML, and those that return a value. There is
very little reason for a component to do both. For exam
ple, it would not be very friendly for "is_netscape" to
output "hi Mom" while it was computing its value, thereby
surprising the "if" statement! Conversely, any value
returned by an HTML component would typically be discarded
by the <& &> tag that invoked it.
TOP-LEVEL COMPONENTS
- The first component invoked for a page (the "top-level
component") resides within the DocumentRoot and is chosen
based on the URL. For example: - http://www.foo.com/mktg/products?id=372
- Apache resolves this URL to a filename, e.g.
/usr/local/www/htdocs/mktg/prods.html. Mason loads and
executes that file as a component. In effect, Mason calls
$m->comp('/mktg/products', id=>372)This component might in turn call other components and
execute some Perl code, or it might be nothing more than
static HTML.dhandlersWhat happens when a user requests a component that doesn't
exist? In this case Mason scans backward through the URI,
checking each directory for a component named dhandler ("default handler"). If found, the dhandler is invoked
and is expected to use "$m->dhandler_arg" as the parameter
to some access function, perhaps a database lookup or
location in another filesystem. In a sense, dhandlers are
similar in spirit to Perl's AUTOLOAD feature; they are the
"component of last resort" when a URL points to a nonexistent component.Consider the following URL, in which "newsfeeds/" exists
but not the subdirectory "LocalNews" nor the component
"Story1":
http://myserver/newsfeeds/LocalNews/Story1In this case Mason constructs the following search path:
/newsfeeds/LocalNews/Story1 => no such thing
/newsfeeds/LocalNews/dhandler => no such thing
/newsfeeds/dhandler => found! (searchends)
/dhandlerThe found dhandler would read "LocalNews/Story1" from
"$m->dhandler_arg" and use it as a retrieval key into a
database of stories.Here's how a simple /newsfeeds/dhandler might look:
<& header &>
<b><% $headline %></b><p>
<% $body %>
<& footer &><%init>
my $arg = $m->dhandler_arg; # get restof path
my ($section,$story) = split("/",$arg); # split outpieces
my $sth = $DBH->prepare("SELECT headline,body FROM news WHERE sec - tion='$section' AND story='$story'");
- my ($headline,$body) = $sth->fetchrow_array;
return 404 if !$headline; # return - "not found" if no such story
</%init> - By default dhandlers do not get a chance to handle
requests to a directory itself (e.g. /newsfeeds). These
are automatically deferred to Apache, which generates an
index page or a FORBIDDEN error. Often this is desirable,
but if necessary the administrator can let in directory
requests as well; see the Allowing requests for directo
ries entry in the Admin manpage. - A component or dhandler that does not want to handle a
particular request may defer control to the next dhandler
by calling "$m->decline". - The administrator can customize the file name used for
dhandlers, or turn off dhandlers entirely, with the the
dhandler_name entry in the Interp manpage Interp parame
ter. - autohandlers
- Autohandlers allow you to grab control and perform some
action just before Mason calls the top-level component.
This might mean adding a standard header and footer,
applying an output filter, or setting up global variables. - Autohandlers are directory based. When Mason determines
the top-level component, it checks that directory and all
parent directories for a component called "autohandler".
If found, the autohandler is called first. After perform
ing its actions, the autohandler typically calls
"$m->call_next" to transfer control to the original
intended component. - "$m->call_next" works just like "$m->comp" except that the
component path and arguments are implicit. You can pass
additional arguments to "$m->call_next"; these are merged
with the original arguments, taking precedence in case of
conflict. This allows you, for example, to override argu
ments passed in the URL. - Here is an autohandler that adds a common header and
footer to each page underneath its directory:
<HTML>
<HEAD><TITLE>McHuffy Incorporated</TITLE></HEAD>
<BODY BGCOLOR="salmon">- <% $m->call_next %>
- <HR>
Copyright 1999 McHuffy Inc.
</BODY>
</HTML> - Same idea, using components for the header/footer:
<& /shared/header &>
<% $m->call_next %>
<& /shared/footer &>The next autohandler applies a filter to its pages, adding
an absolute hostname to relative image URLs:
<% $m->call_next %><%filter>
s{(<imgc=
</%filter>Most of the time autohandler can simply call
"$m->call_next" without needing to know what the next
component is. However, should you need it, the component
object is available from "$m->fetch_next". This is useful
for calling the component manually, e.g. if you want to
suppress some original arguments or if you want to use
"$m->scomp" to store and process the output.What happens if more than one autohandler applies to a
page? Prior to version 0.85, only the most specific auto
handler would execute. In 0.85 and beyond each autohan
dler gets a chance to run. The top-most autohandler runs
first; each "$m->call_next" transfers control to the next
autohandler and finally to the originally called compo
nent. This allows you, for example, to combine general
site-wide templates and more specific section-based tem
plates.Autohandlers can be made even more powerful in conjunction
with Mason's object-oriented style features: methods,
attributes, and inheritance. In the interest of space
these are discussed in a separate section, the Object-Ori
ented Techniques entry in the Devel manpage.The administrator can customize the file name used for
autohandlers, or turn off autohandlers entirely, with the
the autohandler_name entry in the Interp manpage Interp
parameter.dhandlers vs. autohandlersdhandlers and autohandlers both provide a way to exert
control over a large set of URLs. However, each special
izes in a very different application. The key difference
is that dhandlers are invoked only when no appropriate
component exists, while autohandlers are invoked only in
conjunction with a matching component.As a rule of thumb: use an autohandler when you have a set
of components to handle your pages and you want to augment
them with a template/filter. Use a dhandler when you want
to create a set of "virtual URLs" that don't correspond to
any actual components, or to provide default behavior for
a directory.dhandlers and autohandlers can even be used in the same
directory. For example, you might have a mix of real URLs
and virtual URLs to which you would like to apply a common
template/filter.
PASSING PARAMETERS
This section describes Mason's facilities for passing
parameters to components (either from HTTP requests or
component calls) and for accessing parameter values inside
components.
In Component Calls
- Any Perl data type can be passed in a component call:
- <& /sales/header, s=>'dog', l=>[2,3,4], h=>{a=>7,b=>8}
- &>
- This command passes a scalar ($s), a list (@l), and a hash
(%h). The list and hash must be passed as references, but
they will be automatically dereferenced in the called com
ponent. - In HTTP requests
- Consider a CGI-style URL with a query string:
http://www.foo.com/mk- tg/prods.html?str=dog&lst=2&lst=3&lst=4
- or an HTTP request with some POST content. Mason automati
cally parses the GET/POST values and makes them available
to the component as parameters. - Accessing Parameters
- Component parameters, whether they come from GET/POST or
another component, can be accessed in two ways. - 1. Declared named arguments: Components can define an
"<%args>" section listing argument names, types, and
default values. For example:
<%args>
$a
@b # a comment
%c- # another comment
$d=>5
$e=>$d*2
@f=>('foo','baz')
%g=>(joe=>1,bob=>2)
</%args> - Here, $a, @b, and %c are required arguments; the component
generates an error if the caller leaves them unspecified.
$d, $e, @f and %g are optional arguments; they are
assigned the specified default values if unspecified. All
the arguments are available as lexically scoped ("my")
variables in the rest of the component. - Arguments are separated by one or more newlines. Comments
may be used at the end of a line or on their own line. - Default expressions are evaluated in top-to-bottom order,
and one expression may reference an earlier one (as $e
references $d above). - Only valid Perl variable names may be used in "<%args>"
sections. Parameters with non-valid variable names cannot
be pre-declared and must be fetched manually out of the
%ARGS hash (see below). One common example of
undeclarable parameters are the "button.x/button.y" param
eters sent for a form submit. - 2. %ARGS hash: This variable, always available, contains
all of the parameters passed to the component (whether or
not they were declared). It is especially handy for deal
ing with large numbers of parameters, dynamically named
parameters, or parameters with non-valid variable names.
%ARGS can be used with or without an "<%args>" section,
and its contents are unrelated to what you have declared
in "<%args>". - Here's how to pass all of a component's parameters to
another component:
<& template, %ARGS &>Parameter Passing ExamplesThe following examples illustrate the different ways to
pass and receive parameters.1. Passing a scalar id with value 5.
In a URL: /my/URL?id=5
In a component call: <& /my/comp, id => 5 &>
In the called component, if there is a declared argumentnamed...
$id, then $id will equal 5
@id, then @id will equal (5)
%id, then an error occurs - In addition, $ARGS{id} will equal 5.
- 2. Passing a list colors with values red, blue, and
green.
In a URL: /my/URL?colors=red&colors=blue&colors=green
In an component call: <& /my/comp, colors => ['red', - 'blue', 'green'] &>
In the called component, if there is a declared argument - named...
$colors, then $colors will equal ['red', 'blue', - 'green']
@colors, then @colors will equal ('red', 'blue', - 'green')
%colors, then an error occurs - In addition, $ARGS{colors} will equal ['red', 'blue',
- 'green'].
- 3. Passing a hash grades with pairs Alice => 92 and Bob
=> 87.
In a URL: /my/URL?grades=Al- ice&grades=92&grades=Bob&grades=87
In an component call: <& /my/comp, grades => {Alice => - 92, Bob => 87} &>
In the called component, if there is a declared argument - named...
$grades, then $grades will equal {Alice => 92, Bob => - 87}
@grades, then @grades will equal ('Alice', 92, 'Bob', - 87)
%grades, then %grades will equal (Alice => 92, Bob => - 87)
- In addition, $ARGS{grades} will equal ['Al
- ice',92,'Bob',87] in the
URL case, or {Alice => 92, Bob => 87} in the component - call case.
(The discrepancy exists because, in a query string, - there is no detectable
difference between a list or hash.) - Using @_ instead
- If you don't like named parameters, you can pass a tradi
tional list of ordered parameters:
<& /mktg/prods.html', 'dog', [2,3,4], {a=>7,b=>8} &>- and access them as usual through Perl's @_ array:
my ($scalar, $listref, $hashref) = @_;In this case no "<%args>" section is necessary.We generally recommend named parameters for the benefits
of readability, syntax checking, and default value automa
tion. However using @_ may be convenient for very small
components, especially subcomponents created with
"<%def>".
INITIALIZATION AND CLEANUP
The following sections contain blocks of Perl to execute
at specific times.
<%init>
This section contains initialization code that executes as
soon as the component is called. For example: checking
that a user is logged in; selecting rows from a database
into a list; parsing the contents of a file into a data
structure.
Technically a <%init> block is equivalent to a <%perl>
block at the beginning of the component. However, there is
an aesthetic advantage of placing this block at the end of
the component rather than the beginning.
- We've found that the most readable components (especially
for non-programmers) contain HTML in one continuous block
at the top, with simple substitutions for dynamic elements
but no distracting blocks of Perl code. At the bottom an
<%init> block sets up the substitution variables. This
organization allows non-programmers to work with the HTML
without getting distracted or discouraged by Perl code.
For example: - <html>
<head><title><% $headline %></title></head>
<body>
<h2><% $headline %></h2>
By <% $author %>, <% $date %><p> - <% $body %>
- </body></html>
- <%init>
# Fetch article from database
my $dbh = DBI::connect ...;
my $sth = $dbh->prepare("select * from articles where - id = $article_id");
my ($headline,$date,$author,$body) = - $sth->fetchrow_array;
# Massage the fields
$headline = uc($headline);
my ($year,$month,$day) = split('-',$date);
$date = "$month/$day";
</%init> - <%args>
$article_id
</%args> - <%cleanup>
- This section contains cleanup code that executes just
before the component exits. For example: closing a
database connection or closing a file handle. - Technically a <%cleanup> block is equivalent to a <%perl>
block at the end of the component. Since a component cor
responds a subroutine block, and since Perl is so darned
good at cleaning up stuff at the end of blocks, <%cleanup>
sections are rarely needed. - <%once>
- This code executes once when the component is loaded.
Variables declared in this section can be seen in all of a
component's code and persist for the lifetime of the
component. - Useful for declaring persistent component-scoped lexical
variables (especially objects that are expensive to cre
ate), declaring subroutines (both named and anonymous),
and initializing state. - This code does not run inside a request context. You can
not call components or access $m from this section. - Normally this code will execute individually from every
HTTP child that uses the component. However, if the compo
nent is preloaded, this code will only execute once in the
parent. Unless you have total control over what compo
nents will be preloaded, it is safest to avoid initializ
ing variables that can't survive a fork(), e.g. DBI han
dles. Use the following trick to initialize such vari
ables in the <%init> section:
<%once>
my $dbh; # declare but don't assign
...
</%once>- <%init>
if ($m->current_comp->first_time) {
$dbh = DBI::connect ... - }
...
</%init> - <%shared>
- As with "<%once>", variables declared in this section can
be seen in all of a component's code: the main component,
subcomponents, and methods. However, the code runs once
per request (whenever the component is used) and its vari
ables last only til the end of the request. - Useful for initializing variables needed in, say, the main
body and one more subcomponents or methods. See the
Object-Oriented Techniques entry in the Devel manpage for
an example of usage. - Avoid using "<%shared>" for side-effect code that needs to
run at a predictable time during page generation. You may
assume only that "<%shared>" runs just before the first
code that needs it and runs at most once per request.
"<%init>" offers more a predictable execution time. - Any component with a "<%shared>" section incurs an extra
performance penalty, because (as currently implemented)
Mason must recreate its anonymous subroutines the first
time each new request uses the component. The exact
penalty varies between systems and for most applications
will be unnoticeable. However, one should avoid using
"<%shared>" when patently unnecessary, e.g. when an
"<%init>" would work as well.
EMBEDDED COMPONENTS
<%def name>
Each instance of this section creates a subcomponent
embedded inside the current component. Inside you may
place anything that a regular component contains, with the
exception of "<%def>", "<%method>", "<%once>", and
"<%shared>" tags.
The name consists of characters in the set
"[A-Za-z0-9._-]". To call a subcomponent simply use its
name in <& &> or "$m->comp". A subcomponent can only be
seen from the surrounding component.
If you define a subcomponent with the same name as a filebased component in the current directory, the subcomponent
takes precedence. You would need to use an absolute path
to call the file-based component. To avoid this situation
and for general clarity, we recommend that you pick a
unique way to name all of your subcomponents that is
unlikely to interfere with file-based components. The
author prefers to start subcomponent names with ".".
While inside a subcomponent, you may use absolute or rela
tive paths to call file-based components and also call any
of your "sibling" subcomponents.
The lexical scope of a subcomponent is separate from the
main component. However a subcomponent can declare its
own "<%args>" section and have relevant values passed in.
You can also use a "<%shared>" section to declare vari
ables visible from both scopes.
- In the following example, we create a ".link" subcomponent
to produce a standardized hyperlink: - <%def .link>
<font size="4" face="Verdana,Arial,Helvetica">
<a href="http://www.<% $site %>.com"><% $label %></a>
</font><br>
<%args>
$site
$label=>ucfirst($site)
</%args>
</%def> - Visit these sites:
<ul>
<li><& .link, site=>'yahoo' &><br>
<li><& .link, site=>'cmp', label=>'CMP Media' &><br>
<li><& .link, site=>'excite' &>
</ul> - <%method name>
- Each instance of this section creates a method embedded
inside the current component. Methods resemble subcompo
nents in terms of naming, contents, and scope. However,
while subcomponents can only be seen from the parent com
ponent, methods are meant to be called from other compo
nents. - There are two ways to call a method. First, via a path of
the form "comp:method":
<& /foo/bar:method1 &>$m->comp('/foo/bar:method1');Second, via the call_method component method:
my $comp = $m->fetch_comp('/foo/bar');
...
$comp->call_method('method1');Methods are commonly used in conjunction with autohandlers
to make templates more flexible. See the Object-Oriented
Techniques entry in the Devel manpage for more informa
tion.
FLAGS AND ATTRIBUTES
The <%flags> and <%attr> sections consist of key/value
pairs, one per line, joined by '=>'. The key and value in
each pair must be valid Perl hash keys and values respec
tively. An optional comment may follow each line.
<%flags>
Use this section to set official Mason flags that affect
the current component's behavior.
- Currently there is only one flag, <tt>inherit</tt>, which
specifies the component's <i>parent</i> in the form of a
relative or absolute component path. A component inherits
methods and attributes from its parent; see the ObjectOriented Techniques entry in the Devel manpage for exam
ples. - <%flags>
inherit=>'/site_handler'
</%flags> - <%attr>
- Use this section to assign static key/value attributes
that can be queried from other components.
<%attr>
color=>'blue'
fonts=>[qw(arial geneva helvetica)]
</%attr>To query an attribute of a component, use the "attr"
method:
my $color = $comp->attr('color')where $comp is a component object.Mason evaluates attribute values once when loading the
component. This makes them faster but less flexible than
methods.
FILTERING
This section describes several ways to apply filtering
functions over the results of the current component. By
separating out and hiding a filter that, say, changes HTML
in a complex way, we allow non-programmers to work in a
cleaner HTML environment.
<%filter> section
The "<%filter>" section allows you to arbitrarily filter
the output of the current component. Upon entry to this
code, $_ contains the component output, and you are
expected to modify it in place. The code has access to
component arguments and can invoke subroutines, call other
components, etc.
- This simple filter converts the component output to UPPER
CASE: - <%filter>
tr/a-z/A-Z/
</%filter> - The following navigation bar uses a filter to "unlink" and
highlight the item corresponding to the current page:
<a href="/">Home</a> | <a href="/products/">Prod - ucts</a>
<a href="/bg.html">Background</a> | <a href="/fi - nance/">Financials</a>
<a href="/support/">Tech Support</a> | <a href="/con - tact.html">Contact Us</a>
- <%filter>
my $uri = $r->uri;
s{<a href="$uri/?">(.*?)</a>} {<b>$1</b>}i;
</%filter> - This allows a designer to code such a navigation bar intu
itively without "if" statements surrounding each link!
Note that the regular expression need not be very robust
as long as you have control over what will appear in the
body. - $m->call_self command
- This command allows you to filter both the output and the
return value of the current component. It is fairly
advanced; for most purposes the "<%filter>" tag above will
be sufficient and simpler. - "$m->call_self" takes two arguments. The first is a
scalar reference and will be populated with the component
output. The second is either a scalar or list reference
and will be populated with the component return value; the
type of reference determines whether the component will be
called in scalar or list context. Both of these arguments
are optional; you may pass undef if you don't care about
one of them. - "$m->call_self" acts like a fork() in the sense that it
will return twice with different values. When it returns
0, you allow control to pass through to the rest of your
component. When it returns 1, that means the component
has finished and you can begin filtering the output and/or
return value. (Don't worry, it doesn't really do a fork!
See next section for explanation.) - The following examples would generally appear at the top
of a "<%init>" section. Here is a no-op "$m->call_self"
that leaves the output and return value untouched:
if ($m->call_self(my utput, my etval)) { # assumes- Perl 5.005 or greater
$m->out($output);
return $retval; - }
- Here is a simple output filter that makes the output all
uppercase, just like the "<%filter>" example above. Note
that we ignore both the original and the final return
value.
if ($m->call_self(my utput, undef)) {
$m->out(uc($output));
return;- }
- "$m->call_self" can even convert output to a return value
or vice versa. In the next component we provide a nice
friendly format for non-programmers to represent data
with, and use a filter to construct and return a corre
sponding Perl data structure from it:
# id lastname firstname
59286 Sherman Karen
31776 Dawson Robert
29482 Lee Brenda
...- <%init>
if ($m->call_self(my utput, undef)) {
foreach (split("0,$output)) {
next if /^#/ || !/;
my @vals = split(/);
push(@peo - ple,{id=>$vals[0],last=>$vals[1],first=>$vals[2]});
- }
return @people; - }
</%init> - Now we can get a list of hashes directly from this compo
nent. - How filtering works
- "$m->call_self" (and "<%filter>", which is built on it)
uses a bit of magic to accomplish everything in one line.
If you're curious, here's how it works: - o A component foo calls "$m->call_self" for the first
time. - o "$m->call_self" sets an internal flag and calls foo
again recursively, changing the output stream to capture
content into a buffer. - o foo again calls "$m->call_self" which, seeing the flag,
returns 0 immediately. - o foo goes about its business and generates content into
the "$m->call_self" buffer. - o When control is returned to "$m->call_self", it places
the content and return value in the references provided,
and returns 1.
OTHER SYNTAX
<%doc>
Text in this section is treated as a comment and ignored.
Most useful for a component's main documentation. One can
easily write a program to sift through a set of components
and pull out their <%doc> blocks to form a reference page.
- Can also be used for in-line comments, though it is an
admittedly cumbersome comment marker. Another option is
'%#': - %# this is a comment
- These comments differ from HTML comments in that they do
not appear in the HTML. - <%text>
- Text in this section is passed through unmodified by
Mason. Any Mason syntax inside it is ignored. Useful, for
example, when documenting Mason itself from a component:
<%text>
% This is an example of a Perl line.
<% This is an example of an expression block. %>
</%text>This works for almost everything, but doesn't let you out
put </%text> itself! When all else fails, use "$m->out":
%$m->out('The tags are <%text> and </%text>.');Escaping expressionsMason has facilities for escaping the output from <% %> tags, on either a site-wide or a per-expression basis.Any <% %> expression may be terminated by a '|' and one or
more single-letter escape flags (plus arbitrary whites
pace):
<% $file_data |h %>The current valid flags are
h - escape for HTML ('<' => '<', etc.)
u - escape for URL query parameter (':' => '%3A',etc.) - all chars but [a-zA-Z0-9_.-]
n - turn off default escape flagsThe administrator may specify a set of default escape
flags via the the default_escape_flags entry in the Parser
manpage Parser parameter. For example, if the administra
tor specifies
default_escape_flags=>'h'then all <% %> expressions will automatically be HTMLescaped. In this case you would use the "n" flag to turn
off HTML-escaping for a specific expression:
<% $html_block |n %>Future Mason versions will allow user-defined and multiletter escape flags.at end of line
A suppresses the newline before %-lines and section
tags. In HTML components, this is mostly useful for fixed
width areas like <PRE> tags, since browsers ignore white
space for the most part. An example:
<PRE>
foo
%if ($b == 2) {
bar
%}
baz
</PRE>outputs
foo
bar
bazbecause of the newlines on lines 2 and 4. (Lines 3 and 5
do not generate a newline because the entire line is taken
by Perl.) To suppress the newlines:
<PRE>
foo %if ($b == 2) {
bar %}
baz
</PRE>which prints
foobarbazThe backslash has no special meaning outside this context.
In particular, you cannot use it to escape a newline
before a plain text line.
DATA CACHING
Mason's "$m->cache" and "$m->cache_self" methods let com
ponents save and retrieve the results of computation for
improved performance. Anything may be cached, from a
block of HTML to a complex data structure.
Each component gets a private data cache. Except under
special circumstances, one component does not access
another component's cache. Each cached value may be set to
expire under certain conditions or at a certain time.
To use data caching, your Mason installation must be con
figured with a good DBM package like Berkeley DB (DB_File)
or GDBM. See the HTML::Mason::Admin manpage for more
information.
Basic Usage
- Here's the typical usage of "$m->cache":
- my $result = $m->cache(action=>'retrieve');
if (!defined($result)) {
... compute $result> ...
$m->cache(action=>'store', value=>$result); - }
- The first "$m->cache" call attempts to retrieve this com
ponent's cache value. If the value is available it is
placed in "$result". If the value is not available,
"$result" is computed and stored in the cache by the sec
ond "$m->cache" call. - The default action for "$m->cache" is 'retrieve', so the
first line can be written as
my $result = $m->cache;- Multiple Keys/Values
- A cache file can store multiple keys and values. A value
can be a scalar, list reference, or hash reference:
$m->cache(action=>'store',key=>'name',value=>$name);
$m->cache(action=>'store',key=>'friends',value=>@lst);
$m->cache(action=>'store',key=>'map',value=>hsh);The key defaults to 'main' when unspecified, as in the
first example above.Mason uses the MLDBM package to store and retrieve from its cache files, meaning that Mason can cache arbitrarily
deep data structures composed of lists, hashes, and simple
scalars.ExpirationTypical cache items have a useful lifetime after which
they must expire. Mason supports three types of expira
tion:By Time
(e.g. the item expires in an hour, or at midnight). To
expire an item by time, pass one of these options to
the 'store' action.expire_at: takes an absolute expiration time, in Perl time() format (number of seconds since the epoch)expire_in: takes a relative expiration time of the form "<num><unit>", where <num> is a positive number
and <unit> is one of seconds, minutes, hours, days,
weeks, months, years, or any reasonable abbreviation
thereof (m=month, min=minute). E.g. "10min", "1hour".expire_next: takes a string, either 'hour' or 'day'. It indicates an expiration time at the top of the next
hour or day.Examples:
$m->cache(action=>'store', expire_in=>'2 hours');
$m->cache(action=>'store', expire_next=>'hour'); - By Condition
(e.g. the item expires if a certain file or database
table changes). To expire an item based on events
rather than current time, pass the 'expire_if' option
to the 'retrieve' action. - expire_if: calls a given anonymous subroutine and
expires if the subroutine returns a non-zero value.
The subroutine is called with one parameter, the time
when the cache value was last written. - Example:
# expire the cache if 'myfile' is newer
$m->cache(action => 'retrieve',
expire_if => sub { (stat 'myfile')[9] >- shift });
- By Explicit Action
(e.g. a shell command or web interface is responsible
for explicitly expiring the item) To expire an item
from a Perl script, for any component, use
access_data_cache. It takes the same arguments as "$m->cache" plus one additional argument, cache_file.
See the administration manual for details on where
cache files are stored and how they are named.
use HTML::Mason::Utils 'access_data_cache';
access_data_cache (cache_file=>'/usr/local/ma- son/cache/foo+2fbar',
action=>'expire' [, - key=>'fookey']);
- The 'expire' action can also take multiple keys (as a
list reference); this can be used in conjunction with
the 'keys' action to expire all keys matching a par
ticular pattern.
use HTML::Mason::Utils 'access_data_cache';
my @keys = access_data_cache- (cache_file=>'/usr/local/mason/cache/foo+2fbar',
action=>'keys'); - access_data_cache (cache_file=>'/usr/local/ma
- son/cache/foo+2fbar',
action=>'expire', - key=>[grep(/^sales/,@keys)]);
- Busy Locks
- The code shown in "Basic Usage" above,
my $result = $m->cache(action=>'retrieve');
if (!defined($result)) {
... compute $result ...
$m->cache(action=>'store', value=>$result);- }
- can suffer from a kind of race condition for caches that
are accessed frequently and take a long time to recompute. - Suppose that a particular cache value is accessed five
times a second and takes three seconds to recompute. When
the cache expires, the first process comes in, sees that
it is expired, and starts to recompute the value. The
second process comes in and does the same thing. This
sequence continues until the first process finishes and
stores the new value. On average, the value will be
recomputed and written to the cache 15 times! - The solution here is to have the first process notify the
others that it has started recomputing. This can be
accomplished with the busy_lock flag:
my $result = $m->cache(action=>'re- trieve',busy_lock=>'10sec',...);
- With this flag, the first process sets a lock in the cache
that effectively says "I'm busy recomputing his value,
don't bother." Subsequent processes see the lock and
return the old value. The lock is good for 10 seconds (in
this case) and is ignored after that. Thus the time value
you pass to busy_lock indicates how long you're willing to
allow this component to use an expired cache value. - Would some of your caches benefit from busy locks? One
way to find out is to turn on cache logging in the Mason
system logs. If you see large clusters of writes to the
same cache in a short time span, then you might want to
use busy locks when writing to that cache. - Keeping In Memory
- The keep_in_memory flag indicates that the cache value
should be kept in memory after it is stored or retrieved.
Since every child process will store its own copy, this
flag should be used only for small, frequently retrieved
cache values. If used, this flag should be passed to both
the store and retrieve commands. - Caching All Output
- Occasionally you will need to cache the complete output of
a component. One way to accomplish this is to replace the
component with a placeholder that simply calls the compo
nent, then caches and prints the result. For example, if
the component were named "foo", we might rename it to
"foo_main" and put this component in its place:
<% $foo_out %>
<%init>
my $foo_out;
if (!defined ($foo_out = $m->cache)) {
$m->comp('foo_main', STORE=>oo_out);
$m->cache(action=>'store',
expire_in=>'3 hours', value=>$foo_out);- }
- </%init>
- This works, but is cumbersome. Mason offers a better
shortcut: the "$m->cache_self" command that lets a compo
nent cache it's own output and eliminates the need for a
dummy component. It is typically used right at the top of
a "<%init%>" section:
<%init>
return if $m->cache_self(expire_in=>'3 hours'[,- key=>'fookey']);
... <rest of init> ... - </%init>
- "$m->cache_self" is built on top of "$m->cache", so it
inherits all the expiration options described earlier.
"$m->cache_self" can also cache a component's return
value; see the reference for details. - Guarantees (or lack thereof)
- Mason will make a best effort to cache data until it
expires, but will not guarantee it. The data cache is not
a permanent reliable store in itself; you should not place
in the cache critical data (e.g. user session information)
that cannot be regenerated from another source such as a
database. You should write your code as if the cache
might disappear at any time. In particular, - o If the 'store' action cannot get a write lock on the
cache, it simply fails quietly. - o If the 'retrieve' action cannot get a shared lock on the
cache, it simply fails quietly. (This is much more rare.) - o Your Mason administrator will be required to remove
cache files periodically when they get too large; this can
happen any time. - On the other hand, expiration in its various forms is
guaranteed, because Mason does not want you to rely on bad
data to generate your content. If you use the 'expire'
action and Mason cannot get a write lock, it will repeat
the attempt several times and finally die with an error.
SENDING HTTP HEADERS
Mason automatically sends HTTP headers via
$r->send_http_header. It tries to delay sending headers
till the last possible moment, to give components a chance
to affect headers (using $r->header_out, $r->content_type,
etc.) It won't send headers if they've already been sent
manually.
To determine the exact header behavior on your system, you
need to know whether your server's "out_mode" is 'batch'
(meaning all output is buffered until the end) or 'stream'
(all output is sent immediately). Your administrator
should have this information. If your administrator
doesn't know then it is probably 'batch', the default.
In batch mode the header situation is extremely simple:
Mason waits until the very end of the request to send
headers. Any component can modify or augment the headers.
In stream mode the header situation is more complex.
Mason will send headers just before sending the first nonwhitespace output. Any initial whitespace output is
buffered up until after headers are sent. This means that
if you want to affect the headers in stream mode, you must
do so before any component sends non-whitespace output.
Generally this takes place in an "<%init>" section.
- For example, the following top-level component calls
another component to see whether the user has a cookie; if
not, it inserts a new cookie into the header. - <%init>
my $cookie = $m->comp('/shared/get_user_cookie');
if (!$cookie) {
$cookie = new CGI::Cookie (...);
$r->headers_out->add('Set-cookie' => $cookie); - }
...
</%init> - In batch mode this code will always work. In stream mode
this code will work as long as get_user_cookie doesn't output anything besides whitespace (and given its func
tional nature, it shouldn't). - The administrator can turn off automatic header sending
via the the auto_send_headers entry in the ApacheHandler
manpage ApacheHandler parameter. - Note to pre-0.8 users: the mc_suppress_http_header command
is no longer needed. For backwards compatibility, mc_sup
press_http_header is still defined but does nothing. - (Rationale: By the nature of HTTP headers mc_sup
press_http_header was only useful up until the first bytes
of content were sent. Since Mason now waits that long to
send headers anyway, there is never any justification for
suppressing headers.)
USING THE PERL DEBUGGER
The Perl debugger is an indispensable tool for identifying
and fixing bugs in Perl programs. Unfortunately, in a
mod_perl environment one is normally unable to use the
debugger since programs are run from a browser. Mason
removes this limitation by optionally creating a debug
file for each page request, allowing the request to be
replayed from the command line or Perl debugger.
Note: in early 1999 a new module, Apache::DB, was released
that makes it substantially easier to use the Perl debug
ger directly in conjunction with a real Apache server.
Since this mechanism is still new, we continue to support
Mason debug files, and there may be reasons to prefer
Mason's method (e.g. no need to start another Apache
server). However we acknowledge that Apache::DB may even
tually eliminate the need for debug files. For now we
encourage you to try both methods and see which one works
best.
Using debug files
Here is a typical sequence for debugging a Mason page:
- 1. Find the debug file:
- When Mason is running in debug mode, requests generate
"debug files", cycling through filenames "1" through
"20". To find a request's debug file, simply do a
"View Source" in your browser after the request and
look for a comment like this at the very top:
<!-Debug file is '3'.
Full debug path is '/usr/local/mason/de - bug/anon/3'.
--> - 2. Run the debug file:
Debug files basically contain two things: a copy of
the entire HTTP request (serialized with
Data::Dumper), and all the plumbing needed to route that request through Mason. In other words, if you
simply run the debug file like this:
perl /usr/local/mason/debug/anon/3- you should see the HTTP headers and content that the
component would normally send to the browser. - 3. Debug the debug file:
Now you merely add a "-d" option to run the debug file
in Perl's debugger -- at which point you have to deal
the problem of anonymous subroutines. - Mason compiles components down to anonymous subrou
tines which are not easily breakpoint'able (Perl
prefers line numbers or named subroutines). Therefore,
immediately before each component call, Mason calls a
nonce subroutine called "debug_hook" just so you can
breakpoint it like this:
b HTML::Mason::Request::debug_hook- debug_hook is called with the component name as the
second parameter so that you can also breakpoint spe
cific components using a coaditional on $_[1]:
s - b HTML::Mason::Request:odebug_hook $_[1] =~ /com
- ponent name/
n - You can avoid all that typi:g by adding the following
to your ~/.perldb file: :
R - # Perl debugger aliasesefor Mason
$DB::alias{mb} = 's/^mbq';
u - which reduces the previous examples to just:
s - mb t
mb $_[1] =~ /component name/
:
d - The use of debug files opens lots of other debugging
options. For instance, you can read a debug file into the
Emacs editor, with its nifty interface to Perl's debugger.
This allows you to set break points visually or (in trace
mode) watch a cursor bounce through your code in singlestep or continue mode. h
o - Specifying when to create debugofiles k
- Details about configuring debug mode can be found in the
HTML::Mason::Admin manpage. In particular, the administra
tor must decide which of three debugging modes to acti
vate: - never (no debug files)
- always (create debug files for each request)
- error (only generate a debug file when an error occurs)
- How debug files work
- To create a debug file, Mason calls almost every one of
the mod_perl API methods ("$r->xxx"), trapping its result
in a hash. That hash is then serialized by Data::Dumper
and output into a new debug file along with some surround
ing code. - When the debug file is executed, a new object is created
of the class "HTML::Mason::FakeApache", passing the saved
hash as initialization. The FakeApache object acts as a
fake "$r", responding to each method by getting or setting
data in its hash. For most purposes it is indistinguish
able from the original "$r" except that print methods go
to standard output. The debug file then executes your
"handler()" function with the simulated "$r". - When debug files don't work
- The vast majority of mod_perl API methods are simple
get/set functions (e.g. "$r->uri", "$r->content_type")
which are easy to simulate. Many pages only make use of
these methods and can be successfully simulated in debug
mode. - However, a few methods perform tasks requiring the pres
ence of a true Apache server. These cannot be properly
simulated. Some, such as "log_error" and
"send_cgi_header", are generally tangential to the debug
ging effort; for these Mason simply returns without doing
anything and hopes for the best. Others, such as "inter
nal_redirect" and "lookup_uri", perform such integral
functions that they cannot be ignored, and for these
FakeApache aborts with an error. This category includes
any method call expected to return an Apache::Table
object. - In addition, FakeApache is playing something of a catch-up
game: every time a new mod_perl release comes out with new
API methods, those methods will not be recognized by
FakeApache until it is updated in the next Mason release. - The combination of these problems and the existence of the
new Apache::DB package may eventually lead us to stop fur
ther work on FakeApache/debug files. For now, though,
we'll continue to support them as best we can.
USING THE PERL PROFILER
Debug files, mentioned in the previous section, can be
used in conjunction with Devel::DProf to profile a web
request.
- To use profiling, pass the "-p" flag to the debug file:
- % ./3 -p
- This executes the debug file under Devel::DProf and, for
convenience, runs dprofpp. If you wish you can rerun
dprofpp with your choice of options. - Because components are implemented as anonymous subrou
tines, any time spent in components would normally be
reported under an unreadable label like CODE(0xb6cbc). To
remedy this, the "-p" flag automatically adjusts the
tmon.out file so that components are reported by their
component paths. - Much of the time spent in a typical debug file is initial
ization, such as loading Mason and other Perl modules.
The effects of initialization can swamp profile results
and obscure the time actually spent in components. One
remedy is to run multiple iterations of the request inside
the debug file, thus reducing the influence of initializa
tion time. Pass the number of desired iterations via the
"-r" flag:
% ./3 -p -r20Currently there are no special provisions for other pro
filing modules such as Devel::SmallProf. You can try sim
ply:
% perl -d:SmallProf ./3 -r20However, this crashes on our Unix system -- apparently
some bad interaction between Mason and SmallProf -- so it
is unsupported for now.
THE PREVIEWER
Mason comes with a web-based debugging utility that lets
you test your components by throwing fake requests at
them. Adjustable parameters include: UserAgent, Time, HTTP
Referer, O/S and so on. For example, imagine a component
whose color scheme is supposed to change each morning,
noon, and night. Using the Previewer, it would be simple
to set the perceived time forward 1,5 or 8 hours to test
the component at various times of day.
The Previewer also provides a debug trace of a page, show
ing all components being called and indicating the portion
of HTML each component is responsible for. For pages con
structed from more than a few components, these traces are
quite useful for finding the component that is outputting
a particular piece of HTML.
Your administrator will give you the main Previewer URL,
and a set of preview ports that you will use to view your
site under various conditions. For the purpose of this
discussion we'll assume the Previewer is up and working,
that the Previewer URL is http://www.yoursite.com/preview,
and the preview ports are 3001 to 3005.
Take a look at the main Previewer page. The top part con
tains the most frequently used options, such as time and
display mode. The middle part contains a table of your
saved configurations; if this is your first time using the
Previewer, it will be empty. The bottom part contains
less frequently used options, such as setting the user
agent and referer.
Try clicking "Save". This will save the displayed set
tings under the chosen preview port, say 3001, and redraw
the page. Under "Saved Port Settings", you should see a
single row showing this configuration. Your configura
tions are saved permanently in a file. If a username/pass
word is required to access the Previewer, then each user
has his/her own configuration file.
The "View" button should display your site's home page.
If not, then the Previewer may not be set up correctly;
contact your administrator or see the Administrator's
Guide.
Go back to the main Previewer page, change the display
mode from "HTML" to "debug", change the preview port to
3002, and click "Save" again. You should now see a second
saved configuration.
Click "View". This time instead of seeing the home page
as HTML, you'll get a debug trace with several sections.
The first section shows a numbered hierarchy of components
used to generate this page. The second section is the HTML
source, with each line annotated on the left with the num
ber of the component that generated it. Try clicking on
the numbers in the first section; this brings you to the
place in the second section where that component first
appears. If there's a particular piece of HTML you want
to change on a page, searching in the annotated source
will let you quickly determine which component is respon
sible.
The final section of the debug page shows input and output
HTTP headers. Note that some of these are simulated due
to your Previewer settings. For example, if you specified
a particular user agent in your Previewer configuration,
then the User-Agent header is simulated; otherwise it
reflects your actual browser.
OBJECT-ORIENTED TECHNIQUES
- Earlier you learned how to assign a common template to an
entire hierarchy of pages using "autohandlers". The basic
template looks like: - header HTML
<% $m->call_next %>
footer HTML - However, sometimes you'll want a more flexible template
that adjusts to the requested page. You might want to
allow each page or subsection to specify a title, back
ground color, or logo image while leaving the rest of the
template intact. You might want some pages or subsections
to use a different template, or to ignore templates
entirely. - These issues can be addressed with the object-oriented
style primitives introduced in Mason 0.85. - Note: we use the term object-oriented loosely. Mason bor
rows concepts like inheritance, methods, and attributes
from object methodology but implements them in a shallow
way to solve a particular set of problems. Future
redesigns may incorporate a deeper object architecture if
the current prototype proves successful. - Determining inheritance
- Every component may have a single parent. The default par
ent is a component named "autohandler" in the closest par
ent directory. This rule applies to autohandlers too: an
autohandler may not have itself as a parent but may have
an autohandler further up the tree as its parent. - You can use the "inherit" flag to override a component's
parent:
<%flags>
inherit=>'/foo/bar'
</%flags>If you specify undef as the parent, then the component
inherits from no one. This is how to suppress templates.Currently there is no way to specify a parent dynamically
at run-time, or to specify multiple parents.Content wrappingAt page execution time, Mason builds a chain of components
from the called component, its parent, its parent's par
ent, and so on. Execution begins with the top-most compo
nent; calling "$m->call_next" passes control to the next
component in the chain. This is the familiar autohandler
"wrapping" behavior, generalized for any number of arbi
trarily named templates.Accessing methods and attributesA template can access methods and/or attributes of the
requested page. First, use "$m->base_comp" to get a handle
on the appropriate component:
my $self = $m->base_comp;$self now refers to the component corresponding to the
requested page (the component at the end of the chain).To access a method for the page, use "call_method":
$self->call_method('header');or alternatively a path of the form 'SELF:method':
<& SELF:header &>
$m->comp('SELF:header')In the context of a component path, SELF is shorthand for
"$m->base_comp".Each of the above looks for a method named 'header' in the
page component. If no such method exists, the chain of
parents is searched upwards, until ultimately a "method
not found" error occurs. Use 'method_exists' to avoid this
error for questionable method calls:
if ($self->method_exists('header')) { ...To defer to the method of your parent, use "parent":
$m->current_comp->parent->call_method('header')or alternatively a path of the form 'PARENT:method':
<& PARENT:header &>
$m->comp('PARENT:header')In the context of a component path, PARENT is shorthand
for "$m->current_comp->parent".The rules for attributes are similar. To access an
attribute for the page, use "attr":
my $color = $self->attr('color')This looks for an attribute named 'color' in the $self
component. If no such attribute exists, the chain of par
ents is searched upwards, until ultimately an "attribute
not found" error occurs. Use 'attr_exists' to avoid this
error for questionable attributes:
if ($self->attr_exists('color')) { ...Sharing dataA component's main body and its methods occupy separate
lexical scopes. Variables declared, say, in the "<%init>"
section of the main component cannot be seen from methods.To share variables, declare them either in the "<%once>"
or "<%shared>" section. Both sections have an all-inclu
sive scope. The "<%once>" section runs once when the com
ponent loads; its variables are persistent for the life
time of the component. The "<%shared>" section runs once
per request (when needed), just before any code in the
component runs; its variables last only til the end of the
request.In the following example, various sections of code require
information about the logged-in user. We use a "<%shared>"
section to fetch these in a single request.
<%attr>
title=>sub { "Account for $full_name" }
</%attr><%method lefttoc>
<i><% $full_name %></i>
(<a href="logout.html">Log out</a>)<br>
...
</%method>Welcome, <% $fname %>. Here are your options:<%shared>
my $dbh = DBI::connect ...;
my $user = $r->connection->user;
my $sth = $dbh->prepare("select lname,fname, fromusers where user_id = '$user'");
my ($lname,$fname) = $sth->fetchrow_array;
my $full_name = "$first $last";
</%shared>"<%shared>" presents a good alternative to "<%init>" when
data is needed across multiple scopes. Outside these situ
ations, "<%init>" is preferred for its slightly greater
speed and predictable execution model.ExampleLet's say we have three components:
/autohandler
/products/autohandler
/products/index.htmland that a request comes in for /products/index.html./autohandler contains a general template for the site,
referring to a number of standard methods and attributes
for each page:
<head>
<title><& SELF:title &></title>
</head>
<body bgcolor="<% $self->attr('bgcolor') %>">
<& SELF:header &>
<table><tr><td><% $m->call_next %></td></tr></table>
<& SELF:footer &>
</body><%init>
my $self = $m->base_comp;
...
</%init><%attr>
bgcolor=>'white'
</%attr><%method title>
McGuffey Inc.
</%method><%method header>
<h2><& SELF:title &></h2><p>
</%method><%method footer>
</%method>Notice how we provide defaults for each method and
attribute, even if blank./products/autohandler overrides some attributes and meth
ods for the /products section of the site.
<%attr>
bgcolor=>'beige'
</%attr>
<%method title>
McGuffey Inc.: Products
</%method><% $m->call_next %>Note that this component, though it only defines
attributes and methods, must call "$m->call_next" if it
wants the rest of the chain to run./products/index.html might override a few attributes, but
mainly provides a primary section for the body.
COMMON TRAPS
- Do not use print or $r->print
- Most Mason servers operate in "batch" mode, which
means that output is stored in a Mason buffer until
the end of the request. Output sent via print and
$r->print, however, skips the Mason buffer and goes
directly to the client. This will result in output
being printed out of order. Always use "$m->out"
instead of print/$r->print; it does the right thing
with regards to Mason buffering. - Do not call $r->content or ""new CGI""
Mason calls "$r->content" itself to read request
input, emptying the input buffer and leaving a trap
for the unwary: subsequent calls to "$r->content" hang
the server. This is a mod_perl "feature" that may be
fixed in an upcoming release. - For the same reason you should not create a CGI object
like
my $query = new CGI; - when handling a POST; the CGI module will try to
reread request input and hang. Instead, create an
empty object:
my $query = new CGI (""); - such an object can still be used for all of CGI's use
ful HTML output functions. Or, if you really want to
use CGI's input functions, initialize the object from
%ARGS:
my $query = new CGI (ARGS);
AUTHOR
Jonathan Swartz, swartz@pobox.com
SEE ALSO
- the HTML::Mason manpage, the HTML::Mason::Request manpage