allow isa => SomeRandomClass in +foo attr declarations
[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) = @_;
18c2ec0e 227
d05cd563 228 (!$self->has_method($name))
229 || confess "Cannot add an override method if a local method is already present";
18c2ec0e 230
231 $self->add_method($name => Moose::Meta::Method::Overriden->new(
232 override => $method,
233 class => $self,
234 package => $_super_package, # need this for roles
235 name => $name,
236 ));
78cd1d3b 237}
238
239sub add_augment_method_modifier {
ac2dc464 240 my ($self, $name, $method) = @_;
d05cd563 241 (!$self->has_method($name))
ac2dc464 242 || confess "Cannot add an augment method if a local method is already present";
78cd1d3b 243 my $super = $self->find_next_method_by_name($name);
244 (defined $super)
ac2dc464 245 || confess "You cannot augment '$name' because it has no super method";
246 my $_super_package = $super->package_name;
247 # BUT!,... if this is an overriden method ....
05d9eaf6 248 if ($super->isa('Moose::Meta::Method::Overriden')) {
ac2dc464 249 # we need to be sure that we actually
250 # find the next method, which is not
05d9eaf6 251 # an 'override' method, the reason is
ac2dc464 252 # that an 'override' method will not
05d9eaf6 253 # be the one calling inner()
ac2dc464 254 my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);
05d9eaf6 255 $_super_package = $real_super->package_name;
ac2dc464 256 }
78cd1d3b 257 $self->add_method($name => sub {
258 my @args = @_;
78cd1d3b 259 no warnings 'redefine';
52c7c330 260 if ($Moose::INNER_SLOT{$_super_package}) {
53dd42d8 261 local *{$Moose::INNER_SLOT{$_super_package}} = sub {
262 local *{$Moose::INNER_SLOT{$_super_package}} = sub {};
b468a3d3 263 $method->(@args);
264 };
b644e331 265 return $super->body->(@args);
53dd42d8 266 }
267 else {
b644e331 268 return $super->body->(@args);
52c7c330 269 }
ac2dc464 270 });
78cd1d3b 271}
272
1341f10c 273## Private Utility methods ...
274
05d9eaf6 275sub _find_next_method_by_name_which_is_not_overridden {
276 my ($self, $name) = @_;
68efb014 277 foreach my $method ($self->find_all_methods_by_name($name)) {
ac2dc464 278 return $method->{code}
05d9eaf6 279 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
280 }
281 return undef;
282}
283
1341f10c 284sub _fix_metaclass_incompatability {
285 my ($self, @superclasses) = @_;
286 foreach my $super (@superclasses) {
287 # don't bother if it does not have a meta.
288 next unless $super->can('meta');
ac2dc464 289 # get the name, make sure we take
8ecb1fa0 290 # immutable classes into account
ac2dc464 291 my $super_meta_name = ($super->meta->is_immutable
8ecb1fa0 292 ? $super->meta->get_mutable_metaclass_name
293 : blessed($super->meta));
ac2dc464 294 # if it's meta is a vanilla Moose,
295 # then we can safely ignore it.
8ecb1fa0 296 next if $super_meta_name eq 'Moose::Meta::Class';
ac2dc464 297 # but if we have anything else,
1341f10c 298 # we need to check it out ...
299 unless (# see if of our metaclass is incompatible
8ecb1fa0 300 ($self->isa($super_meta_name) &&
1341f10c 301 # and see if our instance metaclass is incompatible
302 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
303 # ... and if we are just a vanilla Moose
304 $self->isa('Moose::Meta::Class')) {
305 # re-initialize the meta ...
306 my $super_meta = $super->meta;
307 # NOTE:
ac2dc464 308 # We might want to consider actually
309 # transfering any attributes from the
310 # original meta into this one, but in
1341f10c 311 # general you should not have any there
ac2dc464 312 # at this point anyway, so it's very
1341f10c 313 # much an obscure edge case anyway
314 $self = $super_meta->reinitialize($self->name => (
ac2dc464 315 'attribute_metaclass' => $super_meta->attribute_metaclass,
5cf3dbcf 316 'method_metaclass' => $super_meta->method_metaclass,
317 'instance_metaclass' => $super_meta->instance_metaclass,
1341f10c 318 ));
319 }
320 }
ac2dc464 321 return $self;
1341f10c 322}
323
d7d8a8c7 324# NOTE:
d9bb6c63 325# this was crap anyway, see
326# Moose::Util::apply_all_roles
d7d8a8c7 327# instead
4498537c 328sub _apply_all_roles {
547dda77 329 Carp::croak 'DEPRECATED: use Moose::Util::apply_all_roles($meta, @roles) instead'
4498537c 330}
1341f10c 331
332sub _process_attribute {
e472c9a5 333 my $self = shift;
d9bb6c63 334 my $name = shift;
335 my %options = ((scalar @_ == 1 && ref($_[0]) eq 'HASH') ? %{$_[0]} : @_);
336
1341f10c 337 if ($name =~ /^\+(.*)/) {
d7d8a8c7 338 return $self->_process_inherited_attribute($1, %options);
1341f10c 339 }
340 else {
d9bb6c63 341 my $attr_metaclass_name;
1341f10c 342 if ($options{metaclass}) {
c1935ade 343 my $metaclass_name = $options{metaclass};
344 eval {
345 my $possible_full_name = 'Moose::Meta::Attribute::Custom::' . $metaclass_name;
ac2dc464 346 Class::MOP::load_class($possible_full_name);
347 $metaclass_name = $possible_full_name->can('register_implementation')
c1935ade 348 ? $possible_full_name->register_implementation
349 : $possible_full_name;
350 };
351 if ($@) {
352 Class::MOP::load_class($metaclass_name);
353 }
d9bb6c63 354 $attr_metaclass_name = $metaclass_name;
1341f10c 355 }
356 else {
d9bb6c63 357 $attr_metaclass_name = $self->attribute_metaclass;
1341f10c 358 }
d9bb6c63 359
360 if ($options{traits}) {
17594769 361 my @traits;
362 foreach my $trait (@{$options{traits}}) {
363 eval {
364 my $possible_full_name = 'Moose::Meta::Attribute::Custom::Trait::' . $trait;
365 Class::MOP::load_class($possible_full_name);
366 push @traits => $possible_full_name->can('register_implementation')
367 ? $possible_full_name->register_implementation
368 : $possible_full_name;
369 };
370 if ($@) {
371 push @traits => $trait;
3bb22459 372 }
d9bb6c63 373 }
374
17594769 375 my $class = Moose::Meta::Class->create_anon_class(
376 superclasses => [ $attr_metaclass_name ],
377 roles => [ @traits ],
378 cache => 1,
379 );
380
d9bb6c63 381 $attr_metaclass_name = $class->name;
382 }
17594769 383
d9bb6c63 384 return $attr_metaclass_name->new($name, %options);
ac2dc464 385 }
1341f10c 386}
387
388sub _process_inherited_attribute {
389 my ($self, $attr_name, %options) = @_;
390 my $inherited_attr = $self->find_attribute_by_name($attr_name);
391 (defined $inherited_attr)
392 || confess "Could not find an attribute by the name of '$attr_name' to inherit from";
1341f10c 393 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
d7d8a8c7 394 return $inherited_attr->clone_and_inherit_options(%options);
1341f10c 395 }
396 else {
397 # NOTE:
398 # kind of a kludge to handle Class::MOP::Attributes
d7d8a8c7 399 return $inherited_attr->Moose::Meta::Attribute::clone_and_inherit_options(%options);
ac2dc464 400 }
1341f10c 401}
402
5cf3dbcf 403## -------------------------------------------------
404
405use Moose::Meta::Method::Constructor;
1f779926 406use Moose::Meta::Method::Destructor;
5cf3dbcf 407
ac2dc464 408# This could be done by using SUPER and altering ->options
409# I am keeping it this way to make it more explicit.
410sub create_immutable_transformer {
411 my $self = shift;
412 my $class = Class::MOP::Immutable->new($self, {
413 read_only => [qw/superclasses/],
414 cannot_call => [qw/
415 add_method
416 alias_method
417 remove_method
418 add_attribute
419 remove_attribute
420 add_package_symbol
421 remove_package_symbol
422 add_role
423 /],
424 memoize => {
425 class_precedence_list => 'ARRAY',
426 compute_all_applicable_attributes => 'ARRAY',
427 get_meta_instance => 'SCALAR',
428 get_method_map => 'SCALAR',
429 # maybe ....
430 calculate_all_roles => 'ARRAY',
431 }
432 });
433 return $class;
434}
435
436sub make_immutable {
437 my $self = shift;
438 $self->SUPER::make_immutable
439 (
440 constructor_class => 'Moose::Meta::Method::Constructor',
441 destructor_class => 'Moose::Meta::Method::Destructor',
442 inline_destructor => 1,
443 # NOTE:
444 # no need to do this,
445 # Moose always does it
446 inline_accessors => 0,
447 @_,
448 );
5cf3dbcf 449}
450
c0e30cf5 4511;
452
453__END__
454
455=pod
456
457=head1 NAME
458
e522431d 459Moose::Meta::Class - The Moose metaclass
c0e30cf5 460
c0e30cf5 461=head1 DESCRIPTION
462
ac2dc464 463This is a subclass of L<Class::MOP::Class> with Moose specific
e522431d 464extensions.
465
ac2dc464 466For the most part, the only time you will ever encounter an
467instance of this class is if you are doing some serious deep
468introspection. To really understand this class, you need to refer
6ba6d68c 469to the L<Class::MOP::Class> documentation.
470
c0e30cf5 471=head1 METHODS
472
473=over 4
474
590868a3 475=item B<initialize>
476
61bdd94f 477=item B<create>
478
17594769 479Overrides original to accept a list of roles to apply to
61bdd94f 480the created class.
481
17594769 482 my $metaclass = Moose::Meta::Class->create( 'New::Class', roles => [...] );
483
484=item B<create_anon_class>
485
486Overrides original to support roles and caching.
487
488 my $metaclass = Moose::Meta::Class->create_anon_class(
489 superclasses => ['Foo'],
490 roles => [qw/Some Roles Go Here/],
491 cache => 1,
492 );
493
5cf3dbcf 494=item B<make_immutable>
495
ac2dc464 496Override original to add default options for inlining destructor
497and altering the Constructor metaclass.
498
499=item B<create_immutable_transformer>
500
501Override original to lock C<add_role> and memoize C<calculate_all_roles>
502
8c9d74e7 503=item B<new_object>
504
02a0fb52 505We override this method to support the C<trigger> attribute option.
506
a15dff8d 507=item B<construct_instance>
508
ac2dc464 509This provides some Moose specific extensions to this method, you
510almost never call this method directly unless you really know what
511you are doing.
6ba6d68c 512
513This method makes sure to handle the moose weak-ref, type-constraint
ac2dc464 514and type coercion features.
ef1d5f4b 515
093b12c2 516=item B<get_method_map>
e9ec68d6 517
ac2dc464 518This accommodates Moose::Meta::Role::Method instances, which are
519aliased, instead of added, but still need to be counted as valid
e9ec68d6 520methods.
521
78cd1d3b 522=item B<add_override_method_modifier ($name, $method)>
523
ac2dc464 524This will create an C<override> method modifier for you, and install
02a0fb52 525it in the package.
526
78cd1d3b 527=item B<add_augment_method_modifier ($name, $method)>
528
ac2dc464 529This will create an C<augment> method modifier for you, and install
02a0fb52 530it in the package.
531
2b14ac61 532=item B<calculate_all_roles>
533
ef333f17 534=item B<roles>
535
ac2dc464 536This will return an array of C<Moose::Meta::Role> instances which are
02a0fb52 537attached to this class.
538
ef333f17 539=item B<add_role ($role)>
540
ac2dc464 541This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
02a0fb52 542to the list of associated roles.
543
ef333f17 544=item B<does_role ($role_name)>
545
ac2dc464 546This will test if this class C<does> a given C<$role_name>. It will
547not only check it's local roles, but ask them as well in order to
02a0fb52 548cascade down the role hierarchy.
549
d79e62fd 550=item B<excludes_role ($role_name)>
551
ac2dc464 552This will test if this class C<excludes> a given C<$role_name>. It will
553not only check it's local roles, but ask them as well in order to
d79e62fd 554cascade down the role hierarchy.
555
9e93dd19 556=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 557
9e93dd19 558This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
559support for taking the C<$params> as a HASH ref.
ac1ef2f9 560
c0e30cf5 561=back
562
563=head1 BUGS
564
ac2dc464 565All complex software has bugs lurking in it, and this module is no
c0e30cf5 566exception. If you find a bug please either email me, or add the bug
567to cpan-RT.
568
c0e30cf5 569=head1 AUTHOR
570
571Stevan Little E<lt>stevan@iinteractive.comE<gt>
572
573=head1 COPYRIGHT AND LICENSE
574
778db3ac 575Copyright 2006-2008 by Infinity Interactive, Inc.
c0e30cf5 576
577L<http://www.iinteractive.com>
578
579This library is free software; you can redistribute it and/or modify
ac2dc464 580it under the same terms as Perl itself.
c0e30cf5 581
8a7a9c53 582=cut
1a563243 583