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