bump version to 0.63
[gitmo/Moose.git] / lib / Moose / Meta / Role / Application / ToClass.pm
1 package Moose::Meta::Role::Application::ToClass;
2
3 use strict;
4 use warnings;
5 use metaclass;
6
7 use Moose::Util  'english_list';
8 use Scalar::Util 'blessed';
9
10 our $VERSION   = '0.63';
11 $VERSION = eval $VERSION;
12 our $AUTHORITY = 'cpan:STEVAN';
13
14 use base 'Moose::Meta::Role::Application';
15
16 sub apply {
17     my ($self, $role, $class) = @_;    
18     $self->SUPER::apply($role, $class);
19     $class->add_role($role);        
20 }
21
22 sub check_role_exclusions {
23     my ($self, $role, $class) = @_;
24     if ($class->excludes_role($role->name)) {
25         $class->throw_error("Conflict detected: " . $class->name . " excludes role '" . $role->name . "'");
26     }
27     foreach my $excluded_role_name ($role->get_excluded_roles_list) {
28         if ($class->does_role($excluded_role_name)) {
29             $class->throw_error("The class " . $class->name . " does the excluded role '$excluded_role_name'");
30         }
31     }
32 }
33
34 sub check_required_methods {
35     my ($self, $role, $class) = @_;
36
37     my @missing;
38     my @is_attr;
39
40     # NOTE:
41     # we might need to move this down below the
42     # the attributes so that we can require any
43     # attribute accessors. However I am thinking
44     # that maybe those are somehow exempt from
45     # the require methods stuff.
46     foreach my $required_method_name ($role->get_required_method_list) {
47
48         if (!$class->find_method_by_name($required_method_name)) {
49             
50             next if $self->is_aliased_method($required_method_name);
51
52             push @missing, $required_method_name;
53         }
54         else {
55             # NOTE:
56             # we need to make sure that the method is
57             # not a method modifier, because those do
58             # not satisfy the requirements ...
59             my $method = $class->find_method_by_name($required_method_name);
60
61             # check if it is a generated accessor ...
62             push @is_attr, $required_method_name,
63                 if $method->isa('Class::MOP::Method::Accessor');
64
65             # NOTE:
66             # All other tests here have been removed, they were tests
67             # for overriden methods and before/after/around modifiers.
68             # But we realized that for classes any overriden or modified
69             # methods would be backed by a real method of that name
70             # (and therefore meet the requirement). And for roles, the
71             # overriden and modified methods are "in statis" and so would
72             # not show up in this test anyway (and as a side-effect they
73             # would not fufill the requirement, which is exactly what we
74             # want them to do anyway).
75             # - SL
76         }
77     }
78
79     return unless @missing || @is_attr;
80
81     my $error = '';
82
83     if (@missing) {
84         my $noun = @missing == 1 ? 'method' : 'methods';
85
86         my $list
87             = Moose::Util::english_list( map { q{'} . $_ . q{'} } @missing );
88
89         $error
90             .= q{'}
91             . $role->name
92             . "' requires the $noun $list "
93             . "to be implemented by '"
94             . $class->name . q{'};
95     }
96
97     if (@is_attr) {
98         my $noun = @is_attr == 1 ? 'method' : 'methods';
99
100         my $list
101             = Moose::Util::english_list( map { q{'} . $_ . q{'} } @is_attr );
102
103         $error .= "\n" if length $error;
104
105         $error
106             .= q{'}
107             . $role->name
108             . "' requires the $noun $list "
109             . "to be implemented by '"
110             . $class->name
111             . "' but the method is only an attribute accessor";
112     }
113
114     $class->throw_error($error);
115 }
116
117 sub check_required_attributes {
118     
119 }
120
121 sub apply_attributes {
122     my ($self, $role, $class) = @_;
123     foreach my $attribute_name ($role->get_attribute_list) {
124         # it if it has one already
125         if ($class->has_attribute($attribute_name) &&
126             # make sure we haven't seen this one already too
127             $class->get_attribute($attribute_name) != $role->get_attribute($attribute_name)) {
128             next;
129         }
130         else {
131             $class->add_attribute(
132                 $attribute_name,
133                 $role->get_attribute($attribute_name)
134             );
135         }
136     }
137 }
138
139 sub apply_methods {
140     my ($self, $role, $class) = @_;
141     foreach my $method_name ($role->get_method_list) {
142         
143         unless ($self->is_method_excluded($method_name)) {
144             # it if it has one already
145             if ($class->has_method($method_name) &&
146                 # and if they are not the same thing ...
147                 $class->get_method($method_name)->body != $role->get_method($method_name)->body) {
148                 next;
149             }
150             else {
151                 # add it, although it could be overriden
152                 $class->add_method(
153                     $method_name,
154                     $role->get_method($method_name)
155                 );         
156             }
157         }
158         
159         if ($self->is_method_aliased($method_name)) {
160             my $aliased_method_name = $self->get_method_aliases->{$method_name};
161             # it if it has one already
162             if ($class->has_method($aliased_method_name) &&
163                 # and if they are not the same thing ...
164                 $class->get_method($aliased_method_name)->body != $role->get_method($method_name)->body) {
165                 $class->throw_error("Cannot create a method alias if a local method of the same name exists");
166             }            
167             $class->add_method(
168                 $aliased_method_name,
169                 $role->get_method($method_name)
170             );                
171         }        
172     }
173     # we must reset the cache here since
174     # we are just aliasing methods, otherwise
175     # the modifiers go wonky.
176     $class->reset_package_cache_flag;        
177 }
178
179 sub apply_override_method_modifiers {
180     my ($self, $role, $class) = @_;
181     foreach my $method_name ($role->get_method_modifier_list('override')) {
182         # it if it has one already then ...
183         if ($class->has_method($method_name)) {
184             next;
185         }
186         else {
187             # if this is not a role, then we need to
188             # find the original package of the method
189             # so that we can tell the class were to
190             # find the right super() method
191             my $method = $role->get_override_method_modifier($method_name);
192             my ($package) = Class::MOP::get_code_info($method);
193             # if it is a class, we just add it
194             $class->add_override_method_modifier($method_name, $method, $package);
195         }
196     }
197 }
198
199 sub apply_method_modifiers {
200     my ($self, $modifier_type, $role, $class) = @_;
201     my $add = "add_${modifier_type}_method_modifier";
202     my $get = "get_${modifier_type}_method_modifiers";
203     foreach my $method_name ($role->get_method_modifier_list($modifier_type)) {
204         $class->$add(
205             $method_name,
206             $_
207         ) foreach $role->$get($method_name);
208     }
209 }
210
211 1;
212
213 __END__
214
215 =pod
216
217 =head1 NAME
218
219 Moose::Meta::Role::Application::ToClass - Compose a role into a class
220
221 =head1 DESCRIPTION
222
223 =head2 METHODS
224
225 =over 4
226
227 =item B<new>
228
229 =item B<meta>
230
231 =item B<apply>
232
233 =item B<check_role_exclusions>
234
235 =item B<check_required_methods>
236
237 =item B<check_required_attributes>
238
239 =item B<apply_attributes>
240
241 =item B<apply_methods>
242
243 =item B<apply_method_modifiers>
244
245 =item B<apply_override_method_modifiers>
246
247 =back
248
249 =head1 BUGS
250
251 All complex software has bugs lurking in it, and this module is no
252 exception. If you find a bug please either email me, or add the bug
253 to cpan-RT.
254
255 =head1 AUTHOR
256
257 Stevan Little E<lt>stevan@iinteractive.comE<gt>
258
259 =head1 COPYRIGHT AND LICENSE
260
261 Copyright 2006-2008 by Infinity Interactive, Inc.
262
263 L<http://www.iinteractive.com>
264
265 This library is free software; you can redistribute it and/or modify
266 it under the same terms as Perl itself.
267
268 =cut
269