Extracting the metaclass from a parameterized role
(Apologies if this is a dupe; I've tried sending this a few times today and have been experiencing issues with my msmtp configs.) tl;dr version: no questions here, just some discoveries that I wanted to spread a little farther and get google-indexed :) A while back I posted on Stack Overflow about extracting the metaclass from inside a role definition: http://stackoverflow.com/questions/1758884/how-can-i-access-the-meta-class-of-the-module-my-moose-role-is-being-applied-to ... which lead me to MooseX::Role::Parameterized (a continued two big thumbs up BTW), which lead me to a few discoveries that I wanted to mention here for posterity: 1. $args{consumer} only contains the metaclass of the target class at the first level of parameterization. If the parameterized role (Role::A) is further including another parameterized role (Role::B), then $args{consumer} in Role::B will actually contain the metaclass of the first parameterized role (MooseX::Role::Parameterized::Meta::Role::Parameterized), rather than (a possible subclass of) Moose::Meta::Class. In my application I'm nesting parameterized roles a few levels deep (the code is cleaner this way rather than having lots of nested if statements), so I've added a 'meta' parameter to the list of params being passed around: # when calling the sub-role: role { # isa Moose::Meta::Class my $meta = $args{consumer}; with Role::B => { # other parameters... metaclass => $meta, }; }; # and in the subrole: parameter metaclass => ( is => 'ro', isa => 'Moose::Meta::Class', required => 1, ); role { # isa Moose::Meta::Class my $meta = $params->metaclass; # isa Moose::Object my $consumer = $meta->name; }; 2. $args{consumer} won't even be a metaclass in the case of a role application to a specific object instance -- in this case it will be the type of the object being applied to. role { my $meta = $args{consumer}; $meta = $meta->meta if not $meta->isa('Moose::Meta::Class'); # isa Moose::Object my $consumer = $meta->name; }; Neither of these discoveries seemed obvious at first, so I wanted to pass it on to save others some heartache. I wonder if it might be useful to offer an API to extract the metaclass, as the code above has essentially become boilerplate for all my parameterized roles.. PS. Earlier today I was about to post here asking how one could apply a parameterized role to an object instance (as there was no example in the documentation), but in stepping through the Moose guts I discovered that indeed it is just as simple as for non-parameterized roles: my $foo = My::Foo->new(); Moose::Util::apply_all_roles($foo, My::Role, { parameter => 'value' });
Re: Extracting the metaclass from a parameterized role
Excerpts from Karen Etheridge's message of Fri Jan 15 19:57:14 -0500 2010: > ... which lead me to MooseX::Role::Parameterized (a continued two big > thumbs up BTW), which lead me to a few discoveries that I wanted to mention > here for posterity: I feel like a lot of this stuff works by accident and should either be explicitly forbidden or documented and cleaned up. Shawn, what do you think? It's your baby. hdp.
Extracting the metaclass from a parameterized role
tl;dr version: no questions here, just some discoveries that I wanted to spread a little farther and get google-indexed :) A while back I posted on Stack Overflow about extracting the metaclass of the currently-composing class from within the role definition: http://stackoverflow.com/questions/1758884/how-can-i-access-the-meta-class-of-the-module-my-moose-role-is-being-applied-to ... which lead me to MooseX::Role::Parameterized (a continued two big thumbs up BTW), which lead me to a few discoveries that I wanted to mention here for posterity: 1. $args{consumer} only contains the metaclass of the target class at the first level of parameterization. If the parameterized role (Role::A) is further including another parameterized role (Role::B), then $args{consumer} in Role::B will actually contain the metaclass of the first parameterized role (MooseX::Role::Parameterized::Meta::Role::Parameterized), rather than (a possible subclass of) Moose::Meta::Class. In my application I'm nesting parameterized roles a few levels deep (the code is cleaner this way rather than having lots of nested if statements), so I've added a 'meta' parameter to the list of params being passed around: # when calling the sub-role: role { # isa Moose::Meta::Class my $meta = $args{consumer}; with Role::B => { # other parameters... metaclass => $meta, }; }; # and in the subrole: parameter metaclass => ( is => 'ro', isa => 'Moose::Meta::Class', required => 1, ); role { # isa Moose::Meta::Class my $meta = $params->metaclass; }; 2. $args{consumer} won't even be a metaclass in the case of a role application to a specific object instance -- in this case it will be the type of the object being applied to. role { my $meta = $args{consumer}; $meta = $meta->meta if not $meta->isa('Moose::Meta::Class'); }; Neither of these discoveries seemed obvious at first, so I wanted to pass it on to save others some heartache. I wonder if the "true" metaclass should perhaps be available via some sort of method call, to avoid the backflips I've done above -- I'm using all the code above as boilerplate in all my parameterized roles now. PS. Earlier today I was about to post here asking how one could apply a parameterized role to an object instance (as there was no example in the documentation), but in stepping through the Moose guts I discovered that indeed it is just as simple as for non-parameterized roles: (oh happy day) my $foo = My::Foo->new(); Moose::Util::apply_all_roles($foo, My::Role, { parameter => 'value' });