01 December 2005
This post may be outdated due to it was written on 2005. The links may be broken. The code may be not working anymore. Leave comments if needed.
用过 Catalyst 的人都知道 Catalyst 的 action 都要用 : Global, : Local, : Path(), : Regex 等等来 register.
这种 sub subroutine : attributes 虽然在 perldoc attributes 里说还是试验性的,但是看起来不会再有大的改变。
不过代码写起来却不是很舒服。我不打算翻译 attributes, 而是写几个简单的例子来试验试验其在 sub 上的功能。

一个最最简单的例子是:

use strict;

test();

sub MODIFY_CODE_ATTRIBUTES {
my ($pkg, $ref, @attrs) = @_;

print "$pkg\n";
print "attrs: $_\n" foreach @attrs;

return;
}

sub test : attribute {
print "test";
}

1;

输出的结果:
main
attrs: attribute
test
    简单的解释一下:
  • MODIFY_CODE_ATTRIBUTES 根据 perldoc attributes 的介绍,将对每一个拥有 attributes 属性后缀的子程序都执行一次。而且对于子程序的执行时间是在 complie 编译时执行。所以 main attrs: attribute 会出现在 test 前面。
  • my ($pkg, $ref, @attrs) = @_; 这三个参数分别对应 包名(这里是 main),$ref 为拥有后缀的子程序的引用(这里的 $ref 差不多是 \&test),而 @attrs 就是那属性后缀名。为什么这里是用 @attrs 是因为属性后缀可以是多个。比如 sub test : attribute kisssherry { 的话我们的输出就会变为
    main
    attrs: attribute
    attrs: kisssherry
    test
    @attrs 接收了 attribute 和 kisssherry
  • 另外得注意的是返回。return 1 是绝对不允许的,return 返回的必须是一个属性列表,作用是配合类继承。
这个返回值怎么配合类继承我也不太明白。一般的做法就是类似 Catalyst 的做法。弄一个 Package 专门用于解析 attributes, 然后将它们保存下来:
package Catalyst::AttrContainer;

use strict;
use base qw/Class::Data::Inheritable Class::Accessor::Fast/;

use Catalyst::Exception;
use NEXT;

__PACKAGE__->mk_classdata($_) for qw/_attr_cache _action_cache/;
__PACKAGE__->_attr_cache( {} );
__PACKAGE__->_action_cache( [] );

# note - see attributes(3pm)
sub MODIFY_CODE_ATTRIBUTES {
my ( $class, $code, @attrs ) = @_;
$class->_attr_cache( { %{ $class->_attr_cache }, $code => [@attrs] } );
$class->_action_cache(
[ @{ $class->_action_cache }, [ $code, [@attrs] ] ] );
return ();
}

sub FETCH_CODE_ATTRIBUTES { $_[0]->_attr_cache->{ $_[1] } || () }

唯一使用了这个类的 Catalyst 模块是 Catalyst::Base;
package Catalyst::Base;

use strict;
use base qw/Catalyst::Component Catalyst::AttrContainer Class::Accessor::Fast/;

...

这样诸位应当知道为什么我们每在一个 Controller 里都要写上 use base 'Catalyst::Base'; 了吧。它的目的就是解析类如 Global/Path/Local 等 attributes. 没写 use base 'Catalyst::Base'; 的话,action 里根本就不会出现你写的 Controller.
而即使现在的 Catalyst 将 Catalyst::Base 拆分为 Catalyst::Controller Catalyst::Model Catalyst::View 后, Catalyst::Controller 目前的代码也只是:
package Catalyst::Controller;

use strict;
use base qw/Catalyst::Base/;

1;

另外有个 Attribute::Handlers 用于专门处理 attributes, 其实也差不多,看看 perldoc 就 OK 了。


blog comments powered by Disqus