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