Programming with Mason
取自 PerlChina.org - wiki
Mason编程介绍
翻 译:cnhackTNT 出 处:中国Perl协会 FPC 原 名:Programming with Mason 作 者:Dave Rolsky 原 文:http://www.perl.com/lpt/a/2002/12/11/mason.html 发 表:December 11, 2002 Perlchina提醒您:请保护作者的著作权,维护作者劳动的结晶。
Dave Rolsky 及 Ken Williams 是 Embedding Perl in HTML with Mason 的作者.
Mason is a powerful framework for generating dynamic text, and is especially useful when creating complex, featureful Web sites. For those (hopefully few) folks who haven’t yet heard of Mason, it is a Perl-based templating framework comparable to frameworks such as Apache::ASP, Embperl, and Template Toolkit. Like the first two, and unlike the latter, Mason operates by embedding Perl in text.
Mason 是一种用来生成动态内容的强有力的程序框架,尤其在构建比较复杂并且特性鲜明的Web站点时它显得十分有用。假如你从未听说过Mason(希望只有少数人没听过),我可以告诉你,Mason是基于Perl的一种模板框架,与Apache::ASP,Embperl 和 Template Toolkit 这些框架相比,Mason有过之而无不及。和前面提到的 Apache::ASP,Embperl相同,而和 Template Toolkit 不同,Mason 是将 Perl 代码嵌入文本来运行的。
Mason is based around the idea of a component. A component is roughly equivalent to a Perl subroutine, and can contain text and/or code. Here is a very simple, but complete component that has both text and code:
Mason 是基于组件这种构思的。组件就大致相当于 Perl 中的子程序,并且这个子程序可以包含文本内容 和/或者 代码。如下是一个简单而完整的包含文本内容和代码的组件:
% my $planet = "World"; Hello, <% $planet %>!
当 Mason 运行这段代码,会输出:
Hello, World!
The rest of this article assumes at least a minimal familiarity with Mason, though if you’re at all familiar with other templating systems, you’ll probably be able to grok the code we show. For more details, I would of course recommend Embedding Perl in HTML with Mason, written by Ken Williams and myself. Mason also comes with its own documentation, which can be seen online at www.masonhq.com.
这篇文章余下的部分假设你对 Mason 有最基本的一些了解,尽管如此,如果你很熟悉其他的模板系统,或许你能够体会我们所展示的代码。如果你想了解更多,我当然会推荐 Ken Williams 及我本人编著的《Embedding Perl in HTML with Mason》一书。同样,你可以在 http://www.masonhq.com 读到 Mason 自带的文档。
As with any powerful and flexible system, Mason is applicable to a lot of problems, and there is always more than one way to do it. It is a Perl-based system, after all!
和其他强大而灵活的系统一样,Mason 对于解决很多问题都很适用,而且其实现方法多种多样(There Is More Than One Way To Do It!),毕竟它是一个基于Perl的系统!
Below you’ll find some cookbook recipes for solving a few typical Web application problems. All the recipes assume that you are using the latest version of Mason, which at the time of this writing is 1.15, though most of them will work untouched with older versions.
以下,你将找到一些用于解决典型的 Web 应用方面问题的处方。所有的处方都假设你使用的是最新版本的 Mason(杜撰此文时的最新版本是1.15),不过其中大多数都可以不加变动的在老版本的Mason上工作。
Putting a Session ID in All URLs
If you’ve ever written a dynamic Web application, then it’s likely that you’ve used sessions to store data as the user moves through the application. Typically, sessions are identified by session IDs that are stored in a cookie.
假如你曾经写过动态Web应用程序,那么当用户在应用程序的不同页面中活动时你可能曾用过 session 来存储相关数据。一个典型的方式就是用存储在 cookie 中的 session ID 来验证 session 。
If you cannot use cookies, then you can store the session ID in the URL. There are security and application problems with this approach (as well as with the user of cookies), but those are outside the scope of this article. The mod_perl user list archives at marc.theaimsgroup.com/?l=apache-modperl contain a number of discussions related to this topic.
如果你不能使用 cookie ,那么你可以将这些 session ID 放在 URL 中。这种方法有一些安全及应用方面的问题(用 cookie 也一样),但已经超出了本文的讨论范围。位于 http://marc.theaimsgroup.com/?l=apache-modperl 的 mod_perl 用户组文档中包含了很多关于这方面的讨论主题,你可以去看看。
Putting the session ID in the URL can be a hassle, because it means that you have to somehow process all the URLs you generate. Using Mason, this isn’t as difficult as it would be otherwise. There are at least two ways to do this.
将 session ID 放在 URL 里的做法可能会引起一场争论,因为它意味着你得以某种方式来处理所有的这些你生成的 URL 。但是使用 Mason 的话,就比其他方式简单多了。在这里至少有两种方法用于处理这种问题。
The first would be to put a filter in your top level autohandler component:
第一种方法就是在你的顶级自动处理器(autohandler)组件中添加一个过滤器:
<%filter> s/href="([^"])+"/'href="' . add_session_id($1) . '"'/eg; s/action="([^"])+"/'href="' . add_session_id($1) . '"'/eg; </%filter>
The add_session_id() subroutine, which should be defined in a module, might look something like this:
这个 add_session_id() 子程序应该事先在某个模块中定义,它看起来可能是这个样子的:
sub add_session_id {
my $url = shift;
return $url if $url =~ m{^\w+://}; # 不改变外部 URL
if ($url =~ /\?/) {
$url =~ s/\?/?session_id=$MasonBook::Session{_session_id}&/;
} else {
$url .= "?session_id=$MasonBook::Session{_session_id}";
}
return $url; }
This routine accounts for external links as well as links with or without an existing query string.
这个子程序对外部链接(连向网络上其他资源的链接,而下面说的本地链接是针对访问你这台服务器的链接)或者本地已经带有请求字串(或者不带请求字串)的链接同样都能进行处理。
The drawback to putting this in a <%filter> section is that it only filters URLs in the content generated by components, and misses any URLs that might be in headers, such as in a redirect. Therefore, you’d need to handle those cases separately with this solution.
以上这种方法有个缺点,它只能过滤包含在由某个组件生成的内容中的 URL ,而不能处理其他的比如包含在头部信息中的 URL。(header,指http请求中的头部信息,比如包含在头部信息中的页面转向信息。) 因此,你需要自己做一些其他的处理来解决这个问题。
Another solution would be to create all URLs (including those intended for redirects) via a dedicated component or subroutine that adds the session id. This latter solution is probably a better idea, as it handles redirects properly. The drawback with this strategy is that you’ll have a Mason component call for every link, instead of just regular HTML.
另外一种解决方案是通过一个专门的组件或者子程序来来给所有的 URL 加上 session id.这种方法可能更好一些(它能正确的处理转向),他的缺点是对于每一个链接都必须调用一个 Mason 组件来进行处理,而以前我们只要用传统的HTML就可以了。
Here is just such a component: 下面是一个对应与这种解决方案的组件例子:
<%args> $scheme => 'http' $username => undef $password => $host => undef $port => undef $path %query => () $fragment => undef </%args> <%init> my $uri = URI->new;
if ($host) {
$uri->scheme($scheme);
if (defined $username) {
$uri->authority( "$username:$password" );
}
$uri->host($host);
$uri->port($port) if $port;
}
# Sometimes we may want to include a path in a query string as part # of the path but the URI module will escape the question mark. my $q;
if ( $path =~ s/\?(.*)$// ) {
$q = $1;
}
$uri->path($path);
# If there was a query string, we integrate it into the query
# parameter.
if ($q) {
%query = ( %query, split /[&=]/, $q );
}
$query{session_id} = $UserSession{session_id};
# $uri->query_form doesn't handle hash ref values properly
while ( my ( $key, $value ) = each %query ) {
$query{$key} = ref $value eq 'HASH' ? [ %$value ] : $value;
}
$uri->query_form(%query) if %query;
$uri->fragment($fragment) if $fragment; </%init> <% $uri->canonical | n %>\
If you didn’t want to put the session ID in the query string, then you might instead make it part of the URL path. The application could retrieve the session id from incoming requests by using a mod_perl handler during the URL translation stage of request handling.
假如你不想把 session ID 放在请求字串中,那么你可以让它成为 URL 路径的一部分。这样,在对请求中的 URL 进行解析处理的时候,应用程序就可以通过一个 mod_perl 处理器来得到请求中的 session id。
This component provides a programmatic interface to URL generation. Here is an example of how to use it, assuming that you’ve saved it as a component called /url:
这个组件为 URL 的生成提供了一个可编程的接口。下面是一个关于如何使用的例子,我们 假设你已经把保存成了一个名为 /url 的组件:
User Authentication and Authorization
让 Autoflush 生效
Every once in a while, you may have to output a very large component or a file to the client. If you simply let this accumulate in the output buffer, you could use up a lot of memory. Furthermore, the slow response time may make the user think that the site has stalled.
时不时的,你要向客户端输出一个很大的组件或者文件。假如你只是简单的让这些内容堆积在输出缓存中,那么会消耗掉很多内存。更有甚者,由此而引发的服务器反应迟缓也许会让用户觉得这个站点已经挂了。
Here is an example that sends out the contents of a potentially large file without sucking up lots of memory.
下面的例子用来输出一个大文件的内容,但是不会占用太多的内存:
<%args>
$filename
</%args>
<%init>
local *FILE;
open FILE, "< $filename" or die "Cannot open $filename: $!";
$m->autoflush(1);
while (<FILE>) {
$m->print($_);
}
$m->autoflush(0);
</%init>
If each line wasn’t too huge, then you might just flush the buffer every once in a while:
假如要输出的每一行都不是很大,那么你只要在 while 的每次循环中刷新(调用 autoflush )一遍缓存即可:
<%args>
$filename
</%args>
<%init>
local *FILE;
open FILE, "< $filename" or die "Cannot open $filename: $!";
while (<FILE>) {
$m->print($_);
$m->flush_buffer unless $. % 10;
}
$m->flush_buffer;
</%init>
The unless $. % 10 bit makes use of the special Perl variable $., which is the current line number of the file being read. If this number modulo 10 is equal to zero, then we flush the buffer. This means that we flush the buffer every 10 lines. (Replace the number 10 with any desired value.)
上面这段代码中 ‘” unless $. % 10 ” 这一句利用了 Perl 中的特殊变量 $. ,该变量保存被读文件的当前行号。假如当前行号是10的倍数(也就是每隔10行),那么就刷新缓存(你可以把10改成你想要的任何数字)。
User Authentication and Authorization 用户身份验证及授权
One problem that Web sites have to solve over and over again is user authentication and authorization. These two topics are related but not the same, as some might think. Authentication is the process of figuring out if someone is who they say they are, and usually involves checking passwords or keys. Authorization comes after this, when we want to determine whether a particular person is allowed to perform a certain action.
Web 站点必须解决的一个问题就是用户的身份验证及授权,这两个部分是相关的,但并不相同。身份验证指的就是验证某人真实身份的过程,这个过程一般需要你输入口令或者密钥。授权一般发生在通过验证后,也就是此时让你来决定是否允许某人做某事。
There are a number of modules on CPAN that are intended to help do these things under mod_perl. In fact, Apache has separate request-handling phases for both authentication and authorization that mod_perl can handle. It is certainly possible to use these modules with Mason.
CPAN上有很多在 mod_perl 环境下可以帮助你做这些事的模块。实际上,Apache 已经把请 求处理分成了不同的阶段,以此来让 mod_perl 对身份验证和授权都能够进行处理。你当然可以在 Mason 中使用这些模块。
You can also do authentication and authorization using Mason components. Authentication will usually involve some sort of reques t for a login and a password, after which you give the user some sort of token (either in a cookie or a session) that indicates that they have been authenticated. You can then check the validity of this token for each request.
你也可以用 Mason 的组件来实现身份验证和授权。身份验证一般牵涉到获取登陆名和密码的过程,如果登陆名和密码相匹配,那么你可以发给用户 某种表示他们已经通过认证了的标志(可以保存在 cookie 或者 session 中),以后你只要在每次请求中对该标志的合法性进行检查即可。
If you have such a token, then authorization simply consists of checking that the user to whom the token belongs is allowed to perform a given action.
假如你已经获得了这样一个验证标志,那么授权过程就是检查该验证标志的所有人是否被允许做某事。
Using Apache::Auth Cookie?
使用 Apache::Auth Cookie? 模块
The Apache::Auth Cookie? module, available from CPAN, is a module that handles both authentication and authorization via mod_perl and can be easily hooked into Mason. Rather than go through all the details of configuring Apache::Auth Cookie?, which requires various settings in your server config file, let’s just skip all that and show you how you’d make the interface to Mason.
Apache::Auth Cookie? 模块(你可以在 CPAN 上找到它)可以在 mod_perl 环境下处理身份验证和授权,它可以很容易地与 Mason 相结合。让我们跳过 Apache::Auth Cookie? 的详细配置过程(因为它需要对你的服务器配置文件进行很多改动)直接告诉你如何将该模块的接口提供给 Mason。
Apache::Auth Cookie? requires that you create a “login script” that will be executed the first time a browser tries to access a protected area. Calling this a script is actually somewhat misleading since it is really a page rather than a script (though it could be a script that generates a page). Regardless, using a Mason component for your “login script” merely requires that you specify the path to your Mason component for the login script parameter.
Apache::Auth Cookie? 模块需要你创建一个 ”登陆脚本”,这个脚本会在浏览器首次访问被保护区域的时候运行。事实上把这个东东称做脚本有些欠妥,因为它比起脚本来更只像是一个页面文件。呵呵,管它像什么,要在 Mason 的组件中使用该脚本仅仅只需要你在组件中指定它的路径。
We’ll call this “script” Auth Cookie Login Form?.comp:
我们将该”脚本”命名为 Auth Cookie Login Form?.comp :
<html> <head> <title>Mason Book AuthCookie Login Form</title> </head> <body>
Your attempt to access this document was denied (<% $r->prev->subprocess_env("AuthCookieReason") %>). Please enter your username and password.
<form action="/AuthCookieLoginSubmit"> <input type="hidden" name="destination" value="<% $r->prev->uri %>">
| Username: | <input type="text" name="credential_0" size="10" maxlength="10"> |
| Password: | <input type="password" name="credential_1" size="8" maxlength="8"> |
| <input type="submit" value="Continue"> | |
</form>
</body> </html>
This component is modified version of the example login script included with the Apache::Auth Cookie? distribution.
以上这个组件修改自 Apache::Auth Cookie? 模块自带的登陆脚本例子。
The action used for this form, , is configured as part of your Auth Cookie? configuration in your httpd.conf file.
该表单的 action 属性作为 Auth Cookie? 配置选项的一部分也在 httpd.conf 文件中进行了配置。
That’s about all it takes to glue Apache::Auth Cookie? and Mason together. The rest of authentication and authorization is handled by configuring mod_perl to use Apache::Auth Cookie? to protect anything on your site that needs authorization. A very simple configuration might include the following directives:
以上所做的工作就将 Apache::Auth Cookie? 模块和 Mason 黏合在一起了。剩下的身份验证和授权的工作就可以交给 mod_perl 和 Apache::Auth Cookie来做,我们现在只要配置一下? mod_perl 让它利用 Apache::Auth Cookie? 模块来保护你的站点上需要进行身份验证的东西了。以下是一个非常简单的配置,它包含如下的一些指定:
PerlSetVar MasonBookLoginScript /AuthCookieLoginForm.comp
<location /authcookieloginsubmit> AuthType MasonBook::AuthCookieHandler AuthName MasonBook SetHandler perl-script PerlHandler MasonBook::AuthCookieHandler->login </location>
<location /protected> AuthType MasonBook::AuthCookieHandler AuthName MasonBook PerlAuthenHandler MasonBook::AuthCookieHandler->authenticate PerlAuthzHandler MasonBook::AuthCookieHandler->authorize require valid-user </location>
The Mason Book?::Auth Cookie Handler? module would look like this:
以下是 Mason Book?::Auth Cookie Handler? 模块的内容:
package MasonBook::AuthCookieHandler;
use strict;
use base qw(Apache::AuthCookie);
use Digest::SHA1;
my $secret = "You think I'd tell you? Hah!";
sub authen_cred {
my $self = shift;
my $r = shift;
my ($username, $password) = @_;
# implementing _is_valid_user() is out of the scope of this chapter
if ( _is_valid_user($username, $password) ) {
my $session_key =
$username . '::' . Digest::SHA1::sha1_hex( $username, $secret );
return $session_key;
}
}
sub authen_ses_key {
my $self = shift;
my $r = shift;
my $session_key = shift;
my ($username, $mac) = split /::/, $session_key;
if ( Digest::SHA1::sha1_hex( $username, $secret ) eq $mac ) {
return $session_key;
}
}
This provides the minimal interface an Apache::Auth Cookie? subclass needs to provide to get authentication working.
以上的模块为让身份验证能工作起来的 Apache::Auth Cookie? 子类提供了一个微型的接口。
by Dave Rolsky
Doing It My Way (Thanks Frank)
我的方法(感谢 Frand)
But what if you don’t want to use Apache::Auth Cookie?? For example, your site may need to work without using cookies. No doubt this was exactly what Frank Sinatra was thinking about when he sang “My Way,” so let’s do it our way.
假如你不想用 Apache::Auth Cookie? 怎么办?比如说,你的站点打算不使用 cookie。呵呵,你不必迟疑了,就照 Frank Sinatra 的方法来做吧。
First, we will show an example authentication system that only uses Mason and passes the authentication token around via the URL (actually, via a session).
首先,让我们给你展示一个身份验证系统的例子,这个例子只需要你使用 Mason 和通过 URL 来传递验证标记(实际上是利用 session)。
This example assumes that we already have some sort of session system that passes the session id around as part of the URL, as discussed previously.
在这个例子中,假设我们已经拥有了先前讨论过的某种将 session id 作为 URL 的一部分进行传递的 session 系统。
We start with a quick login form. We will call this component login_form.html:
我们从建立一个快速登陆表单开始吧,这个组件将被命名为 login_form.html:
<%args> $username => $password => $redirect_to => @errors => () </%args> <html> <head> <title>Mason Book Login</title> </head>
<body>
% if (@errors) {
[编辑] Errors
% foreach (@errors) {
<% $_ | h %>
% }
% }
<form action="login_submit.html"> <input type="hidden" name="redirect_to" value="<% $redirect_to %>">
| Login: | <input type="text" name="username" value="<% $username %>"> |
| Password: | <input type="password" name="password" value="<% $password %>"> |
| <input type="submit" value="Login"> | |
</form>
</body> </html>
This form uses some of the same techniques we show in Chapter 8 (“Building a Mason Site”) to pre-populate the form and to handle errors.
这个表单使用了一些我们曾在第八章(建立一个 Mason 站点)中使用过的技术来预置表单及处理异常。
Now let’s make the component that handles the form submission. This component, called login_submit.html, will check the username and password and, if they are valid, place an authentication token into the user’s session:
现在让我们使这个组件能够对提交表单进行处理。这个名为 login_submit.html 的组件将检查用户名和密码,如果他们合法,那么就保存一个身份验证标志到用户的 session 中:
<%args>
$username
$password
$redirect_to
</%args>
<%init>
if (my @errors = check_login($username, $password) {
$m->comp( 'redirect.mas',
path => 'login_form.html',
query => { errors => \@errors,
username => $username,
password => $password,
redirect_to => $redirect_to } );
}
$MasonBook::Session{username} = $username;
$MasonBook::Session{token} =
Digest::SHA1::sha1_hex( 'My secret phrase', $username );
$m->comp( 'redirect.mas',
path => $redirect_to );
</%init>
This component simply checks (via magic hand waving) that the username and password are valid and if they are, it generates an authentication token, which is added to the user’s session. To generate this token, we take the username, which is also in the session, and combine it with a secret phrase. We then generate a MAC from those two things.
以上这个组件简单的检查了用户名和密码是否合法,如果合法,那么就生成一个验证标志添加到用户的 session 中。这里的这个验证标志是由用户名和一个关键字串加密后生成的(这个标志也称做介质访问控制令牌)。
The authentication and authorization check looks like this:
身份验证和授权的检查的过程看起来像这样:
if ( $MasonBook::Session{token} ) {
if ( $MasonBook::Session{token} eq
Digest::SHA1::sha1_hex( 'My secret phrase',
$MasonBook::Session{username} ) {
# R<... valid login, do something here>
} else {
# R<... someone is trying to be sneaky!>
}
} else { # no token
my $wanted_page = $r->uri;
# Append query string if we have one.
$wanted_page .= '?' . $r->args if $r->args;
$m->comp( 'redirect.mas',
path => '/login/login_form.html',
query => { redirect_to => $wanted_page } );
}
We could put all the pages that require authorization in a single directory tree and have a top-level autohandler in that tree do the check. If there is no token to check, then we redirect the browser to the login page, and after a successful login they’ll return, assuming that they submit valid login credentials.
我们可以把所有需要通过验证才能访问的页面放在一个单独的目录树中,然后弄一个顶级的处理器(此处理器可不是 CPU,是用来处理身份验证的模块) 来对验证进行处理。如果发现用户的请求中不包含验证标志(即未成功登陆),那么就直接把他转到登陆页面,如果他登陆成功,那么就让他返回他需要读取的页面。
Access Controls With Attributes
基于属性的访问控制
The components we saw previously assumed that there are only two access levels, unauthenticated and authenticated. A more complicated version of this code might involve checking that the user has a certain access level or role.
前面提到的那个组件仅仅假设只有两种访问级别,未通过验证和已通过验证。稍微复杂一点的情况就涉及到要明确地对用户拥有什么级别的权限或者扮演什么角色进行检查。
In that case, we’d first check that we had a valid authentication token and then go on to check that the user actually had the appropriate access rights. This is simply an extra step in the authorization process.
这种情况下,我们首先要检查用户是否拥有合法的身份验证标志,然后在对他实际上拥有什么样的权限进行检查。后面这个过程只是在身份验证过程中的一个简单的额外步骤。
Using attributes, we can easily define access controls for different portions of our site. Let’s assume that we have four access levels, “Guest,” “User,” “Editor” and “Admin.” Most of the site is public, and viewable by anyone. Some parts of the site require a valid login, while some require a higher level of privilege.
利用属性,我们可以容易地对站点的不同部分的权限进行定义。假设我们有四个权限,”Guest,” “User,” “Editor” 和 “Admin.” 站点的大部分内容是公开的,任何人都可以访问。其中一部分需要登陆,一部分需要更高的权限。
We implement our access check in our top-level autohandler, , from which all other components must inherit in order for the access control code to be effective.
我们用一个顶级的处理器来执行权限检查,这样的话就保证了所有其他的组件都在检查的范围内,保证了权限控制代码的有效性。
<%init> my $user = get_user(); # again, hand waving
my $required_access = $m->base_comp->attr('required_access');
unless ( $user->has_access_level($required_access) ) {
# R<... do something like send them to another page>
}
$m->call_next; </%init> <%attr> required_access => 'Guest' </%attr>
It is crucial that we set a default access level in this autohandler. By doing this, we are saying that by default, all components are accessible by all people, since every visitor will have at least “Guest” access.
在以上这个处理器中,我们指定了一个默认的访问权限 “Guest”,这点是很重要的,这样只要有有 “Guest” 权限(默认一般就给这个权限)就可以访问其他的组件。
We can override this default elsewhere. For example, in a component called /admin/autohandler, we might have:
我们可以改掉这个默认的权限值。比如在 /admin/autohandler 这个组件中,我们可能会写:
<%attr> required_access => 'Admin' </%attr>
As long as all the components in the directory inherit from the component and don’t override the required_access attribute, we have effectively limited that directory (and its subdirectories) to administration users only. If we, for some reason, had an individual component in the directory that we wanted editors to be able to see, we could simply set the “required_access” attribute for that component to “Editor.”
只要目录中继承于该组件的下一级组件没有覆盖 required_access 属性,那么我们就有效的限制了该目录(及其子目录)只对管理员开放。假如因为某些原因这个目录中的某个组件要对编辑开放,那么只要在该组件中设置 “required_access” 为 “Editor.”(这个属性名不是死的,是可以自己预先定义的)
Managing DBI Connections
管理 DBI 连接
Not infrequently, we see people on the Mason users list asking questions about how to handle caching DBI connections.
在 Mason 的用户邮件列表中我们发现人们经常提问如何处理持续性的 DBI 连接。
Our recipe for this is really simple:
我们的答案很简单:
use Apache::DBI
Rather than reinventing the wheel, use Apache::DBI, which provides the following features:
使用 Apache::DBI 模块就可以避免要自己重新发明轮子了呵呵,它提供如下的特性:
It is completely transparent to use. Once you’ve used it, you simply call DBI->connect() as always and Apache::DBI gives you an existing handle if one is available.
你可以完全透明的使用它,当你要用到它的时候,只要和平常一样简单的调用 DBI->connect() 这个 Apache::DBI 模块就会从可用的连接句柄中提供一个给你。
It makes sure that the handle is live, so that if your RDBMS goes down and then back up, your connections still work just fine.
它确保了每个连接句柄都处于活动的状态,这样就算你的关系数据库被关闭了也没关系,当数据库重新运行的时候这些 DBI 连接还是能很好的工作。
It does not cache handles made before Apache forks, as many DBI drivers do not support using a handle after a fork.
它在 Apache 分出子进程之后才开始建立持续性的连接句柄,而很多其他的 DBI 驱动不支持这个。
Generating Config Files
生成配置文件
Config files are a good candidate for generation by Mason. For example, your production and staging Web server config files might differ in only a few areas. Changes to one usually will need to be propagated to another. This is especially true with mod_perl, where Web server configuration can basically be part of a Web-based application.
Mason 还有一个不错的用途—-生成配置文件。举个例子,你的产品和 Web 服务器配置文件在小部分地方有不同。那么改变其中一个的配置就需要对另一个的配置也做相应的调整。如果你在使用 mod_perl,那么 Web 服务器的配置工作就可以交给基于 Web 的应用程序来做了。
On top of this, you may decide to set up a per-developer environment, either by having each developer run the necessary software on their own machine, or by starting Web servers on many different ports on a single development server. In this scenario, a template-driven config file generator becomes even more appealing.
在这之前,你得预先建立好相应的开发环境—-无论是让每个开发者都运行所需的软件还是在一台开发服务器上让 Web 服务器运行在不同的端口。在这种情形下,一个模板驱动的配置文件生成器就显得很有用了。
Here’s a simple script to drive this generation. This script assumes that all the processes are running on one shared development machine.
这里有个简单的脚本来成配置文件。该脚本假设所有的处理都是运行在一台共享的用来开发的机器上。
#!/usr/bin/perl -w
use strict;
use Cwd; use File::Spec; use HTML::Mason; use User::pwent;
my $comp_root =
File::Spec->rel2abs( File::Spec->catfile( cwd(), 'config' ) );
my $output;
my $interp =
HTML::Mason::Interp->new( comp_root => $comp_root,
out_method => \$output,
);
my $user = getpwuid($<);
$interp->exec( '/httpd.conf.mas', user => $user );
my $file = File::Spec->catfile( $user->dir, 'etc', 'httpd.conf' ); open FILE, ">$file" or die "Cannot open $file: $!"; print FILE $output; close FILE;
A httpd.conf.mas component might look like this:
这个 httpd.conf.mas 组件也许看起来是这样的:
ServerRoot <% $user->dir %>
PidFile <% File::Spec->catfile( $user->dir, 'logs', 'httpd.pid' ) %>
LockFile <% File::Spec->catfile( $user->dir, 'logs', 'httpd.lock' ) %>
Port <% $user->uid + 5000 %>
# loads Apache modules, defines content type handling, etc. <& standard_apache_config.mas &>
<perl> use lib <% File::Spec->catfile( $user->dir, 'project', 'lib' ) %>; </perl>
DocumentRoot <% File::Spec->catfile( $user->dir, 'project', 'htdocs' ) %>
PerlSetVar MasonCompRoot <% File::Spec->catfile( $user->dir, 'project', 'htdocs' ) %> PerlSetVar MasonDataDir <% File::Spec->catfile( $user->dir, 'mason' ) %>
PerlModule HTML::Mason::ApacheHandler
<filesmatch "\.html$"> SetHandler perl-script PerlHandler HTML::Mason::ApacheHandler </filesmatch>
<%args> $user </%args>
This points the server’s document root to the developer’s working directory. Similarly, it adds the project/lib directory to Perl’s @INC via use lib so that the user’s working copy of the project’s modules are seen first. The server will listen on a port equal to the user’s user ID, plus 5,000.
在这个组件中,服务器的文档根目录指向了开发着的工作目录。类似的,他还用 use lib 把 project/lib 目录加到了 Perl 的 @INC 中,这样用户的工作副本中某个项目的模块对于 Perl 来说就是可见的了。服务器监听的端口和用户的 ID+5000 后的数值一样。
Obviously, this is an incomplete example. It doesn’t specify where logs will go, or other necessary config items. It also doesn’t handle generating the config file for a server intended to be run by the root user on a standard port.
很显然,这只是个不完全的例子。它没有指定日志文件或者其他需要的配置项。它也无法生成由 root 用户运行在标准端口的服务器的配置文件。
If You Want More …
假如你想了解更多 …
These recipes were adapted from Chapter 11, “Recipes,” of Embedding Perl in HTML With Mason. And, of course, the book contains a lot more than just recipes. If you’re interested in learning more about Mason, the book is a great place to start.
这些处方是由 《 Embedding Perl in HTML With Mason 》这本书的第十一章:”Recipes,” 改写而来。当然,这本书还包含了很多其他内容。假如你对学习 Mason 感兴趣,那么这本书将是一个很好的开端。
Also, don’t forget to check out the Mason HQ site at www.masonhq.com/, which contains online documentation, user-contributed code and docs, and links to the Mason users mailing list, which is another great resource for developers using Mason.
也别忘了查看 Mason HQ (www.masonhq.com)这个网站,站内包含了在线文档,用户贡献的代码和文档以及指向 Mason 用户邮件列表的链接(这个列表对于 Mason 的开发者来说是一个很有用的资源)。
