setting up 0.53
[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
03e7dbec 12our $VERSION = '0.53';
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
8c9d74e7 123sub new_object {
124 my ($class, %params) = @_;
125 my $self = $class->SUPER::new_object(%params);
126 foreach my $attr ($class->compute_all_applicable_attributes()) {
4078709c 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 }
625d571f 148 }
8c9d74e7 149 }
ac2dc464 150 return $self;
8c9d74e7 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
172 if (defined $self->{'$!_package_cache_flag'} &&
66e08a8a 173 $self->{'$!_package_cache_flag'} == Class::MOP::check_package_cache_flag($self->meta->name)) {
53dd42d8 174 return $self->{'%!methods'};
175 }
176
ac2dc464 177 my $map = $self->{'%!methods'};
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');
ac2dc464 285 # get the name, make sure we take
8ecb1fa0 286 # immutable classes into account
ac2dc464 287 my $super_meta_name = ($super->meta->is_immutable
8ecb1fa0 288 ? $super->meta->get_mutable_metaclass_name
289 : blessed($super->meta));
ac2dc464 290 # if it's meta is a vanilla Moose,
291 # then we can safely ignore it.
8ecb1fa0 292 next if $super_meta_name eq 'Moose::Meta::Class';
ac2dc464 293 # but if we have anything else,
1341f10c 294 # we need to check it out ...
295 unless (# see if of our metaclass is incompatible
8ecb1fa0 296 ($self->isa($super_meta_name) &&
1341f10c 297 # and see if our instance metaclass is incompatible
298 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
299 # ... and if we are just a vanilla Moose
300 $self->isa('Moose::Meta::Class')) {
301 # re-initialize the meta ...
302 my $super_meta = $super->meta;
303 # NOTE:
ac2dc464 304 # We might want to consider actually
305 # transfering any attributes from the
306 # original meta into this one, but in
1341f10c 307 # general you should not have any there
ac2dc464 308 # at this point anyway, so it's very
1341f10c 309 # much an obscure edge case anyway
310 $self = $super_meta->reinitialize($self->name => (
ac2dc464 311 'attribute_metaclass' => $super_meta->attribute_metaclass,
5cf3dbcf 312 'method_metaclass' => $super_meta->method_metaclass,
313 'instance_metaclass' => $super_meta->instance_metaclass,
1341f10c 314 ));
315 }
316 }
ac2dc464 317 return $self;
1341f10c 318}
319
d7d8a8c7 320# NOTE:
d9bb6c63 321# this was crap anyway, see
322# Moose::Util::apply_all_roles
d7d8a8c7 323# instead
4498537c 324sub _apply_all_roles {
547dda77 325 Carp::croak 'DEPRECATED: use Moose::Util::apply_all_roles($meta, @roles) instead'
4498537c 326}
1341f10c 327
328sub _process_attribute {
a3738e5b 329 my ( $self, $name, @args ) = @_;
7e59b803 330
331 @args = %{$args[0]} if scalar @args == 1 && ref($args[0]) eq 'HASH';
d9bb6c63 332
1341f10c 333 if ($name =~ /^\+(.*)/) {
7e59b803 334 return $self->_process_inherited_attribute($1, @args);
1341f10c 335 }
336 else {
7e59b803 337 return $self->_process_new_attribute($name, @args);
338 }
339}
340
341sub _process_new_attribute {
342 my ( $self, $name, @args ) = @_;
7e59b803 343
d5c30e52 344 $self->attribute_metaclass->interpolate_class_and_new($name, @args);
1341f10c 345}
346
347sub _process_inherited_attribute {
348 my ($self, $attr_name, %options) = @_;
349 my $inherited_attr = $self->find_attribute_by_name($attr_name);
350 (defined $inherited_attr)
351 || confess "Could not find an attribute by the name of '$attr_name' to inherit from";
1341f10c 352 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
d7d8a8c7 353 return $inherited_attr->clone_and_inherit_options(%options);
1341f10c 354 }
355 else {
356 # NOTE:
357 # kind of a kludge to handle Class::MOP::Attributes
d7d8a8c7 358 return $inherited_attr->Moose::Meta::Attribute::clone_and_inherit_options(%options);
ac2dc464 359 }
1341f10c 360}
361
5cf3dbcf 362## -------------------------------------------------
363
364use Moose::Meta::Method::Constructor;
1f779926 365use Moose::Meta::Method::Destructor;
5cf3dbcf 366
ac2dc464 367# This could be done by using SUPER and altering ->options
368# I am keeping it this way to make it more explicit.
369sub create_immutable_transformer {
370 my $self = shift;
371 my $class = Class::MOP::Immutable->new($self, {
372 read_only => [qw/superclasses/],
373 cannot_call => [qw/
374 add_method
375 alias_method
376 remove_method
377 add_attribute
378 remove_attribute
ac2dc464 379 remove_package_symbol
380 add_role
381 /],
382 memoize => {
383 class_precedence_list => 'ARRAY',
384 compute_all_applicable_attributes => 'ARRAY',
385 get_meta_instance => 'SCALAR',
386 get_method_map => 'SCALAR',
387 # maybe ....
388 calculate_all_roles => 'ARRAY',
8453c358 389 },
390 # NOTE:
391 # this is ugly, but so are typeglobs,
392 # so whattayahgonnadoboutit
393 # - SL
394 wrapped => {
395 add_package_symbol => sub {
396 my $original = shift;
397 confess "Cannot add package symbols to an immutable metaclass"
398 unless (caller(2))[3] eq 'Class::MOP::Package::get_package_symbol';
399 goto $original->body;
400 },
401 },
ac2dc464 402 });
403 return $class;
404}
405
406sub make_immutable {
407 my $self = shift;
408 $self->SUPER::make_immutable
409 (
410 constructor_class => 'Moose::Meta::Method::Constructor',
411 destructor_class => 'Moose::Meta::Method::Destructor',
412 inline_destructor => 1,
413 # NOTE:
414 # no need to do this,
415 # Moose always does it
416 inline_accessors => 0,
417 @_,
418 );
5cf3dbcf 419}
420
c0e30cf5 4211;
422
423__END__
424
425=pod
426
427=head1 NAME
428
e522431d 429Moose::Meta::Class - The Moose metaclass
c0e30cf5 430
c0e30cf5 431=head1 DESCRIPTION
432
ac2dc464 433This is a subclass of L<Class::MOP::Class> with Moose specific
e522431d 434extensions.
435
ac2dc464 436For the most part, the only time you will ever encounter an
437instance of this class is if you are doing some serious deep
438introspection. To really understand this class, you need to refer
6ba6d68c 439to the L<Class::MOP::Class> documentation.
440
c0e30cf5 441=head1 METHODS
442
443=over 4
444
590868a3 445=item B<initialize>
446
61bdd94f 447=item B<create>
448
17594769 449Overrides original to accept a list of roles to apply to
61bdd94f 450the created class.
451
17594769 452 my $metaclass = Moose::Meta::Class->create( 'New::Class', roles => [...] );
453
454=item B<create_anon_class>
455
456Overrides original to support roles and caching.
457
458 my $metaclass = Moose::Meta::Class->create_anon_class(
459 superclasses => ['Foo'],
460 roles => [qw/Some Roles Go Here/],
461 cache => 1,
462 );
463
5cf3dbcf 464=item B<make_immutable>
465
ac2dc464 466Override original to add default options for inlining destructor
467and altering the Constructor metaclass.
468
469=item B<create_immutable_transformer>
470
471Override original to lock C<add_role> and memoize C<calculate_all_roles>
472
8c9d74e7 473=item B<new_object>
474
02a0fb52 475We override this method to support the C<trigger> attribute option.
476
a15dff8d 477=item B<construct_instance>
478
ac2dc464 479This provides some Moose specific extensions to this method, you
480almost never call this method directly unless you really know what
481you are doing.
6ba6d68c 482
483This method makes sure to handle the moose weak-ref, type-constraint
ac2dc464 484and type coercion features.
ef1d5f4b 485
093b12c2 486=item B<get_method_map>
e9ec68d6 487
ac2dc464 488This accommodates Moose::Meta::Role::Method instances, which are
489aliased, instead of added, but still need to be counted as valid
e9ec68d6 490methods.
491
78cd1d3b 492=item B<add_override_method_modifier ($name, $method)>
493
ac2dc464 494This will create an C<override> method modifier for you, and install
02a0fb52 495it in the package.
496
78cd1d3b 497=item B<add_augment_method_modifier ($name, $method)>
498
ac2dc464 499This will create an C<augment> method modifier for you, and install
02a0fb52 500it in the package.
501
2b14ac61 502=item B<calculate_all_roles>
503
ef333f17 504=item B<roles>
505
ac2dc464 506This will return an array of C<Moose::Meta::Role> instances which are
02a0fb52 507attached to this class.
508
ef333f17 509=item B<add_role ($role)>
510
ac2dc464 511This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
02a0fb52 512to the list of associated roles.
513
ef333f17 514=item B<does_role ($role_name)>
515
ac2dc464 516This will test if this class C<does> a given C<$role_name>. It will
517not only check it's local roles, but ask them as well in order to
02a0fb52 518cascade down the role hierarchy.
519
d79e62fd 520=item B<excludes_role ($role_name)>
521
ac2dc464 522This will test if this class C<excludes> a given C<$role_name>. It will
523not only check it's local roles, but ask them as well in order to
d79e62fd 524cascade down the role hierarchy.
525
9e93dd19 526=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 527
9e93dd19 528This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
529support for taking the C<$params> as a HASH ref.
ac1ef2f9 530
c0e30cf5 531=back
532
533=head1 BUGS
534
ac2dc464 535All complex software has bugs lurking in it, and this module is no
c0e30cf5 536exception. If you find a bug please either email me, or add the bug
537to cpan-RT.
538
c0e30cf5 539=head1 AUTHOR
540
541Stevan Little E<lt>stevan@iinteractive.comE<gt>
542
543=head1 COPYRIGHT AND LICENSE
544
778db3ac 545Copyright 2006-2008 by Infinity Interactive, Inc.
c0e30cf5 546
547L<http://www.iinteractive.com>
548
549This library is free software; you can redistribute it and/or modify
ac2dc464 550it under the same terms as Perl itself.
c0e30cf5 551
8a7a9c53 552=cut
1a563243 553