1 package Moose::Util::MetaRole;
5 use Scalar::Util 'blessed';
7 use List::MoreUtils qw( all );
8 use List::Util qw( first );
11 sub apply_metaclass_roles {
12 Moose::Deprecated::deprecated(
13 feature => 'pre-0.94 MetaRole API',
15 'The old Moose::Util::MetaRole API (before version 0.94) has been deprecated'
18 goto &apply_metaroles;
24 _fixup_old_style_args(\%args);
29 : Class::MOP::class_of( $args{for} );
31 if ( $for->isa('Moose::Meta::Role') ) {
32 return _make_new_metaclass( $for, $args{role_metaroles}, 'role' );
35 return _make_new_metaclass( $for, $args{class_metaroles}, 'class' );
39 sub _fixup_old_style_args {
42 return if $args->{class_metaroles} || $args->{role_metaroles};
44 Moose::Deprecated::deprecated(
45 feature => 'pre-0.94 MetaRole API',
47 'The old Moose::Util::MetaRole API (before version 0.94) has been deprecated'
50 $args->{for} = delete $args->{for_class}
51 if exists $args->{for_class};
54 attribute_metaclass_roles
55 method_metaclass_roles
56 wrapped_method_metaclass_roles
57 instance_metaclass_roles
58 constructor_class_roles
59 destructor_class_roles
62 application_to_class_class_roles
63 application_to_role_class_roles
64 application_to_instance_class_roles
65 application_role_summation_class_roles
69 = blessed $args->{for}
71 : Class::MOP::class_of( $args->{for} );
74 if ( $for->isa('Moose::Meta::Class') ) {
75 $top_key = 'class_metaroles';
77 $args->{class_metaroles}{class} = delete $args->{metaclass_roles}
78 if exists $args->{metaclass_roles};
81 $top_key = 'role_metaroles';
83 $args->{role_metaroles}{role} = delete $args->{metaclass_roles}
84 if exists $args->{metaclass_roles};
87 for my $old_key (@old_keys) {
88 my ($new_key) = $old_key =~ /^(.+)_(?:class|metaclass)_roles$/;
90 $args->{$top_key}{$new_key} = delete $args->{$old_key}
91 if exists $args->{$old_key};
97 sub _make_new_metaclass {
102 return $for unless keys %{$roles};
105 = exists $roles->{$primary}
106 ? _make_new_class( ref $for, $roles->{$primary} )
111 for my $key ( grep { $_ ne $primary } keys %{$roles} ) {
112 my $attr = first {$_}
113 map { $for->meta->find_attribute_by_name($_) } (
118 my $reader = $attr->get_read_method;
120 $classes{ $attr->init_arg }
121 = _make_new_class( $for->$reader(), $roles->{$key} );
124 my $new_meta = $new_metaclass->reinitialize( $for, %classes );
129 sub apply_base_class_roles {
132 my $for = $args{for} || $args{for_class};
134 my $meta = Class::MOP::class_of($for);
136 my $new_base = _make_new_class(
139 [ $meta->superclasses() ],
142 $meta->superclasses($new_base)
143 if $new_base ne $meta->name();
146 sub _make_new_class {
147 my $existing_class = shift;
149 my $superclasses = shift || [$existing_class];
151 return $existing_class unless $roles;
153 my $meta = Class::MOP::Class->initialize($existing_class);
155 return $existing_class
156 if $meta->can('does_role') && all { $meta->does_role($_) }
157 grep { !ref $_ } @{$roles};
159 return Moose::Meta::Class->create_anon_class(
160 superclasses => $superclasses,
168 # ABSTRACT: Apply roles to any metaclass, as well as the object base class
174 package MyApp::Moose;
178 use Moose::Util::MetaRole;
180 use MyApp::Role::Meta::Class;
181 use MyApp::Role::Meta::Method::Constructor;
182 use MyApp::Role::Object;
184 Moose::Exporter->setup_import_methods( also => 'Moose' );
190 Moose->init_meta(%args);
192 Moose::Util::MetaRole::apply_metaroles(
193 for => $args{for_class},
195 class => => ['MyApp::Role::Meta::Class'],
196 constructor => ['MyApp::Role::Meta::Method::Constructor'],
200 Moose::Util::MetaRole::apply_base_class_roles(
201 for => $args{for_class},
202 roles => ['MyApp::Role::Object'],
205 return $args{for_class}->meta();
210 This utility module is designed to help authors of Moose extensions
211 write extensions that are able to cooperate with other Moose
212 extensions. To do this, you must write your extensions as roles, which
213 can then be dynamically applied to the caller's metaclasses.
215 This module makes sure to preserve any existing superclasses and roles
216 already set for the meta objects, which means that any number of
217 extensions can apply roles in any order.
221 The easiest way to use this module is through L<Moose::Exporter>, which can
222 generate the appropriate C<init_meta> method for you, and make sure it is
223 called when imported.
227 This module provides two functions.
229 =head2 apply_metaroles( ... )
231 This function will apply roles to one or more metaclasses for the specified
232 class. It will return a new metaclass object for the class or role passed in
235 It accepts the following parameters:
241 This specifies the class or for which to alter the meta classes. This can be a
242 package name, or an appropriate meta-object (a L<Moose::Meta::Class> or
243 L<Moose::Meta::Role>).
245 =item * class_metaroles => \%roles
247 This is a hash reference specifying which metaroles will be applied to the
248 class metaclass and its contained metaclasses and helper classes.
250 Each key should in turn point to an array reference of role names.
252 It accepts the following keys:
274 =item * role_metaroles => \%roles
276 This is a hash reference specifying which metaroles will be applied to the
277 role metaclass and its contained metaclasses and helper classes.
279 It accepts the following keys:
289 =item required_method
291 =item conflicting_method
293 =item application_to_class
295 =item application_to_role
297 =item application_to_instance
299 =item application_role_summation
305 =head2 apply_base_class_roles( for => $class, roles => \@roles )
307 This function will apply the specified roles to the object's base class.
311 See L<Moose/BUGS> for details on reporting bugs.