Modifying attributes of a Moose::Role at runtime

384 views Asked by At

I have a Moose::Role that contains a network client as an attribute:

package Widget;
use Moose::Role;

has 'network_thingy' => (
    isa => Maybe[ThingyClient],
);

And of course, I have a couple concrete Moose classes which use this role:

package BlueWidget;
use Moose;
with 'Widget';

Now it comes to functional testing of the Widgets. We have the ability to create ThingyServer objects, and it would be much faster and overall excellent to directly use ThingyServer objects instead of spinning up a daemon and having a ThingyClient connect to it over the network. Since ThingyClient & ThingyServer conveniently have the exact same methods, this should be easily possible. But of course, Moose is demanding that I use a ThingyClient when the test eventually constructs a BlueWidget.

I did some research, and came across the Moose::Meta documentation. Seemed perfect! So here's the test code:

my $metarole = Moose::Meta::Role->initialize('Widget');

// first remove the old attribute
$metarole->remove_attribute('network_thingy');

I was going to add a new attribute, but I thought I'd check on the state of the role & class first. Now if I dump out the $metarole, it looks great. There's no network_thingy attribute anymore. But if I construct a BlueWidget class, or just peak inside the metaclass...

$metaclass = Moose::Meta::Class->initialize('BlueWidget');
diag Dumper ($metaclass);

... sure enough network_thingy is still there. This is not at all what I expected. How can I modify/remove/replace an attribute of the Widget role at runtime?

1

There are 1 answers

0
tobyink On

When a class consumes a role, attributes are copied from the role to the class. If you then change the attribute in the role, the copy in the class is unaffected.

So you would need to loop through the classes that have consumed the role, and change the attribute in each class. There's a consumers method in Moose::Meta::Role that could help you get a list of classes that have consumed the role, however it only covers classes that have directly consumed the role, and not, say, subclasses of those.

If the classes have been made immutable (__PACKAGE__->meta->make_immutable), you'll need to make them mutable again before you make modify the attribute.

Overall, it's probably a better idea to just alter the role module (i.e. edit the file); not attempt to tweak the attribute at run time. Maybe set isa to a duck_type type constraint?