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