|
|
||
ぼちぼち自宅の環境も Subversion にしようと重い腰を上げて作業。というか cvs 使ってたけどまともにバージョン管理してるとは言えない状態だったので、ここらでちゃんとやってみることにする。ということでレポジトリ作ってディレクトリ切って、DAV で公開できるようにして、ようやく Trac が動いた。
Trac はお古の自宅サーバーに入れたんだけど、もうディストリビューションが古すぎるもんで一から入れていったらかなりめんどくさかった。
まだ tracd で動いただけなので、明日は Apache で動かせるように設定とかしないとな。
自分も Perl は学習コストが高いと思う派。いや、1990年代頃の CGI = Perl な風味なのでよかったら学習コストは低いっていうかすぐわかるPerl読めば OK 的な感じなんだけども、いまやそういう人は PHP ですね。
Perl のみでいこうとするとオブジェクト指向がまず分かんない。理解不能。Perl の OO のシンタックスがきもいとかじゃなくって、Perl OO で OO を丁寧に解説した本とか情報がほとんどない。宮川さんのメルマガは分かりやすけど、あれも OO 全くわかりませんって人向けではないもんなあ。
ということで、ある程度まで行ったらやっぱ Java とか Ruby とかやるのがいいんじゃないかなと思うけども、ちょっと前までは Ruby やって OO 楽しいーと言うところまで行ってもウェブアプリのまともな環境ないからなーとか言ってでも Java はアレだしスクリプト言語で OO で実行環境も充実、といえば Perl なわけでしたが、いまなら Rails があるという罠があって結局 Perl の OO を学ぶ前に Rubyist になって二度と帰ってこなかったりとか! オゥッ。
自分の体験から行くと、Java をやる前までは OO とかサパーリわからんでした。そんなわけで OO の学習には Java はとてもいい気がする。言語仕様も小さいし、テキストも豊富だし。
ちょっと趣を変えたもの。SYNOPSYS から。
## MyApp2
package MyApp2;
use strict;
use warnings;
use Class::Pluggable2 plugin_method => 'function';
sub new {
bless {}, shift;
}
1;
## MyApp2/Plugin/Hello.pm
package MyApp2::Plugin::Hello;
use strict;
use warnings;
use base qw 'MyApp2::Plugin';
sub function {
my ($self, $app) = @_;
sprintf "Hello, World! %s", $self->config->{foo};
}
1;
## MyApp2/Plugin/Bye.pm
package MyApp2::Plugin::Hello;
use strict;
use warnings;
use base qw 'MyApp2::Plugin';
sub function {
my ($self, $app) = @_;
sprintf "Hello, World! %s", $self->config->{foo};
}
1;
## main.pl
use MyApp2;
MyApp2->plugin_config({
Hello => {
foo => 'bar',
}
});
MyApp2->install_plugin(
hello => 'Hello',
bye => 'Bye',
);
my $app = MyApp2->new;
print $app->hello, "\n";
print $app->bye, "\n";
結果。
[naoya@hydralisk pluggable]$ perl test2.pl Hello, World! bar Good Bye!!!
欠点としては、各プラグインごとにメソッドを一個しか拡張できない。プラグイン一個で複数のメソッドを、拡張される側に追加することはできない。そういう場合は、内部でその辺のメソッドを全部もったインスタンスを作ってそれを返すようにするとかそんな感じか。
あと、この実装はあくまで拡張される側にメソッドを追加するためだけの実装で、Catalyst みたいに既存のクラスの動作をカスタマイズするためのものではないので、一個前のとはちょっと趣が異なる。
package Class::Pluggable2;
use strict;
use warnings;
use Carp;
use UNIVERSAL::require;
use base qw 'Class::Data::Inheritable';
use Class::Pluggable2::Plugin;
sub import {
my $class = shift;
my %args = @_;
my $callpkg = caller;
$class->mk_classdata(plugin_method => $args{plugin_method} || 'do_task');
$class->mk_classdata(namespace => $args{namespace} || join('::', $callpkg, 'Plugin'));
$class->mk_classdata('plugin_config');
{
no strict 'refs';
push @{$class->namespace . "::ISA"}, join('::',$class, 'Plugin');
*{"$callpkg\::plugin_config"} = sub {
my $self = shift;
$class->plugin_config(@_);
};
*{"$callpkg\::install_plugin"} = sub {
my $callpkg = shift;
my %args = @_;
for my $name (keys %args) {
my $plugin_name = $args{$name};
my $plugin_class = $class->namespace . "::$plugin_name";
$plugin_class->require;
croak qq/Could not load plugn "$plugin_class", "$@"/ if $@;
my $plugin = $plugin_class->new({
config => $class->plugin_config->{$plugin_name},
});
$plugin->initialize if $plugin->can('initialize');
{
no strict 'refs';
my $plugin_method = $class->plugin_method;
*{"$callpkg\::$name"} = sub { $plugin->$plugin_method(shift) };
}
}
};
}
}
1;
プラグインの親クラス。いまのところコンストラクタと config のアクセサを持ってるだけ。
package Class::Pluggable2::Plugin; use strict; use warnings; use base qw(Class::Accessor::Fast); __PACKAGE__->mk_accessors(qw(config)); 1;
アプリケーションのコアになるオブジェクト、まあ言うなればずばり Catalyst のコンテキストオブジェクトなんだけど、ああいう感じで全体で使い回されるオブジェクトというのが時々必要になる。特にフレームワークでそういうオブジェクトが良く登場する。
そのオブジェクトはいろいろ機能を持ってるんだけど、フレームワークのコアであったりするが故に、後からいろいろメソッドを追加したいとか、特定のドメインに特化したこういう機能が欲しいとか、そういうことがよく起こる。という要求に応えられるように、コアオブジェクトはプラグイン方式で拡張できると都合が良い。
などなど。
ということで Catalyst の仕組みみたいなのをクラスにしたもの、というか結果的に Catalyst からその部分を持ってきたものみたいなのになっちゃったけど、そういうのを作ってみている。似たようなものが他に CPAN にありそうで車輪の再発明かもしれないけど。(ちなみに Module::Pluggable はちょっと用途が違う)
SYNOPSYS は
# MyApp.pm
package MyApp;
use strict;
use warnings;
use base qw(Class::Pluggable);
sub new { bless {}, shift }
1;
# MyApp/Plugin/Hello.pm
package MyApp::Plugin::Hello;
use strict;
use warnings;
sub hello {
return sprintf "%s 'Hello, World!'\n", ref $self;
}
1;
# MyApp/Plugin/Bye.pm
package MyApp::Plugin::Bye;
use strict;
use warnings;
sub bye {
return "Good Bye\n";
}
1;
# main.pl
use MyApp plugins => ['Hello'];
MyApp->install_plugins('Bye');
MyApp->plugin(prototype => 'HTML::Prototype');
my $app = MyApp->new;
print $app->hello;
print $app->bye;
print $app->prototype->define_javascript_functions;
とこんな感じで。
use MyApp plugins => ['Hello'] とかでプラグインをインストールできる。MyApp->install_plugins でプログラマブルにも可能。use MyApp plugins => ['Hello'] で MyApp::Plugin::Hello がロードされて、MyApp の継承ツリーに入る。的なもの。あと、Catalyst のコードを見てて
MyApp->plugin(prototype => 'HTML::Protytpe');
とすると HTML::Prototype をインスタンス化してそのインスタンスを $c->prototype で取得できるみたいなメソッドがあったので、その機能も追加してみてる。
package Class::Pluggable;
use strict;
use warnings;
use Carp;
use UNIVERSAL::require;
use base qw 'Class::Data::Inheritable';
__PACKAGE__->mk_classdata(registered_plugins => []);
sub import {
my $class = shift;
my %args = @_;
$class->_setup_plugins(@{$args{plugins}}) if $args{plugins};
}
sub install_plugins {
my $class = shift;
$class->_setup_plugins(@_);
}
sub plugin {
my ($class, $name, $plugin, @args) = @_;
$class->_register_plugin($plugin);
eval { $plugin->import };
$class->mk_classdata($name);
my $obj;
eval { $obj = $plugin->new(@args) };
croak qq/Could not instantiate instant plugin "$plugin", "$@"/ if $@;
$class->$name($obj);
}
sub _register_plugin {
my ($class, $plugin) = @_;
$plugin->require;
croak qq/Could not load plugin "$plugin", "$@"/ if $@;
push @{$class->registered_plugins}, $plugin;
}
sub _setup_plugins {
my $class = shift;
my @plugins = ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_;
for my $plugin (@plugins) {
$plugin = "$class\::Plugin::$plugin";
$class->_register_plugin($plugin);
{
no strict 'refs';
push @{"$class\::ISA"}, $plugin;
}
}
}
1;
いろいろ考えなきゃいけないことはあり、
などなど。もうちょっと試しながら作り込んでみよう。
[naoya@hydralisk naoya]$ perldoc Module::Plaggable
No documentation found for "Module::Plaggable".
[naoya@hydralisk naoya]$ sudo cpan Module::Plaggable
CPAN: Storable loaded ok
Going to read /Users/naoya/.cpan/Metadata
Database was generated on Thu, 04 May 2006 05:01:59 GMT
Warning: Cannot install Module::Plaggable, don't know what it is.
Try the command
i /Module::Plaggable/
to find objects with matching identifiers.
僕はこれつかってます。なんかほかにもあったきがするんですけども。
突然すいませんが、なにかご意見いただければと思います。
CPAN Author オメ。
一応、up前に同名のものが無いか探したつもりでしたが、探したりませんでした。これからはもう少し良く探すようにします。