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