Perl 如何访问我的Moose角色所应用模块的元类?
我在类中的一些访问器方法周围使用一些包装器行为。我想将这个角色应用于许多模块,每个模块都有一组不同的属性,我想包装这些属性的访问器。是否有方法从角色内部访问所应用模块的元类?i、 例如:Perl 如何访问我的Moose角色所应用模块的元类?,perl,roles,moose,Perl,Roles,Moose,我在类中的一些访问器方法周围使用一些包装器行为。我想将这个角色应用于许多模块,每个模块都有一组不同的属性,我想包装这些属性的访问器。是否有方法从角色内部访问所应用模块的元类?i、 例如: package My::Foo; use Moose; with 'My::Role::X'; has [ qw(attr1 attr2) ] => ( is => 'rw', # ... ); has 'fields' => ( is => 'bare', isa
package My::Foo;
use Moose;
with 'My::Role::X';
has [ qw(attr1 attr2) ] => (
is => 'rw', # ...
);
has 'fields' => (
is => 'bare', isa => 'ArrayRef[Str]',
default => sub { [qw(attr1 attr2) ] },
);
1;
package My::Role::X;
use Moose::Role;
# this should be a Moose::Meta::Class object
my $target_meta = '????';
# 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;
它看起来会起作用:
普通角色可能要求其使用者具有特定的方法名称列表。由于参数化角色可以直接访问其使用者,因此如果使用者不满足您的需要,您可以对其进行检查并抛出错误
角色专门化的细节不会被扩展的类看到;它甚至不需要传递任何参数,只需要知道要传递给角色的参数(要包装的字段列表)。唯一的关键是必须在类上定义相关属性之后使用角色
因此,消费类和角色的定义如下:
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\u meta
实际上将是父角色的元类(isaMooseX::role::parameterized::meta::role::parameterized
),而不是消费类的元类(isaMoose::Meta::Class
)。为了派生正确的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\u 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!
角色{
我的$params=shift;
我的%args=@;
#可以是Moose::Meta::类,也可以是消费我们的对象
my$meta=$args{consumer};
$meta =元元>元,如果不是元-> ISA(“MOOSE::Meta::类”),这是模块所写的事情之一。注意:我不再考虑上面的“最佳实践”,并且确实重构了所有这(AB)使用的MXRP。IMHO,如果您需要从一个角色中访问<代码>元Meta <代码>,那么您的设计中就有一些臭名昭著的东西。