Amon2で$c->render()を自動で呼ぶようにしてみた。
2014年7月22日
以前書いた
Amon2で$c->render()をwrapした$c->auto_render()を作ってみた(改)
ですが、もう$c->auto_render()を毎回書くのがアレなんで、書かなくて良いようにしてみた。
add_triggerを駆使すれば行けるかと思ったんですが、タイミング的に厳しいので、MyApp::Webを弄る事にします。
defaultのdispatchは、コントローラの結果をそのまま返しているんで、そのまま返さず、dispatchで、$c->renderを呼ぶ感じに変更。
package MyApp::Web;
# dispatcher
use MyApp::Web::Dispatcher;
sub dispatch {
my $self = shift;
my $res = MyApp::Web::Dispatcher->dispatch($self);
# エラー or リダイレクト(すでにレスポンスを持っている)
return $res if ( ref $res eq 'Amon2::Web::Response');
# jsonを返す場合(Amon2::Plugin::Web::JSON)
if ( exists $self->stash->{'json'} ) {
return $self->render_json( $self->stash->{'json'} );
}
# htmlを返す場合
my $template = '';
if ( $self->stash->{'template'} ) {
# templateを手動で指定
$template = $self->stash->{'template'};
}
else {
# templateを自動セット
my $env = $self->request->env;
my ($method) = MyApp::Web::Dispatcher->router->match( $env->{REQUEST_METHOD}, $env->{PATH_INFO} );
my $path = $method->{class} .'/' . $method->{method};
$path =~ s/MyApp::Web::C:://;
$path =~ s/\:\:/\//g;
$path = lc $path;
$template = $path . '.tt';
}
return ( $self->render($template, $self->stash ) or die "response is not generated" );
}
dispatcherがコントローラを呼んだ時に、通常はコントローラが$c->render()の結果を返しますが、コントローラは処理をして$c->stashに入れるだけで、dispatchがrender結果を返すようにします。
$c->stash->{template}が指定されていない場合は、コントローラのpathからtemplateのpathを決定。
Root.pmのsub index{} -> root/index.tt
User.pmのsub info{} -> user/info.tt
みたいな感じ。
Dispatcher->router->match()は、Dispatcher->dispatch()の中でも呼ばれてるのでなんか無駄な感じがしないでもないですが・・。
で、コントローラ。
sub index {
my ( $class, $c ) = @_;
# stashに入れるだけ
$c->stash->{'user'} = { id => 1, name => 'hoge' };
}
sub ajax {
my ( $class, $c ) = @_;
# jsonを返す場合。$c->stash->{'json'}に入れる。
$c->stash->{'json'} = { id => [1,2,3] };
}
こんな感じで、$c->renderを呼ばなくても良くなりました。
追記(2014/12/12)
MyApp::Webで、templateを自動セットの箇所に
$path =~ s/\:\:/\//g;
の1行を追加しました(上記ソースは修正済み)
これで、Classが深くなった場合でも、
MyApp::Web::C::User::Bbs->index(); # -> user/bbs/index.tt
合わせてtemplateも深くなります。