Mason开发手册
取自 PerlChina.org - wiki
目录 |
[编辑] 名称
HTML::Mason::Devel - Mason开发手册
[编辑] 概要
该手册是为那些知道HTML和一点Perl的内容开发者准备的,目的就是帮助大家创建,运行和调试Mason组件。
如果您是网站管理员(或者有相同责任的其他人),您应该阅读系统管理员手册http://www.masonhq.com/docs/manual/Admin.html, 它描述了怎么配置,优化,缓存组件等相当多的内容。
如果您是一个开发者,仅仅是对Mason的能力和实现感兴趣,那么系统管理员手册对您同样也是为您准备的。
我们强烈的建议您通过学习这些例子后,将Mason应用于您的工作中(我知道这里翻得不对,但是我一下没看明白,就不管了),其他的例子可以在目录 samples/里找到(我想是指的Mason的源代码目录)。
虽然mason能够处理除了创建网站之外的更多工作,但是创建网站才是大多数人感兴趣和关心的。本手册也是仅仅把关注如何创建网站。
如果您想把mason用于其他的(非web项目),本手册也是极具参考价值,当然你还得参考其他的文档(主要是在系统管理员手册里):http://www.masonhq.com/docs/manual/Admin.html#running_outside_of_mod_perl
[编辑] 怎样使用本手册
如果您只想学习怎么使用Mason,快速的上手,我们推荐如下的顺序:
- 什么是组件
- 内嵌的Perl
- 调用其他组件
- 顶级组件
- 传送参数
- 初始化和清理
- 为网站准备的特性
- 一般的陷阱
[编辑] 什么是组件
组件 - HTML,Perl,Mason标记的混合体 - Mason最基本的块和计算单位. 在Mason里,网页是合并多个组件的输出,举个例子:一个新闻发布网站的页面可能包括这样几个独立的Mason组件:公司Logo(Masthead), 广告(AD)banner, 左边的内容,中间的内容等,考虑如下的内容布局:
+---------+------------------+ |Masthead | Banner Ad | +---------+------------------+ | | | |+-------+|Text of Article ..| || || | ||Related||Text of Article ..| ||Stories|| | || ||Text of Article ..| |+-------+| | | +------------------+ | | Footer | +---------+------------------+
顶级组件决定整个布局,例如一个HTML表格,而单独的表格单元内容是被子组件的输出填充的。本例中,一个子组件填充Masthead, 一个为ad等等。在其他的实际的页面中包括更多,更细致的子组件。
这种组件的概念在一个web环境下有很多的益处:
- 一致性:
通过嵌入标准的设计元素,你可以保持整个网站一致的实际风格,而且使得仅仅需要改变很少的地方就能改变整个网站成为可能。
- 协作性:
在一个多人或者的团队里,一个人编辑内容的同时,另外一个人可以编辑Masthead什么的(个人觉得配合版本管理什么东西一起用).
- 可重用
一个组件可以同时被多个网站使用,你也可以开发有用的组件库和其他人共享。
大多数组件返回(大块的)HTML代码,顶级组件(即接受URL请求的组件)返回整个页面的HTML代码,其他的子组件返回小块的HTML代码,并被顶级组件包含。
组件接受表单和来自HTTP请求的数据。 当调用其他组件的时候,它像子程序一样接受任意的参数列表,而且可以选择是否返回值。这可以使我们定义一些像子程序但不返回任何的HTML组件, 它可以计算和有返回值。
实际上Mason将组件编译成子程序,所以你可以调试一些由组件组成的页面,例如Perl调试器,Devel::DProf模块
[编辑] 内嵌的Perl代码
这里有一个组件的例子
<%perl>
my $noun = 'World';
my @time = localtime;
</%perl>
Hello <% $noun %>,
% if ( $time[2] < 12 ) {
good morning.
% } else {
good afternoon.
% }
中午12点后,输出如下:
Hello World, good afternoon.
整个短例子示范了三种形式的内嵌的Perl代码,它们被嵌入到HTML中,并按出现的顺序被执行。其他节( <%init> <%clean>...)和组件事件绑定,例如:初始化,清理等。
这些Perl节解析的规则如下(就是在一个组件中被解析的规则)
1、块<% xxx %>会被当成单个的Perl表达式xxx的运算结果替换,它(指<% xxx %>)经常用于组件中的变量替换。比如:Hello, <% $name %> 2、以%开始的行被当成Perl代码解析 3、想插入多行Perl代码可以使用<%perl>...</%perl>, 被这个标签包围起来的代码会被当成Perl运行,并且返回最后一个值,如果没有返回值,则忽略。
<%perl>标签,像所有的Mason块标签,都是大小写无关,并且可以跨多行。
[编辑] 例子和推荐用法
[编辑] % lines
大多数判断和循环语句 - if, while, foreach等等,大多数有副作用的命令,像赋值语句,使用该用法都是非常有用的。为了提高可阅读性,总是在%后放置一个空格: 例如:
- 条件语句
% my $ua = $r->header_in('User-Agent');#这个例子我相信是在mod_perl1下的例子,在mod_perl2下不能通过
#如果想通过,则写成这样的 $ua = $r->headers_in->{'User-Agent'};
% if ($ua =~ /msie/i) {
Welcome, Internet Explorer users
...
% } elsif ($ua =~ /mozilla/i) {
Welcome, Netscape users
...
% }
- 从一个数组里格式化一个HTML列表
<ul>
% foreach $item (@list) {
<li><% $item %></li>
% }
</ul>
- 从一个哈希数组里格式化一个HTML列表
<ul>
% while (my ($key,$value) = each(%ENV)) {
<li>
<b><% $key %></b>: <% $value %>
</li>
% }
</ul>
- 从一个哈希数组的一元数组里格式化一个HTML表格
<table>
% foreach my $h (@loh) {
<tr>
<td><% $h->{foo} %></td>
<td bgcolor=#ee0000><% $h->{bar} %></td>
<td><% $h->{baz} %></td>
</tr>
% }
</table>
[编辑] <% xxx %>
运行其中的表达式(可以是非常复杂的表示),并打印表达式的值。 为了提高阅读性,在标签和表达式左右都加上一个空格。 例如:
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.
[编辑] <%perl>...</%perl>
用于多行Perl代码
[编辑] Mason对象
本节描述Mason范围内的几个对象,不用太担心,刚开始的时候只需要注意Request对象即可。
[编辑] Request对象
对于所有的组件来说,有两个可以任意使用的全局请求对象的变量: $r 和 $m
$r, 当前apache请求对象的Perl API接口, 在mod_perl 1.x你可以使用perldoc Apache看这个对象的描述,但是在mod_perl2, 它在Apache2::RequestRec这个模块中描述。 我们列出一些该对象非常有用的用法:
$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, Mason提供的一个和$r类似的变量,几乎所有使用语法标签功能都可以通过$m的方法来使用,请参见HTML::Mason::Request的文档。
因为这两个变量总是内置于Mason,所以您应该尽量避免使用同名变量,否则可能会出现一些非常奇怪的错误。
[编辑] 组件对象
Mason提供一个组件对象的API,用于查询组件相关的文件,参数等。可以通过文档HTML::Mason::Component查看所有的可用的方法。可以通过变量 $m->current_component 或者 $m->fetch_component得到一个组件对象的实例。
在大多数应用中,你想调用一个组件无须使用该组件对象,请查看下一节。
[编辑] 系统对象
有很多系统对象为Mason请求对象提供服务:例如,HTML::Mason::Lexer, HTML::Mason::Compiler, HTML::Mason::Interp, HTML::Mason::Resolver, 和HTML::Mason::ApacheHandler,系统管理员可以创建这些对象并提供参数以此可以改变Mason的一些行为。作为组件开发者不用担心这些对象,在这里介绍,只是在后面我们偶而会提及这些相关的知识。
[编辑] 调用组件
Mason页面通常情况下不只包含一个组件,而是多个组件按照一定层次组织起来构成的。
[编辑] 生成HTML代码的组件
使用<& ... &>标签调用其他的组件: <& comp_path, [name=>value, ...] &>
- comp_path:
组件的路径,如果以"/"为前导,则使用相对于$comp_root的绝对路径, 如果没有以"/"为前导,则相对于当前位置的路径
- name=>value对:
以name=>value的形式像组件提供参数,比如:player => 'M. Jordan'.
comp_path可以是一个字符串(引号通常可以省略),也可以是一个返回字符串的Perl表达式,大多数的时候可以不需要引号。 对于comp_path, Mason使用一些魔术解析,如果第一个字符是[\w/_.],perl将会认为路径是一个常量字符串,如若不然,则 当成一个perl表达式运行并返回结果为路径。 这里是一些例子:
# relative component paths
<& topimage &>
<& tools/searchbox &>
# absolute component path
<& /shared/masthead, color=>'salmon' &>
# this component path MUST have quotes because it contains 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 &>
请求对象有几个方法也可以调用组件:$m->comp和标签<& ... &>是等价的。 $m->comp('/shared/masthead', color=>'salmon');
$m->scomp类似于sprintf版的$m->comp, 允许用户在输出前进行检查和调试。
my $masthead = $m->scomp('/shared/masthead', color=>'salmon');
$masthead =~ ...;
$m->print($masthead);
[编辑] 内容组件调用
组件可以用使用扩展组件语法过滤页面的部分内容:
<&| /path/to/comp &> this is the content </&> <&| comp, arg1 => 'hi' &> filters can take arguments </&> <&| comp &> content can include <% "tags" %> of all kinds </&> <&| comp1 &> nesting is also <&| comp2 &> OK </&> </&> <&| SELF:method1 &> subcomponents can be filters </&>
过滤组件和正常组件一样使用,唯一的不同是过滤组件希望通过$m->content取得调用时赋予的内容,并对这些内容进行处理。
在结束标签中可选的包括组件的名字,Mason将和开始标签进行验证,以确保在同一层次。多用于多层嵌套标签。为了避免不明确的因素 必须是组件名不包含引号(这里我不太明白,大概是这个意思吧),而且必须使用结束标签。 例如:
<&| "outer" &>
<&| /inner/comp, arg=>'this' &>
<&| .mycomp &>
Yada yada yada
</& .mycomp >
</& /inner/comp >
</&>
这里是一个本地化的例子,在标签中定义了一系列的不同语言的字符串,在组件中根据全局变量选取一个正确的语言字符串,一般可以在顶极的autohandler里设定。
<&| /i18n/itext &>
<en>Hello, <% $name %> This is a string in English</en>
<de>Schoene Gruesse, <% $name %>, diese Worte sind auf Deutsch</de>
<pig>ellohay <% substr($name,2).substr($name,1,1).'ay' %>,
isthay isay igpay atinlay</pig>
</&>
这里是/i18n/itext组件的定义
<% $text %>
<%init>
# this assumes $lang is a global variable which has been set up earlier.
local $_ = $m->content;
my ($text) = m{<$lang>(.*?)</$lang>};
</%init>
您可以明确的通过方法$m->content检查是否传入内容,这样您可以写一些组件用于根据传入的内容进行不同的处理,不管怎么样,在滥用这个组件前(我想这里要说的是像这样的方式
使用组件吧),可能考虑分解成两个明确的组件会更好。
一个正常的组件不调用$m->content不会输出任何内容。(这里有些莫名奇妙,是不是说一个正常的组件使用这种调用方式?)
如果你定一个单独的过滤组件,这样做的结果有些类似<%filter>节,请参考下面的<%filter>节。
[编辑] 高级内容组件调用
$m->content在Mason内部执行是包含组件中的内容的(这个我有些不知道怎么翻译,大意就是说一个带内容的组件调用,其内容是在方法$m->content中由子程序或者什么来实现的),这意味中包含在内容中的标签,perl代码是在组件调用$m->content是才被运行的。根据这个规则我们可以设计出类似与控制器的(MVC中的C),也可以在不同的时候返回不同的值。
使用过滤组件作为控制结构的最主要的技巧是需要设定一个可以在组件和内容中都可以访问的变量,内容中的代码可以存取所有的本组件中的变量,但是作为被当前组件调用的过滤组件不能访问所有的变量,有两个办法可以达到目的: ** 使用全局变量 ** 将一个变量的引用作为参数传入过滤组件
这里是一个使用变量引用参数的例子:
% my $var;
<ol>
<&| list_items , list => \@items, var => \$var &>
<li> <% $var %></li>
</&>
</ol>
list_tiems 组件定义如下:
<%args>
@list
$var
</%args>
% foreach (@list) {
% $$var = $_; # $var is a reference
<% $m->content %>
% }
使用全局变量可能会更简单一些,下面是一个使用全局变量的例子,变量$var被定义为全局变量,系统管理员应该确定在配置将变量配置到Mason参数allow_globals parameter, 在过滤组件中使用local指令可以使$var变量本地化,这样就可以实现嵌套。
<ol>
<&| list_items, list => \@items &>
<li> <% $var %></li>
</&>
</ol>
list_items 组件
<%args>
@list
</%args>
% foreach (@list) {
% local $var = $_;
<% $m->content %>
% }
另外要记住要在Mason配置参数allow_globals中包含变量$var, 开发者应该注意不要在其他的地方使用该变量,以免和过滤组件发生冲突,可以使用local本地化该变量,减少这种冲突的可能性。
另外一个简单的办法是可以是用变量$_ (它本身就是全局变量),而且自动本地花。
<ol>
<&| list_items, list => \@items &>
<li> <% $_ %> </li>
</&>
</ol>
list_items 组件:
<%args>
@list
</%args>
% foreach (@list) {
<% $m->content %>
% }
[编辑] 组件返回值
到目前为止,你看到的组件仅仅是用来返回或者是输出HTML代码,但是实际上组件可以有返回值。
当我们向你说明这个功能是,我们强烈的建议你应该把这样的代码放到模块里,而不是像我们这里展示的那样放进Mason组件里,主要的原因如下:
- 可以在Mason之外重用代码
- 可以在启动mod_perl的时候预先载入模块,仅仅需要很少的内存。
- 做同样的事情,Mason子程序要比Perl模块慢很多。
- 对于模块很容易进行回归测试
像前面说的那样,有时候你想创建一个有返回值的组件,下面有一个例子:is_netscape, 根据用户的浏览器头变量:user-agent判断用户是否使用Netscape浏览器
<%init>
my $ua = $r->header_in('User-Agent');
return ($ua =~ /Mozilla/i && $ua !~ /MSIE/i) ? 1 : 0;
</%init>
由于组件是以Perl子程序的形式实现的,所以它不但能有返回值甚至可以理解标量/列表上下文,例如: 在组件里调用wantarray()可以判断组件是运行一个标量还是一个列表的上下文中。
标签<& ... &>仅仅是调用组件,但是不接受任何返回值,不管被调用组件是否有返回值,如果想得到组件的返回值,使用如下的方式: $m->comp
% if ($m->comp('is_netscape')) {
Welcome, Netscape user!
% }
Mason自动为每一个组件加上一个undef的返回值(类似于return undef), 如果你想一个组件有返回值,必须使用明确的return语句返回值,在这里perl的最后一个表达式就是返回值的技巧不适用。
虽然组件可以输出HTML和返回值,但是很少有原因需要组件在输出HTML的同时又返回值。
[编辑] SubRequest
有时候你想要类似初始化那样一步一步的调用组件,像Mason检查autohandler和dhandlers那样,为了完成该功能,你需要调用方法 subrequest(子请求对象)
一个子请求对象(subrequest)是一个简单的,有所有方法的请求对象。 为了创建一个subrequest,应该使用$m->make_subrequest方法,这个方法可以使用任何属于模块HTML::Mason::Request的参数,像autoflush或者out_method. 一旦你已经创建了一个新的subrequest对象,你就可以通过调用方法exec来运行该对象,其他参数和 $m->comp的参数是一致的。
因为subrequest继承了其父请求对象的所有参数,因此subrequest运行的组件输出和当前组件的输入目的地是一致的,你也可以改变它的输出目的地, 例如:
<%perl> my $req = $m->make_subrequest( comp => '/some/comp', args => [ id => 172 ] ); $req->exec; </%perl>
如果你想截获这个subrequest的输出,仅仅需要传入一个out_method参数给make_subrequest
<%perl>
my $buffer;
my $req =
$m->make_subrequest
( comp => '/some/comp', args => [ id => 172 ], out_method => \$buffer );
$req->exec;
</%perl>
现在$buffer包含组件/some/comp的输出。
为了方便Mason也提供一个方法$m->subexec, 它接受和$m->comp一样的参数,在内部是调用$m->make_subrequest方法,并运行exec方法,这是非常有用的,当你想覆盖父请求对象的一些属性的时候。
缺省的subrequest输出显示在调用组件调用点之后,如果你想截获这些输出,则需要明确的覆盖参数out_method
Mason请求对象只能为一个单独的调用运行exec,如果你想运行多个subrequest,你不得不创建多个subrequest.
[编辑] 顶级组件
一个页面第一个被请求的组件,属于documentroot里的,而且是基于URL请求的组件,例如:
http://www.foo.com/mktg/prods.html?id=372 Mason将转换该请求到对一个实际的文件请求,例如:/usr/local/www/htdocs/mktg/prods.html, Mason将载入并当一个组件来运行它,结果,Mason实现这样的调用:
$m->comp('/mktg/prods.html', id=>372)
该组件或许调用其他组件或者包含一些Perl代码,或者仅仅是一些html代码。
[编辑] dhandlers
当用户请求一个不存在的组件时会发生什么呢?此时Mason根据url路径向后进行扫描,检查每个目录是否存在一个成为dhandler("default handlers")的组件,如果找到它将会被解析,而且它可以使用方法$m->dhandler_args接收请求参数,并用于其他的功能,像一个数据库查询,或者是定位一个其他的文件系统等等。从某种意义上来说,dhandlers类似于Perl的AUTOLOAD,它是一个不存在的weburl调用的最后去处。
考虑下面这个url, newsfeeds目录是存在的,但是子目录LocalNews和组件Story1都是不存在的 http://myserver/newsfeeds/LocalNews/Story1
在这个例子里,mason按照如下的顺序进行搜索:
/newsfeeds/LocalNews/Story1 => no such thing /newsfeeds/LocalNews/dhandler => no such thing /newsfeeds/dhandler => found! (search ends) /dhandler
dhandler将使用$m->dhandler_args读入参数:LocalNews/Story1, 并作为一个关键字存入数据库 我们给出一个简单的/newsfeeds/dhandler实现:
<& header &>
<b><% $headline %></b><p>
<% $body %>
<& footer &>
<%init>
my $arg = $m->dhandler_arg; # get rest of path
my ($section, $story) = split("/", $arg); # split out pieces
my $sth = $DBH->prepare
(qq{SELECT headline,body FROM news
WHERE section = ? AND story = ?);
$sth->execute($section, $story);
my ($headline, $body) = $sth->fetchrow_array;
return 404 if !$headline; # return "not found" if no such story
</%init>
缺省的dhandlers不理会对一个不存在的目录的请求例如newsfeeds/LocalNews,Mason将会把请求直接转给apache处理,一般会转向请求index页面或者是返回一个FORBIDDEN错误,一般情况下这样就可以了,但是如果有需要,系统管理员可以配置Mason让dhandler处理目录不存在的请求,请参考管理员手册的http://www.masonhq.com/docs/manual/Admin.html#allowing_directory_requests.
如果组件或者是dhandler不想处理一个特定的请求或者是跳过,可以调用$m->decline
当在mod_perl下使用dhandler,你会发现有时候apache没有设定content type, 通常会在请求一个不存在的文件或者是目录的时候会发生这种情况, 可以在apache配置文件里<location>...</location>块里通过指令SetType 设定content type, 或者是使用方法$m->content_type.动态的设定content type
系统管理员可以通过配置指令dhandler_name 自定义该文件的名字(将dhandlers换成其他的名字)
[编辑] autohandlers
autohandler允许你在Mason调用一个顶级组件之前截取控制和运行一些代码,比如增加标准的HTML头或者是HTML页脚,或者是设定全局变量。
Autohandlers是基于目录的,当Mason决定存取一个顶级组建的时候,Mason会检查当前目录和当前目录的父目录,看是否存在名字为autohandler的组件,如果发现该 组件,则先调用该组件,之后在autohandler中调用$m->call_next将控制权返回给原始请求的组件。
本质上$m->call_next和$m->comp是一样的,不过$m->call_next的组件路径和其他参数是隐含的。你也可以增加参数,它会被合并到原始参数里,并且优先级高于原始参数,这允许你覆盖由URL上传过来的参数。
下面是一个用户给目录下所有的页面增加一个通用的页头和页脚的例子
<html>
<head><title>McHuffy Incorporated</title></head>
<body style="background-color: pink">
% $m->call_next;
<hr />
Copyright 1999 McHuffy Inc.
</body>
</html>
也可以用子组件的形式来重写本例 例如:
<& /shared/header &>
% $m->call_next;
<& /shared/footer &>
下面这个例子是应用一个过滤标签,将所有页面中的相对图片路径替换成一个绝对路径
% $m->call_next;
<%filter>
s{(<img[^>]+src=\")/} {$1http://images.mysite.com/}ig;
</%filter>
Autohandler大多数时候仅仅简单的调用$m->call_next,无须知道下一个组件是什么,然而,当你需要它的时候,你可以通过$m->fetch_next, 返回下一个组件,这在手动 调用下一个组件时,非常有用,比如你想停止某些原始的组件调用,你也可以调用$m->scomp来将存储和处理一个组件的输出。
如果有多个Autohandler应用到同一个页面上,每个Autohandler都会有机会运行,最顶端的autohandler最先运行,每个autohandler调用$m->call_next将控制权传到下一个组件,直到原始请求的组件获得控制权,这允许你联合使用全站模板和小范围内模板。
autohandler可以结合mason的面向对象的特性,使其功能更强大:比如方法,属性,继承,我们在单独的一节里讨论Mason的面向对象技术。
系统管理员可以通过配置指令:autohandler_name 改变缺省的名字autohandler
[编辑] dhandlers vs. autohandlers
autohandlers 和dhandler都提供一个办法改变一些列的URL请求的行为,但是它们也是有区别的,最大的区别在于dhandler是在没有相应的组件存在时调用,而autohandler在在有相应的组件存在时被调用。
作为一个推荐使用的规则:当你有一系列的组件需要应用filter或者是处理页面的话,选用autohandler, 当需要处理虚拟URL的时候,选用dhandler
它们甚至在同一目录里使用,比如你想混合使用实际URL和虚拟URL的时候,并使用相同的模板和过滤器的时候。
[编辑] 传递参数
本节描述了如何向组件传递参数(包括来时HTTP 请求的参数和调用组件时的参数)和如何在组件里取得参数值。
[编辑] 组件调用中的参数
当调用组件的时候可以向组件传递任何perl的类型参数:
<& /sales/header, s => 'dog', l => [2, 3, 4], h => {a => 7, b => 8} &>
该例子向组件传递标量,数组和哈希表,数组和哈希表以引用的方式传递,在被调用的组件里自动解引用。
[编辑] HTTP Request中的参数
考虑如下形式的CGI查询URL
http://www.foo.com/mktg/prods.html?str=dog&lst=2&lst=3&lst=4
或者是以POST方式传递的参数, Mason自动的解析GET/POST的参数,使他们成为组件的参数。
[编辑] 访问参数
无论组件参数是来自GET/POST或者是其他组件,都有两种方式可以存取他们
1、定义命名参数:组件可以定义一个<%args>节,列出参数名称,类型,缺省值。 比如:
<%args>
$a
@b # a comment
%c
# another comment
$d => 5
$e => $d*2
@f => ('foo', 'baz')
%g => (joe => 1, bob => 2)
</%args>
在本组件定义中$a, @b, %c是必须的参数,如果在请求该组件时没有给出这几个参数的值,Mason将报错,而$d, $e, @f, %g是可选参数值,如果未指定,将使用缺省值。所有的这些参数在组件内部都是字典范围变量(my)。
参数使用一行或者是多行分开,注释放置在定义后,或者单独占一行。
缺省的参数表达式按照从上到下的顺序运行,后面的参数可以引用前面的参数的值,比如说本例中($e = $d * 2, $e可以引用$d的值) <%args>仅能包括合法的Perl变量名,不合法的参数名不能自动预定义(pre-define,在<%args>节),必须通过%ARGS类存取,请参看下面的例子。
2、%ARGS哈希表:
该变量总是存在的,包含所有的参数(不管是来自GET/POST或者是调用组件的参数), 特别适合用来处理大量的参数,动态参数,包含不合法的命名参数等,而且不需要定义<%args>节就可使用变量%ARGS。它的内容和定义<%args>没有直接关系。
下面的例子显示如何将参数传递到另一个被调用的组件
<& template, %ARGS &>
[编辑] 参数传递实例
下面的例子说明如何通过不同的方法传递参数和接收参数
1、传递一个值为5的标量:
在URL中: /my/URL?id=5
在组件调用中: <& /my/comp, id => 5 &>
在被调用的组件中如果定义了命名参数
$id, 则$id的值等于 5
@id, 如果是定义了数组,则数组是包含一个元素5的数组 (5)
%id, 如果定义的哈希表,则出错。
另外$ARGS{id}等于5.
2、传递一个颜色列表,值为:red, blue, 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=Alice&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)
In addition, $grade and $ARGS{grades} will equal
['Alice',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.)
[编辑] 使用变量@_
如果你不喜欢命名参数,你也可以传递传统的列表顺序参数。 <& /mktg/prods.html', 'dog', [2, 3, 4], {a => 7, b => 8} &> 然后通过Perl的@_数组进行存取。 my ($scalar, $listref, $hashref) = @_; 本例中<%args>的定义也不是必须的。
一般我们推荐使用命名参数,它可以很好的增强可读性,有语法检查,并且可以自动赋予缺省值,但是不管怎么说使用@_对于小组件来说是比较合适的, 尤其是对于使用<%def>定义的组件来说。
在Mason 1.21版之前,@_包含调用者参数的副本,1.21之后,不必要的变量复制被清楚了,@_使用调用着参数的别名,类似标准的Perl子程序。 如果组件更新了$_[0], 那么相应的参数值也就跟着更新(如果没有更新,则报错)
大多数用户没有注意到改变,因为<%args>和%ARGS保留参数的副本。
关于@_请参见perlsub手册
[编辑] 初始化和清理
接下来的章节,描述在Mason中在特定时间运行的Perl代码
[编辑] <%init>
该节的代码在组件被调用的时候,实行初始化的工作,比如检查用户是否已经登录,从数据库取出数据放入列表中, 解析一个文件的内容存到数据结构中等等。
技术上来说<%init>和放置在组件最前面个的<%perl>代码是一样的,但是为了审美观,我们觉得将代码放置在美工页面的尾部,比较合理。
我们发现大多数可读组件包含HTML并被放置在页面的最顶端。可以使用<%init>设置初始变量
<html>
<head><title><% $headline %></title></head>
<body>
<h2><% $headline %></h2>
<p>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 = ?");
$sth->execute($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>
该标签里包含的清理代码只有在组件退出时运行,比如关闭数据库连接和释放文件句柄等。
<%cleanup>在技术上等效于放置在组件底部的Perl代码,这意味着如果在组件中间使用了明确的return语句或者退出语句,或者是在组件或者是其子组件发生了错误使脚本停止运行,该代码将不会被运行,因为这个限制而且Perl本身在字典范围(my)内有相当好的清理能力,所以基本上该标签没有太大的用处。
如果你想保证某些代码在退出时必须运行,可以考虑使用mod_perl的cleanup,或者是使用一个定义了DESTORY方法的类或者是模块。
[编辑] <%once>
该标签定义的代码仅仅在组件被装入的时候运行一次,在这个标签中定义的标量能被组件中的所有代码看到,并且变量的生命周期可以一直持续到该组件结束。
该标签对于在组件中于定义变量,定义子程序,初始化状态非常有用。
需要注意的是该标签内的代码并不是运行在一个请求的上下文中,你不能在这里调用其他组件,或者是变量$m , $r, 而且不能在<%once>里试着使用return返回。
一般情况下该标签包含的代码对每次请求该组件的HTTP请求运行一次,但是该组件已经被在请求前被装入,那么该部分代码仅仅是在父组件中运行一次,为了避免在<%init>中初始化的变量在一个新fork前失去作用,可以在<%once>中定义变量,在<%init>中初始化(这一段大意就是这样的,今天状态不好,不想太讲究,想讲明意思就算了)
<%once>
my $dbh; # declare but don't assign
...
</%once>
<%init>
$dbh ||= DBI::connect ...
...
</%init>
另外在预载入的组件中,变量$m和$r也是不可用的。
[编辑] <%share>
类似<%once>, 在该节里定义的词法变量对于其他的所有组件代码有效:代码主体部分,子组件,方法等。不管怎么样,不像<%once>, <%share>每次请求都会运行,并且一直变量一直到代码结束都有效。
<%share>最主要的作用是可以用于初始化一些必须的变量, 例如在主体和子组件里需要的变量等。请查看面向对象技术的例子和使用方法。
有一点非常重要:在<%share>节不能存取变量%ARGS和定义在<%args>节的变量。 但是可以通过$m->request_args方法来取得他们。
另外,不能在<%share>里不能条用该组件和子组件的方法,虽然你可以调用其他组件的方法。
尽量避免在<%share>里使用那些具有副作用的代码,你可以假定<%share>仅仅是在所有的代码之前运行。
