Perlでプラガブルモジュールを作ろう!

第4回 Hook処理を極めて外部からモジュールを拡張する

この記事を読むのに必要な時間:およそ 4 分

今回のテーマ

最終回の今回は,前回までのディープな話題とはうって変わって,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;

このように,同じ登録を2回行ったとしてもこんにちわ!こんにちわ!と表示する事が出来ます。

モジュール書くけど,とりあえず簡単に拡張出来るようにしたい!といったケースに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_hookhook 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メソッドが実行される事になります。

著者プロフィール

大沢和宏(おおさわかずひろ)

趣味で携帯検索サイト iYappoを運営つつコード書きやネタサイトを作る毎日。最近はCodeReposを立ち上げた。執筆活動は,前世紀にThe BASICへ寄稿したのを皮切りに,携帯雑誌のコラム書きや技術誌へ思い出したように執筆している。

URLhttp://blog.yappo.jp/

コメント

コメントの記入