* Moose
[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
5cb193ed 12our $VERSION = '0.13';
d44714be 13our $AUTHORITY = 'cpan:STEVAN';
bc1e29b5 14
8ee73eeb 15use Moose::Meta::Method::Overriden;
16
c0e30cf5 17use base 'Class::MOP::Class';
18
598340d5 19__PACKAGE__->meta->add_attribute('roles' => (
ef333f17 20 reader => 'roles',
21 default => sub { [] }
22));
23
590868a3 24sub initialize {
25 my $class = shift;
26 my $pkg = shift;
27 $class->SUPER::initialize($pkg,
5cf3dbcf 28 'attribute_metaclass' => 'Moose::Meta::Attribute',
29 'method_metaclass' => 'Moose::Meta::Method',
30 'instance_metaclass' => 'Moose::Meta::Instance',
590868a3 31 @_);
1341f10c 32}
590868a3 33
ef333f17 34sub add_role {
35 my ($self, $role) = @_;
36 (blessed($role) && $role->isa('Moose::Meta::Role'))
37 || confess "Roles must be instances of Moose::Meta::Role";
38 push @{$self->roles} => $role;
39}
40
b8aeb4dc 41sub calculate_all_roles {
42 my $self = shift;
43 my %seen;
44 grep { !$seen{$_->name}++ } map { $_->calculate_all_roles } @{ $self->roles };
45}
46
ef333f17 47sub does_role {
48 my ($self, $role_name) = @_;
49 (defined $role_name)
50 || confess "You must supply a role name to look for";
9c429218 51 foreach my $class ($self->class_precedence_list) {
74f6d830 52 next unless $class->can('meta');
9c429218 53 foreach my $role (@{$class->meta->roles}) {
54 return 1 if $role->does_role($role_name);
55 }
ef333f17 56 }
57 return 0;
58}
59
d79e62fd 60sub excludes_role {
61 my ($self, $role_name) = @_;
62 (defined $role_name)
63 || confess "You must supply a role name to look for";
74f6d830 64 foreach my $class ($self->class_precedence_list) {
65 next unless $class->can('meta');
5cb193ed 66 # NOTE:
67 # in the pretty rare instance when a Moose metaclass
68 # is itself extended with a role, this check needs to
69 # be done since some items in the class_precedence_list
70 # might in fact be Class::MOP based still.
71 next unless $class->meta->can('roles');
9c429218 72 foreach my $role (@{$class->meta->roles}) {
73 return 1 if $role->excludes_role($role_name);
74 }
d79e62fd 75 }
76 return 0;
77}
78
8c9d74e7 79sub new_object {
80 my ($class, %params) = @_;
81 my $self = $class->SUPER::new_object(%params);
82 foreach my $attr ($class->compute_all_applicable_attributes()) {
715ea0b7 83 # FIXME:
84 # this does not accept undefined
85 # values, nor does it accept false
86 # values to be passed into the init-arg
5faf11bb 87 next unless $params{$attr->init_arg} && $attr->can('has_trigger') && $attr->has_trigger;
88 $attr->trigger->($self, $params{$attr->init_arg}, $attr);
8c9d74e7 89 }
90 return $self;
91}
92
a15dff8d 93sub construct_instance {
94 my ($class, %params) = @_;
ddd0ec20 95 my $meta_instance = $class->get_meta_instance;
575db57d 96 # FIXME:
97 # the code below is almost certainly incorrect
98 # but this is foreign inheritence, so we might
99 # have to kludge it in the end.
ddd0ec20 100 my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
74f6d830 101 foreach my $attr ($class->compute_all_applicable_attributes()) {
ddd0ec20 102 $attr->initialize_instance_slot($meta_instance, $instance, \%params)
a15dff8d 103 }
104 return $instance;
105}
106
a7d0cd00 107
093b12c2 108# FIXME:
109# This is ugly
110sub get_method_map {
111 my $self = shift;
5cf3dbcf 112 my $map = $self->{'%!methods'};
a7d0cd00 113
093b12c2 114 my $class_name = $self->name;
115 my $method_metaclass = $self->method_metaclass;
116
117 foreach my $symbol ($self->list_all_package_symbols('CODE')) {
118
119 my $code = $self->get_package_symbol('&' . $symbol);
120
121 next if exists $map->{$symbol} &&
122 defined $map->{$symbol} &&
123 $map->{$symbol}->body == $code;
124
125 my $gv = B::svref_2object($code)->GV;
126
127 my $pkg = $gv->STASH->NAME;
37ee30c9 128 if ($pkg->can('meta') && $pkg->meta && $pkg->meta->isa('Moose::Meta::Role')) {
093b12c2 129 #my $role = $pkg->meta->name;
130 #next unless $self->does_role($role);
131 }
132 else {
133 next if ($gv->STASH->NAME || '') ne $class_name &&
134 ($gv->NAME || '') ne '__ANON__';
135 }
136
137 $map->{$symbol} = $method_metaclass->wrap($code);
138 }
139
140 return $map;
a7d0cd00 141}
142
093b12c2 143### ---------------------------------------------
144
a2eec5e7 145sub add_attribute {
146 my $self = shift;
147 my $name = shift;
148 if (scalar @_ == 1 && ref($_[0]) eq 'HASH') {
149 # NOTE:
150 # if it is a HASH ref, we de-ref it.
151 # this will usually mean that it is
152 # coming from a role
153 $self->SUPER::add_attribute($name => %{$_[0]});
154 }
155 else {
156 # otherwise we just pass the args
157 $self->SUPER::add_attribute($name => @_);
158 }
159}
160
78cd1d3b 161sub add_override_method_modifier {
162 my ($self, $name, $method, $_super_package) = @_;
d05cd563 163 (!$self->has_method($name))
164 || confess "Cannot add an override method if a local method is already present";
78cd1d3b 165 # need this for roles ...
166 $_super_package ||= $self->name;
167 my $super = $self->find_next_method_by_name($name);
168 (defined $super)
169 || confess "You cannot override '$name' because it has no super method";
093b12c2 170 $self->add_method($name => Moose::Meta::Method::Overriden->wrap(sub {
78cd1d3b 171 my @args = @_;
78cd1d3b 172 no warnings 'redefine';
52c7c330 173 if ($Moose::SUPER_SLOT{$_super_package}) {
174 local *{$Moose::SUPER_SLOT{$_super_package}}
175 = sub { $super->(@args) };
176 return $method->(@args);
177 } else {
178 confess "Trying to call override modifier'd method without super()";
179 }
093b12c2 180 }));
78cd1d3b 181}
182
183sub add_augment_method_modifier {
05d9eaf6 184 my ($self, $name, $method) = @_;
d05cd563 185 (!$self->has_method($name))
186 || confess "Cannot add an augment method if a local method is already present";
78cd1d3b 187 my $super = $self->find_next_method_by_name($name);
188 (defined $super)
05d9eaf6 189 || confess "You cannot augment '$name' because it has no super method";
190 my $_super_package = $super->package_name;
191 # BUT!,... if this is an overriden method ....
192 if ($super->isa('Moose::Meta::Method::Overriden')) {
193 # we need to be sure that we actually
194 # find the next method, which is not
195 # an 'override' method, the reason is
196 # that an 'override' method will not
197 # be the one calling inner()
198 my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);
199 $_super_package = $real_super->package_name;
200 }
78cd1d3b 201 $self->add_method($name => sub {
202 my @args = @_;
78cd1d3b 203 no warnings 'redefine';
52c7c330 204 if ($Moose::INNER_SLOT{$_super_package}) {
205 local *{$Moose::INNER_SLOT{$_super_package}}
206 = sub { $method->(@args) };
207 return $super->(@args);
208 } else {
209 return $super->(@args);
210 }
78cd1d3b 211 });
212}
213
1341f10c 214## Private Utility methods ...
215
05d9eaf6 216sub _find_next_method_by_name_which_is_not_overridden {
217 my ($self, $name) = @_;
68efb014 218 foreach my $method ($self->find_all_methods_by_name($name)) {
05d9eaf6 219 return $method->{code}
220 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
221 }
222 return undef;
223}
224
1341f10c 225sub _fix_metaclass_incompatability {
226 my ($self, @superclasses) = @_;
227 foreach my $super (@superclasses) {
228 # don't bother if it does not have a meta.
229 next unless $super->can('meta');
8ecb1fa0 230 # get the name, make sure we take
231 # immutable classes into account
232 my $super_meta_name = ($super->meta->is_immutable
233 ? $super->meta->get_mutable_metaclass_name
234 : blessed($super->meta));
1341f10c 235 # if it's meta is a vanilla Moose,
8ecb1fa0 236 # then we can safely ignore it.
237 next if $super_meta_name eq 'Moose::Meta::Class';
1341f10c 238 # but if we have anything else,
239 # we need to check it out ...
240 unless (# see if of our metaclass is incompatible
8ecb1fa0 241 ($self->isa($super_meta_name) &&
1341f10c 242 # and see if our instance metaclass is incompatible
243 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
244 # ... and if we are just a vanilla Moose
245 $self->isa('Moose::Meta::Class')) {
246 # re-initialize the meta ...
247 my $super_meta = $super->meta;
248 # NOTE:
249 # We might want to consider actually
250 # transfering any attributes from the
251 # original meta into this one, but in
252 # general you should not have any there
253 # at this point anyway, so it's very
254 # much an obscure edge case anyway
255 $self = $super_meta->reinitialize($self->name => (
5cf3dbcf 256 'attribute_metaclass' => $super_meta->attribute_metaclass,
257 'method_metaclass' => $super_meta->method_metaclass,
258 'instance_metaclass' => $super_meta->instance_metaclass,
1341f10c 259 ));
260 }
261 }
262 return $self;
263}
264
265sub _apply_all_roles {
266 my ($self, @roles) = @_;
267 ($_->can('meta') && $_->meta->isa('Moose::Meta::Role'))
268 || confess "You can only consume roles, $_ is not a Moose role"
269 foreach @roles;
270 if (scalar @roles == 1) {
271 $roles[0]->meta->apply($self);
272 }
273 else {
68efb014 274 # FIXME
275 # we should make a Moose::Meta::Role::Composite
276 # which is a smaller version of Moose::Meta::Role
277 # which does not use any package stuff
1341f10c 278 Moose::Meta::Role->combine(
279 map { $_->meta } @roles
280 )->apply($self);
281 }
282}
283
284sub _process_attribute {
285 my ($self, $name, %options) = @_;
286 if ($name =~ /^\+(.*)/) {
287 my $new_attr = $self->_process_inherited_attribute($1, %options);
288 $self->add_attribute($new_attr);
289 }
290 else {
291 if ($options{metaclass}) {
c1935ade 292 my $metaclass_name = $options{metaclass};
293 eval {
294 my $possible_full_name = 'Moose::Meta::Attribute::Custom::' . $metaclass_name;
295 Class::MOP::load_class($possible_full_name);
296 $metaclass_name = $possible_full_name->can('register_implementation')
297 ? $possible_full_name->register_implementation
298 : $possible_full_name;
299 };
300 if ($@) {
301 Class::MOP::load_class($metaclass_name);
302 }
303 $self->add_attribute($metaclass_name->new($name, %options));
1341f10c 304 }
305 else {
306 $self->add_attribute($name, %options);
307 }
308 }
309}
310
311sub _process_inherited_attribute {
312 my ($self, $attr_name, %options) = @_;
313 my $inherited_attr = $self->find_attribute_by_name($attr_name);
314 (defined $inherited_attr)
315 || confess "Could not find an attribute by the name of '$attr_name' to inherit from";
316 my $new_attr;
317 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
318 $new_attr = $inherited_attr->clone_and_inherit_options(%options);
319 }
320 else {
321 # NOTE:
322 # kind of a kludge to handle Class::MOP::Attributes
323 $new_attr = Moose::Meta::Attribute::clone_and_inherit_options(
324 $inherited_attr, %options
325 );
326 }
327 return $new_attr;
328}
329
5cf3dbcf 330## -------------------------------------------------
331
332use Moose::Meta::Method::Constructor;
1f779926 333use Moose::Meta::Method::Destructor;
5cf3dbcf 334
335{
336 # NOTE:
337 # the immutable version of a
338 # particular metaclass is
339 # really class-level data so
340 # we don't want to regenerate
341 # it any more than we need to
342 my $IMMUTABLE_METACLASS;
343 sub make_immutable {
344 my $self = shift;
345
346 $IMMUTABLE_METACLASS ||= Class::MOP::Immutable->new($self, {
347 read_only => [qw/superclasses/],
348 cannot_call => [qw/
349 add_method
350 alias_method
351 remove_method
352 add_attribute
353 remove_attribute
354 add_package_symbol
355 remove_package_symbol
356 add_role
357 /],
358 memoize => {
359 class_precedence_list => 'ARRAY',
360 compute_all_applicable_attributes => 'ARRAY',
361 get_meta_instance => 'SCALAR',
362 get_method_map => 'SCALAR',
363 # maybe ....
364 calculate_all_roles => 'ARRAY',
365 }
366 });
367
368 $IMMUTABLE_METACLASS->make_metaclass_immutable(
369 $self,
370 constructor_class => 'Moose::Meta::Method::Constructor',
1f779926 371 destructor_class => 'Moose::Meta::Method::Destructor',
372 inline_destructor => 1,
373 # NOTE:
374 # no need to do this,
375 # Moose always does it
5cf3dbcf 376 inline_accessors => 0,
377 @_,
378 )
379 }
380}
381
c0e30cf5 3821;
383
384__END__
385
386=pod
387
388=head1 NAME
389
e522431d 390Moose::Meta::Class - The Moose metaclass
c0e30cf5 391
c0e30cf5 392=head1 DESCRIPTION
393
e522431d 394This is a subclass of L<Class::MOP::Class> with Moose specific
395extensions.
396
6ba6d68c 397For the most part, the only time you will ever encounter an
398instance of this class is if you are doing some serious deep
399introspection. To really understand this class, you need to refer
400to the L<Class::MOP::Class> documentation.
401
c0e30cf5 402=head1 METHODS
403
404=over 4
405
590868a3 406=item B<initialize>
407
5cf3dbcf 408=item B<make_immutable>
409
8c9d74e7 410=item B<new_object>
411
02a0fb52 412We override this method to support the C<trigger> attribute option.
413
a15dff8d 414=item B<construct_instance>
415
6ba6d68c 416This provides some Moose specific extensions to this method, you
417almost never call this method directly unless you really know what
418you are doing.
419
420This method makes sure to handle the moose weak-ref, type-constraint
421and type coercion features.
ef1d5f4b 422
093b12c2 423=item B<get_method_map>
e9ec68d6 424
68efb014 425This accommodates Moose::Meta::Role::Method instances, which are
e9ec68d6 426aliased, instead of added, but still need to be counted as valid
427methods.
428
78cd1d3b 429=item B<add_override_method_modifier ($name, $method)>
430
02a0fb52 431This will create an C<override> method modifier for you, and install
432it in the package.
433
78cd1d3b 434=item B<add_augment_method_modifier ($name, $method)>
435
02a0fb52 436This will create an C<augment> method modifier for you, and install
437it in the package.
438
2b14ac61 439=item B<calculate_all_roles>
440
ef333f17 441=item B<roles>
442
02a0fb52 443This will return an array of C<Moose::Meta::Role> instances which are
444attached to this class.
445
ef333f17 446=item B<add_role ($role)>
447
02a0fb52 448This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
449to the list of associated roles.
450
ef333f17 451=item B<does_role ($role_name)>
452
02a0fb52 453This will test if this class C<does> a given C<$role_name>. It will
454not only check it's local roles, but ask them as well in order to
455cascade down the role hierarchy.
456
d79e62fd 457=item B<excludes_role ($role_name)>
458
459This will test if this class C<excludes> a given C<$role_name>. It will
460not only check it's local roles, but ask them as well in order to
461cascade down the role hierarchy.
462
9e93dd19 463=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 464
9e93dd19 465This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
466support for taking the C<$params> as a HASH ref.
ac1ef2f9 467
c0e30cf5 468=back
469
470=head1 BUGS
471
472All complex software has bugs lurking in it, and this module is no
473exception. If you find a bug please either email me, or add the bug
474to cpan-RT.
475
c0e30cf5 476=head1 AUTHOR
477
478Stevan Little E<lt>stevan@iinteractive.comE<gt>
479
480=head1 COPYRIGHT AND LICENSE
481
b77fdbed 482Copyright 2006, 2007 by Infinity Interactive, Inc.
c0e30cf5 483
484L<http://www.iinteractive.com>
485
486This library is free software; you can redistribute it and/or modify
487it under the same terms as Perl itself.
488
8a7a9c53 489=cut
1a563243 490