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