Amon2のDispatcher をカスタマイズしてCatalystライクなコントローラーを実装する
最近のWAFは、
get '/' => 'Foo#bar';
みたいなのをつらつらと書いていくのが主流みたいなんですが、コントローラに書いてある方が直観的だと思うのはCatalyst病に感染してるからでしょうか。
PerlのAttributeが微妙ってのもあって、CatalystライクなDispatchはあんまよろしくないのかもしれません。
個人的にはCatalystで慣れたってのもあるんで、Amon2でもCatalystライクなDispatchで書きたいなーと思う次第です。
Dispatcher
まずはDispatcherをすっきりさせます。
package MyApp::Web::Dispatcher; use strict; use warnings; use utf8; use Amon2::Web::Dispatcher::RouterBoom; base 'MyApp::Web::C'; use Module::Find; useall 'MyApp::Web::C'; 1;
すっきりしましたw
コントローラの親クラス
コントローラの親になるMyApp::Web::Attributeってのを作りました。
今回のキモな部分です。
Sub::Attributeは事前にインストール済みです。
package MyApp::Web::Attribute; use strict; use warnings; use utf8; use Sub::Attribute; # GET sub _get : ATTR_SUB { my($class, $sym_ref, $code_ref, $attr_name, $attr_data) = @_; my $method = *$sym_ref{NAME}; // コントローラのメソッド名(index) my $call = (caller 4)[0]; // 呼び元(MyApp::Web::Dispatcher)を取得。 # $attr_data = '/'; $class = 'MyApp::Web::C::Root'; $method = 'index' $call->router->add(['GET','HEAD'], $attr_data, { class => $class, method => $method } ); } # POST sub _post : ATTR_SUB { my($class, $sym_ref, $code_ref, $attr_name, $attr_data) = @_; my $method = *$sym_ref{NAME}; my $call = (caller 4)[0]; $call->router->add('POST', $attr_data, { class => $class, method => $method } ); } # ANY sub _any : ATTR_SUB { my($class, $sym_ref, $code_ref, $attr_name, $attr_data) = @_; my $method = *$sym_ref{NAME}; my $call = (caller 4)[0]; $call->router->add(undef, $attr_data, { class => $class, method => $method } ); } # DELETE sub _delete : ATTR_SUB { my($class, $sym_ref, $code_ref, $attr_name, $attr_data) = @_; my $method = *$sym_ref{NAME}; my $call = (caller 4)[0]; $call->router->add('DELETE', $attr_data, { class => $class, method => $method } ); } 1;
やってる事は、Amon2::Web::Dispatcher::RouterBoomのimportで、methodを追加してる部分をそれぞれに分けた感じです。
Sub::Attributeを使うと
sub hogehoge : ATTR_SUB {}
で、作られたメソッドがAttributeとして呼ぶことができるようになります。
-> sub foo : hogehoge {}
今回は、_get,_post,_any,_deleteの4つをAttributeとして追加しました。
下記のようなCODEを直接呼ぶやつは考慮してません。
get '/' => sub { my ($c) = @_; };
MyApp::Web::Dispatcher自体は、今までどおりに使えるので、書けば動くと思いますが、、
コントローラ
package MyApp::Web::C::Root; use strict; use warnings; use utf8; # 上で作ったクラスを継承 use parent 'MyApp::Web::Attribute'; sub index : _get(/) { my ( $class, $c ) = @_; # ... } sub user : _get(/user/{id}) { my ( $class, $c, $args ) = @_; # ... } 1;
やりたかった事は中途半端に達成できた。。
コントローラでMyApp::Web::Attributeを継承する必要があるのと、毎回pathを指定しないといけないってのがアレかなー・・。
結局、MyApp::Web::Dispatcherの内容をコントローラに移しただけっていう(ry
追記
Dispatchをコントローラに移した結果、pathの一覧が欲しくなった、、、
(元も子もない?w)
一番上ですっきりさせたMyApp::Web::Dispatcherを下記に。
package MyApp::Web::Dispatcher; use strict; use warnings; use utf8; use Amon2::Web::Dispatcher::RouterBoom; base 'MyApp::Web::C'; use Module::Find; useall 'MyApp::Web::C'; # 追加 -- use Data::Dumper; $c->log->debug( Dumper __PACKAGE__->router->routes ); # -- 1;
こんな感じで、今addされてるpathの一覧が取れます。
開発時にしか使わないけど。。