看起来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;
附录:我发现如果参数化角色消耗另一个参数化角色,那么$target_meta
在嵌套角色中实际上将是父角色(isa MooseX::Role::Parameterized::Meta::Role::Parameterized
)的元类,而不是消费类(isa 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!