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