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