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.'
16 . ' Using this API will throw an error in Moose 2.0200.'
19 goto &apply_metaroles;
25 _fixup_old_style_args(\%args);
30 : Class::MOP::class_of( $args{for} );
32 if ( $for->isa('Moose::Meta::Role') ) {
33 return _make_new_metaclass( $for, $args{role_metaroles}, 'role' );
36 return _make_new_metaclass( $for, $args{class_metaroles}, 'class' );
40 sub _fixup_old_style_args {
43 return if $args->{class_metaroles} || $args->{role_metaroles};
45 Moose::Deprecated::deprecated(
46 feature => 'pre-0.94 MetaRole API',
48 'The old Moose::Util::MetaRole API (before version 0.94) has been deprecated.'
49 . ' Using this API will throw an error in Moose 2.0200.'
52 $args->{for} = delete $args->{for_class}
53 if exists $args->{for_class};
56 attribute_metaclass_roles
57 method_metaclass_roles
58 wrapped_method_metaclass_roles
59 instance_metaclass_roles
60 constructor_class_roles
61 destructor_class_roles
64 application_to_class_class_roles
65 application_to_role_class_roles
66 application_to_instance_class_roles
67 application_role_summation_class_roles
71 = blessed $args->{for}
73 : Class::MOP::class_of( $args->{for} );
76 if ( $for->isa('Moose::Meta::Class') ) {
77 $top_key = 'class_metaroles';
79 $args->{class_metaroles}{class} = delete $args->{metaclass_roles}
80 if exists $args->{metaclass_roles};
83 $top_key = 'role_metaroles';
85 $args->{role_metaroles}{role} = delete $args->{metaclass_roles}
86 if exists $args->{metaclass_roles};
89 for my $old_key (@old_keys) {
90 my ($new_key) = $old_key =~ /^(.+)_(?:class|metaclass)_roles$/;
92 $args->{$top_key}{$new_key} = delete $args->{$old_key}
93 if exists $args->{$old_key};
99 sub _make_new_metaclass {
104 return $for unless keys %{$roles};
107 = exists $roles->{$primary}
108 ? _make_new_class( ref $for, $roles->{$primary} )
113 for my $key ( grep { $_ ne $primary } keys %{$roles} ) {
114 my $attr = first {$_}
115 map { $for->meta->find_attribute_by_name($_) } (
120 my $reader = $attr->get_read_method;
122 $classes{ $attr->init_arg }
123 = _make_new_class( $for->$reader(), $roles->{$key} );
126 my $new_meta = $new_metaclass->reinitialize( $for, %classes );
131 sub apply_base_class_roles {
134 my $for = $args{for} || $args{for_class};
136 my $meta = Class::MOP::class_of($for);
138 my $new_base = _make_new_class(
141 [ $meta->superclasses() ],
144 $meta->superclasses($new_base)
145 if $new_base ne $meta->name();
148 sub _make_new_class {
149 my $existing_class = shift;
151 my $superclasses = shift || [$existing_class];
153 return $existing_class unless $roles;
155 my $meta = Class::MOP::Class->initialize($existing_class);
157 return $existing_class
158 if $meta->can('does_role') && all { $meta->does_role($_) }
159 grep { !ref $_ } @{$roles};
161 return Moose::Meta::Class->create_anon_class(
162 superclasses => $superclasses,
170 # ABSTRACT: Apply roles to any metaclass, as well as the object base class
176 package MyApp::Moose;
180 use Moose::Util::MetaRole;
182 use MyApp::Role::Meta::Class;
183 use MyApp::Role::Meta::Method::Constructor;
184 use MyApp::Role::Object;
186 Moose::Exporter->setup_import_methods( also => 'Moose' );
192 Moose->init_meta(%args);
194 Moose::Util::MetaRole::apply_metaroles(
195 for => $args{for_class},
197 class => => ['MyApp::Role::Meta::Class'],
198 constructor => ['MyApp::Role::Meta::Method::Constructor'],
202 Moose::Util::MetaRole::apply_base_class_roles(
203 for => $args{for_class},
204 roles => ['MyApp::Role::Object'],
207 return $args{for_class}->meta();
212 This utility module is designed to help authors of Moose extensions
213 write extensions that are able to cooperate with other Moose
214 extensions. To do this, you must write your extensions as roles, which
215 can then be dynamically applied to the caller's metaclasses.
217 This module makes sure to preserve any existing superclasses and roles
218 already set for the meta objects, which means that any number of
219 extensions can apply roles in any order.
223 The easiest way to use this module is through L<Moose::Exporter>, which can
224 generate the appropriate C<init_meta> method for you, and make sure it is
225 called when imported.
229 This module provides two functions.
231 =head2 apply_metaroles( ... )
233 This function will apply roles to one or more metaclasses for the specified
234 class. It will return a new metaclass object for the class or role passed in
237 It accepts the following parameters:
243 This specifies the class or for which to alter the meta classes. This can be a
244 package name, or an appropriate meta-object (a L<Moose::Meta::Class> or
245 L<Moose::Meta::Role>).
247 =item * class_metaroles => \%roles
249 This is a hash reference specifying which metaroles will be applied to the
250 class metaclass and its contained metaclasses and helper classes.
252 Each key should in turn point to an array reference of role names.
254 It accepts the following keys:
276 =item * role_metaroles => \%roles
278 This is a hash reference specifying which metaroles will be applied to the
279 role metaclass and its contained metaclasses and helper classes.
281 It accepts the following keys:
291 =item required_method
293 =item conflicting_method
295 =item application_to_class
297 =item application_to_role
299 =item application_to_instance
301 =item application_role_summation
307 =head2 apply_base_class_roles( for => $class, roles => \@roles )
309 This function will apply the specified roles to the object's base class.
313 See L<Moose/BUGS> for details on reporting bugs.