Tidy the pp constructor generator
[gitmo/Mouse.git] / lib / Mouse / Meta / Role.pm
CommitLineData
a2227e71 1package Mouse::Meta::Role;
5af36247 2use Mouse::Util qw(:meta not_supported); # enables strict and warnings
74be9f76 3
6d28c5cf 4use Mouse::Meta::Module;
f3bb863f 5our @ISA = qw(Mouse::Meta::Module);
a2227e71 6
e058b279 7sub method_metaclass;
6cfa1e5e 8
8e64d0fa 9sub _construct_meta {
acf0f643 10 my $class = shift;
7a50b450 11
acf0f643 12 my %args = @_;
13
5132ec42 14 $args{methods} = {};
15 $args{attributes} = {};
16 $args{required_methods} = [];
17 $args{roles} = [];
274b6cce 18
9009aca1 19 my $self = bless \%args, ref($class) || $class;
20 if($class ne __PACKAGE__){
21 $self->meta->_initialize_object($self, \%args);
22 }
7a50b450 23
9009aca1 24 return $self;
7a50b450 25}
26
27sub create_anon_role{
28 my $self = shift;
29 return $self->create(undef, @_);
30}
31
43165725 32sub is_anon_role;
a2227e71 33
43165725 34sub get_roles;
afc73948 35
e7264861 36sub calculate_all_roles {
37 my $self = shift;
38 my %seen;
39 return grep { !$seen{ $_->name }++ }
40 ($self, map { $_->calculate_all_roles } @{ $self->get_roles });
41}
42
6cfa1e5e 43sub get_required_method_list{
44 return @{ $_[0]->{required_methods} };
45}
afc73948 46
59089ec3 47sub add_required_methods {
ea249879 48 my($self, @methods) = @_;
71e7b544 49 my %required = map{ $_ => 1 } @{$self->{required_methods}};
50 push @{$self->{required_methods}}, grep{ !$required{$_}++ && !$self->has_method($_) } @methods;
51 return;
59089ec3 52}
53
6cfa1e5e 54sub requires_method {
55 my($self, $name) = @_;
56 return scalar( grep{ $_ eq $name } @{ $self->{required_methods} } ) != 0;
57}
58
274b6cce 59sub add_attribute {
60 my $self = shift;
61 my $name = shift;
6cfa1e5e 62
63 $self->{attributes}->{$name} = (@_ == 1) ? $_[0] : { @_ };
c9313657 64 return;
da0c885d 65}
66
3a63a2e7 67sub _check_required_methods{
45f22b92 68 my($role, $consumer, $args) = @_;
3a63a2e7 69
71e7b544 70 if($args->{_to} eq 'role'){
45f22b92 71 $consumer->add_required_methods($role->get_required_method_list);
71e7b544 72 }
73 else{ # to class or instance
45f22b92 74 my $consumer_class_name = $consumer->name;
2d2e77f9 75
7a50b450 76 my @missing;
3a63a2e7 77 foreach my $method_name(@{$role->{required_methods}}){
2d2e77f9 78 next if exists $args->{aliased_methods}{$method_name};
79 next if exists $role->{methods}{$method_name};
45f22b92 80 next if $consumer_class_name->can($method_name);
2d2e77f9 81
82 push @missing, $method_name;
3a63a2e7 83 }
7a50b450 84 if(@missing){
2d2e77f9 85 $role->throw_error(sprintf "'%s' requires the method%s %s to be implemented by '%s'",
86 $role->name,
87 (@missing == 1 ? '' : 's'), # method or methods
5af36247 88 Mouse::Util::quoted_english_list(@missing),
45f22b92 89 $consumer_class_name);
7a50b450 90 }
91 }
2e92bb89 92
3a63a2e7 93 return;
2e92bb89 94}
95
3a63a2e7 96sub _apply_methods{
45f22b92 97 my($role, $consumer, $args) = @_;
da0c885d 98
7a50b450 99 my $alias = $args->{-alias};
100 my $excludes = $args->{-excludes};
e0b163e1 101
3a63a2e7 102 foreach my $method_name($role->get_method_list){
103 next if $method_name eq 'meta';
104
71e7b544 105 my $code = $role->get_method_body($method_name);
6cfa1e5e 106
2d2e77f9 107 if(!exists $excludes->{$method_name}){
45f22b92 108 if(!$consumer->has_method($method_name)){
2d2e77f9 109 # The third argument $role is used in Role::Composite
45f22b92 110 $consumer->add_method($method_name => $code, $role);
6cfa1e5e 111 }
2e92bb89 112 }
2e92bb89 113
2d2e77f9 114 if(exists $alias->{$method_name}){
3a63a2e7 115 my $dstname = $alias->{$method_name};
6cfa1e5e 116
45f22b92 117 my $dstcode = $consumer->get_method_body($dstname);
7a50b450 118
119 if(defined($dstcode) && $dstcode != $code){
71e7b544 120 $role->throw_error("Cannot create a method alias if a local method of the same name exists");
21498b08 121 }
3a63a2e7 122 else{
45f22b92 123 $consumer->add_method($dstname => $code, $role);
2e92bb89 124 }
59089ec3 125 }
126 }
127
3a63a2e7 128 return;
129}
130
131sub _apply_attributes{
45f22b92 132 my($role, $consumer, $args) = @_;
3a63a2e7 133
71e7b544 134 for my $attr_name ($role->get_attribute_list) {
45f22b92 135 next if $consumer->has_attribute($attr_name);
3a63a2e7 136
45f22b92 137 $consumer->add_attribute($attr_name => $role->get_attribute($attr_name));
da0c885d 138 }
3a63a2e7 139 return;
140}
141
142sub _apply_modifiers{
45f22b92 143 my($role, $consumer, $args) = @_;
3a63a2e7 144
2d2e77f9 145 if(my $modifiers = $role->{override_method_modifiers}){
146 foreach my $method_name (keys %{$modifiers}){
45f22b92 147 $consumer->add_override_method_modifier($method_name => $modifiers->{$method_name});
2d2e77f9 148 }
149 }
150
151 for my $modifier_type (qw/before around after/) {
71e7b544 152 my $modifiers = $role->{"${modifier_type}_method_modifiers"}
153 or next;
154
3a63a2e7 155 my $add_modifier = "add_${modifier_type}_method_modifier";
d99db7b6 156
71e7b544 157 foreach my $method_name (keys %{$modifiers}){
2d2e77f9 158 foreach my $code(@{ $modifiers->{$method_name} }){
45f22b92 159 next if $consumer->{"_applied_$modifier_type"}{$method_name, $code}++; # skip applied modifiers
160 $consumer->$add_modifier($method_name => $code);
d99db7b6 161 }
162 }
163 }
3a63a2e7 164 return;
da0c885d 165}
0fc8adbc 166
3a63a2e7 167sub _append_roles{
45f22b92 168 my($role, $consumer, $args) = @_;
21498b08 169
45f22b92 170 my $roles = ($args->{_to} eq 'role') ? $consumer->get_roles : $consumer->roles;
3a63a2e7 171
172 foreach my $r($role, @{$role->get_roles}){
45f22b92 173 if(!$consumer->does_role($r->name)){
3a63a2e7 174 push @{$roles}, $r;
21498b08 175 }
176 }
3a63a2e7 177 return;
178}
21498b08 179
3a63a2e7 180# Moose uses Application::ToInstance, Application::ToClass, Application::ToRole
181sub apply {
45f22b92 182 my $self = shift;
183 my $consumer = shift;
7a50b450 184
71e7b544 185 my %args = (@_ == 1) ? %{ $_[0] } : @_;
60b5c3be 186
f774b7de 187 my $instance;
188
45f22b92 189 if(Mouse::Util::is_a_metaclass($consumer)){ # Application::ToClass
71e7b544 190 $args{_to} = 'class';
191 }
45f22b92 192 elsif(Mouse::Util::is_a_metarole($consumer)){ # Application::ToRole
71e7b544 193 $args{_to} = 'role';
194 }
195 else{ # Appplication::ToInstance
196 $args{_to} = 'instance';
45f22b92 197 $instance = $consumer;
60b5c3be 198
45f22b92 199 $consumer = (Mouse::Util::class_of($instance) || 'Mouse::Meta::Class')->create_anon_class(
f774b7de 200 superclasses => [ref $instance],
71e7b544 201 cache => 1,
202 );
71e7b544 203 }
60b5c3be 204
71e7b544 205 if($args{alias} && !exists $args{-alias}){
206 $args{-alias} = $args{alias};
207 }
208 if($args{excludes} && !exists $args{-excludes}){
209 $args{-excludes} = $args{excludes};
210 }
60b5c3be 211
2d2e77f9 212 $args{aliased_methods} = {};
71e7b544 213 if(my $alias = $args{-alias}){
2d2e77f9 214 @{$args{aliased_methods}}{ values %{$alias} } = ();
60b5c3be 215 }
71e7b544 216
217 if(my $excludes = $args{-excludes}){
218 $args{-excludes} = {}; # replace with a hash ref
219 if(ref $excludes){
220 %{$args{-excludes}} = (map{ $_ => undef } @{$excludes});
60b5c3be 221 }
222 else{
71e7b544 223 $args{-excludes}{$excludes} = undef;
60b5c3be 224 }
225 }
226
45f22b92 227 $self->_check_required_methods($consumer, \%args);
228 $self->_apply_attributes($consumer, \%args);
229 $self->_apply_methods($consumer, \%args);
230 $self->_apply_modifiers($consumer, \%args);
231 $self->_append_roles($consumer, \%args);
f774b7de 232
233
234 if(defined $instance){ # Application::ToInstance
235 # rebless instance
45f22b92 236 bless $instance, $consumer->name;
237 $consumer->_initialize_object($instance, $instance);
f774b7de 238 }
239
71e7b544 240 return;
241}
242
21498b08 243
71e7b544 244sub combine {
245 my($role_class, @role_specs) = @_;
21498b08 246
71e7b544 247 require 'Mouse/Meta/Role/Composite.pm'; # we don't want to create its namespace
7a50b450 248
71e7b544 249 my $composite = Mouse::Meta::Role::Composite->create_anon_role();
250
251 foreach my $role_spec (@role_specs) {
252 my($role_name, $args) = @{$role_spec};
253 $role_name->meta->apply($composite, %{$args});
21498b08 254 }
71e7b544 255 return $composite;
21498b08 256}
257
47379288 258sub add_before_method_modifier {
259 my ($self, $method_name, $method) = @_;
3a63a2e7 260
47379288 261 push @{ $self->{before_method_modifiers}{$method_name} ||= [] }, $method;
262 return;
263}
264sub add_around_method_modifier {
265 my ($self, $method_name, $method) = @_;
c9313657 266
47379288 267 push @{ $self->{around_method_modifiers}{$method_name} ||= [] }, $method;
268 return;
269}
270sub add_after_method_modifier {
271 my ($self, $method_name, $method) = @_;
c2f128e7 272
47379288 273 push @{ $self->{after_method_modifiers}{$method_name} ||= [] }, $method;
274 return;
275}
c9313657 276
47379288 277sub get_before_method_modifiers {
278 my ($self, $method_name) = @_;
279 return @{ $self->{before_method_modifiers}{$method_name} ||= [] }
280}
281sub get_around_method_modifiers {
282 my ($self, $method_name) = @_;
283 return @{ $self->{around_method_modifiers}{$method_name} ||= [] }
284}
285sub get_after_method_modifiers {
286 my ($self, $method_name) = @_;
287 return @{ $self->{after_method_modifiers}{$method_name} ||= [] }
3a63a2e7 288}
47f36c05 289
6cfa1e5e 290sub add_override_method_modifier{
291 my($self, $method_name, $method) = @_;
292
60b5c3be 293 if($self->has_method($method_name)){
294 # This error happens in the override keyword or during role composition,
295 # so I added a message, "A local method of ...", only for compatibility (gfx)
8e64d0fa 296 $self->throw_error("Cannot add an override of method '$method_name' "
60b5c3be 297 . "because there is a local version of '$method_name'"
298 . "(A local method of the same name as been found)");
299 }
6cfa1e5e 300
301 $self->{override_method_modifiers}->{$method_name} = $method;
302}
303
8e64d0fa 304sub get_override_method_modifier {
305 my ($self, $method_name) = @_;
306 return $self->{override_method_modifiers}->{$method_name};
6cfa1e5e 307}
308
67199842 309sub does_role {
310 my ($self, $role_name) = @_;
311
312 (defined $role_name)
fce211ae 313 || $self->throw_error("You must supply a role name to look for");
67199842 314
315 # if we are it,.. then return true
316 return 1 if $role_name eq $self->name;
3a63a2e7 317 # otherwise.. check our children
318 for my $role (@{ $self->get_roles }) {
67199842 319 return 1 if $role->does_role($role_name);
320 }
321 return 0;
322}
323
a2227e71 3241;
1820fffe 325__END__
326
327=head1 NAME
328
329Mouse::Meta::Role - The Mouse Role metaclass
330
a25ca8d6 331=head1 VERSION
332
4bc73e47 333This document describes Mouse version 0.50_03
a25ca8d6 334
1820fffe 335=head1 SEE ALSO
336
337L<Moose::Meta::Role>
338
339=cut