今回のテーマ
最終回の今回は,前回までのディープな話題とはうって変わって,CPANへのモジュール登録数が世界第2位のmiyagawaさんが作成されたClass::Triggerや,Perl界隈のみならず様々な場所で話題になっているPlaggerにて実装されているHook処理にフォーカスを当てた話題を扱ってから,この連載を〆させていただこうかと思います。
サンプルアプリケーション
本連載では,プラガブルなモジュールを作製するという事を考えて,Gopperという実際に実行可能なサンプルアプリケーションを元に解説を行ないます。 GopperはCodeRepos上のsvnリポジトリに置いてあるので各自checkoutしてください。
svn co -r 1641 http://svn.coderepos.org/share/lang/perl/Gopper/trunk Gopper
Hook処理とは?
モジュール本体のコードを書き換えることなく,外部から元のモジュールの処理を拡張する事を可能にするという, プラガブルなアプリケーションを書くための手法の一つです。
直感的に理解できるように,現在CPANに登録されているモジュールを紹介しつつ解説します。
Class::Trigger
Hook処理を実装する事が出来る代表的なCPANモジュールといえばClass::Triggerでしょう。
Class::TriggerはSledgeを拡張するために使われている事で有名です。
Class::Triggerは,とてもシンプルに実装されており,trigger pointに対して実行するコードを設定する事と,trigger pointに登録されているコードを呼び出すという2種類の処理を記述するだけで,簡単に外部から拡張可能なモジュールを作成できます。
下記のようなモジュールをあらかじめ用意しておき
# Hello.pm
package Hello;
use strict;
use warnings;
use Class::Trigger;
sub say {
__PACKAGE__->call_trigger('say');
}
1;
# hello.pl
use strict;
use warnings;
use Hello;
Hello->add_trigger( say => sub { print 'こんにちわ!' } );
Hello->say;
add_triggerメソッドでtrigger pointと,そのtrigger pointが呼び出された時に実行するコードをセットし,call_triggerで指定されたtrigger pointに対応するコードを実行します。
add_triggerは,同じフック名でのコードリファレンス登録を複数回行うことが出来ます。
# hello.pl
use strict;
use warnings;
use Hello;
Hello->add_trigger( say => sub { print 'こんにちわ!' } );
Hello->add_trigger( say => sub { print 'こんにちわ!' } );
Hello->say;
モジュール書くけど,とりあえず簡単に拡張出来るようにしたい!といったケースにClass::Triggerを使うと,とても楽に開発を行えます。もちろん簡単な事しか出来ないわけでは無いので複雑な物にも対応出来ますよ。
既存の複雑なプログラムをClass::Triggerを使ってプラガブルにする。という内容でClass::Trigger作者のmiyagawaさんが今年のYAPC::Asia 2007 Tokyoにて発表しており,その資料が公開されていますので興味がある方は是非ご覧ください。
Plagger
筆者がClass::Componentを作る動機となったのは,Plaggerのhook処理を他のモジュールで使いたい!という欲望が根底にありました。 言ってみればPlaggerはClass::Componentのお母さんのようなものです。CatalystとDBIx::Classは,それぞれお父さんになりますでしょうか。
PlaggerもClass::Triggerの作者と同じ人が作っているだけあって,Class::Triggerと同じような実装になっています。 Hookに関連する処理の大部分はPlagger.pm中に実装されており,メソッドを表1にて抜き出しました。
表1 Plaggerで実装されているHook関連のメソッド
| メソッド名 | 役割 |
|---|---|
| register_hook | hook pointを登録します |
| run_hook | 指定したhook pointを実行します |
| run_hook_once | 指定したhook pointを実行し,順番にhook pointのコードを処理し,最初に戻り値があった時点で以降に登録されたコードの実行は行ないません |
Hook関連のメソッドは少なく,シンプルですね。
register_hookは主にPluginから利用されるメソッドになっており,hook pointとhook poinに対するコードをPlaggerに対して登録します。
Class::Triggerで言う所のadd_triggerになります。
実際のプラグインのコードを引用すると下記のように利用されます。
# Plagger::Plugin::Publish::CHTML
sub register {
my($self, $context) = @_;
$context->register_hook(
$self,
'publish.feed' => \&feed,
'publish.finalize' => \&finalize,
);
$self->chtml_init($context);
}
run_hookとrun_hook_onceはClass::Triggerのcall_triggerに相当し,指定されたhook pointに対してregister_hookにて登録されたコードを実行します。
# in Plagger->do_run_with_feedsB
$self->run_hook('publish.finalize');
もし先ほどの Publish::CHTML プラグインを使っている場合には,hook point として publish.finalizeとPlagger::Plugin::Publish::CHTMLのfinalizeメソッドが関連づけられているので,上記のrun_hookを実行した場合にはfinalizeメソッドが実行される事になります。

