
ちょっとSoozyにもChainedっぽい仕組みを取り入れたくてCatalystのAttributesのコードを追いかけてました。
んで、とりあえず名前のエスケープと文字の長さの制限を加えてみたので、軽くメモしてみます。
AttaributeってのはControllerの中の
sub default : Private { }やsub root : Chaind('/') ...のような:の右側にある奴の事です。主にattributesまわりの処理に偏って書いているので本来の役割とは違う書き方をしてるかもしれません。
どのコードがどこに在る物なのかとかは空気呼んで把握して下さい。
今回のAttributes探索では、Catalyst->setupの中からCatalyst->setup_componentsを呼び出している所から始まります。
モジュール名で想像出来る通り、Catalyst::AttrContainerがCatalystでのAttributes実装に関しての重要な役割を持っています。
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 ();
}Perlがソースコードをコンパイルする時にattributesを見つけると、attributesが記述されてるメソッドのコードリファレンスとattributesを引数にしてMODIFY_CODE_ATTRIBUTESメソッドを呼び出します。これでControllerのrequire時のattributes処理は終わりです。
まずは
my @classes =デファオルトのCatalyst::DispatchTypeモジュールをloadします。
$self->_load_dispatch_types( @{ $self->preload_dispatch_types } );
@{ $self->registered_dispatch_types }{@classes} = (1) x @classes;
そして、先ほどのsetup_componentsでloadしたControllerとかのモジュールそれぞれにregister_actionsメソッドがあれば、register_actionsメソッドを呼び出す処理をしていきます。
foreach my $comp ( values %{ $c->components } ) {
$comp->register_actions($c) if $comp->can('register_actions');
}
このメソッドの出だしは
sub register_actions {
my ( $self, $c ) = @_;
my $class = ref $self || $self;
my $namespace = $self->action_namespace($c);
my %methods;
$methods{ $self->can($_) } = $_
for @{ Class::Inspector->methods($class) || [] };となっており、コンポーネント中の全メソッドを取り出してあります。# Advanced inheritance support for plugins and the likeと、コンポーネントをロードした時に保存していたattributesの情報を取り出しています。
my @action_cache;
{
no strict 'refs';
for my $isa ( @{"$class\::ISA"}, $class ) {
push @action_cache, @{ $isa->_action_cache }
if $isa->can('_action_cache');
}
}
foreach my $cache (@action_cache) {
my $code = $cache->[0];
my $method = delete $methods{$code}; # avoid dupe registers
next unless $method;
my $attrs = $self->_parse_attrs( $c, $method, @{ $cache->[1] } );と、予め保存しておいたattributesの定義元メソッドが実際に存在するかチェックしてから_parse_attrsメソッドに飛びます。
まずはChained('/')のような文字列を解析します
if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )これで$keyにChainedが、$valueに/が入りました。
{if ( defined $value ) {
( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
}
push( @{ $raw_attributes{$key} }, $value );
}
そして、独自Attributesの処理です
my $meth = "_parse_${key}_attr";
if ( $self->can($meth) ) {
( $key, $value ) = $self->$meth( $c, $name, $value );
}
push( @{ $final_attributes{$key} }, $value );よくあるケースなのか分からないですが、Controllerにsub _parse_OriginalAttr_attr { PathPart => shift->path_prefix }な感じでメソッド作っておいてからsub root: Chained('/') OriginalAttr CaptureArgs(0) {とやると、動的にPathPartに値が入れられるみたいな用途で使ってるんだか分からないけど、その独自Attributesを処理してくれます。
my $priv = 0;$action->attributesは
foreach my $key ( keys %{ $action->attributes } ) {
next if $key eq 'Private';
my $class = "Catalyst::DispatchType::$key";
unless ( $registered->{$class} ) {
eval "require $class";
push( @{ $self->dispatch_types }, $class->new ) unless $@;
$registered->{$class} = 1;
}
}
sub root: Chained('/') PathPart('') CaptureArgs(0) {ってメソッドの場合だと$action->attributes({ Chained => ['/'], PathPart => [], CaptureArgs => [0] })と同等の内容が入っています。最後に
foreach my $type ( @{ $self->dispatch_types } ) {
$type->register( $c, $action );
}ロードされてるCatalyst::DispatchType::*モジュール達にメソッドとattributesの対応を投げつけ、各DispatchTypeが使うattributesがあったらDispatchType側でハンドリングしてもらうように頼んでおしまい。
その後にも初期化処理は色々あるけど、長くなりすぎて疲れるのでおわり。
実際使う時にはCatalyst::Dispatcher->prepare_actionとかCatalyst::Dispatcher->dispatchとかCatalyst::Dispatcher->forwardとか追いかければいいのかな。
と、Catalystでアプリを作ったのが1回だけしかない人間の便所の裏のチラシ
Posted by Yappo at 2007年03月14日 23:23 | TrackBack | Perl