find_method_by_name in Moose::Meta::Role, + bug fix for composition over inherited...
[gitmo/Moose.git] / lib / Moose / Meta / Class.pm
CommitLineData
c0e30cf5 1
2package Moose::Meta::Class;
3
4use strict;
5use warnings;
6
648e79ae 7use Class::MOP;
8
6ba6d68c 9use Carp 'confess';
54b1cdf0 10use Scalar::Util 'weaken', 'blessed', 'reftype';
a15dff8d 11
1341f10c 12our $VERSION = '0.07';
bc1e29b5 13
c0e30cf5 14use base 'Class::MOP::Class';
15
598340d5 16__PACKAGE__->meta->add_attribute('roles' => (
ef333f17 17 reader => 'roles',
18 default => sub { [] }
19));
20
590868a3 21sub initialize {
22 my $class = shift;
23 my $pkg = shift;
24 $class->SUPER::initialize($pkg,
25 ':attribute_metaclass' => 'Moose::Meta::Attribute',
ddd0ec20 26 ':instance_metaclass' => 'Moose::Meta::Instance',
590868a3 27 @_);
1341f10c 28}
590868a3 29
ef333f17 30sub 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
b8aeb4dc 37sub calculate_all_roles {
38 my $self = shift;
39 my %seen;
40 grep { !$seen{$_->name}++ } map { $_->calculate_all_roles } @{ $self->roles };
41}
42
ef333f17 43sub does_role {
44 my ($self, $role_name) = @_;
45 (defined $role_name)
46 || confess "You must supply a role name to look for";
9c429218 47 foreach my $class ($self->class_precedence_list) {
74f6d830 48 next unless $class->can('meta');
9c429218 49 foreach my $role (@{$class->meta->roles}) {
50 return 1 if $role->does_role($role_name);
51 }
ef333f17 52 }
53 return 0;
54}
55
d79e62fd 56sub excludes_role {
57 my ($self, $role_name) = @_;
58 (defined $role_name)
59 || confess "You must supply a role name to look for";
74f6d830 60 foreach my $class ($self->class_precedence_list) {
61 next unless $class->can('meta');
9c429218 62 foreach my $role (@{$class->meta->roles}) {
63 return 1 if $role->excludes_role($role_name);
64 }
d79e62fd 65 }
66 return 0;
67}
68
8c9d74e7 69sub new_object {
70 my ($class, %params) = @_;
71 my $self = $class->SUPER::new_object(%params);
72 foreach my $attr ($class->compute_all_applicable_attributes()) {
715ea0b7 73 # FIXME:
74 # this does not accept undefined
75 # values, nor does it accept false
76 # values to be passed into the init-arg
5faf11bb 77 next unless $params{$attr->init_arg} && $attr->can('has_trigger') && $attr->has_trigger;
78 $attr->trigger->($self, $params{$attr->init_arg}, $attr);
8c9d74e7 79 }
80 return $self;
81}
82
a15dff8d 83sub construct_instance {
84 my ($class, %params) = @_;
ddd0ec20 85 my $meta_instance = $class->get_meta_instance;
575db57d 86 # FIXME:
87 # the code below is almost certainly incorrect
88 # but this is foreign inheritence, so we might
89 # have to kludge it in the end.
ddd0ec20 90 my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
74f6d830 91 foreach my $attr ($class->compute_all_applicable_attributes()) {
ddd0ec20 92 $attr->initialize_instance_slot($meta_instance, $instance, \%params)
a15dff8d 93 }
94 return $instance;
95}
96
a7d0cd00 97sub has_method {
98 my ($self, $method_name) = @_;
99 (defined $method_name && $method_name)
100 || confess "You must define a method name";
101
102 my $sub_name = ($self->name . '::' . $method_name);
103
104 no strict 'refs';
105 return 0 if !defined(&{$sub_name});
106 my $method = \&{$sub_name};
107
108 return 1 if blessed($method) && $method->isa('Moose::Meta::Role::Method');
109 return $self->SUPER::has_method($method_name);
110}
111
a2eec5e7 112sub add_attribute {
113 my $self = shift;
114 my $name = shift;
115 if (scalar @_ == 1 && ref($_[0]) eq 'HASH') {
116 # NOTE:
117 # if it is a HASH ref, we de-ref it.
118 # this will usually mean that it is
119 # coming from a role
120 $self->SUPER::add_attribute($name => %{$_[0]});
121 }
122 else {
123 # otherwise we just pass the args
124 $self->SUPER::add_attribute($name => @_);
125 }
126}
127
78cd1d3b 128sub add_override_method_modifier {
129 my ($self, $name, $method, $_super_package) = @_;
d05cd563 130 (!$self->has_method($name))
131 || confess "Cannot add an override method if a local method is already present";
78cd1d3b 132 # need this for roles ...
133 $_super_package ||= $self->name;
134 my $super = $self->find_next_method_by_name($name);
135 (defined $super)
136 || confess "You cannot override '$name' because it has no super method";
05d9eaf6 137 $self->add_method($name => bless sub {
78cd1d3b 138 my @args = @_;
139 no strict 'refs';
140 no warnings 'redefine';
141 local *{$_super_package . '::super'} = sub { $super->(@args) };
142 return $method->(@args);
05d9eaf6 143 } => 'Moose::Meta::Method::Overriden');
78cd1d3b 144}
145
146sub add_augment_method_modifier {
05d9eaf6 147 my ($self, $name, $method) = @_;
d05cd563 148 (!$self->has_method($name))
149 || confess "Cannot add an augment method if a local method is already present";
78cd1d3b 150 my $super = $self->find_next_method_by_name($name);
151 (defined $super)
05d9eaf6 152 || confess "You cannot augment '$name' because it has no super method";
153 my $_super_package = $super->package_name;
154 # BUT!,... if this is an overriden method ....
155 if ($super->isa('Moose::Meta::Method::Overriden')) {
156 # we need to be sure that we actually
157 # find the next method, which is not
158 # an 'override' method, the reason is
159 # that an 'override' method will not
160 # be the one calling inner()
161 my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);
162 $_super_package = $real_super->package_name;
163 }
78cd1d3b 164 $self->add_method($name => sub {
165 my @args = @_;
166 no strict 'refs';
167 no warnings 'redefine';
05d9eaf6 168 local *{$_super_package . '::inner'} = sub { $method->(@args) };
78cd1d3b 169 return $super->(@args);
170 });
171}
172
1341f10c 173## Private Utility methods ...
174
05d9eaf6 175sub _find_next_method_by_name_which_is_not_overridden {
176 my ($self, $name) = @_;
177 my @methods = $self->find_all_methods_by_name($name);
178 foreach my $method (@methods) {
179 return $method->{code}
180 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
181 }
182 return undef;
183}
184
1341f10c 185sub _fix_metaclass_incompatability {
186 my ($self, @superclasses) = @_;
187 foreach my $super (@superclasses) {
188 # don't bother if it does not have a meta.
189 next unless $super->can('meta');
190 # if it's meta is a vanilla Moose,
191 # then we can safely ignore it.
192 next if blessed($super->meta) eq 'Moose::Meta::Class';
193 # but if we have anything else,
194 # we need to check it out ...
195 unless (# see if of our metaclass is incompatible
196 ($self->isa(blessed($super->meta)) &&
197 # and see if our instance metaclass is incompatible
198 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
199 # ... and if we are just a vanilla Moose
200 $self->isa('Moose::Meta::Class')) {
201 # re-initialize the meta ...
202 my $super_meta = $super->meta;
203 # NOTE:
204 # We might want to consider actually
205 # transfering any attributes from the
206 # original meta into this one, but in
207 # general you should not have any there
208 # at this point anyway, so it's very
209 # much an obscure edge case anyway
210 $self = $super_meta->reinitialize($self->name => (
211 ':attribute_metaclass' => $super_meta->attribute_metaclass,
212 ':method_metaclass' => $super_meta->method_metaclass,
213 ':instance_metaclass' => $super_meta->instance_metaclass,
214 ));
215 }
216 }
217 return $self;
218}
219
220sub _apply_all_roles {
221 my ($self, @roles) = @_;
222 ($_->can('meta') && $_->meta->isa('Moose::Meta::Role'))
223 || confess "You can only consume roles, $_ is not a Moose role"
224 foreach @roles;
225 if (scalar @roles == 1) {
226 $roles[0]->meta->apply($self);
227 }
228 else {
229 Moose::Meta::Role->combine(
230 map { $_->meta } @roles
231 )->apply($self);
232 }
233}
234
235sub _process_attribute {
236 my ($self, $name, %options) = @_;
237 if ($name =~ /^\+(.*)/) {
238 my $new_attr = $self->_process_inherited_attribute($1, %options);
239 $self->add_attribute($new_attr);
240 }
241 else {
242 if ($options{metaclass}) {
243 Moose::_load_all_classes($options{metaclass});
244 $self->add_attribute($options{metaclass}->new($name, %options));
245 }
246 else {
247 $self->add_attribute($name, %options);
248 }
249 }
250}
251
252sub _process_inherited_attribute {
253 my ($self, $attr_name, %options) = @_;
254 my $inherited_attr = $self->find_attribute_by_name($attr_name);
255 (defined $inherited_attr)
256 || confess "Could not find an attribute by the name of '$attr_name' to inherit from";
257 my $new_attr;
258 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
259 $new_attr = $inherited_attr->clone_and_inherit_options(%options);
260 }
261 else {
262 # NOTE:
263 # kind of a kludge to handle Class::MOP::Attributes
264 $new_attr = Moose::Meta::Attribute::clone_and_inherit_options(
265 $inherited_attr, %options
266 );
267 }
268 return $new_attr;
269}
270
05d9eaf6 271package Moose::Meta::Method::Overriden;
272
273use strict;
274use warnings;
275
276our $VERSION = '0.01';
277
278use base 'Class::MOP::Method';
279
c0e30cf5 2801;
281
282__END__
283
284=pod
285
286=head1 NAME
287
e522431d 288Moose::Meta::Class - The Moose metaclass
c0e30cf5 289
c0e30cf5 290=head1 DESCRIPTION
291
e522431d 292This is a subclass of L<Class::MOP::Class> with Moose specific
293extensions.
294
6ba6d68c 295For the most part, the only time you will ever encounter an
296instance of this class is if you are doing some serious deep
297introspection. To really understand this class, you need to refer
298to the L<Class::MOP::Class> documentation.
299
c0e30cf5 300=head1 METHODS
301
302=over 4
303
590868a3 304=item B<initialize>
305
8c9d74e7 306=item B<new_object>
307
02a0fb52 308We override this method to support the C<trigger> attribute option.
309
a15dff8d 310=item B<construct_instance>
311
6ba6d68c 312This provides some Moose specific extensions to this method, you
313almost never call this method directly unless you really know what
314you are doing.
315
316This method makes sure to handle the moose weak-ref, type-constraint
317and type coercion features.
ef1d5f4b 318
e9ec68d6 319=item B<has_method ($name)>
320
321This accomidates Moose::Meta::Role::Method instances, which are
322aliased, instead of added, but still need to be counted as valid
323methods.
324
78cd1d3b 325=item B<add_override_method_modifier ($name, $method)>
326
02a0fb52 327This will create an C<override> method modifier for you, and install
328it in the package.
329
78cd1d3b 330=item B<add_augment_method_modifier ($name, $method)>
331
02a0fb52 332This will create an C<augment> method modifier for you, and install
333it in the package.
334
2b14ac61 335=item B<calculate_all_roles>
336
ef333f17 337=item B<roles>
338
02a0fb52 339This will return an array of C<Moose::Meta::Role> instances which are
340attached to this class.
341
ef333f17 342=item B<add_role ($role)>
343
02a0fb52 344This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
345to the list of associated roles.
346
ef333f17 347=item B<does_role ($role_name)>
348
02a0fb52 349This will test if this class C<does> a given C<$role_name>. It will
350not only check it's local roles, but ask them as well in order to
351cascade down the role hierarchy.
352
d79e62fd 353=item B<excludes_role ($role_name)>
354
355This will test if this class C<excludes> a given C<$role_name>. It will
356not only check it's local roles, but ask them as well in order to
357cascade down the role hierarchy.
358
9e93dd19 359=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 360
9e93dd19 361This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
362support for taking the C<$params> as a HASH ref.
ac1ef2f9 363
c0e30cf5 364=back
365
366=head1 BUGS
367
368All complex software has bugs lurking in it, and this module is no
369exception. If you find a bug please either email me, or add the bug
370to cpan-RT.
371
c0e30cf5 372=head1 AUTHOR
373
374Stevan Little E<lt>stevan@iinteractive.comE<gt>
375
376=head1 COPYRIGHT AND LICENSE
377
378Copyright 2006 by Infinity Interactive, Inc.
379
380L<http://www.iinteractive.com>
381
382This library is free software; you can redistribute it and/or modify
383it under the same terms as Perl itself.
384
8a7a9c53 385=cut
1a563243 386