首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何访问我的Moose角色要应用到的模块的元类?

如何访问我的Moose角色要应用到的模块的元类?
EN

Stack Overflow用户
提问于 2009-11-19 04:36:19
回答 1查看 1.5K关注 0票数 7

我正在使用Moose roles对类中的一些访问器方法应用一些包装器行为。我想将这个角色应用于许多模块,每个模块都有一组不同的属性,我想包装这些属性的访问器。有没有办法从角色内部访问要应用的模块的元类?例如,类似于以下内容:

代码语言:javascript
运行
复制
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;
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2009-11-19 05:21:37

看起来MooseX::Role::Parameterized可以做到这一点:

普通角色可能要求其使用者具有特定的方法名列表。由于参数化角色可以直接访问其使用者,因此如果使用者不满足您的需求,则可以对其进行检查并引发错误。(link)

角色专门化的详细信息不会被添加到类中;它甚至不需要传递任何参数,只需要知道要传递给角色的参数(要包装的字段列表)。唯一的关键是角色必须在类上定义了相关属性之后才能使用。

因此,使用的类和角色的定义如下:

代码语言:javascript
运行
复制
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)的元类。为了派生正确的元类,您需要显式地将其作为参数传递。我已经将此作为“最佳实践”模板添加到我的所有参数化角色中:

代码语言:javascript
运行
复制
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实际上将是执行消费的对象的类:

代码语言:javascript
运行
复制
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

因此,在提取参数化角色开头的元类时,此代码是必需的:

代码语言:javascript
运行
复制
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!
票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1758884

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档