今回のテーマ
最終回の今回は、
サンプルアプリケーション
本連載では、
svn co -r 1641 http://svn.coderepos.org/share/lang/perl/Gopper/trunk Gopper
Hook処理とは?
モジュール本体のコードを書き換えることなく、
直感的に理解できるように、
Class::Trigger
Hook処理を実装する事が出来る代表的なCPANモジュールといえばClass::Triggerでしょう。
Class::TriggerはSledgeを拡張するために使われている事で有名です。
Class::Triggerは、
下記のようなモジュールをあらかじめ用意しておき
# 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_
add_
# 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作者のmiyagawaさんが今年のYAPC::Asia 2007 Tokyoにて発表しており、
Plagger
筆者がClass::Componentを作る動機となったのは、
PlaggerもClass::Triggerの作者と同じ人が作っているだけあって、
メソッド名 | 役割 |
---|---|
register_ | hook pointを登録します |
run_ | 指定したhook pointを実行します |
run_ | 指定したhook pointを実行し、 |
Hook関連のメソッドは少なく、
register_
Class::Triggerで言う所のadd_
実際のプラグインのコードを引用すると下記のように利用されます。
# Plagger::Plugin::Publish::CHTML
sub register {
my($self, $context) = @_;
$context->register_hook(
$self,
'publish.feed' => \&feed,
'publish.finalize' => \&finalize,
);
$self->chtml_init($context);
}
run_
# in Plagger->do_run_with_feedsB
$self->run_hook('publish.finalize');
もし先ほどの Publish::CHTML プラグインを使っている場合には、
Class::ComponentでHookを使う
ここまでは、
Class::ComponentもClass::TriggerやPlaggerと同じ考え方でHookを実装していく事ができます。
Pluginでのhook point登録
Class::ComponentでもClass::Triggerのadd_
前々回、
# in Gopper::Plugin::Protocol::Gopher
sub request_read :Hook('request.read') {
my($self, $c, $stash) = @_;
my $line = $stash->engine->get_line( $stash->engine_conn->{handle} );
$c->log(debug => "request: $line");
$stash->request->request_line($line);
$self->RC_OK;
}
この例では、
Class::Componentでの実装は、
my $request_read = sub : Hook('request.read') {
hook pointを実行する
登録されたhook pointを実行するにはrun_
# in Gopper
sub run {
my $self = shift;
$self->log(debug => "engine.preper");
$self->run_hook('engine.preper');
$self->engine->run($self); # while
}
この例ではengine.
run_
my $results = $self->run_hook('foo');
for my $result (@{ $results }) {
....
}
このような形で、
現在の問題点
現在のrun_
Gopperでも
# in Gopper
sub run_request_hook {
my($self, $hook, $stash, $code_checker) = @_;
return RC_OK unless my $hooks =
$self->class_component_hooks->{$hook};
for my $obj (@{ $hooks }) {
my($plugin, $method) =
($obj->{plugin}, $obj->{method});
my $code = $plugin->$method($self, $stash);
return $code || RC_BAD_REQUEST
unless $code_checker->($code);
}
return RC_OK;
}
のような形で独自のrun_
class_
毎回標準のrun_
おまけ
本連載では紹介しなかったプラガブルモジュールを軽く紹介します。
Module::Pluggable
プラガブルモジュールを作る時の定番モジュールのModule::Pluggableは、
簡単なコードサンプルを記述すると、
package MyPlug;
use strict;
use warnings;
use Module::Pluggable require => 1;
for my $plugin (__PACKAGE__->plugins) {
$plugin->say;
}
1;
# 簡易な例として書いてるので大分乱暴なコードになっています。
その後、
package MyPlug::Plugin::EN;
use strict;
use warnings;
sub say { print "hello\n" }
1;
package MyPlug::Plugin::JP;
use strict;
use warnings;
sub say { print "こんにちわ\n" }
1;
この例のコードを実際に動かしてみます。
$ perl -MMyPlug -e ''
こんにちわ
hello
しっかりとPlugin以下のモジュールをrequireして実行できていますね。
Hook::Modular
Hook::Modularは、
Class::Hooakble
Class::HooakbleはCPAN上には無くCodeRepos上で開発が進められている、
最後に
全4回とPerlでのプラガブルなモジュールを作るための役立つエッセンスを紹介してきました。
Class::Componentや本連載上で取り上げたモジュール以外にもプラガブルなモジュールがCPANには沢山転がっているので、
もし、
とはいえ私はプラガブルアプリよりも、
Enjoy!