MooseX :: Role ::Parameterizedがトリックを実行するように見えます:
通常のロールでは、コンシューマーがメソッド名の特定のリストを持っている必要があります。パラメータ化されたロールはそのコンシューマに直接アクセスできるため、コンシューマがニーズを満たさない場合は、ロールを検査してエラーをスローできます。(リンク)
役割の専門化の詳細は、拡張されるクラスから除外されます。パラメータを渡す必要はありません。知っておく必要があるのは、ロールに渡すパラメータ(ラップするフィールドのリスト)だけです。唯一の重要な点は、関連する属性がクラスで定義された後にロールを使用する必要があるということです。
したがって、消費されるクラスとロールは次のように定義されます。
package My::Foo;
use Moose;
my @fields = qw(attr1 attr2);
has \@fields => (
is => 'rw', # ...
);
has 'fields' => (
is => 'bare', isa => 'ArrayRef[Str]',
default => sub { \@fields },
);
with 'My::Role::X' => {};
1;
package My::Role::X;
use MooseX::Role::Parameterized;
role {
my $p = shift;
my %args = @_;
# this should be a Moose::Meta::Class object
my $target_meta = $args{consumer};
# get Class::MOP::Attribute object out of the metaclass
my $fields_attr = $target_meta->find_attribute_by_name('fields');
# extract the value of this attribute - should be a coderef
my $fields_to_modify = $fields_attr->default;
# evaluate the coderef to get the arrayref
$fields_to_modify = &$fields_to_modify if ref $fields_to_modify eq 'CODE';
around $_ => sub {
# ...
} for @$fields_to_modify;
};
1;
補遺:パラメーター化されたロールが別のパラメーター化されたロールを消費する場合、ネストされたロールには、消費クラス(isa)のメタクラスではなく、$target_meta
実際には親ロール(isa )のメタクラスが含まれることを発見しました。適切なメタクラスを派生させるには、それをパラメーターとして明示的に渡す必要があります。これを「ベストプラクティス」テンプレートとしてすべてのパラメーター化された役割に追加しました。MooseX::Role::Parameterized::Meta::Role::Parameterized
Moose::Meta::Class
package MyApp::Role::SomeRole;
use MooseX::Role::Parameterized;
# because we are used by an earlier role, meta is not actually the meta of the
# consumer, but of the higher-level parameterized role.
parameter metaclass => (
is => 'ro', isa => 'Moose::Meta::Class',
required => 1,
);
# ... other parameters here...
role {
my $params = shift;
my %args = @_;
# isa a Moose::Meta::Class
my $meta = $params->metaclass;
# class name of what is consuming us, om nom nom
my $consumer = $meta->name;
# ... code here...
}; # end role
no Moose::Role;
1;
補遺2 :クラスではなく、オブジェクトインスタンス$target_meta
にロールが適用されている場合、ロールには実際に消費を行うオブジェクトのクラスが含まれることをさらに発見しました。
package main;
use My::Foo;
use Moose::Util;
my $foo = My::Foo->new;
Moose::Util::apply_all_roles($foo, MyApp::Role::SomeRole, { parameter => 'value' });
package MyApp::Role::SomeRole;
use MooseX::Role::Parameterized;
# ... use same code as above (in addendum 1):
role {
my $meta = $args{consumer};
my $consumer = $meta->name; # fail! My::Foo does not implement the 'name' method
したがって、このコードは、パラメーター化された役割の開始時にメタクラスを抽出するときに必要です。
role {
my $params = shift;
my %args = @_;
# could be a Moose::Meta::Class, or the object consuming us
my $meta = $args{consumer};
$meta = $meta->meta if not $meta->isa('Moose::Meta::Class'); # <-- important!