fixing trigger/coerce bug, adding test and reformating some yuval code :P
[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
48045612 12our $VERSION = '0.21';
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,
ac2dc464 28 'attribute_metaclass' => 'Moose::Meta::Attribute',
5cf3dbcf 29 'method_metaclass' => 'Moose::Meta::Method',
ac2dc464 30 'instance_metaclass' => 'Moose::Meta::Instance',
590868a3 31 @_);
ac2dc464 32}
590868a3 33
61bdd94f 34sub create {
35 my ($self, $package_name, %options) = @_;
36
37 (ref $options{roles} eq 'ARRAY')
38 || confess "You must pass an ARRAY ref of roles"
39 if exists $options{roles};
40
41 my $class = $self->SUPER::create($package_name, %options);
42
48045612 43 if (exists $options{roles}) {
61bdd94f 44 Moose::Util::apply_all_roles($class, @{$options{roles}});
45 }
46
47 return $class;
48}
49
17594769 50my %ANON_CLASSES;
51
52sub create_anon_class {
53 my ($self, %options) = @_;
54
55 my $cache_ok = delete $options{cache};
17594769 56
57 # something like Super::Class|Super::Class::2=Role|Role::1
58 my $cache_key = join '=' => (
6d5cbd2b 59 join('|', sort @{$options{superclasses} || []}),
60 join('|', sort @{$options{roles} || []}),
17594769 61 );
62
6d5cbd2b 63 if ($cache_ok && defined $ANON_CLASSES{$cache_key}) {
17594769 64 return $ANON_CLASSES{$cache_key};
65 }
66
67 my $new_class = $self->SUPER::create_anon_class(%options);
68
6d5cbd2b 69 $ANON_CLASSES{$cache_key} = $new_class
70 if $cache_ok;
17594769 71
72 return $new_class;
73}
74
ef333f17 75sub add_role {
76 my ($self, $role) = @_;
77 (blessed($role) && $role->isa('Moose::Meta::Role'))
78 || confess "Roles must be instances of Moose::Meta::Role";
79 push @{$self->roles} => $role;
80}
81
b8aeb4dc 82sub calculate_all_roles {
83 my $self = shift;
84 my %seen;
85 grep { !$seen{$_->name}++ } map { $_->calculate_all_roles } @{ $self->roles };
86}
87
ef333f17 88sub does_role {
89 my ($self, $role_name) = @_;
90 (defined $role_name)
91 || confess "You must supply a role name to look for";
9c429218 92 foreach my $class ($self->class_precedence_list) {
81c3738f 93 next unless $class->can('meta') && $class->meta->can('roles');
9c429218 94 foreach my $role (@{$class->meta->roles}) {
95 return 1 if $role->does_role($role_name);
96 }
ef333f17 97 }
98 return 0;
99}
100
d79e62fd 101sub excludes_role {
102 my ($self, $role_name) = @_;
103 (defined $role_name)
104 || confess "You must supply a role name to look for";
ac2dc464 105 foreach my $class ($self->class_precedence_list) {
106 next unless $class->can('meta');
5cb193ed 107 # NOTE:
108 # in the pretty rare instance when a Moose metaclass
ac2dc464 109 # is itself extended with a role, this check needs to
5cb193ed 110 # be done since some items in the class_precedence_list
ac2dc464 111 # might in fact be Class::MOP based still.
112 next unless $class->meta->can('roles');
9c429218 113 foreach my $role (@{$class->meta->roles}) {
114 return 1 if $role->excludes_role($role_name);
115 }
d79e62fd 116 }
117 return 0;
118}
119
8c9d74e7 120sub new_object {
121 my ($class, %params) = @_;
122 my $self = $class->SUPER::new_object(%params);
123 foreach my $attr ($class->compute_all_applicable_attributes()) {
4078709c 124 # if we have a trigger, then ...
125 if ($attr->can('has_trigger') && $attr->has_trigger) {
126 # make sure we have an init-arg ...
127 if (defined(my $init_arg = $attr->init_arg)) {
128 # now make sure an init-arg was passes ...
129 if (exists $params{$init_arg}) {
130 # and if get here, fire the trigger
131 $attr->trigger->(
132 $self,
133 # check if there is a coercion
134 ($attr->should_coerce
135 # and if so, we need to grab the
136 # value that is actually been stored
137 ? $attr->get_read_method_ref->($self)
138 # otherwise, just get the value from
139 # the constructor params
140 : $params{$init_arg}),
141 $attr
142 );
143 }
144 }
625d571f 145 }
8c9d74e7 146 }
ac2dc464 147 return $self;
8c9d74e7 148}
149
a15dff8d 150sub construct_instance {
151 my ($class, %params) = @_;
ddd0ec20 152 my $meta_instance = $class->get_meta_instance;
575db57d 153 # FIXME:
154 # the code below is almost certainly incorrect
155 # but this is foreign inheritence, so we might
ac2dc464 156 # have to kludge it in the end.
ddd0ec20 157 my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
ac2dc464 158 foreach my $attr ($class->compute_all_applicable_attributes()) {
ddd0ec20 159 $attr->initialize_instance_slot($meta_instance, $instance, \%params)
a15dff8d 160 }
161 return $instance;
162}
163
093b12c2 164# FIXME:
165# This is ugly
ac2dc464 166sub get_method_map {
093b12c2 167 my $self = shift;
53dd42d8 168
169 if (defined $self->{'$!_package_cache_flag'} &&
66e08a8a 170 $self->{'$!_package_cache_flag'} == Class::MOP::check_package_cache_flag($self->meta->name)) {
53dd42d8 171 return $self->{'%!methods'};
172 }
173
ac2dc464 174 my $map = $self->{'%!methods'};
175
093b12c2 176 my $class_name = $self->name;
177 my $method_metaclass = $self->method_metaclass;
ac2dc464 178
093b12c2 179 foreach my $symbol ($self->list_all_package_symbols('CODE')) {
ac2dc464 180
093b12c2 181 my $code = $self->get_package_symbol('&' . $symbol);
ac2dc464 182
183 next if exists $map->{$symbol} &&
184 defined $map->{$symbol} &&
185 $map->{$symbol}->body == $code;
186
53dd42d8 187 my ($pkg, $name) = Class::MOP::get_code_info($code);
ac2dc464 188
53dd42d8 189 if ($pkg->can('meta')
4f8f3aab 190 # NOTE:
191 # we don't know what ->meta we are calling
53dd42d8 192 # here, so we need to be careful cause it
193 # just might blow up at us, or just complain
194 # loudly (in the case of Curses.pm) so we
4f8f3aab 195 # just be a little overly cautious here.
196 # - SL
197 && eval { no warnings; blessed($pkg->meta) }
198 && $pkg->meta->isa('Moose::Meta::Role')) {
093b12c2 199 #my $role = $pkg->meta->name;
200 #next unless $self->does_role($role);
201 }
202 else {
53dd42d8 203 next if ($pkg || '') ne $class_name &&
204 ($name || '') ne '__ANON__';
205
093b12c2 206 }
ac2dc464 207
093b12c2 208 $map->{$symbol} = $method_metaclass->wrap($code);
209 }
ac2dc464 210
093b12c2 211 return $map;
a7d0cd00 212}
213
093b12c2 214### ---------------------------------------------
215
a2eec5e7 216sub add_attribute {
217 my $self = shift;
e472c9a5 218 $self->SUPER::add_attribute(
219 (blessed $_[0] && $_[0]->isa('Class::MOP::Attribute')
220 ? $_[0]
221 : $self->_process_attribute(@_))
222 );
a2eec5e7 223}
224
78cd1d3b 225sub add_override_method_modifier {
226 my ($self, $name, $method, $_super_package) = @_;
d05cd563 227 (!$self->has_method($name))
228 || confess "Cannot add an override method if a local method is already present";
78cd1d3b 229 # need this for roles ...
230 $_super_package ||= $self->name;
231 my $super = $self->find_next_method_by_name($name);
232 (defined $super)
ac2dc464 233 || confess "You cannot override '$name' because it has no super method";
093b12c2 234 $self->add_method($name => Moose::Meta::Method::Overriden->wrap(sub {
78cd1d3b 235 my @args = @_;
78cd1d3b 236 no warnings 'redefine';
52c7c330 237 if ($Moose::SUPER_SLOT{$_super_package}) {
b644e331 238 local *{$Moose::SUPER_SLOT{$_super_package}} = sub { $super->body->(@args) };
b468a3d3 239 return $method->(@args);
52c7c330 240 } else {
b468a3d3 241 confess "Trying to call override modifier'd method without super()";
52c7c330 242 }
093b12c2 243 }));
78cd1d3b 244}
245
246sub add_augment_method_modifier {
ac2dc464 247 my ($self, $name, $method) = @_;
d05cd563 248 (!$self->has_method($name))
ac2dc464 249 || confess "Cannot add an augment method if a local method is already present";
78cd1d3b 250 my $super = $self->find_next_method_by_name($name);
251 (defined $super)
ac2dc464 252 || confess "You cannot augment '$name' because it has no super method";
253 my $_super_package = $super->package_name;
254 # BUT!,... if this is an overriden method ....
05d9eaf6 255 if ($super->isa('Moose::Meta::Method::Overriden')) {
ac2dc464 256 # we need to be sure that we actually
257 # find the next method, which is not
05d9eaf6 258 # an 'override' method, the reason is
ac2dc464 259 # that an 'override' method will not
05d9eaf6 260 # be the one calling inner()
ac2dc464 261 my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);
05d9eaf6 262 $_super_package = $real_super->package_name;
ac2dc464 263 }
78cd1d3b 264 $self->add_method($name => sub {
265 my @args = @_;
78cd1d3b 266 no warnings 'redefine';
52c7c330 267 if ($Moose::INNER_SLOT{$_super_package}) {
53dd42d8 268 local *{$Moose::INNER_SLOT{$_super_package}} = sub {
269 local *{$Moose::INNER_SLOT{$_super_package}} = sub {};
b468a3d3 270 $method->(@args);
271 };
b644e331 272 return $super->body->(@args);
53dd42d8 273 }
274 else {
b644e331 275 return $super->body->(@args);
52c7c330 276 }
ac2dc464 277 });
78cd1d3b 278}
279
1341f10c 280## Private Utility methods ...
281
05d9eaf6 282sub _find_next_method_by_name_which_is_not_overridden {
283 my ($self, $name) = @_;
68efb014 284 foreach my $method ($self->find_all_methods_by_name($name)) {
ac2dc464 285 return $method->{code}
05d9eaf6 286 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
287 }
288 return undef;
289}
290
1341f10c 291sub _fix_metaclass_incompatability {
292 my ($self, @superclasses) = @_;
293 foreach my $super (@superclasses) {
294 # don't bother if it does not have a meta.
295 next unless $super->can('meta');
ac2dc464 296 # get the name, make sure we take
8ecb1fa0 297 # immutable classes into account
ac2dc464 298 my $super_meta_name = ($super->meta->is_immutable
8ecb1fa0 299 ? $super->meta->get_mutable_metaclass_name
300 : blessed($super->meta));
ac2dc464 301 # if it's meta is a vanilla Moose,
302 # then we can safely ignore it.
8ecb1fa0 303 next if $super_meta_name eq 'Moose::Meta::Class';
ac2dc464 304 # but if we have anything else,
1341f10c 305 # we need to check it out ...
306 unless (# see if of our metaclass is incompatible
8ecb1fa0 307 ($self->isa($super_meta_name) &&
1341f10c 308 # and see if our instance metaclass is incompatible
309 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
310 # ... and if we are just a vanilla Moose
311 $self->isa('Moose::Meta::Class')) {
312 # re-initialize the meta ...
313 my $super_meta = $super->meta;
314 # NOTE:
ac2dc464 315 # We might want to consider actually
316 # transfering any attributes from the
317 # original meta into this one, but in
1341f10c 318 # general you should not have any there
ac2dc464 319 # at this point anyway, so it's very
1341f10c 320 # much an obscure edge case anyway
321 $self = $super_meta->reinitialize($self->name => (
ac2dc464 322 'attribute_metaclass' => $super_meta->attribute_metaclass,
5cf3dbcf 323 'method_metaclass' => $super_meta->method_metaclass,
324 'instance_metaclass' => $super_meta->instance_metaclass,
1341f10c 325 ));
326 }
327 }
ac2dc464 328 return $self;
1341f10c 329}
330
d7d8a8c7 331# NOTE:
d9bb6c63 332# this was crap anyway, see
333# Moose::Util::apply_all_roles
d7d8a8c7 334# instead
4498537c 335sub _apply_all_roles {
547dda77 336 Carp::croak 'DEPRECATED: use Moose::Util::apply_all_roles($meta, @roles) instead'
4498537c 337}
1341f10c 338
339sub _process_attribute {
e472c9a5 340 my $self = shift;
d9bb6c63 341 my $name = shift;
342 my %options = ((scalar @_ == 1 && ref($_[0]) eq 'HASH') ? %{$_[0]} : @_);
343
1341f10c 344 if ($name =~ /^\+(.*)/) {
d7d8a8c7 345 return $self->_process_inherited_attribute($1, %options);
1341f10c 346 }
347 else {
d9bb6c63 348 my $attr_metaclass_name;
1341f10c 349 if ($options{metaclass}) {
c1935ade 350 my $metaclass_name = $options{metaclass};
351 eval {
352 my $possible_full_name = 'Moose::Meta::Attribute::Custom::' . $metaclass_name;
ac2dc464 353 Class::MOP::load_class($possible_full_name);
354 $metaclass_name = $possible_full_name->can('register_implementation')
c1935ade 355 ? $possible_full_name->register_implementation
356 : $possible_full_name;
357 };
358 if ($@) {
359 Class::MOP::load_class($metaclass_name);
360 }
d9bb6c63 361 $attr_metaclass_name = $metaclass_name;
1341f10c 362 }
363 else {
d9bb6c63 364 $attr_metaclass_name = $self->attribute_metaclass;
1341f10c 365 }
d9bb6c63 366
367 if ($options{traits}) {
17594769 368 my @traits;
369 foreach my $trait (@{$options{traits}}) {
370 eval {
371 my $possible_full_name = 'Moose::Meta::Attribute::Custom::Trait::' . $trait;
372 Class::MOP::load_class($possible_full_name);
373 push @traits => $possible_full_name->can('register_implementation')
374 ? $possible_full_name->register_implementation
375 : $possible_full_name;
376 };
377 if ($@) {
378 push @traits => $trait;
3bb22459 379 }
d9bb6c63 380 }
381
17594769 382 my $class = Moose::Meta::Class->create_anon_class(
383 superclasses => [ $attr_metaclass_name ],
384 roles => [ @traits ],
385 cache => 1,
386 );
387
d9bb6c63 388 $attr_metaclass_name = $class->name;
389 }
17594769 390
d9bb6c63 391 return $attr_metaclass_name->new($name, %options);
ac2dc464 392 }
1341f10c 393}
394
395sub _process_inherited_attribute {
396 my ($self, $attr_name, %options) = @_;
397 my $inherited_attr = $self->find_attribute_by_name($attr_name);
398 (defined $inherited_attr)
399 || confess "Could not find an attribute by the name of '$attr_name' to inherit from";
1341f10c 400 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
d7d8a8c7 401 return $inherited_attr->clone_and_inherit_options(%options);
1341f10c 402 }
403 else {
404 # NOTE:
405 # kind of a kludge to handle Class::MOP::Attributes
d7d8a8c7 406 return $inherited_attr->Moose::Meta::Attribute::clone_and_inherit_options(%options);
ac2dc464 407 }
1341f10c 408}
409
5cf3dbcf 410## -------------------------------------------------
411
412use Moose::Meta::Method::Constructor;
1f779926 413use Moose::Meta::Method::Destructor;
5cf3dbcf 414
ac2dc464 415# This could be done by using SUPER and altering ->options
416# I am keeping it this way to make it more explicit.
417sub create_immutable_transformer {
418 my $self = shift;
419 my $class = Class::MOP::Immutable->new($self, {
420 read_only => [qw/superclasses/],
421 cannot_call => [qw/
422 add_method
423 alias_method
424 remove_method
425 add_attribute
426 remove_attribute
427 add_package_symbol
428 remove_package_symbol
429 add_role
430 /],
431 memoize => {
432 class_precedence_list => 'ARRAY',
433 compute_all_applicable_attributes => 'ARRAY',
434 get_meta_instance => 'SCALAR',
435 get_method_map => 'SCALAR',
436 # maybe ....
437 calculate_all_roles => 'ARRAY',
438 }
439 });
440 return $class;
441}
442
443sub make_immutable {
444 my $self = shift;
445 $self->SUPER::make_immutable
446 (
447 constructor_class => 'Moose::Meta::Method::Constructor',
448 destructor_class => 'Moose::Meta::Method::Destructor',
449 inline_destructor => 1,
450 # NOTE:
451 # no need to do this,
452 # Moose always does it
453 inline_accessors => 0,
454 @_,
455 );
5cf3dbcf 456}
457
c0e30cf5 4581;
459
460__END__
461
462=pod
463
464=head1 NAME
465
e522431d 466Moose::Meta::Class - The Moose metaclass
c0e30cf5 467
c0e30cf5 468=head1 DESCRIPTION
469
ac2dc464 470This is a subclass of L<Class::MOP::Class> with Moose specific
e522431d 471extensions.
472
ac2dc464 473For the most part, the only time you will ever encounter an
474instance of this class is if you are doing some serious deep
475introspection. To really understand this class, you need to refer
6ba6d68c 476to the L<Class::MOP::Class> documentation.
477
c0e30cf5 478=head1 METHODS
479
480=over 4
481
590868a3 482=item B<initialize>
483
61bdd94f 484=item B<create>
485
17594769 486Overrides original to accept a list of roles to apply to
61bdd94f 487the created class.
488
17594769 489 my $metaclass = Moose::Meta::Class->create( 'New::Class', roles => [...] );
490
491=item B<create_anon_class>
492
493Overrides original to support roles and caching.
494
495 my $metaclass = Moose::Meta::Class->create_anon_class(
496 superclasses => ['Foo'],
497 roles => [qw/Some Roles Go Here/],
498 cache => 1,
499 );
500
5cf3dbcf 501=item B<make_immutable>
502
ac2dc464 503Override original to add default options for inlining destructor
504and altering the Constructor metaclass.
505
506=item B<create_immutable_transformer>
507
508Override original to lock C<add_role> and memoize C<calculate_all_roles>
509
8c9d74e7 510=item B<new_object>
511
02a0fb52 512We override this method to support the C<trigger> attribute option.
513
a15dff8d 514=item B<construct_instance>
515
ac2dc464 516This provides some Moose specific extensions to this method, you
517almost never call this method directly unless you really know what
518you are doing.
6ba6d68c 519
520This method makes sure to handle the moose weak-ref, type-constraint
ac2dc464 521and type coercion features.
ef1d5f4b 522
093b12c2 523=item B<get_method_map>
e9ec68d6 524
ac2dc464 525This accommodates Moose::Meta::Role::Method instances, which are
526aliased, instead of added, but still need to be counted as valid
e9ec68d6 527methods.
528
78cd1d3b 529=item B<add_override_method_modifier ($name, $method)>
530
ac2dc464 531This will create an C<override> method modifier for you, and install
02a0fb52 532it in the package.
533
78cd1d3b 534=item B<add_augment_method_modifier ($name, $method)>
535
ac2dc464 536This will create an C<augment> method modifier for you, and install
02a0fb52 537it in the package.
538
2b14ac61 539=item B<calculate_all_roles>
540
ef333f17 541=item B<roles>
542
ac2dc464 543This will return an array of C<Moose::Meta::Role> instances which are
02a0fb52 544attached to this class.
545
ef333f17 546=item B<add_role ($role)>
547
ac2dc464 548This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
02a0fb52 549to the list of associated roles.
550
ef333f17 551=item B<does_role ($role_name)>
552
ac2dc464 553This will test if this class C<does> a given C<$role_name>. It will
554not only check it's local roles, but ask them as well in order to
02a0fb52 555cascade down the role hierarchy.
556
d79e62fd 557=item B<excludes_role ($role_name)>
558
ac2dc464 559This will test if this class C<excludes> a given C<$role_name>. It will
560not only check it's local roles, but ask them as well in order to
d79e62fd 561cascade down the role hierarchy.
562
9e93dd19 563=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 564
9e93dd19 565This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
566support for taking the C<$params> as a HASH ref.
ac1ef2f9 567
c0e30cf5 568=back
569
570=head1 BUGS
571
ac2dc464 572All complex software has bugs lurking in it, and this module is no
c0e30cf5 573exception. If you find a bug please either email me, or add the bug
574to cpan-RT.
575
c0e30cf5 576=head1 AUTHOR
577
578Stevan Little E<lt>stevan@iinteractive.comE<gt>
579
580=head1 COPYRIGHT AND LICENSE
581
778db3ac 582Copyright 2006-2008 by Infinity Interactive, Inc.
c0e30cf5 583
584L<http://www.iinteractive.com>
585
586This library is free software; you can redistribute it and/or modify
ac2dc464 587it under the same terms as Perl itself.
c0e30cf5 588
8a7a9c53 589=cut
1a563243 590