If you are the webmaster (or otherwise responsible for the Mason installation), you should also read HTML::Mason::Admin. There you will find FAQs about virtual site configuration, performance tuning, component caching, and so on.
I 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.
+---------+------------------+ |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 Masthead, one for the Footer, etc. In practice pages are built up from as few as one, to as many as twenty or more components.
This component approach reaps many benefits in a web environment. The first benefit is consistency: by embedding standard design elements in components, you ensure a consistent look and make it possible to update the entire site with just a few edits. The second benefit is concurrency: 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'' components, 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 arbitrary parameter lists just like a subroutine, and optionally 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 subroutines, so you can debug and profile component-based web pages with standard Perl tools that understand the subroutine concept, e.g. you can use the Perl debugger to step through components, and Devel::DProf to profile their performance.
% my ($noun, $timeofday) = ('World'); <%perl> my @time = split /[\s:]/, localtime; $timeofday = "evening"; # default if next tests fail if ( $time[3] < 12 ) { $timeofday = "morning"; } elsif ( $time[3] > 12 and $time[3] < 18 ) { $timeofday = "afternoon"; } </%perl> Good <% $timeofday %>, <% $noun %>!<BR> How are ya?
After 6 pm, the output of this component is:
Good evening, World! How are ya?
This short example demonstrates the three primary in-line Perl sections you can embed in your components. By ``in-line'' I mean these sections are generally embedded within HTML and execute in the order they appear. Other, specialized Perl sections are tied to component events like initialization and cleanup, argument definition, etc. Those are covered later in Other Perl Sections.
The parsing rules for these Perl sections are as follows:
$name
%>!'.
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.
In addition to Perl code, Perl sections may also contain Mason commands. These keywords, identified by their mc_ prefix, collectively provide an interface to Mason services such as data caching, file includes, and so on. The majority of Mason commands are for advanced users, but a few (like mc_comp(), for calling other components) see widespread use. HTML::Mason::Commands is the reference for all Mason commands.
http://www.foo.com/mktg/prods.html
Here, prods.html
may be nothing more than a static HTML file, since components don't have to contain any Mason stuff.
Now consider a CGI-style URL with a few GET parameters attached:
http://www.foo.com/mktg/prods.html?str=dog&lst=2&lst=3&lst=4
This component must now contain some Mason elements in order to read the
query arguments and do something interesting with them. To read the
arguments, prods.html should contain a <%perl_args%>
section (see Parameter Passing below) describing the input variables and their data types, in this case a
scalar ($str
) and a three-element list (@lst
). Thus components are relieved of the form-parsing chores common to CGI
scripts.
A catch-all %ARGS
hash is also available for components that
wish to deal with HTTP request arguments directly. %ARGS
is
defined automatically; components need not list it in a
<%perl_args%>
section.
These arguments are subsequently available to the component as regular Perl variables.
$r->path_info
(the virtual location) 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 non-existent component.
Consider the following URL, in which newsfeeds/
exists but not the subdirectory LocalNews
nor the component locStory1.html
:
http://myserver/newsfeeds/LocalNews/locStory1.html,
In this case Mason constructs the following search path:
/newsfeeds/LocalNews/locStory1.htm => no such thing /newsfeeds/LocalNews/dhandler => no such thing /newsfeeds/dhandler => found! (search ends) /dhandler
The found dhandler would read ``/LocalNews/locStory1.html'' from
$r->path_info
and use it as a retrieval key. Optionally, the Mason command mc_dhandler_arg() returns the same path_info stripped of the leading slash
(``LocalNews/locStory1.html''). This is sometimes more useful that the
absolute path returned by $r->path_info
.
To call one component from another, use Mason's mc_comp command:
mc_comp (compPath, name=>value, ...[, STORE=>ref ])
player=>'M. Jordan'
.
The optional STORE parameter takes a scalar reference as an argument, and tells the component to direct its output into the named variable instead of standard output (programmers: think sprintf vs. printf). For example:
<% mc_comp('/shared/mastHead', color=>'salmon', STORE=>\$mh_text) %>
mc_comp()
call:
mc_comp('/mktg/prods.html', 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 in /mktg/prods.html they are
automatically dereferenced into a local list and hash. While convenient,
this means that @b
and %c
are copies--changing
them does not modify the caller's version. To pass a reference, declare it
as a scalar value.
Unlike Perl subroutines, Mason components do not read their parameters from Perl's @_
array. Rather, Mason
imposes its own typed argument passing protocol in which components define
a
<%perl_args%>
section listing parameter names, types, and default values. For example:
<%perl_args> $a @b %c $d=>5 @e=>('foo','baz') %f=>(joe=>1,bob=>2) </%perl_args>
Here, $a, @b, and %c
are required arguments; the component
generates an error if the caller leaves them unspecified. $d, @e, and
%f
are optional arguments; they are assigned the specified
default values if unspecified.
Mason in fact adds a return undef
to the bottom of each component to provide an empty default return value.
For HTML-generating components, this allows the convenient idiom:
<% mc_comp('foo') %>
which, if you think about it, actually prints two things: foo internally
prints some HTML, while <%mc_comp('foo')%>
prints the return value of foo (undef
).
To return your own value from a component, you must use an explicit return
statement. In this case the component behaves like a normal Perl subroutine
with regard to return values and scalar/list context:
You are <% mc_comp('isNetscape') ? '' : 'NOT' %> using Netscape!
1. Initialize arguments in HTML::Mason::Commands package 2. <%perl_init%> section 3. Output HTTP headers (if not output already) 4. Primary section (HTML + embedded Perl sections) 5. <%perl_cleanup%> section
Technically a <%perl_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. In the following example, a database query is used to preload
the @persons
list-of-hashes; it lets us hide the technical
details at the bottom.
<H2>Birthdays Next Week</H2> <TABLE BORDER=1> <TR><TH>Name</TH><TH>Birthday</TH></TR> % foreach (@persons) { <TR><TD><%$_->{name}%></TD><TD><%$_->{birthday}%></TD></TR> % } </TABLE>
<%PERL_INIT> # Assuming DBI/DBD and Date::Manip are already loaded ... # Query MySQL for employees with birthdays next week. # Results are stored in the @persons list-of-hashes.
my (@persons, $name, $birthday); # local vars
# Calculate "MM-DD" dates for this and next Sunday my $Sun = UnixDate(&ParseDate("Sunday"), "%m-%d"); my $nextSun = UnixDate(&DateCalc("Sunday", "+7 days"), "%m-%d");
my $dbh = DBI->connect('DBI:mysql:myDB', 'nobody' ); my $sth = $dbh->prepare( qq{ SELECT name, DATE_FORMAT(birthday, 'm-d') FROM emp WHERE DATE_FORMAT(birthday,'m-d') BETWEEN '$Sun' AND '$nextSun' } ); $sth->execute; # other DBDs want this after the bind $sth->bind_columns(undef, \($name, $birthday) );
while ($sth->fetch) { push (@persons, {name=>$name, birthday=>$birthday} ); } </%PERL_INIT>
Since <%perl_init>
sections fire before any HTTP headers are sent, they should do their work
quickly to prevent dead time on the browser side.
Technically a <%perl_cleanup> block is equivalent to a <%perl> block at the end of the component, but has aesthetic value as marking a cleanup section.
Recall that the end of a component corresponds to the end of a subroutine block. Since Perl is so darned good at cleaning up stuff at the end of blocks, <%perl_cleanup> sections are rarely needed.
<PRE> foo %if ($b == 2) { bar %} baz </PRE>
outputs
foo bar baz
because of the newlines on lines 1 and 3. (Lines 2 and 4 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
foobarbaz
The backslash has no special meaning outside this context. In particular, you cannot use it to escape a newline before a plain text line.
Can also be used for in-line comments, though I admit it is a somewhat 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.
<%perl_off> % This is an example of a Perl line. <% This is an example of an expression block. %> </%perl_off>
This works for almost everything, but doesn't let you output </%perl_off> itself! When all else fails, use mc_out():
%mc_out('The tags are <%perl_off> and </%perl_off>.');
% my $ua = $r->header_in('User-Agent'); % if ($ua =~ /msie/i) { Welcome, Internet Explorer users ... % } elsif ($ua =~ /mozilla/i) { Welcome, Netscape users ... % }
<ul> % foreach $item (@list) { <li><% $item %> % } </ul>
<ul> % while (my ($key,$value) = each(%ENV)) { <li> <b><% $key %></b>: <% $value %> % } </ul>
<table> <tr> % foreach my $h (@loh) { <td><% $h->{foo} %></td> <td bgcolor=#ee0000><% $h->{bar} %></td> <td><% $h->{baz} %></td> % } </tr> </table>
For more than three lines of Perl, consider using a <%perl> block.
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 enter this site.
For side-effect commands like assignments, consider using a % line or <%perl> block instead.
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.
my $result = mc_cache(action=>'retrieve'); if (!defined($result)) { ... compute $result> ... mc_cache(action=>'store', value=>$result); }
The first mc_cache call attempts to retrieve this component'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 second mc_cache call.
The default action for mc_cache is 'retrieve', so the first line can be written as
my $result = mc_cache();
mc_cache(action=>'store',key=>'name',value=>$name); mc_cache(action=>'store',key=>'friends',value=>\@lst); mc_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.
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 sec, seconds, min, minutes, hours, days, weeks, or months. You can also use the first letter or singular form of any of these. (This notation was adapted from Sullivan Beck's Date::Manip package.) E.g. ``30sec'', ``10min'', ``1hour'', ``4h''.
expire_next: takes a string, either 'hour' or 'day'. It indicates an expiration time at the top of the next hour or day.
Examples:
mc_cache(action=>'store', expire_in=>'15min'); mc_cache(action=>'store', expire_in=>'2 hours'); mc_cache(action=>'store', expire_next=>'hour');
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' has changed since the cache was written mc_cache(action => 'retrieve', expire_if => sub { [stat 'myfile']->[9] > $_[0] });
use HTML::Mason::Utils 'access_data_cache'; access_data_cache (cache_file=>'/usr/local/mason/cache/foo::bar', action=>'expire' [, key=>'fookey']);
<% $foo_out %> <%perl_init> my $foo_out; if (!defined ($foo_out = mc_cache())) { mc_comp('foo_main', STORE=>\$foo_out); mc_cache(action=>'store', expire_in=>'3 hours', value=>$foo_out); } </%perl_init>
This works, but is cumbersome. Mason offers a better shortcut: the
mc_cache_self() command that lets a component cache it's own output and eliminates the need
for a dummy component. It is typically used right at the top of a <%perl_init%>
section:
... <primary section, body of page> ... <%perl_init> return if mc_cache_self(expire_in=>'3 hours'[, key=>'fookey']); ... <rest of perl_init> ... </%perl_init%>
mc_cache_self is built on top of mc_cache, so it inherits all the expiration options described earlier.
$r
``request object'' available as a global in all components, granting access
to a variety of server internals, HTTP request data, and server API
methods.
$r
is fully described in the Apache documentation -- here is a sampling of
methods useful to component developers:
$r->uri # the HTTP request URI $r->headers_in(..) # the named HTTP header line $r->server->port # (note two arrows!) port # (usu. 80) $r->content_type # set or retrieve content-type
$r->content() # don't use this one! (see Tips and Traps)
<%perl_init>
section).
That means if you want to send your own HTTP header, you have to do it in
the <%perl_init%>
section. You send headers with Apache commands headers_out and
send_http_header.
To prevent Mason from sending out the default header, call mc_suppress_http_header(1). Here's an example:
<%perl_init> ... mc_suppress_http_header(1); # necessary because of next line my $registered = mc_comp('isUserRegistered'); if (!$registered) { mc_comp('/shared/http/redirect',url=>'/registerScreen'); } ... </%perl_init>
The component isUserRegistered returns 0 or 1 indicating whether the user has registered (e.g. by looking for a cookie). If the result is 0, we use an HTTP redirect to go to the registration screen. Mason would normally send the default header upon reaching the primary section of isUserRegistered - that is why we must call mc_suppress_http_header.
To cancel header suppression, call mc_suppress_http_header(0).
Here is a typical sequence for debugging a Mason page:
<!-- Debug file is '3'. Full debug path is '/usr/local/mason/debug/anon/3'. -->
perl /usr/local/mason/debug/anon/3
you should see the HTTP headers and content that the component would normally send to the browser.
-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 subroutines 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::Interp::debug_hook
Since debug_hook is called with the component name as the second parameter, you can also breakpoint specific components using a conditional on $_[1]:
b HTML::Mason::Interp::debug_hook $_[1] =~ /component name/
You can avoid all that typing by adding the following to your ~/.perldb file:
# Perl debugger aliases for Mason $DB::alias{mb} = 's/^mb\b/b HTML::Mason::Interp::debug_hook/';
which reduces the previous examples to just:
mb mb $_[1] =~ /component name/
Details about configuring debug mode can be found in HTML::Mason::Admin. In particular, the administrator must decide which of three debugging modes to activate:
never (no debug files)
always (create debug files for each request)
error (only generate a debug file when an error occurs)
The Previewer also provides a debug trace of a page, showing all components being called and indicating the portion of HTML each component is responsible for. For pages constructed 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 contains 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 settings 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 configurations are saved permanently in a file. If a username/password 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 Manual.
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 number 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 responsible.
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.
sub {..}
, which creates a subroutine within a subroutine, which makes Perl very
unhappy. Instead, use local anonymous subroutines or create another
component and call with mc_comp.
$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 useful HTML output functions. Or, if you really want to use CGI's input functions, initialize the object from %ARGS:
my $query = new CGI (%ARGS);
$name
from the database, calculating
$salary, etc. This organization allows non-programmers to work with the
HTML without getting distracted or discouraged by Perl code.
This technique does sacrifice some performance for readability.