日向夏特殊応援部隊

俺様向けメモ

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がいずれも定義されていない場合は、

  1. 最初の$child(Module::Starter::Simple)は$parentが定義されて無いので@ISAに変更なし
  2. load_pluginsのcanチェックはスルー
  3. $parentに$childを代入($parent = 'Module::Starter::Simple')
  4. 次以降の$childには$parentとしてModule::Starter::Simpleがあるので$childの@ISAにpushする。
  5. $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っぽぃって思うんですが皆さん如何ですか?(ぇ

*1:しかしここの和訳難しい。。。

*2:未確認ですからあしからずw