On Sun, May 09, 2010 at 04:56:21AM -0400, Mike Friedman wrote: > Greetings, > > I've got a class X. X has a metaclass M. I want M to make X do role Y. > That's it. I have been banging my head against a half dozen Moose > documentation pages but can't seem to puzzle out this seemingly simple > operation.
So... is there a reason that using 'with' in the class definition isn't sufficient? (The subject line mentions runtime application, but your code sample below is a Moose::Exporter package, which seems to be counter to that idea.) > Which one of these things do I need to use? Moose::Exporter, > Moose::Util::MetaRole? (This seems to be about meta roles rather than > roles, but I don't _think_ I need to use a meta role to just make a > class consume a given role, or do I?) Yes, MetaRole is for metaclass roles, as the name implies. Moose::Exporter is for if you want to make custom exporters (this can do what you want, see below, but there's really no need for it unless you're also doing other things like exporting functions or applying metaclass roles). > I also found this thing: Moose::Meta::Role::Application::ToClass which > sounds really nice but has zero documentation. I looked at the source > but, of course, I have no idea WTF is going on. This is an entirely internal class that you really should have no reason to use directly. > Here is one thing I tried, which doesn't seem to complain, but also > doesn't seem to apply the role. (In my test, the class does not cause > an exception even though it fails to implement a method required by > the role.) You were close - the reason this doesn't work is because initialize is for creating a metaclass for a class that doesn't yet have one; it's typically not very useful for roles. The thing you're looking for is Moose::Util::apply_all_roles, like so: package MyApp; use Moose (); use Moose::Exporter; use MyApp::Meta::Class; Moose::Exporter->setup_import_methods( with_meta => [ 'handler' ], also => 'Moose', ); sub init_meta { my $class = shift; my %options = @_; my $meta = Moose->init_meta( @_ , metaclass => 'MyApp::Meta::Class' ); Moose::Util::apply_all_roles( $options{for_class}, 'MyApp::Role::App' ); return $meta; } ... 1; On another note, it's typically preferable to create metaclass traits rather than writing an actual metaclass yourself - this makes it work more nicely with other extensions (such as various MooseX modules). It probably won't harm anything if this is an app-specific metaclass extension that you're only writing one variation of, but it also wouldn't harm anything for it to be a role, and getting used to the good practice would probably help in the future. In that case, the exporter would look something like: package MyApp; use Moose (); use Moose::Exporter; my ($import, $unimport, $init_meta) = Moose::Exporter->setup_import_methods( with_meta => [ 'handler' ], also => 'Moose', install => [ 'import', 'unimport' ], class_metaroles => { class => [ 'MyApp::Meta::Trait::Class' ], }, ); sub init_meta { my ($package, %options) = @_; Moose->init_meta(%options); Moose::Util::apply_all_roles($options{for_class}, 'MyApp::Role::App'); goto $init_meta; } ... 1; -doy