Devel::REPLのプラグイン(1)

めも。


Devel::REPLの、プラグインの呼び出しには(たぶん)2種類あって、
1つめがDevel::PERLに定義されてるメソッドをhookする(before,after,aroundする)。
こっちのタイプは単純にMoose::Roleを使ってる。
Plugin::Colorsとかだと、

package Devel::REPL::Plugin::Colors;

use Moose::Role;
...
around print => sub {
  my $orig = shift;
  my $self = shift;
  print {$self->out_fh} color($self->normal_color);
  $orig->($self, @_);
  print {$self->out_fh} color('reset');
};

みたいな感じでDevel::REPL->print()にhookしてる。

このaroundとかが、どこでDevel::REPL->print()に設定されるかというと、
MooseX::Object::Pluggable->_load_and_apply_role()の中の、$role->meta->apply($self)の部分。

package MooseX::Object::Pluggable;
...
sub _load_and_apply_role{
    my ($self, $role) = @_;
    die("You must provide a role name") unless $role;

    eval { Class::MOP::load_class($role) };
    confess("Failed to load role: ${role} $@") if $@;

    carp("Using 'override' is strongly discouraged and may not behave ".
         "as you expect it to. Please use 'around'")
        if scalar keys %{ $role->meta->get_override_method_modifiers_map };

    $role->meta->apply( $self );
    return 1;
}

$roleには"Devel::Plugin::Colors"とか、プラグインクラス名が入ってて、
$role->metaはMoose::Meta::Role。たぶんuse Moose::Roleするとexportしてくれるのかな。んでapply()する。
Moose::Meta::Role->apply()は既存のインスタンスとかクラスとかにMixin(?)するメソッドで、
定義されてるメソッドだけじゃなくてaroundとかbeforeとかも一緒にやってくれる。


わすれないようにサンプル。

use strict;
use warnings;

package Foo;

sub new { bless {}, shift }
sub hello { print "hello\n" }

package Foo::Plugin::Hello;
use Moose::Role;

before 'hello' => sub {
  print "before hello\n"; 
};

sub buy {
  print "buy\n"
}

package main;

my $foo = Foo->new;
Foo::Plugin::Hello->meta->apply($foo);
$foo->hello();
$foo->buy();

#結果
before hello
hello
buy

まあこんな感じでDevel::REPLのメソッドが呼ばれるたびに、Pluginのaroundやらbeforeやらが呼ばれるみたい。