error_tests
[gitmo/Moose.git] / lib / Moose / Meta / Role.pm
CommitLineData
e185c027 1
2package Moose::Meta::Role;
3
4use strict;
5use warnings;
6use metaclass;
7
bdabd620 8use Carp 'confess';
9use Scalar::Util 'blessed';
10
11use Moose::Meta::Class;
e185c027 12
ef333f17 13our $VERSION = '0.02';
e185c027 14
bdabd620 15## Attributes
16
80572233 17## the meta for the role package
18
bdabd620 19__PACKAGE__->meta->add_attribute('_role_meta' => (
20 reader => '_role_meta',
21 init_arg => ':role_meta'
80572233 22));
23
24## roles
25
26__PACKAGE__->meta->add_attribute('roles' => (
27 reader => 'get_roles',
28 default => sub { [] }
e185c027 29));
30
80572233 31## attributes
32
e185c027 33__PACKAGE__->meta->add_attribute('attribute_map' => (
34 reader => 'get_attribute_map',
35 default => sub { {} }
36));
37
1331430a 38## required methods
39
40__PACKAGE__->meta->add_attribute('required_methods' => (
41 reader => 'get_required_methods_map',
42 default => sub { {} }
43));
44
80572233 45## method modifiers
46
47__PACKAGE__->meta->add_attribute('before_method_modifiers' => (
48 reader => 'get_before_method_modifiers_map',
bdabd620 49 default => sub { {} } # (<name> => [ (CODE) ])
e185c027 50));
51
80572233 52__PACKAGE__->meta->add_attribute('after_method_modifiers' => (
53 reader => 'get_after_method_modifiers_map',
bdabd620 54 default => sub { {} } # (<name> => [ (CODE) ])
80572233 55));
56
57__PACKAGE__->meta->add_attribute('around_method_modifiers' => (
58 reader => 'get_around_method_modifiers_map',
bdabd620 59 default => sub { {} } # (<name> => [ (CODE) ])
80572233 60));
61
62__PACKAGE__->meta->add_attribute('override_method_modifiers' => (
63 reader => 'get_override_method_modifiers_map',
64 default => sub { {} } # (<name> => CODE)
65));
66
bdabd620 67## Methods
80572233 68
e185c027 69sub new {
70 my $class = shift;
71 my %options = @_;
bdabd620 72 $options{':role_meta'} = Moose::Meta::Class->initialize(
a7d0cd00 73 $options{role_name},
74 ':method_metaclass' => 'Moose::Meta::Role::Method'
75 );
e185c027 76 my $self = $class->meta->new_object(%options);
77 return $self;
78}
79
80572233 80## subroles
81
82sub add_role {
83 my ($self, $role) = @_;
84 (blessed($role) && $role->isa('Moose::Meta::Role'))
85 || confess "Roles must be instances of Moose::Meta::Role";
86 push @{$self->get_roles} => $role;
87}
88
89sub does_role {
90 my ($self, $role_name) = @_;
91 (defined $role_name)
92 || confess "You must supply a role name to look for";
bdabd620 93 # if we are it,.. then return true
94 return 1 if $role_name eq $self->name;
95 # otherwise.. check our children
80572233 96 foreach my $role (@{$self->get_roles}) {
bdabd620 97 return 1 if $role->does_role($role_name);
80572233 98 }
99 return 0;
100}
101
1331430a 102## required methods
103
104sub add_required_methods {
105 my ($self, @methods) = @_;
106 $self->get_required_methods_map->{$_} = undef foreach @methods;
107}
108
109sub get_required_method_list {
110 my ($self) = @_;
111 keys %{$self->get_required_methods_map};
112}
113
114sub requires_method {
115 my ($self, $method_name) = @_;
116 exists $self->get_required_methods_map->{$method_name} ? 1 : 0;
117}
118
80572233 119## methods
120
e185c027 121# NOTE:
122# we delegate to some role_meta methods for convience here
123# the Moose::Meta::Role is meant to be a read-only interface
124# to the underlying role package, if you want to manipulate
125# that, just use ->role_meta
126
bdabd620 127sub name { (shift)->_role_meta->name }
128sub version { (shift)->_role_meta->version }
e185c027 129
bdabd620 130sub get_method { (shift)->_role_meta->get_method(@_) }
131sub has_method { (shift)->_role_meta->has_method(@_) }
132sub alias_method { (shift)->_role_meta->alias_method(@_) }
e185c027 133sub get_method_list {
134 my ($self) = @_;
bdabd620 135 grep {
136 # NOTE:
137 # this is a kludge for now,... these functions
138 # should not be showing up in the list at all,
139 # but they do, so we need to switch Moose::Role
140 # and Moose to use Sub::Exporter to prevent this
1331430a 141 !/^(meta|has|extends|blessed|confess|augment|inner|override|super|before|after|around|with|requires)$/
bdabd620 142 } $self->_role_meta->get_method_list;
e185c027 143}
144
145# ... however the items in statis (attributes & method modifiers)
146# can be removed and added to through this API
147
148# attributes
149
150sub add_attribute {
151 my ($self, $name, %attr_desc) = @_;
152 $self->get_attribute_map->{$name} = \%attr_desc;
153}
154
155sub has_attribute {
156 my ($self, $name) = @_;
157 exists $self->get_attribute_map->{$name} ? 1 : 0;
158}
159
160sub get_attribute {
161 my ($self, $name) = @_;
162 $self->get_attribute_map->{$name}
163}
164
165sub remove_attribute {
166 my ($self, $name) = @_;
167 delete $self->get_attribute_map->{$name}
168}
169
170sub get_attribute_list {
171 my ($self) = @_;
172 keys %{$self->get_attribute_map};
173}
174
175# method modifiers
176
80572233 177# mimic the metaclass API
178sub add_before_method_modifier { (shift)->_add_method_modifier('before', @_) }
179sub add_around_method_modifier { (shift)->_add_method_modifier('around', @_) }
180sub add_after_method_modifier { (shift)->_add_method_modifier('after', @_) }
181
182sub _add_method_modifier {
e185c027 183 my ($self, $modifier_type, $method_name, $method) = @_;
80572233 184 my $accessor = "get_${modifier_type}_method_modifiers_map";
185 $self->$accessor->{$method_name} = []
186 unless exists $self->$accessor->{$method_name};
187 push @{$self->$accessor->{$method_name}} => $method;
e185c027 188}
189
80572233 190sub add_override_method_modifier {
191 my ($self, $method_name, $method) = @_;
192 $self->get_override_method_modifiers_map->{$method_name} = $method;
e185c027 193}
194
80572233 195sub has_before_method_modifiers { (shift)->_has_method_modifiers('before', @_) }
196sub has_around_method_modifiers { (shift)->_has_method_modifiers('around', @_) }
197sub has_after_method_modifiers { (shift)->_has_method_modifiers('after', @_) }
198
199# override just checks for one,..
200# but we can still re-use stuff
201sub has_override_method_modifier { (shift)->_has_method_modifiers('override', @_) }
202
203sub _has_method_modifiers {
e185c027 204 my ($self, $modifier_type, $method_name) = @_;
80572233 205 my $accessor = "get_${modifier_type}_method_modifiers_map";
206 # NOTE:
207 # for now we assume that if it exists,..
208 # it has at least one modifier in it
209 (exists $self->$accessor->{$method_name}) ? 1 : 0;
e185c027 210}
211
80572233 212sub get_before_method_modifiers { (shift)->_get_method_modifiers('before', @_) }
213sub get_around_method_modifiers { (shift)->_get_method_modifiers('around', @_) }
214sub get_after_method_modifiers { (shift)->_get_method_modifiers('after', @_) }
215
216sub _get_method_modifiers {
e185c027 217 my ($self, $modifier_type, $method_name) = @_;
80572233 218 my $accessor = "get_${modifier_type}_method_modifiers_map";
219 @{$self->$accessor->{$method_name}};
220}
221
222sub get_override_method_modifier {
223 my ($self, $method_name) = @_;
224 $self->get_override_method_modifiers_map->{$method_name};
e185c027 225}
226
227sub get_method_modifier_list {
228 my ($self, $modifier_type) = @_;
80572233 229 my $accessor = "get_${modifier_type}_method_modifiers_map";
230 keys %{$self->$accessor};
e185c027 231}
232
bdabd620 233## applying a role to a class ...
234
235sub apply {
236 my ($self, $other) = @_;
237
1331430a 238 # NOTE:
239 # we might need to move this down below the
240 # the attributes so that we can require any
241 # attribute accessors. However I am thinking
242 # that maybe those are somehow exempt from
243 # the require methods stuff.
244 foreach my $required_method_name ($self->get_required_method_list) {
245 ($other->has_method($required_method_name))
246 || confess "Role (" . $self->name . ") requires the method '$required_method_name'" .
247 "is implemented by the class '" . $other->name . "'";
248 }
249
bdabd620 250 foreach my $attribute_name ($self->get_attribute_list) {
251 # skip it if it has one already
252 next if $other->has_attribute($attribute_name);
253 # add it, although it could be overriden
254 $other->add_attribute(
255 $attribute_name,
256 %{$self->get_attribute($attribute_name)}
257 );
258 }
259
260 foreach my $method_name ($self->get_method_list) {
261 # skip it if it has one already
262 next if $other->has_method($method_name);
263 # add it, although it could be overriden
264 $other->alias_method(
265 $method_name,
266 $self->get_method($method_name)
267 );
268 }
269
270 foreach my $method_name ($self->get_method_modifier_list('override')) {
271 # skip it if it has one already
272 next if $other->has_method($method_name);
273 # add it, although it could be overriden
274 $other->add_override_method_modifier(
275 $method_name,
276 $self->get_override_method_modifier($method_name),
277 $self->name
278 );
279 }
280
281 foreach my $method_name ($self->get_method_modifier_list('before')) {
282 $other->add_before_method_modifier(
283 $method_name,
284 $_
285 ) foreach $self->get_before_method_modifiers($method_name);
286 }
287
288 foreach my $method_name ($self->get_method_modifier_list('after')) {
289 $other->add_after_method_modifier(
290 $method_name,
291 $_
292 ) foreach $self->get_after_method_modifiers($method_name);
293 }
294
295 foreach my $method_name ($self->get_method_modifier_list('around')) {
296 $other->add_around_method_modifier(
297 $method_name,
298 $_
299 ) foreach $self->get_around_method_modifiers($method_name);
300 }
301
bdabd620 302 $other->add_role($self);
303}
304
a7d0cd00 305package Moose::Meta::Role::Method;
306
307use strict;
308use warnings;
309
310our $VERSION = '0.01';
311
312use base 'Class::MOP::Method';
e185c027 313
3141;
315
316__END__
317
318=pod
319
320=head1 NAME
321
322Moose::Meta::Role - The Moose Role metaclass
323
324=head1 DESCRIPTION
325
79592a54 326Moose's Roles are being actively developed, please see L<Moose::Role>
327for more information.
328
e185c027 329=head1 METHODS
330
331=over 4
332
333=item B<meta>
334
335=item B<new>
336
78cd1d3b 337=item B<apply>
338
e185c027 339=back
340
341=over 4
342
343=item B<name>
344
345=item B<version>
346
347=item B<role_meta>
348
349=back
350
351=over 4
352
80572233 353=item B<get_roles>
354
355=item B<add_role>
356
357=item B<does_role>
358
359=back
360
361=over 4
362
e185c027 363=item B<get_method>
364
365=item B<has_method>
366
bdabd620 367=item B<alias_method>
368
e185c027 369=item B<get_method_list>
370
371=back
372
373=over 4
374
375=item B<add_attribute>
376
377=item B<has_attribute>
378
379=item B<get_attribute>
380
381=item B<get_attribute_list>
382
383=item B<get_attribute_map>
384
385=item B<remove_attribute>
386
387=back
388
389=over 4
390
1331430a 391=item B<add_required_methods>
392
393=item B<get_required_method_list>
394
395=item B<get_required_methods_map>
396
397=item B<requires_method>
398
399=back
400
401=over 4
402
80572233 403=item B<add_after_method_modifier>
404
405=item B<add_around_method_modifier>
406
407=item B<add_before_method_modifier>
408
409=item B<add_override_method_modifier>
410
411=over 4
412
413=back
414
415=item B<has_after_method_modifiers>
416
417=item B<has_around_method_modifiers>
418
419=item B<has_before_method_modifiers>
420
421=item B<has_override_method_modifier>
422
423=over 4
424
425=back
426
427=item B<get_after_method_modifiers>
e185c027 428
80572233 429=item B<get_around_method_modifiers>
e185c027 430
80572233 431=item B<get_before_method_modifiers>
e185c027 432
433=item B<get_method_modifier_list>
434
80572233 435=over 4
436
437=back
438
439=item B<get_override_method_modifier>
440
441=item B<get_after_method_modifiers_map>
442
443=item B<get_around_method_modifiers_map>
444
445=item B<get_before_method_modifiers_map>
e185c027 446
80572233 447=item B<get_override_method_modifiers_map>
e185c027 448
449=back
450
451=head1 BUGS
452
453All complex software has bugs lurking in it, and this module is no
454exception. If you find a bug please either email me, or add the bug
455to cpan-RT.
456
457=head1 AUTHOR
458
459Stevan Little E<lt>stevan@iinteractive.comE<gt>
460
461=head1 COPYRIGHT AND LICENSE
462
463Copyright 2006 by Infinity Interactive, Inc.
464
465L<http://www.iinteractive.com>
466
467This library is free software; you can redistribute it and/or modify
468it under the same terms as Perl itself.
469
470=cut