roles
[gitmo/Moose.git] / lib / Moose / Meta / Class.pm
1
2 package Moose::Meta::Class;
3
4 use strict;
5 use warnings;
6
7 use Class::MOP;
8
9 use Carp         'confess';
10 use Scalar::Util 'weaken', 'blessed', 'reftype';
11
12 our $VERSION = '0.06';
13
14 use base 'Class::MOP::Class';
15
16 __PACKAGE__->meta->add_attribute('roles' => (
17     reader  => 'roles',
18     default => sub { [] }
19 ));
20
21 sub initialize {
22     my $class = shift;
23     my $pkg   = shift;
24     $class->SUPER::initialize($pkg,
25         ':attribute_metaclass' => 'Moose::Meta::Attribute', 
26         ':instance_metaclass'  => 'Moose::Meta::Instance', 
27         @_);
28 }
29
30 sub add_role {
31     my ($self, $role) = @_;
32     (blessed($role) && $role->isa('Moose::Meta::Role'))
33         || confess "Roles must be instances of Moose::Meta::Role";
34     push @{$self->roles} => $role;
35 }
36
37 sub does_role {
38     my ($self, $role_name) = @_;
39     (defined $role_name)
40         || confess "You must supply a role name to look for";
41     foreach my $class ($self->class_precedence_list) {
42         foreach my $role (@{$class->meta->roles}) {
43             return 1 if $role->does_role($role_name);
44         }
45     }
46     return 0;
47 }
48
49 sub excludes_role {
50     my ($self, $role_name) = @_;
51     (defined $role_name)
52         || confess "You must supply a role name to look for";
53     foreach my $class ($self->class_precedence_list) {        
54         foreach my $role (@{$class->meta->roles}) {
55             return 1 if $role->excludes_role($role_name);
56         }
57     }
58     return 0;
59 }
60
61 sub new_object {
62     my ($class, %params) = @_;
63     my $self = $class->SUPER::new_object(%params);
64     foreach my $attr ($class->compute_all_applicable_attributes()) {
65         next unless $params{$attr->init_arg} && $attr->can('has_trigger') && $attr->has_trigger;
66         $attr->trigger->($self, $params{$attr->init_arg}, $attr);
67     }
68     return $self;    
69 }
70
71 sub construct_instance {
72     my ($class, %params) = @_;
73     my $meta_instance = $class->get_meta_instance;
74     # FIXME:
75     # the code below is almost certainly incorrect
76     # but this is foreign inheritence, so we might
77     # have to kludge it in the end. 
78     my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
79     foreach my $attr ($class->compute_all_applicable_attributes()) {
80         $attr->initialize_instance_slot($meta_instance, $instance, \%params)
81     }
82     return $instance;
83 }
84
85 sub has_method {
86     my ($self, $method_name) = @_;
87     (defined $method_name && $method_name)
88         || confess "You must define a method name";    
89
90     my $sub_name = ($self->name . '::' . $method_name);   
91     
92     no strict 'refs';
93     return 0 if !defined(&{$sub_name});        
94         my $method = \&{$sub_name};
95         
96         return 1 if blessed($method) && $method->isa('Moose::Meta::Role::Method');
97     return $self->SUPER::has_method($method_name);    
98 }
99
100 sub add_override_method_modifier {
101     my ($self, $name, $method, $_super_package) = @_;
102     (!$self->has_method($name))
103         || confess "Cannot add an override method if a local method is already present";
104     # need this for roles ...
105     $_super_package ||= $self->name;
106     my $super = $self->find_next_method_by_name($name);
107     (defined $super)
108         || confess "You cannot override '$name' because it has no super method";    
109     $self->add_method($name => bless sub {
110         my @args = @_;
111         no strict   'refs';
112         no warnings 'redefine';
113         local *{$_super_package . '::super'} = sub { $super->(@args) };
114         return $method->(@args);
115     } => 'Moose::Meta::Method::Overriden');
116 }
117
118 sub add_augment_method_modifier {
119     my ($self, $name, $method) = @_;  
120     (!$self->has_method($name))
121         || confess "Cannot add an augment method if a local method is already present";    
122     my $super = $self->find_next_method_by_name($name);
123     (defined $super)
124         || confess "You cannot augment '$name' because it has no super method";    
125     my $_super_package = $super->package_name;   
126     # BUT!,... if this is an overriden method ....     
127     if ($super->isa('Moose::Meta::Method::Overriden')) {
128         # we need to be sure that we actually 
129         # find the next method, which is not 
130         # an 'override' method, the reason is
131         # that an 'override' method will not 
132         # be the one calling inner()
133         my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);        
134         $_super_package = $real_super->package_name;
135     }      
136     $self->add_method($name => sub {
137         my @args = @_;
138         no strict   'refs';
139         no warnings 'redefine';
140         local *{$_super_package . '::inner'} = sub { $method->(@args) };
141         return $super->(@args);
142     });    
143 }
144
145 sub _find_next_method_by_name_which_is_not_overridden {
146     my ($self, $name) = @_;
147     my @methods = $self->find_all_methods_by_name($name);
148     foreach my $method (@methods) {
149         return $method->{code} 
150             if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
151     }
152     return undef;
153 }
154
155 package Moose::Meta::Method::Overriden;
156
157 use strict;
158 use warnings;
159
160 our $VERSION = '0.01';
161
162 use base 'Class::MOP::Method';
163
164 1;
165
166 __END__
167
168 =pod
169
170 =head1 NAME
171
172 Moose::Meta::Class - The Moose metaclass
173
174 =head1 DESCRIPTION
175
176 This is a subclass of L<Class::MOP::Class> with Moose specific 
177 extensions.
178
179 For the most part, the only time you will ever encounter an 
180 instance of this class is if you are doing some serious deep 
181 introspection. To really understand this class, you need to refer 
182 to the L<Class::MOP::Class> documentation.
183
184 =head1 METHODS
185
186 =over 4
187
188 =item B<initialize>
189
190 =item B<new_object>
191
192 We override this method to support the C<trigger> attribute option.
193
194 =item B<construct_instance>
195
196 This provides some Moose specific extensions to this method, you 
197 almost never call this method directly unless you really know what 
198 you are doing. 
199
200 This method makes sure to handle the moose weak-ref, type-constraint
201 and type coercion features. 
202
203 =item B<has_method ($name)>
204
205 This accomidates Moose::Meta::Role::Method instances, which are 
206 aliased, instead of added, but still need to be counted as valid 
207 methods.
208
209 =item B<add_override_method_modifier ($name, $method)>
210
211 This will create an C<override> method modifier for you, and install 
212 it in the package.
213
214 =item B<add_augment_method_modifier ($name, $method)>
215
216 This will create an C<augment> method modifier for you, and install 
217 it in the package.
218
219 =item B<roles>
220
221 This will return an array of C<Moose::Meta::Role> instances which are 
222 attached to this class.
223
224 =item B<add_role ($role)>
225
226 This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it 
227 to the list of associated roles.
228
229 =item B<does_role ($role_name)>
230
231 This will test if this class C<does> a given C<$role_name>. It will 
232 not only check it's local roles, but ask them as well in order to 
233 cascade down the role hierarchy.
234
235 =item B<excludes_role ($role_name)>
236
237 This will test if this class C<excludes> a given C<$role_name>. It will 
238 not only check it's local roles, but ask them as well in order to 
239 cascade down the role hierarchy.
240
241 =item B<add_attribute $attr_name, %params>
242
243 This method does the same thing as L<Class::MOP::Class/add_attribute>, but adds
244 suport for delegation.
245
246 =back
247
248 =head1 INTERNAL METHODS
249
250 =over 4
251
252 =item compute_delegation
253
254 =item generate_delegation_list
255
256 =item generate_delgate_method
257
258 =item get_delegatable_methods
259
260 =item filter_delegations
261
262 =back
263
264 =head1 BUGS
265
266 All complex software has bugs lurking in it, and this module is no 
267 exception. If you find a bug please either email me, or add the bug
268 to cpan-RT.
269
270 =head1 AUTHOR
271
272 Stevan Little E<lt>stevan@iinteractive.comE<gt>
273
274 =head1 COPYRIGHT AND LICENSE
275
276 Copyright 2006 by Infinity Interactive, Inc.
277
278 L<http://www.iinteractive.com>
279
280 This library is free software; you can redistribute it and/or modify
281 it under the same terms as Perl itself. 
282
283 =cut
284