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