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