今日はちょっと Catalyst で forward してみますー。forwardメソッドを呼び出して返り値を受け取るときにうまく値が受け取れない。。。そんな経験をしたことはありませんか?(僕はあります...orz
【追記ここから】
id:charsbarさんよりブクマコメントいただきました。
な、なるほどΣ(・ω・ノ)ノ ・・そんなわけで、以下の内容は forward の返り値ってこんな風に処理されてるのか〜くらいの参考情報として見て頂けるといいかなと思います。
【追記ここまで】
というわけで、今回はテストを兼ねて、色々な値を返すメソッドに forward してみました。
# Controller
どうですか?予想通りの結果だったでしょうか??
予想通りだった人はこの下の説明を読む必要はありません。予想外の結果だった人は、この下の説明を読んだら少しわかるかもしれません。ではいきますよー!!
まずは forwardメソッドの実装を見てみます。forward が実行されるときには、以下のようにdispatchメソッドが呼ばれます。
Catalyst/Dispatcher.pm
さらに、dispatchメソッドの中ではexecuteメソッドが呼ばれます。
Catalyst/Action.pm
executeメソッドは Catalyst.pm に定義されています。このメソッドでは(他にも色々やってますが)、eval文のところで forwardメソッドの返り値を受け取って、その値が真だったらその値を、偽だったら 0 を $c->state にセットします。そして、最終的には呼び出し元にこの $c->stash の値が返されます。
Catalyst.pm
そのため、返り値が undef や ''(空文字)の場合には 0 が返され、リストや配列、ハッシュの場合にはスカラコンテキストで評価された値が返されます。スカラコンテキストで評価した場合、リストは最後の要素が、配列は個数が、ハッシュの場合は特殊な値が?返されるので注意です。どちらにせよ、本当に返したい値は返せていません。この部分の実装がなんかイマイチな気が・・。(^^;
実際にこれらの値をやりとりしたいのであれば、多少面倒ですがリファレンスにするか、もしくはforwardメソッド内で値を stash に入れてしまうか、ですね。もっとスマートな解決策があればいいんですが><
【追記ここから】
id:charsbarさんよりブクマコメントいただきました。
forwardの返り値はコントローラの処理を続行するかどうかの判別に使うものなので(C::Controller.pmの_DISPATCHなど参照)、有意なデータが要るならモデルから取るなりstashに入れておくなりした方がよろしいかと
な、なるほどΣ(・ω・ノ)ノ ・・そんなわけで、以下の内容は forward の返り値ってこんな風に処理されてるのか〜くらいの参考情報として見て頂けるといいかなと思います。
【追記ここまで】
というわけで、今回はテストを兼ねて、色々な値を返すメソッドに forward してみました。
# Controller
sub hoge : Local {
my ($self, $c) = @_;
my $value_a = $c->forward('method_a');
my $value_b = $c->forward('method_b');
my $value_c = $c->forward('method_c');
my $value_d = $c->forward('method_d');
my $value_e = $c->forward('method_e');
my $value_f = $c->forward('method_f');
use Data::Dumper;
warn Dumper $value_a; # 0
warn Dumper $value_b; # 0
warn Dumper $value_c; # foo
warn Dumper $value_d; # 1
warn Dumper $value_e; # 3
warn Dumper $value_f; # 2/8
$c->res->body(1);
}
sub method_a : Private { # undef を返す場合
my ($self, $c) = @_;
return undef;
}
sub method_b : Private { # ''(空文字)を返す場合
my ($self, $c) = @_;
return '';
}
sub method_c : Private { # リストを返す場合
my ($self, $c) = @_;
return ( qw/hoge fuga foo/ );
}
sub method_d : Private { # リストを返す場合_2
my ($self, $c) = @_;
return ( foo => 1, bar => 1 );
}
sub method_e : Private { # 配列を返す場合
my ($self, $c) = @_;
my @array = ( qw/hoge fuga foo/ );
return @array;
}
sub method_f : Private { # ハッシュを返す場合
my ($self, $c) = @_;
my %hash = ( foo => 1, bar => 1 );
return %hash;
}
どうですか?予想通りの結果だったでしょうか??
予想通りだった人はこの下の説明を読む必要はありません。予想外の結果だった人は、この下の説明を読んだら少しわかるかもしれません。ではいきますよー!!
まずは forwardメソッドの実装を見てみます。forward が実行されるときには、以下のようにdispatchメソッドが呼ばれます。
Catalyst/Dispatcher.pm
sub forward {
my $self = shift;
my ( $c, $command ) = @_;
my ( $action, $args, $captures ) = $self->_command2action(@_);
unless ($action) {
my $error =
qq/Couldn't forward to command "$command": /
. qq/Invalid action or component./;
$c->error($error);
$c->log->debug($error) if $c->debug;
return 0;
}
local $c->request->{arguments} = $args;
$action->dispatch( $c ); # ココ
return $c->state;
}
さらに、dispatchメソッドの中ではexecuteメソッドが呼ばれます。
Catalyst/Action.pm
sub dispatch { # Execute ourselves against a context
my ( $self, $c ) = @_;
return $c->execute( $self->class, $self ); # ココ
}
executeメソッドは Catalyst.pm に定義されています。このメソッドでは(他にも色々やってますが)、eval文のところで forwardメソッドの返り値を受け取って、その値が真だったらその値を、偽だったら 0 を $c->state にセットします。そして、最終的には呼び出し元にこの $c->stash の値が返されます。
Catalyst.pm
sub execute {
my ( $c, $class, $code ) = @_;
#..snip..
# &$code( $class, $c, @{ $c->req->args } ) でforwardメソッドを実行
eval { $c->state( &$code( $class, $c, @{ $c->req->args } ) || 0 ) };
# ..snip..
return $c->state;
}
そのため、返り値が undef や ''(空文字)の場合には 0 が返され、リストや配列、ハッシュの場合にはスカラコンテキストで評価された値が返されます。スカラコンテキストで評価した場合、リストは最後の要素が、配列は個数が、ハッシュの場合は特殊な値が?返されるので注意です。どちらにせよ、本当に返したい値は返せていません。この部分の実装がなんかイマイチな気が・・。(^^;
実際にこれらの値をやりとりしたいのであれば、多少面倒ですがリファレンスにするか、もしくはforwardメソッド内で値を stash に入れてしまうか、ですね。もっとスマートな解決策があればいいんですが><
