bump version to 0.87
[gitmo/Moose.git] / lib / Moose / Util / MetaRole.pm
CommitLineData
231be3be 1package Moose::Util::MetaRole;
2
3use strict;
4use warnings;
5
92d82041 6our $VERSION = '0.87';
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
54 );
55
aa1bb57e 56 my $old_meta = Class::MOP::class_of($for);
231be3be 57 my $new_metaclass
aa1bb57e 58 = _make_new_class( ref $old_meta, $options->{metaclass_roles} );
231be3be 59
72d15b83 60 # This could get called for a Moose::Meta::Role as well as a Moose::Meta::Class
231be3be 61 my %classes = map {
62 $_ => _make_new_class( $old_meta->$_(), $options->{ $_ . '_roles' } )
72d15b83 63 }
64 grep { $old_meta->can($_) }
65 qw(
231be3be 66 attribute_metaclass
67 method_metaclass
8286fcd6 68 wrapped_method_metaclass
231be3be 69 instance_metaclass
70 );
71
72 return $new_metaclass->reinitialize( $for, %classes );
73}
74
75sub apply_base_class_roles {
76 my %options = @_;
77
78 my $for = $options{for_class};
79
95f64261 80 my $meta = Class::MOP::class_of($for);
231be3be 81
82 my $new_base = _make_new_class(
83 $for,
84 $options{roles},
85 [ $meta->superclasses() ],
86 );
87
88 $meta->superclasses($new_base)
89 if $new_base ne $meta->name();
90}
91
92sub _make_new_class {
93 my $existing_class = shift;
94 my $roles = shift;
95 my $superclasses = shift || [$existing_class];
96
97 return $existing_class unless $roles;
98
8f05895e 99 my $meta = Class::MOP::Class->initialize($existing_class);
231be3be 100
101 return $existing_class
102 if $meta->can('does_role') && all { $meta->does_role($_) } @{$roles};
103
104 return Moose::Meta::Class->create_anon_class(
105 superclasses => $superclasses,
106 roles => $roles,
107 cache => 1,
108 )->name();
109}
110
1111;
c59bc009 112
113__END__
114
115=head1 NAME
116
117Moose::Util::MetaRole - Apply roles to any metaclass, as well as the object base class
118
119=head1 SYNOPSIS
120
121 package MyApp::Moose;
122
c59bc009 123 use Moose ();
124 use Moose::Exporter;
49a86a99 125 use Moose::Util::MetaRole;
c59bc009 126
127 use MyApp::Role::Meta::Class;
128 use MyApp::Role::Meta::Method::Constructor;
129 use MyApp::Role::Object;
130
131 Moose::Exporter->setup_import_methods( also => 'Moose' );
132
133 sub init_meta {
134 shift;
135 my %options = @_;
136
137 Moose->init_meta(%options);
138
139 Moose::Util::MetaRole::apply_metaclass_roles(
140 for_class => $options{for_class},
141 metaclass_roles => ['MyApp::Role::Meta::Class'],
142 constructor_class_roles => ['MyApp::Role::Meta::Method::Constructor'],
143 );
144
145 Moose::Util::MetaRole::apply_base_class_roles(
146 for_class => $options{for_class},
147 roles => ['MyApp::Role::Object'],
148 );
149
150 return $options{for_class}->meta();
151 }
152
153=head1 DESCRIPTION
154
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
52a919fe 158can then be dynamically applied to the caller's metaclasses.
c59bc009 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
8286fcd6 198=item * wrapped_method_metaclass_roles => \@roles
199
c59bc009 200=item * instance_metaclass_roles => \@roles
201
202=item * constructor_class_roles => \@roles
203
204=item * destructor_class_roles => \@roles
205
206These parameter all specify one or more roles to be applied to the
207specified metaclass. You can pass any or all of these parameters at
208once.
209
210=back
211
212=head2 apply_base_class_roles( for_class => $class, roles => \@roles )
213
214This function will apply the specified roles to the object's base class.
215
216=head1 AUTHOR
217
218Dave Rolsky E<lt>autarch@urth.orgE<gt>
219
220=head1 COPYRIGHT AND LICENSE
221
2840a3b2 222Copyright 2009 by Infinity Interactive, Inc.
c59bc009 223
224L<http://www.iinteractive.com>
225
226This library is free software; you can redistribute it and/or modify
227it under the same terms as Perl itself.
228
229=cut