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