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