I'm using Moose roles to apply some wrapper behaviour around some accessor methods in a class. I want to apply this role to a number of modules, each of which have a different set of attributes whose accessors I want to wrap. Is there a way to access the meta class of the module being applied to, from within the role? i.e. something like this:
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;
It looks like MooseX::Role::Parameterized will do the trick:
The details of the role specialization is kept from the class being augmented; it doesn't even need to pass any parameters all it needs to know is what parameters (the list of fields to wrap) to pass to the role. The only key is that the role must be used after the relevant attributes have been defined on the class.
Therefore, the consumed class and the role become defined like so:
Addendum: I have discovered that if a parameterized role consumes another parameterized role, then
$target_meta
in the nested role will actually be the meta-class of the parent role (isaMooseX::Role::Parameterized::Meta::Role::Parameterized
), rather than the meta-class of the consuming class (isaMoose::Meta::Class
). In order for the proper meta-class to be derived, you need to explicitly pass it as a parameter. I have added this to all my parameterized roles as a "best practice" template:Addendum 2: I have further discovered that if the role is being applied to an object instance, as opposed to a class, then
$target_meta
in the role will actually be the class of the object doing the consuming:Therefore, this code is necessary when extracting the meta-class at the start of the parameterized role: