Fix typo in my own name
[gitmo/Moose.git] / lib / Moose / Util / MetaRole.pm
CommitLineData
231be3be 1package Moose::Util::MetaRole;
2
3use strict;
4use warnings;
5
6use List::MoreUtils qw( all );
7
8f05895e 8my @Classes = qw( constructor_class destructor_class error_class );
9
231be3be 10sub apply_metaclass_roles {
11 my %options = @_;
12
13 my $for = $options{for_class};
14
9eaf623d 15 my %old_classes
16 = map { $_ => $for->meta->$_ } grep { $for->meta->can($_) } @Classes;
231be3be 17
8f05895e 18 my $meta = _make_new_metaclass( $for, \%options );
231be3be 19
9eaf623d 20 for my $c ( grep { $meta->can($_) } @Classes ) {
8f05895e 21 if ( $options{ $c . '_roles' } ) {
22 my $class = _make_new_class(
23 $meta->$c(),
24 $options{ $c . '_roles' }
25 );
231be3be 26
8f05895e 27 $meta->$c($class);
28 }
29 else {
30 $meta->$c( $old_classes{$c} );
31 }
231be3be 32 }
33
34 return $meta;
35}
36
37sub _make_new_metaclass {
38 my $for = shift;
39 my $options = shift;
40
41 return $for->meta()
42 unless grep { exists $options->{ $_ . '_roles' } }
43 qw(
44 metaclass
45 attribute_metaclass
46 method_metaclass
47 instance_metaclass
48 );
49
50 my $new_metaclass
51 = _make_new_class( ref $for->meta(), $options->{metaclass_roles} );
52
53 my $old_meta = $for->meta();
54
72d15b83 55 # This could get called for a Moose::Meta::Role as well as a Moose::Meta::Class
231be3be 56 my %classes = map {
57 $_ => _make_new_class( $old_meta->$_(), $options->{ $_ . '_roles' } )
72d15b83 58 }
59 grep { $old_meta->can($_) }
60 qw(
231be3be 61 attribute_metaclass
62 method_metaclass
63 instance_metaclass
64 );
65
66 return $new_metaclass->reinitialize( $for, %classes );
67}
68
69sub apply_base_class_roles {
70 my %options = @_;
71
72 my $for = $options{for_class};
73
74 my $meta = $for->meta();
75
76 my $new_base = _make_new_class(
77 $for,
78 $options{roles},
79 [ $meta->superclasses() ],
80 );
81
82 $meta->superclasses($new_base)
83 if $new_base ne $meta->name();
84}
85
86sub _make_new_class {
87 my $existing_class = shift;
88 my $roles = shift;
89 my $superclasses = shift || [$existing_class];
90
91 return $existing_class unless $roles;
92
8f05895e 93 my $meta = Class::MOP::Class->initialize($existing_class);
231be3be 94
95 return $existing_class
96 if $meta->can('does_role') && all { $meta->does_role($_) } @{$roles};
97
98 return Moose::Meta::Class->create_anon_class(
99 superclasses => $superclasses,
100 roles => $roles,
101 cache => 1,
102 )->name();
103}
104
1051;
c59bc009 106
107__END__
108
109=head1 NAME
110
111Moose::Util::MetaRole - Apply roles to any metaclass, as well as the object base class
112
113=head1 SYNOPSIS
114
115 package MyApp::Moose;
116
117 use strict;
118 use warnings;
119
120 use Moose ();
121 use Moose::Exporter;
122 use Moose::Util::Meta::Role;
123
124 use MyApp::Role::Meta::Class;
125 use MyApp::Role::Meta::Method::Constructor;
126 use MyApp::Role::Object;
127
128 Moose::Exporter->setup_import_methods( also => 'Moose' );
129
130 sub init_meta {
131 shift;
132 my %options = @_;
133
134 Moose->init_meta(%options);
135
136 Moose::Util::MetaRole::apply_metaclass_roles(
137 for_class => $options{for_class},
138 metaclass_roles => ['MyApp::Role::Meta::Class'],
139 constructor_class_roles => ['MyApp::Role::Meta::Method::Constructor'],
140 );
141
142 Moose::Util::MetaRole::apply_base_class_roles(
143 for_class => $options{for_class},
144 roles => ['MyApp::Role::Object'],
145 );
146
147 return $options{for_class}->meta();
148 }
149
150=head1 DESCRIPTION
151
dd9a6d87 152B<The whole concept behind this module is still considered
153experimental, and it could go away in the future!>
154
c59bc009 155This utility module is designed to help authors of Moose extensions
156write extensions that are able to cooperate with other Moose
157extensions. To do this, you must write your extensions as roles, which
158can then be dynamically applyied to the caller's metaclasses.
159
160This module makes sure to preserve any existing superclasses and roles
161already set for the meta objects, which means that any number of
162extensions can apply roles in any order.
163
164=head1 USAGE
165
166B<It is very important that you only call this module's functions when
167your module is imported by the caller>. The process of applying roles
168to the metaclass reinitializes the metaclass object, which wipes out
169any existing attributes already defined. However, as long as you do
170this when your module is imported, the caller should not have any
171attributes defined yet.
172
173The easiest way to ensure that this happens is to use
174L<Moose::Exporter> and provide an C<init_meta> method that will be
175called when imported.
176
177=head1 FUNCTIONS
178
179This module provides two functions.
180
181=head2 apply_metaclass_roles( ... )
182
183This function will apply roles to one or more metaclasses for the
184specified class. It accepts the following parameters:
185
186=over 4
187
188=item * for_class => $name
189
190This specifies the class for which to alter the meta classes.
191
192=item * metaclass_roles => \@roles
193
194=item * attribute_metaclass_roles => \@roles
195
196=item * method_metaclass_roles => \@roles
197
198=item * instance_metaclass_roles => \@roles
199
200=item * constructor_class_roles => \@roles
201
202=item * destructor_class_roles => \@roles
203
204These parameter all specify one or more roles to be applied to the
205specified metaclass. You can pass any or all of these parameters at
206once.
207
208=back
209
210=head2 apply_base_class_roles( for_class => $class, roles => \@roles )
211
212This function will apply the specified roles to the object's base class.
213
eaeacdd6 214=head1 PROBLEMS WITH METACLASS ROLES AND SUBCLASS
215
216Because of the way this module works, there is an ordering problem
217which occurs in certain situations. This sequence of events causes an
218error:
219
220=over 4
221
222=item 1.
223
224There is a class (ClassA) which uses some extension(s) that apply
225roles to the metaclass.
226
227=item 2.
228
229You have another class (ClassB) which wants to subclass ClassA and
230apply some more extensions.
231
232=back
233
234Normally, the call to C<extends> will happen at run time, I<after> the
235additional extensions are applied. This causes an error when we try to
236make the metaclass for ClassB compatible with the metaclass for
237ClassA.
238
239We hope to be able to fix this in the future.
240
241For now the workaround is for ClassB to make sure it extends ClassA
242I<before> it loads extensions:
243
244 package ClassB;
245
246 use Moose;
247
248 BEGIN { extends 'ClassA' }
249
250 use MooseX::SomeExtension;
251
c59bc009 252=head1 AUTHOR
253
254Dave Rolsky E<lt>autarch@urth.orgE<gt>
255
256=head1 COPYRIGHT AND LICENSE
257
258Copyright 2008 by Infinity Interactive, Inc.
259
260L<http://www.iinteractive.com>
261
262This library is free software; you can redistribute it and/or modify
263it under the same terms as Perl itself.
264
265=cut