開発中のシステムではArkという新進気鋭のウェブアプリケーションフレームワーク(WAF)を使わせていただいています。
Arkの良さは色々ありますが、一言で説明するなら「軽量版Catalyst」といったところでしょうか。 「将来的にはFastCGI環境かmod_perl環境で動かしたいが、最初はCGIで公開したい」といった場合(まさに私の今回の用途にぴったりです)でも、HTTP::Engineのおかげで基本的にはそのまま移行できます。軽くてCGIとの相性がよいWAFならば素敵なものが色々ありますが、ControllerがCatalyst互換なのはなかなか嬉しいですね。
さて、勇んでAmikeco::Web::Controller::Worldあたりのコントローラーを書いていて気が付いたのですが、detachでChainedアクションの連鎖が止まらないことに気が付きました。detachで飛んだ先のアクションは勿論ちゃんと処理してくれて、detachで飛ばす元のアクション内の残りの処理はきちんと省くのですが、肝心の次のアクションが処理されてしまうのです。
不具合の詳細とその極めて単純な対策を、以下に記してみます。
不具合の詳細
変に日本語で説明しようとするから分かりづらいのであって、擬似コードをご覧いただければ一目瞭然です。疑似といっても開発中のものから説明に不要な処理を省いただけですけれども。
sub world
:Chained('/')
:PathPart('world')
:CaptureArgs(0)
{
my ($self, $c) = @_;
$c->stash->{world_model} = $c->model('World');
}
sub fetch_world
:Chained('world')
:PathPart('')
:CaptureArgs(1)
{
my ($self, $c, $world_name) = @_;
my $world;
try eval {
$world = $c->stash->{world_model}->fetch_world($world_name);
};
if (catch my $exception) {
$c->detach('world_not_found', $exception);
}
# 地点A
$c->stash->{world} = $world;
}
sub read_world
:Chained('fetch_world')
:PathPart('')
:Args(0)
{
# 地点B
my ($self, $c) = @_;
$c->stash->{mode} = 'read';
$c->view('MT')->template('world/read');
}
といった書き方をすると、ディスパッチテーブルは以下のようになります。
/world/*/ : /world (0) -> /fetch_world (1) => /read_world
例えば/world/bahamut/とした場合はBahamutサーバーの情報を表示できることになります。
本当は/world/でlist_all_worldsなるアクションを呼ぶのですが、説明とは無関係のため省いています。
ここで、例えばありもしないワールドである/world/foobar/とした場合は、world_not_foundアクションを実行しつつ、他の処理は省いて欲しいですね。ところが、fetch_worldアクションの地点Aの処理が省かれるだけで、実は後続のチェーンドアクションであるread_world(地点B)も実行されてしまうのです。これが、Chainedアクションの連鎖が止まらない問題の意味するところです。
解決策:Arkを最新版にする
さて、説明は長いのですがその対策は簡単で、Arkを最新版にすることです。公式にはversion 0.1rcが公開されています($VERSIONは0.001000_001です)が、github上の0.001000_002では直っています。
余談
Arkの素晴らしさや、Amikecoで考えていることなど、色々ありはしますが、それはおいおい記していこうと思います。
What is Amikeco?
なお、AmikecoというのはいわゆるMyApp名です。名前の由来は、頭字語の名前がしばしばそうであるように、まず通りの良い名前を決めてから、各アルファベットに該当する単語を辞書とにらめっこして探しているので、実はまだなかったりします。
コメントする