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