bump version to 0.89_02 and set release date
[gitmo/Moose.git] / lib / Moose / Util / MetaRole.pm
CommitLineData
231be3be 1package Moose::Util::MetaRole;
2
3use strict;
4use warnings;
5
6e56c6e0 6our $VERSION = '0.89_02';
ae18d5ec 7$VERSION = eval $VERSION;
8our $AUTHORITY = 'cpan:STEVAN';
9
231be3be 10use List::MoreUtils qw( all );
11
8f05895e 12my @Classes = qw( constructor_class destructor_class error_class );
13
231be3be 14sub apply_metaclass_roles {
15 my %options = @_;
16
17 my $for = $options{for_class};
18
9f605368 19 my %old_classes = map { $_ => Class::MOP::class_of($for)->$_ }
20 grep { Class::MOP::class_of($for)->can($_) }
21 @Classes;
231be3be 22
8f05895e 23 my $meta = _make_new_metaclass( $for, \%options );
231be3be 24
9eaf623d 25 for my $c ( grep { $meta->can($_) } @Classes ) {
8f05895e 26 if ( $options{ $c . '_roles' } ) {
27 my $class = _make_new_class(
28 $meta->$c(),
29 $options{ $c . '_roles' }
30 );
231be3be 31
8f05895e 32 $meta->$c($class);
33 }
34 else {
35 $meta->$c( $old_classes{$c} );
36 }
231be3be 37 }
38
39 return $meta;
40}
41
42sub _make_new_metaclass {
43 my $for = shift;
44 my $options = shift;
45
6fa72211 46 return Class::MOP::class_of($for)
231be3be 47 unless grep { exists $options->{ $_ . '_roles' } }
48 qw(
49 metaclass
50 attribute_metaclass
51 method_metaclass
8286fcd6 52 wrapped_method_metaclass
231be3be 53 instance_metaclass
d401dc20 54 application_to_class_class
55 application_to_role_class
56 application_to_instance_class
231be3be 57 );
58
aa1bb57e 59 my $old_meta = Class::MOP::class_of($for);
231be3be 60 my $new_metaclass
aa1bb57e 61 = _make_new_class( ref $old_meta, $options->{metaclass_roles} );
231be3be 62
72d15b83 63 # This could get called for a Moose::Meta::Role as well as a Moose::Meta::Class
231be3be 64 my %classes = map {
65 $_ => _make_new_class( $old_meta->$_(), $options->{ $_ . '_roles' } )
72d15b83 66 }
67 grep { $old_meta->can($_) }
68 qw(
231be3be 69 attribute_metaclass
70 method_metaclass
8286fcd6 71 wrapped_method_metaclass
231be3be 72 instance_metaclass
d401dc20 73 application_to_class_class
74 application_to_role_class
75 application_to_instance_class
231be3be 76 );
77
78 return $new_metaclass->reinitialize( $for, %classes );
79}
80
81sub apply_base_class_roles {
82 my %options = @_;
83
84 my $for = $options{for_class};
85
95f64261 86 my $meta = Class::MOP::class_of($for);
231be3be 87
88 my $new_base = _make_new_class(
89 $for,
90 $options{roles},
91 [ $meta->superclasses() ],
92 );
93
94 $meta->superclasses($new_base)
95 if $new_base ne $meta->name();
96}
97
98sub _make_new_class {
99 my $existing_class = shift;
100 my $roles = shift;
101 my $superclasses = shift || [$existing_class];
102
103 return $existing_class unless $roles;
104
8f05895e 105 my $meta = Class::MOP::Class->initialize($existing_class);
231be3be 106
107 return $existing_class
386c056b 108 if $meta->can('does_role') && all { $meta->does_role($_) }
109 grep { !ref $_ } @{$roles};
231be3be 110
111 return Moose::Meta::Class->create_anon_class(
112 superclasses => $superclasses,
113 roles => $roles,
114 cache => 1,
115 )->name();
116}
117
1181;
c59bc009 119
120__END__
121
122=head1 NAME
123
124Moose::Util::MetaRole - Apply roles to any metaclass, as well as the object base class
125
126=head1 SYNOPSIS
127
128 package MyApp::Moose;
129
c59bc009 130 use Moose ();
131 use Moose::Exporter;
49a86a99 132 use Moose::Util::MetaRole;
c59bc009 133
134 use MyApp::Role::Meta::Class;
135 use MyApp::Role::Meta::Method::Constructor;
136 use MyApp::Role::Object;
137
138 Moose::Exporter->setup_import_methods( also => 'Moose' );
139
140 sub init_meta {
141 shift;
142 my %options = @_;
143
144 Moose->init_meta(%options);
145
146 Moose::Util::MetaRole::apply_metaclass_roles(
147 for_class => $options{for_class},
148 metaclass_roles => ['MyApp::Role::Meta::Class'],
149 constructor_class_roles => ['MyApp::Role::Meta::Method::Constructor'],
150 );
151
152 Moose::Util::MetaRole::apply_base_class_roles(
153 for_class => $options{for_class},
154 roles => ['MyApp::Role::Object'],
155 );
156
157 return $options{for_class}->meta();
158 }
159
160=head1 DESCRIPTION
161
162This utility module is designed to help authors of Moose extensions
163write extensions that are able to cooperate with other Moose
164extensions. To do this, you must write your extensions as roles, which
52a919fe 165can then be dynamically applied to the caller's metaclasses.
c59bc009 166
167This module makes sure to preserve any existing superclasses and roles
168already set for the meta objects, which means that any number of
169extensions can apply roles in any order.
170
171=head1 USAGE
172
173B<It is very important that you only call this module's functions when
174your module is imported by the caller>. The process of applying roles
175to the metaclass reinitializes the metaclass object, which wipes out
176any existing attributes already defined. However, as long as you do
177this when your module is imported, the caller should not have any
178attributes defined yet.
179
180The easiest way to ensure that this happens is to use
95056a1e 181L<Moose::Exporter>, which can generate the appropriate C<init_meta>
182method for you, and make sure it is called when imported.
c59bc009 183
184=head1 FUNCTIONS
185
186This module provides two functions.
187
188=head2 apply_metaclass_roles( ... )
189
190This function will apply roles to one or more metaclasses for the
191specified class. It accepts the following parameters:
192
193=over 4
194
195=item * for_class => $name
196
197This specifies the class for which to alter the meta classes.
198
199=item * metaclass_roles => \@roles
200
201=item * attribute_metaclass_roles => \@roles
202
203=item * method_metaclass_roles => \@roles
204
8286fcd6 205=item * wrapped_method_metaclass_roles => \@roles
206
c59bc009 207=item * instance_metaclass_roles => \@roles
208
209=item * constructor_class_roles => \@roles
210
211=item * destructor_class_roles => \@roles
212
d401dc20 213=item * application_to_class_class_roles => \@roles
214
215=item * application_to_role_class_roles => \@roles
216
217=item * application_to_instance_class_roles => \@roles
218
c59bc009 219These parameter all specify one or more roles to be applied to the
220specified metaclass. You can pass any or all of these parameters at
221once.
222
223=back
224
225=head2 apply_base_class_roles( for_class => $class, roles => \@roles )
226
227This function will apply the specified roles to the object's base class.
228
229=head1 AUTHOR
230
231Dave Rolsky E<lt>autarch@urth.orgE<gt>
232
233=head1 COPYRIGHT AND LICENSE
234
2840a3b2 235Copyright 2009 by Infinity Interactive, Inc.
c59bc009 236
237L<http://www.iinteractive.com>
238
239This library is free software; you can redistribute it and/or modify
240it under the same terms as Perl itself.
241
242=cut