
さて、前回ではattributeの扱い方を軽く見てみました。
ただ、あんなコードを毎回書くのは面倒です。
そこでAttribute::Handlersモジュールの登場です。
概要としては、独自attributeの作成を簡単にしてくれます。
もちろん関数以外のattributeの作成が出来ますが、今回も関数のみに絞ります。
詳細はperldocしてください。
基本的な使い方も簡単で
use strict;こんな感じでかけます。
use Attribute::Handlers;
sub ah_test1 : ATTR(CODE) {
my($package, $symbol, $referent, $attr, $data, $phase) = @_;
print "ah_test1: $package\n";
}
sub test : ah_test1 {
my $str = shift;
print "test: $str\n";
}
print "start\n";
test('value');
print "end\n";
]$ perl ./ah1.plとなります。
ah_test1: main
start
test: value
end
ちなみに、ATTR attributeがついた関数は、任意のperl実行フェーズで動かすことが可能です。
サンプルとして
use strict;だとすると、実行結果が
use Attribute::Handlers;
sub ah_test1 : ATTR(CODE,INIT,BEGIN,CHECK,END) {
my($package, $symbol, $referent, $attr, $data, $phase) = @_;
print "ah_test1: $package : $phase\n";
}
sub test : ah_test1 {
my $str = shift;
print "test: $str\n";
}
print "start\n";
test('value');
print "end\n";
$ perl ./ah2.plとなります。
ah_test1: main : BEGIN
ah_test1: main : CHECK
ah_test1: main : INIT
start
test: value
end
ah_test1: main : END
さて、ATTR attributeがついた関数には決められた引数が与えられます。
今回の例のah_test1でも、明示的に呼び出しを行っていないことからも分かるとおり、Attribute::Handlersより呼び出しが行われています。
で、
my($package, $symbol, $referent, $attr, $data, $phase) = @_;こんな感じの引数なわけですが、それぞれ。
use strict;実行結果
use Attribute::Handlers;
use Data::Dumper;
sub ah_test1 : ATTR(CODE) {
print "\n" . Dumper(@_) . "\n";
}
sub test : ah_test1(attr,{attr => 'hoge'}) {
my $str = shift;
print "test: $str\n";
}
print "start\n";
test('value');
print "end\n";
$ perl ./ah3.pl$VAR1 = 'main';
$VAR2 = \*::test;
$VAR3 = sub { "DUMMY" };
$VAR4 = 'ah_test1';
$VAR5 = [
'attr',
{
'attr' => 'hoge'
}
];
$VAR6 = 'CHECK';start
test: value
end
大まかには、こんな感じです。
次は応用例として。
前回もattributeは複数設定できると書きましたが、shellのパイプのように複数のattributeを繋げて処理することも出来ます。
その場合は、ちょっとattributeのシンボルテーブルをいじってあげます。
use strict;前回の最後のサンプルのような感じになります。
use Attribute::Handlers;
use Data::Dumper;
my $i = 1;
sub pipe1 : ATTR(CODE) {
my($package, $symbol, $referent, $attr, $data, $phase) = @_;
print "$attr: $package\n";
no warnings 'redefine';
*{$symbol} = sub {
my $c = $i++;
print "$attr [$c] start\n";
my $ret = &$referent($_[0] . " ($data [$c])");
print "$attr [$c] return = $ret\n";
print "$attr [$c] end\n";
return $ret . "($attr [$c])";
};
}
sub pipe2 : ATTR(CODE) {
my($package, $symbol, $referent, $attr, $data, $phase) = @_;
print "$attr: $package\n";
no warnings 'redefine';
*{$symbol} = sub {
my $c = $i++;
print "$attr [$c] start\n";
my $ret = &$referent("($data [$c]) " . $_[0]);
print "$attr [$c] return = $ret\n";
print "$attr [$c] end\n";
return $ret . "($attr [$c])";
};
}
sub test : pipe1('foo') pipe2('bar') pipe1('baz') pipe1('foo2') pipe2('bar2') pipe1('baz2') {
my $str = shift;
print "test: $str\n";
return $str;
}
print "START\n";
print test('value') ."\n";
print "END\n";
$ perl ./ah4.plまた、このサンプルで、どのタイミングでATTR attributeが実行されているかも分かったかと思います。
pipe1: main
pipe2: main
pipe1: main
pipe1: main
pipe2: main
pipe1: main
START
pipe1 [1] start
pipe2 [2] start
pipe1 [3] start
pipe1 [4] start
pipe2 [5] start
pipe1 [6] start
test: (bar [5]) (bar2 [2]) value (baz2 [1]) (foo2 [3]) (baz [4]) (foo [6])
pipe1 [6] return = (bar [5]) (bar2 [2]) value (baz2 [1]) (foo2 [3]) (baz [4]) (foo [6])
pipe1 [6] end
pipe2 [5] return = (bar [5]) (bar2 [2]) value (baz2 [1]) (foo2 [3]) (baz [4]) (foo [6])(pipe1 [6])
pipe2 [5] end
pipe1 [4] return = (bar [5]) (bar2 [2]) value (baz2 [1]) (foo2 [3]) (baz [4]) (foo [6])(pipe1 [6])(pipe2 [5])
pipe1 [4] end
pipe1 [3] return = (bar [5]) (bar2 [2]) value (baz2 [1]) (foo2 [3]) (baz [4]) (foo [6])(pipe1 [6])(pipe2 [5])(pipe1 [4])
pipe1 [3] end
pipe2 [2] return = (bar [5]) (bar2 [2]) value (baz2 [1]) (foo2 [3]) (baz [4]) (foo [6])(pipe1 [6])(pipe2 [5])(pipe1 [4])(pipe1 [3])
pipe2 [2] end
pipe1 [1] return = (bar [5]) (bar2 [2]) value (baz2 [1]) (foo2 [3]) (baz [4]) (foo [6])(pipe1 [6])(pipe2 [5])(pipe1 [4])(pipe1 [3])(pipe2 [2])
pipe1 [1] end
(bar [5]) (bar2 [2]) value (baz2 [1]) (foo2 [3]) (baz [4]) (foo [6])(pipe1 [6])(pipe2 [5])(pipe1 [4])(pipe1 [3])(pipe2 [2])(pipe1 [1])
END
Catalystのようなフレームワークでの活用や、独自アプリケーションソフトのプラグイン機構なんかで取り入れるとしっくりするかと思われます。
ちょっと頑張って、プラグイン機構が組み込まれた、なんちゃってサーバを作ってみました。
プラグインの骨組みはAHappli::Pluginsに書かれています。
プラグイン本体はAHappli::Plugins::Fooのような場所におきます。
プラグインの組み込みは、Catalystと同様にuseする時に組み込むプラグインを指定します。例。
肝はattributeにあるので、プラグイン側のメソッドは適当でもちゃんと動きます。
本当に簡単に作っているので、挙動などはコードを見て把握してみてください。
全ファイルのダウンロードはhttp://tech.yappo.jp/download/AHappli.tar.gzにて。
もうちょっとしっかり作りこめば、それらしい物が作れるはずでしょう。
CPANにはAttribute::Handlersを使ったモジュールがいくつかありますので、そっちも参考にしてみてください。
MFPM: Attribute::Handlers