Vincent
Vincent

Reputation: 67

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

I want to define a Role with a Class attribute and a builder. This fails whatever I've tried. I found two workaround:

  1. not using a class attribute but a plain attribute. It is problematic in my situation because I would like to modify the attribute (for all instances) in my real code.
  2. putting the builder into the class (instead of into the role). It is also problematic because it means modifying all classes that use this role.

Here is a minimal example:

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";

And the result:

$ 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

Update: I workaround it by adding 'lazy => 1'. Then it works perfectly. Without 'lazy', the builder is probably called before the role is fully imported.

Upvotes: 0

Views: 123

Answers (1)

Jonathan
Jonathan

Reputation: 343

Your use of lazy in this context isn't so much a workaround as the correct solution. MooseX::ClassAttribute is implemented as a Moose::Role. Before MyRole is fully composed into Appli, Perl calls class_has, which attempts to call _build_value as a method of the Appli class. Since that method hasn't yet been composed into Appli, MooseX::ClassAttribute dies with the above error. Using lazy defers evaluation of _build_value until after MyRole has been fully composed into Appli.

Upvotes: 2

Related Questions