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