Plack::Builder は Middleware をラップしてくれたり、 Plack::App::URLMap を使ったマッピングを担ってくれる。

通常は次のように使う

use Plack::Builder;
use Plack::Session::Store::File;

my $app  = sub { ... };
my $app2 = sub { ... };

builder {
    enable "Session",
        store => Plack::Session::Store::File->new(...);

    mount "/foo" => $app2;
    mount "/"    => $app;
};

Twitter の OAuth をサービスのログインに使いたいとする。この場合、コールバックURLを指定する必要があるが、ログイン処理は /login 以下にまとめるのが良いだろう。

例えば、

http://localhost:5000/login
http://localhost:5000/login/callback

のようなパスにマップしたいときは、次のように builder をネストさせる

use Plack::Builder;
use Plack::Session::Store::File;

builder {
    enable "Session",
        store => Plack::Session::Store::File->new(...);

    mount '/login' => builder {
        mount '/callback' => sub {
            my $env = shift;
            return [200, [], ["in callback"]];
        };

        mount '/' => sub {
            my $env = shift;
            return [200, [], ['<a href="...">Login</a>']];
        };
    };

    mount '/' => sub { ... };

};

この例では "/login" にアクセスすると、 mount された "/login" の中で mount された "/" が呼ばれる。

"/login/callback" にアクセスすると、 mount された "/login" の中で mount された "/callback" が呼ばれる。

builder の注意点

  • mount を一度でも使った場合は、 fallback として "/" を mount する必要がある($app を最後に書くだけではダメ)
  • mount の第一引数は "/" から始まり、前方一致で判定される。ただし、最初の "/" から次の "/" までは完全一致している必要がある。
    • この例の場合、 "/loginx" にアクセスされた場合は一番外側の builder で mount された "/" に fallback する。
    • "/login/callbackx" にアクセスされた場合は mount された "/login" の中で mount された "/" に fallback する。

$env の注意点

$env->{PATH_INFO} と $env->{SCRIPT_NAME} は、 Plack::App::URLMap によって、その coderef の位置する部分に適する形に書き換える。

つまり、

(snip)
    mount '/login' => builder {
        mount '/callback' => sub {
            my $env = shift;

            # $env->{PATH_INFO} eq ''
            # $env->{SCRIPT_NAME} eq '/login/callback'
        };

        mount '/' => sub {
            my $env = shift;
            # $env->{PATH_INFO} eq ''
            # $env->{SCRIPT_NAME} eq '/login'
        };
    };
(snip)

のようになる。