0_04
[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) {
fa1be058 245 unless ($other->has_method($required_method_name)) {
246 if ($other->isa('Moose::Meta::Role')) {
247 $other->add_required_methods($required_method_name);
248 }
249 else {
250 confess "'" . $self->name . "' requires the method '$required_method_name' " .
251 "to be implemented by '" . $other->name . "'";
252 }
253 }
1331430a 254 }
255
bdabd620 256 foreach my $attribute_name ($self->get_attribute_list) {
257 # skip it if it has one already
258 next if $other->has_attribute($attribute_name);
259 # add it, although it could be overriden
260 $other->add_attribute(
261 $attribute_name,
262 %{$self->get_attribute($attribute_name)}
263 );
264 }
265
266 foreach my $method_name ($self->get_method_list) {
267 # skip it if it has one already
268 next if $other->has_method($method_name);
269 # add it, although it could be overriden
270 $other->alias_method(
271 $method_name,
272 $self->get_method($method_name)
273 );
274 }
275
276 foreach my $method_name ($self->get_method_modifier_list('override')) {
277 # skip it if it has one already
278 next if $other->has_method($method_name);
279 # add it, although it could be overriden
280 $other->add_override_method_modifier(
281 $method_name,
282 $self->get_override_method_modifier($method_name),
283 $self->name
284 );
285 }
286
287 foreach my $method_name ($self->get_method_modifier_list('before')) {
288 $other->add_before_method_modifier(
289 $method_name,
290 $_
291 ) foreach $self->get_before_method_modifiers($method_name);
292 }
293
294 foreach my $method_name ($self->get_method_modifier_list('after')) {
295 $other->add_after_method_modifier(
296 $method_name,
297 $_
298 ) foreach $self->get_after_method_modifiers($method_name);
299 }
300
301 foreach my $method_name ($self->get_method_modifier_list('around')) {
302 $other->add_around_method_modifier(
303 $method_name,
304 $_
305 ) foreach $self->get_around_method_modifiers($method_name);
306 }
307
bdabd620 308 $other->add_role($self);
309}
310
a7d0cd00 311package Moose::Meta::Role::Method;
312
313use strict;
314use warnings;
315
316our $VERSION = '0.01';
317
318use base 'Class::MOP::Method';
e185c027 319
3201;
321
322__END__
323
324=pod
325
326=head1 NAME
327
328Moose::Meta::Role - The Moose Role metaclass
329
330=head1 DESCRIPTION
331
79592a54 332Moose's Roles are being actively developed, please see L<Moose::Role>
02a0fb52 333for more information. For the most part, this has no user-serviceable
334parts inside. It's API is still subject to some change (although
335probably not that much really).
79592a54 336
e185c027 337=head1 METHODS
338
339=over 4
340
341=item B<meta>
342
343=item B<new>
344
78cd1d3b 345=item B<apply>
346
e185c027 347=back
348
349=over 4
350
351=item B<name>
352
353=item B<version>
354
355=item B<role_meta>
356
357=back
358
359=over 4
360
80572233 361=item B<get_roles>
362
363=item B<add_role>
364
365=item B<does_role>
366
367=back
368
369=over 4
370
e185c027 371=item B<get_method>
372
373=item B<has_method>
374
bdabd620 375=item B<alias_method>
376
e185c027 377=item B<get_method_list>
378
379=back
380
381=over 4
382
383=item B<add_attribute>
384
385=item B<has_attribute>
386
387=item B<get_attribute>
388
389=item B<get_attribute_list>
390
391=item B<get_attribute_map>
392
393=item B<remove_attribute>
394
395=back
396
397=over 4
398
1331430a 399=item B<add_required_methods>
400
401=item B<get_required_method_list>
402
403=item B<get_required_methods_map>
404
405=item B<requires_method>
406
407=back
408
409=over 4
410
80572233 411=item B<add_after_method_modifier>
412
413=item B<add_around_method_modifier>
414
415=item B<add_before_method_modifier>
416
417=item B<add_override_method_modifier>
418
419=over 4
420
421=back
422
423=item B<has_after_method_modifiers>
424
425=item B<has_around_method_modifiers>
426
427=item B<has_before_method_modifiers>
428
429=item B<has_override_method_modifier>
430
431=over 4
432
433=back
434
435=item B<get_after_method_modifiers>
e185c027 436
80572233 437=item B<get_around_method_modifiers>
e185c027 438
80572233 439=item B<get_before_method_modifiers>
e185c027 440
441=item B<get_method_modifier_list>
442
80572233 443=over 4
444
445=back
446
447=item B<get_override_method_modifier>
448
449=item B<get_after_method_modifiers_map>
450
451=item B<get_around_method_modifiers_map>
452
453=item B<get_before_method_modifiers_map>
e185c027 454
80572233 455=item B<get_override_method_modifiers_map>
e185c027 456
457=back
458
459=head1 BUGS
460
461All complex software has bugs lurking in it, and this module is no
462exception. If you find a bug please either email me, or add the bug
463to cpan-RT.
464
465=head1 AUTHOR
466
467Stevan Little E<lt>stevan@iinteractive.comE<gt>
468
469=head1 COPYRIGHT AND LICENSE
470
471Copyright 2006 by Infinity Interactive, Inc.
472
473L<http://www.iinteractive.com>
474
475This library is free software; you can redistribute it and/or modify
476it under the same terms as Perl itself.
477
478=cut