Module::Starterのplugin機構が面白い件について
ちょっとCatalystの設計上、どうしてもやってみたい拡張があって、周辺技術を調べてる最中、Module::Starterのplugin機構が面白い事に気づきました。
Module::Starter->import
というかModule::Starterってimportが定義してあるだけです。w
sub import { my $class = shift; my @plugins = ((@_ ? @_ : 'Module::Starter::Simple'), $class); my $parent; no strict 'refs'; while (my $child = shift @plugins) { eval "require $child;"; die "couldn't load plugin $child: $@" if $@; push @{"${child}::ISA"}, $parent if $parent; if ($child->can("load_plugins") and @plugins) { $parent->load_plugins(@plugins); last; } $parent = $child; } }
呼び出しに関しては、
use Module::Starter qw/ Module::Starter::Simple Module::Starter::Smart /;
みたいに使う訳ですが。このwhileループ内の処理が面白い。
まず@pluginsですけど、何もしない場合は、
my @plugins = qw/Module::Starter::Simple Module::Starter/;
ってなります。その次のmy $parentの扱いが非常に気になった訳ですが、
plugins指定されたモジュールの中にload_pluginsがいずれも定義されていない場合は、
- 最初の$child(Module::Starter::Simple)は$parentが定義されて無いので@ISAに変更なし
- load_pluginsのcanチェックはスルー
- $parentに$childを代入($parent = 'Module::Starter::Simple')
- 次以降の$childには$parentとしてModule::Starter::Simpleがあるので$childの@ISAにpushする。
- $parentにModule::Starterを設定
となるのでload_pluginsが見つかるまでは最初に設定したplugin順に子供になっていき、最後にModule::Starterが最も子孫なクラスになる。
仮に途中でload_pluginsメソッドがあればそれに依存した形でpluginの読み込みとなり、
plugin機構自体捻じ曲げる事が出来る。
pluginのコンセプトがModule::Starter::Pluginのpodに書いてあります。
まさに下記の引用部分がそのまんまですね。*1
By default, the given modules are required and arranged in an is-a chain. That is, Module::Starter subclasses the last plugin given, which subclasses the second-to-last, up to the first plugin given, which is the base class. If a plugin provides a load_plugins method, however, the remaining plugins to be loaded are passed to that method, which is responsible for loading the rest of the plugins.
で、このことが示唆している2つのコンセプトとして続けて書いてあるのが、
engine plugins An engine is a plugin that stands alone, implementing the public create_distro method and all the functionality required to carry out that implementation. The only engine included with Module::Starter is Module::Starter::Simple, and I'm not sure any more will be seen in the wild any time soon.
まぁ大体はcreate_distroを始めとしたimplementすべきメソッドを全部用意した物が唯一のEngine pluginになりますよって事ですね。で、代表例はModule::Starter::Simpleです、って事でしょう。
M::S::PBPだとかM::S::SmartってのはベースクラスをM::S::Simpleにしているので、Engine足りえるんでしょうね。
plain old plugins Other plugins are designed to subclass an engine and alter its behavior, just as a normal subclass alters its parent class's. These plugins may add features to Module::Starter engines, or may just provide general APIs for other plugins to exploit (like Module::Starter::Plugin::Template.) The template plugin is a simple example of a plugin that alters an engine to accept further plugins. Other plugins like template will probably be written in the near future, and plugins that exploit the API provided by Module::Starter::Plugin::Template will be available on the CPAN.
他のプラグインってのはengineのサブクラスであり、その挙動を変える物ですよって事ですね。
またさらに別のpluginの為の標準的なAPIを提供する事があって、恐らくこのplugin機構から考えて、先に読まないとダメって事もあるんでしょうね。恐らくそれの代表がModule::Starter::Plugin::Templateって事なんでしょう。*2
にしてもこのplugin機構って、結構この数行にコンセプトが凝縮されてて、私的には非常にPerlっぽぃって思うんですが皆さん如何ですか?(ぇ