プラグインはどのクラスで new すべきか
小粋空間さんの記事で、プラグイン オブジェクトを生成するバリエーションがいろいろと紹介されています。いずれの方法でも、プラグインとして登録でき動作するのですが、今回、その違いでハマったことがあったので情報展開しておきます。
まず、new メソッドによるオブジェクト インスタンスの生成についてですが、これは以下の二つの記述は同じ動作と考えて差し支えありません。ですので、new の位置は今回は特に気にしないことにします。実際、間接オブジェクト形式での記述では動作に違いがあるのですが、ここでは割愛します。
package MT::Plugin::MyPlugin; my $instance1 = new MT::Plugin::MyPlugin ({...}); my $instance2 = MT::Plugin::MyPlugin->new ({...});
次に以下の二つのプラグインのソースコードです。どちらもやっていることはほぼ同じです。
- package で名前空間を宣言
- MT::Plugins のサブクラスとして宣言
- プラグインのインスタンスを生成
- MT にプラグインを追加
- MT::Plugin::init_app をオーバーライド
するだけの単純なプラグインになります。違いは、new で指定しているクラス名ですが、これらのプラグインを MT に登録すると、確かにプラグイン一覧に表示されますし、特にエラーも発生しません。何が違うのでしょうか?
### src.1 package MT::Plugin::MyPlugin; use base qw( MT::Plugin ); my $plugin = MT::Plugin::MyPlugin->new ({...}); MT->add_plugin ($plugin); sub init_app { # override MT::Plugin::init_app }
### src.2 package MT::Plugin::MyPlugin; use base qw( MT::Plugin ); my $plugin = MT::Plugin->new ({...}); MT->add_plugin ($plugin); sub init_app { # override MT::Plugin::init_app }
src.2 の方では、$plugin は MT::Plugin クラスのインスタンスであって、init_app は MT::Plugin::MyPlugin 名前空間に属します。そのため、一見正しく動作しているようですが、MT にしてみれば MT::Plugin::MyPlugin なんていうクラスは知らない訳ですから、init_app がコールバックされません。これに気がつくまで 1 時間浪費しました orz 普通にテンプレートタグを拡張するようなプラグインでは問題になりませんが、少し複雑なことをやろうとして MT::Plugin のメソッドをオーバーライドするような時に問題になります。反対に、package で宣言した名前で new しておけば、どんな場合でも問題は発生しません。
以上を踏まえてプラグインの初期化コードを書くと以下のようになるでしょうか。
### src.3 package MT::Plugin::SKR::MyPlugin; use base qw( MT::Plugin ); my $plugin = __PACKAGE__->new ({...}); MT->add_plugin ($plugin);
- package で宣言する名前空間には会社名や自分の名前を入れる。他社製の同じ名前のプラグインがあっても名前空間が衝突しにくい。
- new に指定するクラス名は、特殊リテラル __PACKAGE__ を利用する。package 名を変更した時の変更忘れを防げる。DRY。