Save global variables (Mouse/Util.pm)
[gitmo/Mouse.git] / lib / Mouse / Meta / Role.pm
CommitLineData
a2227e71 1package Mouse::Meta::Role;
2use strict;
3use warnings;
59089ec3 4use Carp 'confess';
74be9f76 5
67199842 6use Mouse::Util qw(version authority identifier);
a2227e71 7
acf0f643 8do {
9 my %METACLASS_CACHE;
10
11 # because Mouse doesn't introspect existing classes, we're forced to
12 # only pay attention to other Mouse classes
13 sub _metaclass_cache {
14 my $class = shift;
15 my $name = shift;
16 return $METACLASS_CACHE{$name};
17 }
18
19 sub initialize {
20 my $class = shift;
21 my $name = shift;
22 $METACLASS_CACHE{$name} = $class->new(name => $name)
23 if !exists($METACLASS_CACHE{$name});
24 return $METACLASS_CACHE{$name};
25 }
26};
27
28sub new {
29 my $class = shift;
30 my %args = @_;
31
59089ec3 32 $args{attributes} ||= {};
33 $args{required_methods} ||= [];
47f36c05 34 $args{roles} ||= [];
274b6cce 35
acf0f643 36 bless \%args, $class;
37}
a2227e71 38
513854c7 39sub name { $_[0]->{name} }
40
59089ec3 41sub add_required_methods {
42 my $self = shift;
43 my @methods = @_;
44 push @{$self->{required_methods}}, @methods;
45}
46
67199842 47
48
274b6cce 49sub add_attribute {
50 my $self = shift;
51 my $name = shift;
69ac1dcf 52 my $spec = shift;
9c85e9dc 53 $self->{attributes}->{$name} = $spec;
da0c885d 54}
55
274b6cce 56sub has_attribute { exists $_[0]->{attributes}->{$_[1]} }
57sub get_attribute_list { keys %{ $_[0]->{attributes} } }
69ac1dcf 58sub get_attribute { $_[0]->{attributes}->{$_[1]} }
274b6cce 59
2e92bb89 60# copied from Class::Inspector
61sub get_method_list {
62 my $self = shift;
63 my $name = $self->name;
64
65 no strict 'refs';
66 # Get all the CODE symbol table entries
8632b6fe 67 my @functions =
faa45e54 68 grep !/^(?:has|with|around|before|after|augment|inner|override|super|blessed|extends|confess|excludes|requires)$/,
2e92bb89 69 grep { defined &{"${name}::$_"} }
70 keys %{"${name}::"};
71 wantarray ? @functions : \@functions;
72}
73
e0b163e1 74# Moose uses Application::ToInstance, Application::ToClass, Application::ToRole
da0c885d 75sub apply {
76 my $self = shift;
2e92bb89 77 my $selfname = $self->name;
da0c885d 78 my $class = shift;
2e92bb89 79 my $classname = $class->name;
4aaa2ed6 80 my %args = @_;
da0c885d 81
e0b163e1 82 if ($class->isa('Mouse::Object')) {
83 Carp::croak('Mouse does not support Application::ToInstance yet');
84 }
85
4aaa2ed6 86 if ($class->isa('Mouse::Meta::Class')) {
87 for my $name (@{$self->{required_methods}}) {
88 unless ($classname->can($name)) {
89 confess "'$selfname' requires the method '$name' to be implemented by '$classname'";
90 }
2e92bb89 91 }
92 }
93
94 {
95 no strict 'refs';
96 for my $name ($self->get_method_list) {
8632b6fe 97 next if $name eq 'meta';
21498b08 98
67199842 99 my $class_function = "${classname}::${name}";
100 my $role_function = "${selfname}::${name}";
101 if (defined &$class_function) {
2e92bb89 102 # XXX what's Moose's behavior?
21498b08 103 #next;
104 } else {
e82cf08d 105 *{$class_function} = \&{$role_function};
21498b08 106 }
107 if ($args{alias} && $args{alias}->{$name}) {
108 my $dstname = $args{alias}->{$name};
109 unless ($classname->can($dstname)) {
bded4514 110 *{"${classname}::${dstname}"} = \&$role_function;
21498b08 111 }
2e92bb89 112 }
59089ec3 113 }
114 }
115
b1b81553 116 if ($class->isa('Mouse::Meta::Class')) {
117 # apply role to class
118 for my $name ($self->get_attribute_list) {
119 next if $class->has_attribute($name);
120 my $spec = $self->get_attribute($name);
05b9dc92 121
122 my $metaclass = 'Mouse::Meta::Attribute';
123 if ( my $metaclass_name = $spec->{metaclass} ) {
124 my $new_class = Mouse::Util::resolve_metaclass_alias(
125 'Attribute',
126 $metaclass_name
127 );
128 if ( $metaclass ne $new_class ) {
129 $metaclass = $new_class;
130 }
131 }
132
133 $metaclass->create($class, $name, %$spec);
b1b81553 134 }
135 } else {
136 # apply role to role
137 # XXX Room for speed improvement
138 for my $name ($self->get_attribute_list) {
139 next if $class->has_attribute($name);
140 my $spec = $self->get_attribute($name);
141 $class->add_attribute($name, $spec);
142 }
da0c885d 143 }
d99db7b6 144
b1b81553 145 # XXX Room for speed improvement in role to role
67199842 146 for my $modifier_type (qw/before after around override/) {
d99db7b6 147 my $add_method = "add_${modifier_type}_method_modifier";
148 my $modified = $self->{"${modifier_type}_method_modifiers"};
149
150 for my $method_name (keys %$modified) {
151 for my $code (@{ $modified->{$method_name} }) {
152 $class->$add_method($method_name => $code);
153 }
154 }
155 }
47f36c05 156
157 # append roles
158 push @{ $class->roles }, $self, @{ $self->roles };
da0c885d 159}
0fc8adbc 160
21498b08 161sub combine_apply {
162 my(undef, $class, @roles) = @_;
163 my $classname = $class->name;
164
165 if ($class->isa('Mouse::Meta::Class')) {
166 for my $role_spec (@roles) {
167 my $self = $role_spec->[0]->meta;
168 for my $name (@{$self->{required_methods}}) {
169 unless ($classname->can($name)) {
170 my $method_required = 0;
171 for my $role (@roles) {
172 $method_required = 1 if $self->name ne $role->[0] && $role->[0]->can($name);
173 }
174 confess "'".$self->name."' requires the method '$name' to be implemented by '$classname'"
175 unless $method_required;
176 }
177 }
178 }
179 }
180
181 {
182 no strict 'refs';
183 for my $role_spec (@roles) {
184 my $self = $role_spec->[0]->meta;
185 my $selfname = $self->name;
186 my %args = %{ $role_spec->[1] };
187 for my $name ($self->get_method_list) {
8632b6fe 188 next if $name eq 'meta';
21498b08 189
67199842 190 my $class_function = "${classname}::${name}";
191 my $role_function = "${selfname}::${name}";
192 if (defined &$class_function) {
21498b08 193 # XXX what's Moose's behavior?
194 #next;
195 } else {
67199842 196 *$class_function = *$role_function;
21498b08 197 }
198 if ($args{alias} && $args{alias}->{$name}) {
199 my $dstname = $args{alias}->{$name};
200 unless ($classname->can($dstname)) {
bded4514 201 *{"${classname}::${dstname}"} = \&$role_function;
21498b08 202 }
203 }
204 }
205 }
206 }
207
208
209 if ($class->isa('Mouse::Meta::Class')) {
210 # apply role to class
211 for my $role_spec (@roles) {
212 my $self = $role_spec->[0]->meta;
213 for my $name ($self->get_attribute_list) {
214 next if $class->has_attribute($name);
215 my $spec = $self->get_attribute($name);
05b9dc92 216
217 my $metaclass = 'Mouse::Meta::Attribute';
218 if ( my $metaclass_name = $spec->{metaclass} ) {
219 my $new_class = Mouse::Util::resolve_metaclass_alias(
220 'Attribute',
221 $metaclass_name
222 );
223 if ( $metaclass ne $new_class ) {
224 $metaclass = $new_class;
225 }
226 }
227
228 $metaclass->create($class, $name, %$spec);
21498b08 229 }
230 }
231 } else {
232 # apply role to role
233 # XXX Room for speed improvement
234 for my $role_spec (@roles) {
235 my $self = $role_spec->[0]->meta;
236 for my $name ($self->get_attribute_list) {
237 next if $class->has_attribute($name);
238 my $spec = $self->get_attribute($name);
239 $class->add_attribute($name, $spec);
240 }
241 }
242 }
243
244 # XXX Room for speed improvement in role to role
67199842 245 for my $modifier_type (qw/before after around override/) {
21498b08 246 my $add_method = "add_${modifier_type}_method_modifier";
247 for my $role_spec (@roles) {
248 my $self = $role_spec->[0]->meta;
249 my $modified = $self->{"${modifier_type}_method_modifiers"};
250
251 for my $method_name (keys %$modified) {
252 for my $code (@{ $modified->{$method_name} }) {
253 $class->$add_method($method_name => $code);
254 }
255 }
256 }
257 }
258
259 # append roles
260 my %role_apply_cache;
67199842 261 my $apply_roles = $class->roles;
21498b08 262 for my $role_spec (@roles) {
263 my $self = $role_spec->[0]->meta;
67199842 264 push @$apply_roles, $self unless $role_apply_cache{$self}++;
265 for my $role (@{ $self->roles }) {
266 push @$apply_roles, $role unless $role_apply_cache{$role}++;
21498b08 267 }
268 }
269}
270
67199842 271for my $modifier_type (qw/before after around override/) {
c2f128e7 272 no strict 'refs';
fc0e0bbd 273 *{ __PACKAGE__ . '::' . "add_${modifier_type}_method_modifier" } = sub {
274 my ($self, $method_name, $method) = @_;
275
276 push @{ $self->{"${modifier_type}_method_modifiers"}->{$method_name} },
277 $method;
278 };
279
c2f128e7 280 *{ __PACKAGE__ . '::' . "get_${modifier_type}_method_modifiers" } = sub {
281 my ($self, $method_name, $method) = @_;
282 @{ $self->{"${modifier_type}_method_modifiers"}->{$method_name} || [] }
283 };
284}
285
47f36c05 286sub roles { $_[0]->{roles} }
287
67199842 288
289# This is currently not passing all the Moose tests.
290sub does_role {
291 my ($self, $role_name) = @_;
292
293 (defined $role_name)
294 || confess "You must supply a role name to look for";
295
296 # if we are it,.. then return true
297 return 1 if $role_name eq $self->name;
298
299 for my $role (@{ $self->{roles} }) {
300 return 1 if $role->does_role($role_name);
301 }
302 return 0;
303}
304
305
a2227e71 3061;
307