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