またはまった。default アクションでの args の扱い

sub default : Private {
    my ( $self, $c ) = @_;
    shift @{ $c->req->args };
    ...
}

というmizzyさんのhackを施して過ごして来ましたが、アクションの階層を深くした時に意図しない結果になってしまいました。
Controller::Fooアクションで/foo/1にアクセスした時、defaultアクションにおけるargsは['foo', '1']となるのでshiftすればいいのですが、
Controller::Foo::Barアクションで/foo/bar/1にアクセスした時、defaultアクションにおけるargsは['foo', 'bar', '1']となるのでshift一回では足りません。
この場合、$c->namespaceに'foo/bar'と入っているので'/'の数に合わせてshiftするように修正してみました。

sub default : Private {
    my ( $self, $c ) = @_;

    for ( 0 .. $c->namespace =~ tr{/}{} ) {
        shift @{ $c->req->args };
    }

たっぷり罠が潜んでいそうです。

こんなことしなくても良いのです。 Catalyst::Manual::Introの 「Actions」→「Action types」→「Built-in Private Actions」→「default : Private」に 「これが嫌ならPathを使え」と載っています。
package MyApp::Controller::Foo::Bar;
sub default : Private {
    my ( $self, $c ) = @_;
    $c->res->body(join q{, }, @{ $c->req->args });
}
  • URIhttp://localhost:3000/foo/bar/1
  • 結果:foo, bar, 1
PrivateをPathに変えてみると
package MyApp::Controller::Foo::Bar;
sub default : Path {
    my ( $self, $c ) = @_;
    $c->res->body(join q{, }, @{ $c->req->args });
}
  • URIhttp://localhost:3000/foo/bar/1
  • 結果:1
となります。 なお、default : Privateは推奨されないそうです。特に理由が無い限りdefaultアクションの属性はPathにしておいた方が良さそうです。