Add my name to Changes
[gitmo/Mouse.git] / lib / Mouse / Meta / Role.pm
CommitLineData
a2227e71 1package Mouse::Meta::Role;
2use strict;
3use warnings;
59089ec3 4use Carp 'confess';
74be9f76 5
3a63a2e7 6use base qw(Mouse::Meta::Module);
a2227e71 7
3a63a2e7 8sub _new {
acf0f643 9 my $class = shift;
10 my %args = @_;
11
3a63a2e7 12 $args{methods} ||= {};
59089ec3 13 $args{attributes} ||= {};
14 $args{required_methods} ||= [];
47f36c05 15 $args{roles} ||= [];
274b6cce 16
acf0f643 17 bless \%args, $class;
18}
a2227e71 19
afc73948 20sub get_roles { $_[0]->{roles} }
21
22
59089ec3 23sub add_required_methods {
24 my $self = shift;
25 my @methods = @_;
26 push @{$self->{required_methods}}, @methods;
27}
28
274b6cce 29sub add_attribute {
30 my $self = shift;
31 my $name = shift;
69ac1dcf 32 my $spec = shift;
9c85e9dc 33 $self->{attributes}->{$name} = $spec;
da0c885d 34}
35
3a63a2e7 36sub _check_required_methods{
37 my($role, $class, $args, @other_roles) = @_;
38
39 if($class->isa('Mouse::Meta::Class')){
40 my $class_name = $class->name;
41 foreach my $method_name(@{$role->{required_methods}}){
42 unless($class_name->can($method_name)){
43 my $role_name = $role->name;
44 my $has_method = 0;
45
46 foreach my $another_role_spec(@other_roles){
47 my $another_role_name = $another_role_spec->[0];
48 if($role_name ne $another_role_name && $another_role_name->can($method_name)){
49 $has_method = 1;
50 last;
51 }
52 }
53
54 confess "'$role_name' requires the method '$method_name' to be implemented by '$class_name'"
55 unless $has_method;
56 }
57 }
58 }
2e92bb89 59
3a63a2e7 60 return;
2e92bb89 61}
62
3a63a2e7 63sub _apply_methods{
64 my($role, $class, $args) = @_;
da0c885d 65
3a63a2e7 66 my $role_name = $role->name;
67 my $class_name = $class->name;
68 my $alias = $args->{alias};
e0b163e1 69
3a63a2e7 70 foreach my $method_name($role->get_method_list){
71 next if $method_name eq 'meta';
72
73 my $code = $role_name->can($method_name);
74 if(do{ no strict 'refs'; defined &{$class_name . '::' . $method_name} }){
75 # XXX what's Moose's behavior?
76 }
77 else{
78 $class->add_method($method_name => $code);
2e92bb89 79 }
2e92bb89 80
3a63a2e7 81 if($alias && $alias->{$method_name}){
82 my $dstname = $alias->{$method_name};
83 if(do{ no strict 'refs'; defined &{$class_name . '::' . $dstname} }){
84 # XXX wat's Moose's behavior?
21498b08 85 }
3a63a2e7 86 else{
87 $class->add_method($dstname => $code);
2e92bb89 88 }
59089ec3 89 }
90 }
91
3a63a2e7 92 return;
93}
94
95sub _apply_attributes{
96 my($role, $class, $args) = @_;
97
b1b81553 98 if ($class->isa('Mouse::Meta::Class')) {
99 # apply role to class
3a63a2e7 100 for my $attr_name ($role->get_attribute_list) {
101 next if $class->has_attribute($attr_name);
102
103 my $spec = $role->get_attribute($attr_name);
05b9dc92 104
3a63a2e7 105 my $attr_metaclass = 'Mouse::Meta::Attribute';
05b9dc92 106 if ( my $metaclass_name = $spec->{metaclass} ) {
3a63a2e7 107 $attr_metaclass = Mouse::Util::resolve_metaclass_alias(
05b9dc92 108 'Attribute',
109 $metaclass_name
110 );
05b9dc92 111 }
112
3a63a2e7 113 $attr_metaclass->create($class, $attr_name => %$spec);
b1b81553 114 }
115 } else {
116 # apply role to role
3a63a2e7 117 for my $attr_name ($role->get_attribute_list) {
118 next if $class->has_attribute($attr_name);
119
120 my $spec = $role->get_attribute($attr_name);
121 $class->add_attribute($attr_name => $spec);
b1b81553 122 }
da0c885d 123 }
d99db7b6 124
3a63a2e7 125 return;
126}
127
128sub _apply_modifiers{
129 my($role, $class, $args) = @_;
130
67199842 131 for my $modifier_type (qw/before after around override/) {
3a63a2e7 132 my $add_modifier = "add_${modifier_type}_method_modifier";
133 my $modifiers = $role->{"${modifier_type}_method_modifiers"};
d99db7b6 134
3a63a2e7 135 while(my($method_name, $modifier_codes) = each %{$modifiers}){
136 foreach my $code(@{$modifier_codes}){
137 $class->$add_modifier($method_name => $code);
d99db7b6 138 }
139 }
140 }
3a63a2e7 141 return;
da0c885d 142}
0fc8adbc 143
3a63a2e7 144sub _append_roles{
145 my($role, $class, $args) = @_;
21498b08 146
3a63a2e7 147 my $roles = $class->isa('Mouse::Meta::Class') ? $class->roles : $class->get_roles;
148
149 foreach my $r($role, @{$role->get_roles}){
150 if(!$class->does_role($r->name)){
151 push @{$roles}, $r;
21498b08 152 }
153 }
3a63a2e7 154 return;
155}
21498b08 156
3a63a2e7 157# Moose uses Application::ToInstance, Application::ToClass, Application::ToRole
158sub apply {
159 my($self, $class, %args) = @_;
160
161 if ($class->isa('Mouse::Object')) {
162 Carp::croak('Mouse does not support Application::ToInstance yet');
21498b08 163 }
164
3a63a2e7 165 $self->_check_required_methods($class, \%args);
166 $self->_apply_methods($class, \%args);
167 $self->_apply_attributes($class, \%args);
168 $self->_apply_modifiers($class, \%args);
169 $self->_append_roles($class, \%args);
170 return;
171}
21498b08 172
3a63a2e7 173sub combine_apply {
174 my(undef, $class, @roles) = @_;
05b9dc92 175
3a63a2e7 176 foreach my $role_spec (@roles) {
177 my($role_name, $args) = @{$role_spec};
21498b08 178
3a63a2e7 179 my $role = $role_name->meta;
21498b08 180
3a63a2e7 181 $role->_check_required_methods($class, $args, @roles);
182 $role->_apply_methods($class, $args);
183 $role->_apply_attributes($class, $args);
184 $role->_apply_modifiers($class, $args);
185 $role->_append_roles($class, $args);
21498b08 186 }
3a63a2e7 187 return;
21498b08 188}
189
67199842 190for my $modifier_type (qw/before after around override/) {
3a63a2e7 191
192 my $modifier = "${modifier_type}_method_modifiers";
193 my $add_method_modifier = sub {
fc0e0bbd 194 my ($self, $method_name, $method) = @_;
195
3a63a2e7 196 push @{ $self->{$modifier}->{$method_name} ||= [] }, $method;
197 return;
fc0e0bbd 198 };
3370794f 199 my $has_method_modifiers = sub{
200 my($self, $method_name) = @_;
201 my $m = $self->{$modifier}->{$method_name};
202 return $m && @{$m} != 0;
203 };
3a63a2e7 204 my $get_method_modifiers = sub {
205 my ($self, $method_name) = @_;
206 return @{ $self->{$modifier}->{$method_name} ||= [] }
c2f128e7 207 };
c2f128e7 208
3a63a2e7 209 no strict 'refs';
210 *{ 'add_' . $modifier_type . '_method_modifier' } = $add_method_modifier;
3370794f 211 *{ 'has_' . $modifier_type . '_method_modifiers' } = $has_method_modifiers;
3a63a2e7 212 *{ 'get_' . $modifier_type . '_method_modifiers' } = $get_method_modifiers;
213}
47f36c05 214
67199842 215# This is currently not passing all the Moose tests.
216sub does_role {
217 my ($self, $role_name) = @_;
218
219 (defined $role_name)
220 || confess "You must supply a role name to look for";
221
222 # if we are it,.. then return true
223 return 1 if $role_name eq $self->name;
3a63a2e7 224 # otherwise.. check our children
225 for my $role (@{ $self->get_roles }) {
67199842 226 return 1 if $role->does_role($role_name);
227 }
228 return 0;
229}
230
231
a2227e71 2321;
233