はじめに
はじめまして。大沢と申します。
この連載では、
今回は、
本連載で使うサンプルアプリケーション
本連載では、
GopperはCodeRepos上のsvnリポジトリに置いてあるので各自checkoutしてください。
svn co -r 271 http://svn.coderepos.org/share/lang/perl/Gopper/trunk Gopper
サンプルアプリケーションは連載中にも頻繁にupdateされる事が予想されますので、
なお、
Gopper
Gopperとは、
サーバの各フェーズにフックポイントがあり、
現在はシンプルな機能しか持ち合わせていませんが、
Class::Componentとは
Class::Componentとは、
Class::Accessor::Fastなどのアクセサモジュールのプラガブル版だと思っていただければ間違いありません。
例えばMyClassというモジュールでプラグイン機能を使いたくなった場合には、
package MyClass;
use strict;
use warnings;
use Class::Component;
1;
とClass::Componentをuseして下さい。Class::Componentをロードする時に別途パラメータを付加する必要が無ければuse baseでも問題ありませんが、
特長
Class::Componentの特長としては以下のような特徴があります。
- Component追加で基本メソッドを拡張出来る
- Plugin追加で、
フックポイントへのフック処理追加や、 Pluginメソッドを追加出来る - Pluginで追加するメソッドの実装方式(普通にメソッド生やす、
AUTOLOAD、 SingletonMethod)を選べ、 選ばなければメソッド生えない - Plaggerのようなconfigを利用するPluginを簡単に実装できる
- モジュール独自にAttribute処理を追加出来る
- Class::Componentを利用したモジュールを、
さらに別モジュールが継承する事が出来る - Class::Componentを使ったオブジェクトをYAML::DumpしてYAMLに変換した後でも、
そのYAMLをYAML::Loadすれば普通に動くので、 オブジェクトの永続化を取り易い
他にも特長がありますが、
主要コンポーネント
Class::Componentは、
Component
作成したモジュールの基本的な動作をカスタマイズするコンポーネントになっています。
大まかな挙動としては、
作成するモジュールにとって、
MyClassモジュールに対する名前空間はMyClass::Component::*になります。
Attribute
Pluginで利用するAttributeを定義するコンポーネントとなっています。
MyClassモジュールに対する名前空間はMyClass::Attribute::*となっており、
package MyClass::Plugin::Foo;
use strict;
use warnings;
use base 'Class::Component::Plugin';
sub plugin_method: Simple {
my($plugin, $context, $args) = @_;
...
}
1;
といった形で利用する事が出来ます。
Plugin
いわゆるプラグインで、
Componentと違い、
先ほど説明したAttributeをメソッドに対して利用する事により、
最小構成での実装例
Gopperは少々繁雑な処理をしているため、
MyClassモジュールを作成し、
example.pl
use strict;
use warnings;
use MyClass;
my $obj = MyClass->new({
config => {
Hello => {
msg => 'world'
}
}
});
$obj->hello; # hello world を表示
print $obj->run_hook( 'bar' )->[0]; # bog を表示
print $obj->baz; # news を表示
newしたときのオプションとしてconfigを渡しています。
configはPluginの名前をkeyにしてvalueに、
MyClass
package MyClass;
use strict;
use warnings;
use Class::Component;
__PACKAGE__->load_components(qw/ Autocall::Autoload /);
__PACKAGE__->load_plugins(qw/ Hello Baz /);
1;
load_
このComponentは、
Autocall::*と名前がついているComponentは、
これが、
また、
MyClass::Attribute::News
package MyClass::Attribute::News;
use strict;
use warnings;
use base 'Class::Component::Attribute';
sub register {
my($class, $plugin, $c, $method, $value, $code) = @_;
no strict 'refs';
no warnings 'redefine';
my $cname = ref($plugin) or return;
*{"$cname\::$method"} = sub {
$code->(@_);
'news';
};
}
1;
NewsというAttributeを定義します。
このAttributeを定義されたメソッドは、
MyClass::Plugin::Hello
package MyClass::Plugin::Hello;
use strict;
use warnings;
use base 'Class::Component::Plugin';
sub hello :Method {
my($self, $c, $args) = @_;
print 'hello ' . $self->config->{msg};
}
sub hello_hello :Hook('bar') {
my($self, $c, $args) = @_;
'bog'
}
1;
helloメソッドではexample.
それぞれ、
helloメソッドはexample.
MyClass::Plugin::Baz
package MyClass::Plugin::Baz;
use strict;
use warnings;
use base 'Class::Component::Plugin';
sub baz :Method :News {
my($self, $c, $args) = @_;
'hello baz method'
}
1;
bazメソッドの戻り値はhello baz methodになるかと思いきや、
Plugin1つにつき、
名前空間の補間
load_
例えば今回のMyClassを例に取ると、
もし、
ComponentやAttributeに関しても同様にMyClass::(Component|Attribute)::*が無ければClass::Component::(Component|Attribute)::*を探します。
厳密に言うと@ISAの継承順ではなくClass::C3の探索ルールと殆ど同じアルゴリズムで探索を行ないます。
次回予告
今回は、
次回以降から、