33ee5dfdc4c8e058a5f726c7ea53b28e74ae7e91
[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 Scalar::Util    'blessed';
8
9 use base 'Moose::Meta::Role::Application';
10
11 sub apply {
12     my ($self, $role1, $role2) = @_;
13     $self->SUPER::apply($role1, $role2);
14     $role2->add_role($role1);
15 }
16
17 sub check_role_exclusions {
18     my ($self, $role1, $role2) = @_;
19     if ( $role2->excludes_role($role1->name) ) {
20         Moose::Util::throw("Conflict detected: " . $role2->name . " excludes role '" . $role1->name . "'");
21     }
22     foreach my $excluded_role_name ($role1->get_excluded_roles_list) {
23         if ( $role2->does_role($excluded_role_name) ) {
24             Moose::Util::throw("The role " . $role2->name . " does the excluded role '$excluded_role_name'");
25         }
26         $role2->add_excluded_roles($excluded_role_name);
27     }
28 }
29
30 sub check_required_methods {
31     my ($self, $role1, $role2) = @_;
32     foreach my $required_method ($role1->get_required_method_list) {
33         my $required_method_name = $required_method->name;
34
35         next if $self->is_aliased_method($required_method_name);
36
37         $role2->add_required_methods($required_method)
38             unless $role2->find_method_by_name($required_method_name);
39     }
40 }
41
42 sub check_required_attributes {
43
44 }
45
46 sub apply_attributes {
47     my ($self, $role1, $role2) = @_;
48     foreach my $attribute_name ($role1->get_attribute_list) {
49         # it if it has one already
50         if ($role2->has_attribute($attribute_name) &&
51             # make sure we haven't seen this one already too
52             $role2->get_attribute($attribute_name) != $role1->get_attribute($attribute_name)) {
53
54             my $role2_name = $role2->name;
55
56             Moose::Util::throw( "Role '"
57                     . $role1->name
58                     . "' has encountered an attribute conflict"
59                     . " while being composed into '$role2_name'."
60                     . " This is a fatal error and cannot be disambiguated."
61                     . " The conflicting attribute is named '$attribute_name'." );
62         }
63         else {
64             $role2->add_attribute(
65                 $role1->get_attribute($attribute_name)->clone
66             );
67         }
68     }
69 }
70
71 sub apply_methods {
72     my ( $self, $role1, $role2 ) = @_;
73     foreach my $method ( $role1->_get_local_methods ) {
74
75         my $method_name = $method->name;
76
77         next if $method->isa('Class::MOP::Method::Meta');
78
79         unless ( $self->is_method_excluded($method_name) ) {
80
81             my $role2_method = $role2->get_method($method_name);
82             if (   $role2_method
83                 && $role2_method->body != $method->body ) {
84
85                 # method conflicts between roles result in the method becoming
86                 # a requirement
87                 $role2->add_conflicting_method(
88                     name  => $method_name,
89                     roles => [ $role1->name, $role2->name ],
90                 );
91             }
92             else {
93                 $role2->add_method(
94                     $method_name,
95                     $method,
96                 );
97             }
98         }
99
100         next unless $self->is_method_aliased($method_name);
101
102         my $aliased_method_name = $self->get_method_aliases->{$method_name};
103
104         my $role2_method = $role2->get_method($aliased_method_name);
105
106         if (   $role2_method
107             && $role2_method->body != $method->body ) {
108
109             Moose::Util::throw(
110                 "Cannot create a method alias if a local method of the same name exists"
111             );
112         }
113
114         $role2->add_method(
115             $aliased_method_name,
116             $role1->get_method($method_name)
117         );
118
119         if ( !$role2->has_method($method_name) ) {
120             $role2->add_required_methods($method_name)
121                 unless $self->is_method_excluded($method_name);
122         }
123     }
124 }
125
126 sub apply_override_method_modifiers {
127     my ($self, $role1, $role2) = @_;
128     foreach my $method_name ($role1->get_method_modifier_list('override')) {
129         # it if it has one already then ...
130         if ($role2->has_method($method_name)) {
131             # if it is being composed into another role
132             # we have a conflict here, because you cannot
133             # combine an overridden method with a locally
134             # defined one
135             Moose::Util::throw("Role '" . $role1->name . "' has encountered an 'override' method conflict " .
136                     "during composition (A local method of the same name as been found). This " .
137                     "is fatal error.");
138         }
139         else {
140             # if we are a role, we need to make sure
141             # we dont have a conflict with the role
142             # we are composing into
143             if ($role2->has_override_method_modifier($method_name) &&
144                 $role2->get_override_method_modifier($method_name) != $role2->get_override_method_modifier($method_name)) {
145
146                 Moose::Util::throw("Role '" . $role1->name . "' has encountered an 'override' method conflict " .
147                         "during composition (Two 'override' methods of the same name encountered). " .
148                         "This is fatal error.");
149             }
150             else {
151                 # if there is no conflict,
152                 # just add it to the role
153                 $role2->add_override_method_modifier(
154                     $method_name,
155                     $role1->get_override_method_modifier($method_name)
156                 );
157             }
158         }
159     }
160 }
161
162 sub apply_method_modifiers {
163     my ($self, $modifier_type, $role1, $role2) = @_;
164     my $add = "add_${modifier_type}_method_modifier";
165     my $get = "get_${modifier_type}_method_modifiers";
166     foreach my $method_name ($role1->get_method_modifier_list($modifier_type)) {
167         $role2->$add(
168             $method_name,
169             $_
170         ) foreach $role1->$get($method_name);
171     }
172 }
173
174
175 1;
176
177 # ABSTRACT: Compose a role into another role
178
179 __END__
180
181 =pod
182
183 =head1 DESCRIPTION
184
185 =head2 METHODS
186
187 =over 4
188
189 =item B<new>
190
191 =item B<meta>
192
193 =item B<apply>
194
195 =item B<check_role_exclusions>
196
197 =item B<check_required_methods>
198
199 =item B<check_required_attributes>
200
201 =item B<apply_attributes>
202
203 =item B<apply_methods>
204
205 =item B<apply_method_modifiers>
206
207 =item B<apply_override_method_modifiers>
208
209 =back
210
211 =head1 BUGS
212
213 See L<Moose/BUGS> for details on reporting bugs.
214
215 =cut
216