为什么Moose Role不排除排除特定角色属性?

时间:2014-12-19 21:27:36

标签: perl class moose mop

我有一个Moose :: Role(其中包括):

package My::Role;

use strict;
use warnings;

use Moose::Role;
use MooseX::ClassAttribute;

class_has table => (
    is => 'ro'
    isa => 'Str',
    lazy => 1,
);

has id => (
    is => 'ro',
    isa => 'Int',
    predicate => 'has_id',
    writer => '_id',
    required => 0,
);

has other => (
    is => 'rw',
    isa => 'Int',
);

...

1;

然后,在使用该角色的模块中,

package Some::Module;

with 'My::Role' => {
    -excludes => [qw( id table )]
};

has module_id => (
    is => 'ro',
    isa => 'Int',
);
...

1;

然后,在脚本中,我实例化了Some :: Module的实例:

my $some_module = Some::Module->new({ other => 3 });

我可以致电

$some_module->id;  # I'd expect this to die but returns undef.

但是,我无法拨打电话

$some_module->table;  # this dies as I'd expect

因为我预计调用$ some_module-> table会导致脚本停止。调用 $ some_module-> id不会。

当我使用Data :: Dumper转储$ some_module元的属性列表时 class它显示id属性已定义但table属性不是。

有谁知道为什么' id'不会排除角色中定义的属性 来自元类但是表格' class_attribute会吗?问题是,如 如上所述,Some :: Module的用户可以在应用时调用id() 需要调用module_id()。

此外,在转储$ some_module对象时,' id'不会出现在转储中。

编辑:

这是一个说明问题的示例。我已定义了一个角色 实现一个id,然后我在My :: Product包中使用该角色。 但是我在消费它时排除了id。当我打印属性时 从元对象中可以看出它实际上就存在了。我受到了印象 在消费它时从角色中排除id不会允许它被调用。 我希望它不仅不会出现在元对象中,而且还会死掉 试图打电话。

#!/usr/bin/perl

package My::Model;

use Moose::Role;
use MooseX::ClassAttribute;

class_has first_name => (
    is  => 'rw',
    isa => 'Str',
);

class_has last_name => (
    is  => 'rw',
    isa => 'Str',
);

has id => (
    is        => 'rw',
    isa       => 'Int',
    predicate => 'has_id',
    writer    => '_id',
    required  => 0,
);

1;

package My::Product;

use Moose;
use Class::MOP::Class;
use Data::Dumper;

with 'My::Model' => { -excludes => [ qw( first_name id ) ], };

has count => (
    is => 'rw',
    isa => 'Int',
);

has product_id => (
    is        => 'ro',
    isa       => 'Int',
    required  => 0,
    predicate => 'has_product_id'
);

sub create_classes {
    my @list = ();
    foreach my $subclass (qw( one two three )) {
          Class::MOP::Class->create(
            "My::Product::"
              . $subclass => (
                superclasses => ["My::Product"],
              )
          );
        push @list, "My::Product::$subclass";
    }

    return \@list;
}

__PACKAGE__->meta()->make_immutable;

1;

package main;

use strict;
use warnings;
use Data::Dumper;

my $product = My::Product->new();
my $classes = $product->create_classes();

my @class_list;
foreach my $class ( @{ $classes } ) {
    my $temp = $class->new( { count => time } );
    $temp->first_name('Don');
    $temp->last_name('MouseCop');
    push @class_list, $temp;
}

warn "what is the id for the first obj => " . $class_list[0]->id ;
warn "what is the first_name for the first obj => " . $class_list[0]->first_name ;
warn "what is the last_name for the first obj => " . $class_list[0]->last_name ;

warn "\nAttribute list:\n";
foreach my $attr ( $class_list[2]->meta->get_all_attributes ) {
    warn "name => " . $attr->name;
#    warn Dumper( $attr );
}

编辑2: 在转储$ attr时,我发现first_name和id在method_exclusions中。

 'role_applications' => [
                        bless( {
                                 'class' => $VAR1->{'associated_class'},
                                 'role' => $VAR1->{'associated_class'}{'roles'}[0],
                                 'method_aliases' => {},
                                 'method_exclusions' => [
                                                          'first_name',
                                                          'id'
                                                        ]
                               }, 'Moose::Meta::Class::__ANON__::SERIAL::8' )
                      ]

1 个答案:

答案 0 :(得分:1)

我不知道这个内部是如何工作的,但我相信这与你排除的两个方法是属性方法的事实有关。我能找到的唯一相关文章是here,其中包含:

  

角色属性与类的属性类似,除外   它们实际上并未应用。 这意味着那些方法   属性访问器生成的角色不会在角色中生成,   但只有在角色应用于班级后才会创建

因此,我猜测问题在于,在构建类时,应用角色(并排除方法),但之后应用角色的属性和访问方法(包括id和first_name)。

要演示,请将id属性更改为_id,为其指定一个不同的编写器并创建一个id sub来访问它:

# This replaces id
has _id => (
    is        => 'rw',
    isa       => 'Int',
    writer => 'set_id',
    required  => 0,
);

sub id {
    my $self = shift;
    return $self->_id();
}

该脚本现在会因异常而死:

Can't locate object method "id" via package "My::Product::one" at ./module.pm line 89.