Perlでプラグインという仕組みを利用するには <2>

さて前回id:ya_ken:20060619:1150725716途中まで説明したCGI::Applicationのプラグインの実装方法の続きを書く。

今回のポイントを簡単に言えば「継承したわけではないのに、あるクラスにどうやって関数を追加するか」なんだ。そこで参考になるのが、CGI::Applicationについてるサンプリコードだ。

sub import {                                                                                    
    my $caller = scalar(caller);                                                                
    $caller->add_callback('init', 'my_setup');                                                  
    goto &Exporter::import;                                                                     
}

この関数はプラグイン側に書かれるもので、何をしているかを分かりやすく言うと「CGI::Applicationのinitという処理にフックさせている」というわけ。

順を追って書こう、まず超重要なのはimport関数が何なのか?ということだ。Perlでuse xxxと書く時、Perlの仕様でxxxモジュールのimportメソッドが(あれば)呼ばれるんだ。だからプラグインにあるimportメソッドはそれがuseされた時点で自動的に呼び出される。そして、CGI::Applicationのプラグインをuseする場所はCGI::Applicationのサブクラスになるから$caller->add_callback()を呼ぶ事が出来るというわけ。これでフックの登録の仕組みは分かると思う。

次にgoto &Exporter::import部分を説明するが、説明の都合上Export::importのソースコードを読み解く事を強くお勧めする。ここではExport::importを適当に簡略化したコードを載せておく

sub import {
    my $pkg = shift;
    my $callpkg = caller(0);

    my @list_of_subroutins;

    ## @list_of_subroutinsを呼び出し元($pkg)の@EXPORTなどから初期化

    *{"$callpkg?::$_"} = ?&{"$pkg?::$_"} foreach @list_of_siubroutins;
}

実際に試してもらうのが一番良いのだけど、goto &Exporter::importの結果、このimportの$pkgはプラグインのパッケージ、$callpkgにはプラグインを呼び出したパッケージ、つまりCGI::ApplicationのサブクラスでCGI::Application::Plugin::Sessionをuseした場合は次のようになるはずだ。

$pkg
CGI::Application::Plugin::Session
$callpkg
あなたが作ったCGI::Applicationのサブクラスのパッケージ

そして、Exporter::importの最後の行でCGI::Application::Plugin::Sessionのエクスポート用関数をあなたが作ったCGI::Applicationのサブクラスにくっつけているというわけ。

これによってあなたのクラスから例えば$self->sessionみたいな関数が呼べるようになるというわけ。


じつはこれで昨日書いた2通りのプラグインの両方の説明を兼ねてしまったんだな。計画性も無く書いたから、ぐだぐだになってしまった。

まぁ、この仕組みをもう少しきれいにしてPluggableモジュールを作ったので、その仕組みを近いうちに書いてみよう。