ziguzagu.org

CGI::Application::Dispatch勉強中

今日は一日mod_perlとCGI::Application::Dispatchの勉強。

CGI::Application::Dispatchは、CGI::Applicationで書かれたモジュールのインスタンスCGIをいちいち書かないで、URIから実行するモジュールを判別してインスタンス生成する、という仕組みを提供してくれます。モジュールをrunモードみたいに切り替える、という感じでしょうか(ちょっとわかりにくい…)。

PODに詳細はありますが、自分ぐらいの初級レベルの人(PerlOOP暦半年、CGI::App暦3ヶ月)でもわかるように、頭の中の整理もかねてメモしておきます。その前に、CGI::ApplicationとDispatchに関しては、ハテナオヤさんがUNIX USERのPerl短期連載で紹介していた記事が激しく参考になります。Perlの話題は、雑誌媒体への露出がPHP/Javaに比べると極端にすくないような気がするので、短期といわず長期的にやってもらいたいですね。

では、自分なりのまとめメモです。

通常、CGI::Applicationを使用する場合は、CGI::Applicationを継承したモジュールと、それをインスタンス化するCGIが必要になります。

### CGI::Applicationを継承したモジュール
package My::App;
use strict;
use warnings;
use base qw(CGI::Application);
 
sub setup {
    my $self = shift;
    $self->start_mode('index');
    $self->run_modes(
        index => 'do_index',
        edit  => 'do_edit',
    );
}
 
sub do_index { ... }
sub do_edit  { ... }
1;
#!/usr/bin/perl
### app.cgi - My::Appをインスタンス化、実行するCGI
use strict;
use warnings;
use lib qw(./lib);
use My::App;
my $app = My::App->new();
$app->run();

この場合のURIは、

http://example.com/app.cgi?rm=index
http://example.com/app.cgi?rm=edit

となります。PATH_INFO形式が利用できる場合は、

http://example.com/app.cgi/index
http://example.com/app.cgi/edit

とすることもできます。

これに、My::Searchというモジュールが追加になった場合、それをインスタンス化するCGIは次のようになります。

#!/usr/bin/perl
### search.cgi - My::Searchをインスタンス化、実行するCGI
use strict;
use warnings;
use lib qw(./lib);
use My::Search;
my $app = My::Search->new();
$app->run();

app.cgiとsearch.cgiの差はインスタンス化するモジュール名だけですね。このような単純な違いしかないCGIをモジュールの追加のたびに書くのは(たったこれだけでも)非効率的ですし、単純だからこそミスも発生しやすいです。

ここで、CGI::Application::Dispatchの出番です。Dispatchを利用した場合、モジュールのインスタンス化は、URIに含まれたモジュール名から自動的に行ってくれます。CGIをいちいち書く必要はありません。

Dispatchモジュールを使った場合のURIは、

/dispatchのcgi/モジュール名/runモード

とすることができます。上記2つのモジュールの例(My::Searchはsearch/configのrunモードをもつ)からいくと、

http://example.com/dispatch.cgi/app/index
http://example.com/dispatch.cgi/app/edit
http://example.com/dispatch.cgi/search/search
http://example.com/dispatch.cgi/search/config

となります。モジュールがどんなに増えても、必要なCGIはdispatch.cgiのみです。で、そのdispatch.cgiは以下のようになります。

#!/usr/bin/perl
use strict;
use warnings;
use CGI::Carp qw(fatalsToBrowser);
use CGI::Application::Dispatch;
use lib qw(./lib);
 
CGI::Application::Dispatch->dispatch(
    PREFIX  => 'My',   # URIに含まれるモジュール名の名前空間
    DEFAULT => 'App',  # モジュール名が指定されなかった場合に実行するモジュール名
);

これだけです。なるほど、楽チンだ。でも、ふと疑問に思いました。実行するモジュールが増えてMy::App::Hogeなんてのができた場合どうするんだろう?そういった場合にもちゃんと対応できるよう、URIとモジュール名をマップする仕組みが提供されていました。My::App::Hogeを/dispatch.cgi/hoge/で実行されるようにするには、

CGI::Application::Dispatch->dispatch(
    PREFIX  => 'My',
    TABLE   => {
        app    => 'App',
        search => 'Search',
        hoge   => 'App::Hoge',
    },
);

とします。ハッシュのキーをモジュールのエイリアスとしてURIに含めることができます。なるほどです。この仕組みがあれば、モジュール名のマップを作るdispatch.cgi以外にCGIを書く必要はまったくなさそうです。ただ、TABLEを定義した場合は、DEFAULTでの指定が無効になるようです(ちょっとだけはまりました…)DEFAULTの値は実際のモジュール名ではなくハッシュのキーに指定した値で指定します。

Dispatchモジュールは、CGI::Applicationフレームワークでは必須のものではありませんが、このdispatchという概念や仕組みは、その他いろいろなWebフレームワークにもあるようなので、今後もっと高機能なフレームワーク(SledgeやCatalyst)に手を出すためにもちゃんと理解しておくべき仕組みのように思います。

また、CGI::Application::Dispatchには、mod_perlを使ってApacheハンドラとして動作させたり、rewriteを使って技術に依存しないCoolなURI(.cgiとかがURIに含まれない)を作りやすくできる、とかいった副産物もあるようなです。でも自分の検索ぢからではあまり参考になる資料が発見できなかったので、もう少しいろいろ試してからまたまとめてみようかなと思います。

追記(2005/09/24) : こちらでまとめました