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