在 Moose::Role 中为 MooseX::ClassAttribute 编写构建器

Writing a builder for a MooseX::ClassAttribute in a Moose::Role

我想定义一个具有 Class 属性和构建器的角色。无论我尝试过什么,这都失败了。我找到了两个解决方法:

  1. 不使用 class 属性,而是使用普通属性。在我的情况下这是有问题的,因为我想在我的真实代码中修改属性(对于所有实例)。
  2. 将生成器放入 class(而不是放入角色)。这也是有问题的,因为这意味着修改所有使用此角色的 classes。

这是一个最小的例子:

package MyRole;
use Moose::Role;
use MooseX::ClassAttribute;

sub _build_value {
    return "in MyRole";
}

class_has 'value' => (
    is => 'ro',
    isa => 'Str',
    builder => '_build_value',
);

1;

package Appli;
use Moose;

with 'MyRole';

1;

package main;

my $e=Appli->new();

print $e->value, "\n";

结果:

$ perl ./test.pl 
Appli does not support builder method '_build_value' for attribute 'value' at /usr/share/perl5/MooseX/ClassAttribute/Trait/Attribute.pm line 81.
Class::MOP::Class:::around(CODE(0x13d6c78), Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460), "Appli") called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 164
Moose::Meta::Class::__ANON__::SERIAL::11::_wrapped__call_builder(Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460), "Appli") called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 95
Moose::Meta::Class::__ANON__::SERIAL::11::_call_builder(Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460), "Appli") called at /usr/share/perl5/MooseX/ClassAttribute/Trait/Attribute.pm line 54
MooseX::ClassAttribute::Trait::Attribute::_initialize(Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/share/perl5/MooseX/ClassAttribute/Trait/Attribute.pm line 32
Class::MOP::Class:::after(Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 57
Moose::Meta::Class::__ANON__::SERIAL::11::_wrapped_attach_to_class(Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 95
Moose::Meta::Class::__ANON__::SERIAL::11::attach_to_class(Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/share/perl5/MooseX/ClassAttribute/Trait/Class.pm line 66
MooseX::ClassAttribute::Trait::Class::_attach_class_attribute(Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0), Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460)) called at /usr/share/perl5/MooseX/ClassAttribute/Trait/Mixin/HasClassAttributes.pm line 40
MooseX::ClassAttribute::Trait::Mixin::HasClassAttributes::add_class_attribute(Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0), Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460)) called at /usr/share/perl5/MooseX/ClassAttribute/Trait/Class.pm line 41
Class::MOP::Class:::around(CODE(0x17fae38), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0), Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 164
Moose::Meta::Class::__ANON__::SERIAL::10::_wrapped_add_class_attribute(Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0), Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 95
Moose::Meta::Class::__ANON__::SERIAL::10::add_class_attribute(Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0), Moose::Meta::Class::__ANON__::SERIAL::11=HASH(0x1982460)) called at /usr/share/perl5/MooseX/ClassAttribute/Trait/Application/ToClass.pm line 44
MooseX::ClassAttribute::Trait::Application::ToClass::_apply_class_attributes(Moose::Meta::Class::__ANON__::SERIAL::8=HASH(0x1a29940), Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/share/perl5/MooseX/ClassAttribute/Trait/Application.pm line 13
Class::MOP::Class:::after(Moose::Meta::Class::__ANON__::SERIAL::8=HASH(0x1a29940), Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 57
Moose::Meta::Class::__ANON__::SERIAL::8::_wrapped_apply_attributes(Moose::Meta::Class::__ANON__::SERIAL::8=HASH(0x1a29940), Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 95
Moose::Meta::Class::__ANON__::SERIAL::8::apply_attributes(Moose::Meta::Class::__ANON__::SERIAL::8=HASH(0x1a29940), Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Moose/Meta/Role/Application.pm line 59
Moose::Meta::Role::Application::apply(Moose::Meta::Class::__ANON__::SERIAL::8=HASH(0x1a29940), Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Moose/Meta/Role/Application/ToClass.pm line 31
Moose::Meta::Role::Application::ToClass::apply(Moose::Meta::Class::__ANON__::SERIAL::8=HASH(0x1a29940), Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class::__ANON__::SERIAL::10=HASH(0x19683d0)) called at /usr/share/perl5/MooseX/ClassAttribute/Trait/Application/ToClass.pm line 27
Class::MOP::Class:::around(CODE(0x14ae360), Moose::Meta::Class::__ANON__::SERIAL::8=HASH(0x1a29940), Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class=HASH(0x175d658), HASH(0x1a22b28)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 164
Moose::Meta::Class::__ANON__::SERIAL::8::_wrapped_apply(Moose::Meta::Class::__ANON__::SERIAL::8=HASH(0x1a29940), Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class=HASH(0x175d658), HASH(0x1a22b28)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Class/MOP/Method/Wrapped.pm line 95
Moose::Meta::Class::__ANON__::SERIAL::8::apply(Moose::Meta::Class::__ANON__::SERIAL::8=HASH(0x1a29940), Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class=HASH(0x175d658), HASH(0x1a22b28)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Moose/Meta/Role.pm line 472
Moose::Meta::Role::apply(Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x1a27298), Moose::Meta::Class=HASH(0x175d658)) called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Moose/Util.pm line 172
Moose::Util::_apply_all_roles(Moose::Meta::Class=HASH(0x175d658), undef, "MyRole") called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Moose/Util.pm line 114
Moose::Util::apply_all_roles(Moose::Meta::Class=HASH(0x175d658), "MyRole") called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Moose.pm line 59
Moose::with(Moose::Meta::Class=HASH(0x175d658), "MyRole") called at /usr/lib/x86_64-linux-gnu/perl5/5.22/Moose/Exporter.pm line 419
Moose::with("MyRole") called at ./test.pl line 20

更新: 我通过添加 'lazy => 1' 解决了这个问题。然后它完美地工作。如果没有 'lazy',构建器可能会在角色完全导入之前被调用。

您在此上下文中使用 lazy 与其说是解决方法,不如说是正确的解决方案。 MooseX::ClassAttribute 实现为 Moose::Role。在 MyRole 完全组成 Appli 之前,Perl 调用 class_has,它试图调用 _build_value 作为 Appli class 的方法。由于该方法尚未组成 Appli,因此 MooseX::ClassAttribute 因上述错误而终止。使用 lazy 延迟 _build_value 的评估,直到 MyRole 完全组成 Appli.