067958c92de24d91499c1f82fa7dd415fe3b9d7f
[gitmo/Moose.git] / lib / Moose / Meta / Role / Application / ToRole.pm
1 package Moose::Meta::Role::Application::ToRole;
2
3 use strict;
4 use warnings;
5 use metaclass;
6
7 use Carp            'confess';
8 use Scalar::Util    'blessed';
9
10 use Data::Dumper;
11
12 our $VERSION   = '0.55_02';
13 $VERSION = eval $VERSION;
14 our $AUTHORITY = 'cpan:STEVAN';
15
16 use base 'Moose::Meta::Role::Application';
17
18 sub apply {
19     my ($self, $role1, $role2) = @_;    
20     $self->SUPER::apply($role1, $role2);   
21     $role2->add_role($role1);     
22 }
23
24 sub check_role_exclusions {
25     my ($self, $role1, $role2) = @_;
26     confess "Conflict detected: " . $role2->name . " excludes role '" . $role1->name . "'"
27         if $role2->excludes_role($role1->name);
28     foreach my $excluded_role_name ($role1->get_excluded_roles_list) {
29         confess "The class " . $role2->name . " does the excluded role '$excluded_role_name'"
30             if $role2->does_role($excluded_role_name);
31         $role2->add_excluded_roles($excluded_role_name);
32     }
33 }
34
35 sub check_required_methods {
36     my ($self, $role1, $role2) = @_;
37     foreach my $required_method_name ($role1->get_required_method_list) {
38             
39         next if $self->is_aliased_method($required_method_name);
40                     
41         $role2->add_required_methods($required_method_name)
42             unless $role2->find_method_by_name($required_method_name);
43     }
44 }
45
46 sub check_required_attributes {
47     
48 }
49
50 sub apply_attributes {
51     my ($self, $role1, $role2) = @_;
52     foreach my $attribute_name ($role1->get_attribute_list) {
53         # it if it has one already
54         if ($role2->has_attribute($attribute_name) &&
55             # make sure we haven't seen this one already too
56             $role2->get_attribute($attribute_name) != $role1->get_attribute($attribute_name)) {
57             confess "Role '" . $role1->name . "' has encountered an attribute conflict " .
58                     "during composition. This is fatal error and cannot be disambiguated.";
59         }
60         else {
61             $role2->add_attribute(
62                 $attribute_name,
63                 $role1->get_attribute($attribute_name)
64             );
65         }
66     }
67 }
68
69 sub apply_methods {
70     my ($self, $role1, $role2) = @_;
71     foreach my $method_name ($role1->get_method_list) {
72         
73         next if $self->is_method_excluded($method_name);        
74
75         if ($self->is_method_aliased($method_name)) {
76             my $aliased_method_name = $self->get_method_aliases->{$method_name};
77             # it if it has one already
78             if ($role2->has_method($aliased_method_name) &&
79                 # and if they are not the same thing ...
80                 $role2->get_method($aliased_method_name)->body != $role1->get_method($method_name)->body) {
81                 confess "Cannot create a method alias if a local method of the same name exists";
82             }
83
84             $role2->alias_method(
85                 $aliased_method_name,
86                 $role1->get_method($method_name)
87             );
88
89             if (!$role2->has_method($method_name)) {
90                 $role2->add_required_methods($method_name);
91             }
92
93             next;
94         }        
95         
96         # it if it has one already
97         if ($role2->has_method($method_name) &&
98             # and if they are not the same thing ...
99             $role2->get_method($method_name)->body != $role1->get_method($method_name)->body) {
100             # method conflicts between roles result
101             # in the method becoming a requirement
102             $role2->add_required_methods($method_name);
103         }
104         else {
105             # add it, although it could be overriden
106             $role2->alias_method(
107                 $method_name,
108                 $role1->get_method($method_name)
109             );
110                         
111         }
112         
113     }
114 }
115
116 sub apply_override_method_modifiers {
117     my ($self, $role1, $role2) = @_;
118     foreach my $method_name ($role1->get_method_modifier_list('override')) {
119         # it if it has one already then ...
120         if ($role2->has_method($method_name)) {
121             # if it is being composed into another role
122             # we have a conflict here, because you cannot
123             # combine an overriden method with a locally
124             # defined one
125             confess "Role '" . $role1->name . "' has encountered an 'override' method conflict " .
126                     "during composition (A local method of the same name as been found). This " .
127                     "is fatal error.";
128         }
129         else {
130             # if we are a role, we need to make sure
131             # we dont have a conflict with the role
132             # we are composing into
133             if ($role2->has_override_method_modifier($method_name) &&
134                 $role2->get_override_method_modifier($method_name) != $role2->get_override_method_modifier($method_name)) {
135                 confess "Role '" . $role1->name . "' has encountered an 'override' method conflict " .
136                         "during composition (Two 'override' methods of the same name encountered). " .
137                         "This is fatal error.";
138             }
139             else {
140                 # if there is no conflict,
141                 # just add it to the role
142                 $role2->add_override_method_modifier(
143                     $method_name,
144                     $role1->get_override_method_modifier($method_name)
145                 );
146             }
147         }
148     }
149 }
150
151 sub apply_method_modifiers {
152     my ($self, $modifier_type, $role1, $role2) = @_;
153     my $add = "add_${modifier_type}_method_modifier";
154     my $get = "get_${modifier_type}_method_modifiers";
155     foreach my $method_name ($role1->get_method_modifier_list($modifier_type)) {
156         $role2->$add(
157             $method_name,
158             $_
159         ) foreach $role1->$get($method_name);
160     }
161 }
162
163
164 1;
165
166 __END__
167
168 =pod
169
170 =head1 NAME
171
172 Moose::Meta::Role::Application::ToRole - Compose a role into another role
173
174 =head1 DESCRIPTION
175
176 =head2 METHODS
177
178 =over 4
179
180 =item B<new>
181
182 =item B<meta>
183
184 =item B<apply>
185
186 =item B<check_role_exclusions>
187
188 =item B<check_required_methods>
189
190 =item B<check_required_attributes>
191
192 =item B<apply_attributes>
193
194 =item B<apply_methods>
195
196 =item B<apply_method_modifiers>
197
198 =item B<apply_override_method_modifiers>
199
200 =back
201
202 =head1 BUGS
203
204 All complex software has bugs lurking in it, and this module is no
205 exception. If you find a bug please either email me, or add the bug
206 to cpan-RT.
207
208 =head1 AUTHOR
209
210 Stevan Little E<lt>stevan@iinteractive.comE<gt>
211
212 =head1 COPYRIGHT AND LICENSE
213
214 Copyright 2006-2008 by Infinity Interactive, Inc.
215
216 L<http://www.iinteractive.com>
217
218 This library is free software; you can redistribute it and/or modify
219 it under the same terms as Perl itself.
220
221 =cut
222