custom error support
[gitmo/Moose.git] / lib / Moose / Meta / Class.pm
CommitLineData
c0e30cf5 1
2package Moose::Meta::Class;
3
4use strict;
5use warnings;
6
0addec44 7use Class::MOP;
648e79ae 8
11c86f15 9use Carp ();
21f1e231 10use Scalar::Util 'weaken', 'blessed';
a15dff8d 11
eaa35e6e 12our $VERSION = '0.50';
d44714be 13our $AUTHORITY = 'cpan:STEVAN';
bc1e29b5 14
8ee73eeb 15use Moose::Meta::Method::Overriden;
3f9e4b0a 16use Moose::Meta::Method::Augmented;
8ee73eeb 17
c0e30cf5 18use base 'Class::MOP::Class';
19
598340d5 20__PACKAGE__->meta->add_attribute('roles' => (
ef333f17 21 reader => 'roles',
22 default => sub { [] }
23));
24
11c86f15 25__PACKAGE__->meta->add_attribute('error_builder' => (
26 reader => 'error_builder',
27 default => 'confess',
28));
29
30__PACKAGE__->meta->add_attribute('error_class' => (
31 reader => 'error_class',
32));
33
34
590868a3 35sub initialize {
36 my $class = shift;
37 my $pkg = shift;
685f7e44 38 return Class::MOP::get_metaclass_by_name($pkg)
39 || $class->SUPER::initialize($pkg,
40 'attribute_metaclass' => 'Moose::Meta::Attribute',
41 'method_metaclass' => 'Moose::Meta::Method',
42 'instance_metaclass' => 'Moose::Meta::Instance',
43 @_
44 );
ac2dc464 45}
590868a3 46
61bdd94f 47sub create {
48 my ($self, $package_name, %options) = @_;
49
50 (ref $options{roles} eq 'ARRAY')
11c86f15 51 || $self->throw_error("You must pass an ARRAY ref of roles", data => $options{roles})
61bdd94f 52 if exists $options{roles};
53
54 my $class = $self->SUPER::create($package_name, %options);
55
48045612 56 if (exists $options{roles}) {
61bdd94f 57 Moose::Util::apply_all_roles($class, @{$options{roles}});
58 }
59
60 return $class;
61}
62
17594769 63my %ANON_CLASSES;
64
65sub create_anon_class {
66 my ($self, %options) = @_;
67
68 my $cache_ok = delete $options{cache};
17594769 69
70 # something like Super::Class|Super::Class::2=Role|Role::1
71 my $cache_key = join '=' => (
6d5cbd2b 72 join('|', sort @{$options{superclasses} || []}),
73 join('|', sort @{$options{roles} || []}),
17594769 74 );
75
6d5cbd2b 76 if ($cache_ok && defined $ANON_CLASSES{$cache_key}) {
17594769 77 return $ANON_CLASSES{$cache_key};
78 }
79
80 my $new_class = $self->SUPER::create_anon_class(%options);
81
6d5cbd2b 82 $ANON_CLASSES{$cache_key} = $new_class
83 if $cache_ok;
17594769 84
85 return $new_class;
86}
87
ef333f17 88sub add_role {
89 my ($self, $role) = @_;
90 (blessed($role) && $role->isa('Moose::Meta::Role'))
11c86f15 91 || $self->throw_error("Roles must be instances of Moose::Meta::Role", data => $role);
ef333f17 92 push @{$self->roles} => $role;
93}
94
b8aeb4dc 95sub calculate_all_roles {
96 my $self = shift;
97 my %seen;
98 grep { !$seen{$_->name}++ } map { $_->calculate_all_roles } @{ $self->roles };
99}
100
ef333f17 101sub does_role {
102 my ($self, $role_name) = @_;
103 (defined $role_name)
11c86f15 104 || $self->throw_error("You must supply a role name to look for");
9c429218 105 foreach my $class ($self->class_precedence_list) {
81c3738f 106 next unless $class->can('meta') && $class->meta->can('roles');
9c429218 107 foreach my $role (@{$class->meta->roles}) {
108 return 1 if $role->does_role($role_name);
109 }
ef333f17 110 }
111 return 0;
112}
113
d79e62fd 114sub excludes_role {
115 my ($self, $role_name) = @_;
116 (defined $role_name)
11c86f15 117 || $self->throw_error("You must supply a role name to look for");
ac2dc464 118 foreach my $class ($self->class_precedence_list) {
119 next unless $class->can('meta');
5cb193ed 120 # NOTE:
121 # in the pretty rare instance when a Moose metaclass
ac2dc464 122 # is itself extended with a role, this check needs to
5cb193ed 123 # be done since some items in the class_precedence_list
ac2dc464 124 # might in fact be Class::MOP based still.
125 next unless $class->meta->can('roles');
9c429218 126 foreach my $role (@{$class->meta->roles}) {
127 return 1 if $role->excludes_role($role_name);
128 }
d79e62fd 129 }
130 return 0;
131}
132
8c9d74e7 133sub new_object {
134 my ($class, %params) = @_;
135 my $self = $class->SUPER::new_object(%params);
136 foreach my $attr ($class->compute_all_applicable_attributes()) {
4078709c 137 # if we have a trigger, then ...
138 if ($attr->can('has_trigger') && $attr->has_trigger) {
139 # make sure we have an init-arg ...
140 if (defined(my $init_arg = $attr->init_arg)) {
141 # now make sure an init-arg was passes ...
142 if (exists $params{$init_arg}) {
143 # and if get here, fire the trigger
144 $attr->trigger->(
145 $self,
146 # check if there is a coercion
147 ($attr->should_coerce
148 # and if so, we need to grab the
149 # value that is actually been stored
150 ? $attr->get_read_method_ref->($self)
151 # otherwise, just get the value from
152 # the constructor params
153 : $params{$init_arg}),
154 $attr
155 );
156 }
157 }
625d571f 158 }
8c9d74e7 159 }
ac2dc464 160 return $self;
8c9d74e7 161}
162
a15dff8d 163sub construct_instance {
164 my ($class, %params) = @_;
ddd0ec20 165 my $meta_instance = $class->get_meta_instance;
575db57d 166 # FIXME:
167 # the code below is almost certainly incorrect
168 # but this is foreign inheritence, so we might
ac2dc464 169 # have to kludge it in the end.
ddd0ec20 170 my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
ac2dc464 171 foreach my $attr ($class->compute_all_applicable_attributes()) {
c32c2c61 172 $attr->initialize_instance_slot($meta_instance, $instance, \%params);
a15dff8d 173 }
174 return $instance;
175}
176
093b12c2 177# FIXME:
178# This is ugly
ac2dc464 179sub get_method_map {
093b12c2 180 my $self = shift;
53dd42d8 181
182 if (defined $self->{'$!_package_cache_flag'} &&
66e08a8a 183 $self->{'$!_package_cache_flag'} == Class::MOP::check_package_cache_flag($self->meta->name)) {
53dd42d8 184 return $self->{'%!methods'};
185 }
186
ac2dc464 187 my $map = $self->{'%!methods'};
188
093b12c2 189 my $class_name = $self->name;
190 my $method_metaclass = $self->method_metaclass;
ac2dc464 191
0addec44 192 my %all_code = $self->get_all_package_symbols('CODE');
ac2dc464 193
0addec44 194 foreach my $symbol (keys %all_code) {
195 my $code = $all_code{$symbol};
ac2dc464 196
197 next if exists $map->{$symbol} &&
198 defined $map->{$symbol} &&
199 $map->{$symbol}->body == $code;
200
53dd42d8 201 my ($pkg, $name) = Class::MOP::get_code_info($code);
ac2dc464 202
53dd42d8 203 if ($pkg->can('meta')
4f8f3aab 204 # NOTE:
205 # we don't know what ->meta we are calling
53dd42d8 206 # here, so we need to be careful cause it
207 # just might blow up at us, or just complain
208 # loudly (in the case of Curses.pm) so we
4f8f3aab 209 # just be a little overly cautious here.
210 # - SL
211 && eval { no warnings; blessed($pkg->meta) }
212 && $pkg->meta->isa('Moose::Meta::Role')) {
093b12c2 213 #my $role = $pkg->meta->name;
214 #next unless $self->does_role($role);
215 }
216 else {
2887c827 217
218 # NOTE:
219 # in 5.10 constant.pm the constants show up
220 # as being in the right package, but in pre-5.10
221 # they show up as constant::__ANON__ so we
222 # make an exception here to be sure that things
223 # work as expected in both.
224 # - SL
225 unless ($pkg eq 'constant' && $name eq '__ANON__') {
226 next if ($pkg || '') ne $class_name ||
227 (($name || '') ne '__ANON__' && ($pkg || '') ne $class_name);
228 }
53dd42d8 229
093b12c2 230 }
ac2dc464 231
1b2aea39 232 $map->{$symbol} = $method_metaclass->wrap(
233 $code,
234 package_name => $class_name,
235 name => $symbol
236 );
093b12c2 237 }
ac2dc464 238
093b12c2 239 return $map;
a7d0cd00 240}
241
093b12c2 242### ---------------------------------------------
243
a2eec5e7 244sub add_attribute {
245 my $self = shift;
e472c9a5 246 $self->SUPER::add_attribute(
247 (blessed $_[0] && $_[0]->isa('Class::MOP::Attribute')
248 ? $_[0]
249 : $self->_process_attribute(@_))
250 );
a2eec5e7 251}
252
78cd1d3b 253sub add_override_method_modifier {
254 my ($self, $name, $method, $_super_package) = @_;
18c2ec0e 255
d05cd563 256 (!$self->has_method($name))
11c86f15 257 || $self->throw_error("Cannot add an override method if a local method is already present");
18c2ec0e 258
259 $self->add_method($name => Moose::Meta::Method::Overriden->new(
3f9e4b0a 260 method => $method,
261 class => $self,
262 package => $_super_package, # need this for roles
263 name => $name,
18c2ec0e 264 ));
78cd1d3b 265}
266
267sub add_augment_method_modifier {
ac2dc464 268 my ($self, $name, $method) = @_;
d05cd563 269 (!$self->has_method($name))
11c86f15 270 || $self->throw_error("Cannot add an augment method if a local method is already present");
3f9e4b0a 271
272 $self->add_method($name => Moose::Meta::Method::Augmented->new(
273 method => $method,
274 class => $self,
275 name => $name,
276 ));
78cd1d3b 277}
278
1341f10c 279## Private Utility methods ...
280
05d9eaf6 281sub _find_next_method_by_name_which_is_not_overridden {
282 my ($self, $name) = @_;
68efb014 283 foreach my $method ($self->find_all_methods_by_name($name)) {
ac2dc464 284 return $method->{code}
05d9eaf6 285 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
286 }
287 return undef;
288}
289
1341f10c 290sub _fix_metaclass_incompatability {
291 my ($self, @superclasses) = @_;
292 foreach my $super (@superclasses) {
293 # don't bother if it does not have a meta.
294 next unless $super->can('meta');
ac2dc464 295 # get the name, make sure we take
8ecb1fa0 296 # immutable classes into account
ac2dc464 297 my $super_meta_name = ($super->meta->is_immutable
8ecb1fa0 298 ? $super->meta->get_mutable_metaclass_name
299 : blessed($super->meta));
ac2dc464 300 # if it's meta is a vanilla Moose,
301 # then we can safely ignore it.
8ecb1fa0 302 next if $super_meta_name eq 'Moose::Meta::Class';
ac2dc464 303 # but if we have anything else,
1341f10c 304 # we need to check it out ...
305 unless (# see if of our metaclass is incompatible
8ecb1fa0 306 ($self->isa($super_meta_name) &&
1341f10c 307 # and see if our instance metaclass is incompatible
308 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
309 # ... and if we are just a vanilla Moose
310 $self->isa('Moose::Meta::Class')) {
311 # re-initialize the meta ...
312 my $super_meta = $super->meta;
313 # NOTE:
ac2dc464 314 # We might want to consider actually
315 # transfering any attributes from the
316 # original meta into this one, but in
1341f10c 317 # general you should not have any there
ac2dc464 318 # at this point anyway, so it's very
1341f10c 319 # much an obscure edge case anyway
320 $self = $super_meta->reinitialize($self->name => (
ac2dc464 321 'attribute_metaclass' => $super_meta->attribute_metaclass,
5cf3dbcf 322 'method_metaclass' => $super_meta->method_metaclass,
323 'instance_metaclass' => $super_meta->instance_metaclass,
1341f10c 324 ));
325 }
326 }
ac2dc464 327 return $self;
1341f10c 328}
329
d7d8a8c7 330# NOTE:
d9bb6c63 331# this was crap anyway, see
332# Moose::Util::apply_all_roles
d7d8a8c7 333# instead
4498537c 334sub _apply_all_roles {
547dda77 335 Carp::croak 'DEPRECATED: use Moose::Util::apply_all_roles($meta, @roles) instead'
4498537c 336}
1341f10c 337
338sub _process_attribute {
a3738e5b 339 my ( $self, $name, @args ) = @_;
7e59b803 340
341 @args = %{$args[0]} if scalar @args == 1 && ref($args[0]) eq 'HASH';
d9bb6c63 342
1341f10c 343 if ($name =~ /^\+(.*)/) {
7e59b803 344 return $self->_process_inherited_attribute($1, @args);
1341f10c 345 }
346 else {
7e59b803 347 return $self->_process_new_attribute($name, @args);
348 }
349}
350
351sub _process_new_attribute {
352 my ( $self, $name, @args ) = @_;
7e59b803 353
d5c30e52 354 $self->attribute_metaclass->interpolate_class_and_new($name, @args);
1341f10c 355}
356
357sub _process_inherited_attribute {
358 my ($self, $attr_name, %options) = @_;
359 my $inherited_attr = $self->find_attribute_by_name($attr_name);
360 (defined $inherited_attr)
11c86f15 361 || $self->throw_error("Could not find an attribute by the name of '$attr_name' to inherit from", data => $attr_name);
1341f10c 362 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
d7d8a8c7 363 return $inherited_attr->clone_and_inherit_options(%options);
1341f10c 364 }
365 else {
366 # NOTE:
367 # kind of a kludge to handle Class::MOP::Attributes
d7d8a8c7 368 return $inherited_attr->Moose::Meta::Attribute::clone_and_inherit_options(%options);
ac2dc464 369 }
1341f10c 370}
371
5cf3dbcf 372## -------------------------------------------------
373
374use Moose::Meta::Method::Constructor;
1f779926 375use Moose::Meta::Method::Destructor;
5cf3dbcf 376
ac2dc464 377# This could be done by using SUPER and altering ->options
378# I am keeping it this way to make it more explicit.
379sub create_immutable_transformer {
380 my $self = shift;
381 my $class = Class::MOP::Immutable->new($self, {
382 read_only => [qw/superclasses/],
383 cannot_call => [qw/
384 add_method
385 alias_method
386 remove_method
387 add_attribute
388 remove_attribute
ac2dc464 389 remove_package_symbol
390 add_role
391 /],
392 memoize => {
393 class_precedence_list => 'ARRAY',
394 compute_all_applicable_attributes => 'ARRAY',
395 get_meta_instance => 'SCALAR',
396 get_method_map => 'SCALAR',
397 # maybe ....
398 calculate_all_roles => 'ARRAY',
8453c358 399 },
400 # NOTE:
401 # this is ugly, but so are typeglobs,
402 # so whattayahgonnadoboutit
403 # - SL
404 wrapped => {
405 add_package_symbol => sub {
406 my $original = shift;
11c86f15 407 $self->throw_error("Cannot add package symbols to an immutable metaclass")
8453c358 408 unless (caller(2))[3] eq 'Class::MOP::Package::get_package_symbol';
409 goto $original->body;
410 },
411 },
ac2dc464 412 });
413 return $class;
414}
415
416sub make_immutable {
417 my $self = shift;
418 $self->SUPER::make_immutable
419 (
420 constructor_class => 'Moose::Meta::Method::Constructor',
421 destructor_class => 'Moose::Meta::Method::Destructor',
422 inline_destructor => 1,
423 # NOTE:
424 # no need to do this,
425 # Moose always does it
426 inline_accessors => 0,
427 @_,
428 );
5cf3dbcf 429}
430
11c86f15 431#{ package Moose::Meta::Class::ErrorRoutines; %Carp::Internal?
432
433our $level;
434
435sub throw_error {
436 my ( $self, @args ) = @_;
437 local $level = 1;
438 $self->raise_error($self->create_error(@args));
439}
440
441sub raise_error {
442 my ( $self, @args ) = @_;
443 die @args;
444}
445
446sub create_error {
447 my ( $self, @args ) = @_;
448
449 if ( @args % 2 == 1 ) {
450 unshift @args, "message";
451 }
452
453 my %args = @args;
454
455 local $level = $level + 1;
456
457 if ( my $class = $args{class} || ( ref $self && $self->error_class ) ) {
458 return $self->create_error_object( %args, class => $class );
459 } else {
460 my $builder = $args{builder} || ( ref($self) ? $self->error_builder : "confess" );
461
462 my $builder_method = ( ( ref($builder) && ref($builder) eq 'CODE' )
463 ? $builder
464 : ( $self->can("create_error_$builder") || "create_error_confess" ));
465
466 return $self->$builder_method(%args);
467 }
468}
469
470sub create_error_object {
471 my ( $self, %args ) = @_;
472
473 my $class = delete $args{class};
474
475 $class->new(
476 metaclass => $self,
477 %args,
478 depth => ( ($args{depth} || 1) + ( $level + 1 ) ),
479 );
480}
481
482sub create_error_croak {
483 my ( $self, @args ) = @_;
484 $self->_create_error_carpmess( @args );
485}
486
487sub create_error_confess {
488 my ( $self, @args ) = @_;
489 $self->_create_error_carpmess( @args, longmess => 1 );
490}
491
492sub _create_error_carpmess {
493 my ( $self, %args ) = @_;
494
495 my $carp_level = $level + 1 + ( $args{depth} || 1 );
496
497 local $Carp::CarpLevel = $carp_level; # $Carp::CarpLevel + $carp_level ?
498 local $Carp::MaxArgNums = 20; # default is 8, usually we use named args which gets messier though
499
500 my @args = exists $args{message} ? $args{message} : ();
501
502 if ( $args{longmess} ) {
503 return Carp::longmess(@args);
504 } else {
505 return Carp::shortmess(@args);
506 }
507}
508
c0e30cf5 5091;
510
511__END__
512
513=pod
514
515=head1 NAME
516
e522431d 517Moose::Meta::Class - The Moose metaclass
c0e30cf5 518
c0e30cf5 519=head1 DESCRIPTION
520
ac2dc464 521This is a subclass of L<Class::MOP::Class> with Moose specific
e522431d 522extensions.
523
ac2dc464 524For the most part, the only time you will ever encounter an
525instance of this class is if you are doing some serious deep
526introspection. To really understand this class, you need to refer
6ba6d68c 527to the L<Class::MOP::Class> documentation.
528
c0e30cf5 529=head1 METHODS
530
531=over 4
532
590868a3 533=item B<initialize>
534
61bdd94f 535=item B<create>
536
17594769 537Overrides original to accept a list of roles to apply to
61bdd94f 538the created class.
539
17594769 540 my $metaclass = Moose::Meta::Class->create( 'New::Class', roles => [...] );
541
542=item B<create_anon_class>
543
544Overrides original to support roles and caching.
545
546 my $metaclass = Moose::Meta::Class->create_anon_class(
547 superclasses => ['Foo'],
548 roles => [qw/Some Roles Go Here/],
549 cache => 1,
550 );
551
5cf3dbcf 552=item B<make_immutable>
553
ac2dc464 554Override original to add default options for inlining destructor
555and altering the Constructor metaclass.
556
557=item B<create_immutable_transformer>
558
559Override original to lock C<add_role> and memoize C<calculate_all_roles>
560
8c9d74e7 561=item B<new_object>
562
02a0fb52 563We override this method to support the C<trigger> attribute option.
564
a15dff8d 565=item B<construct_instance>
566
ac2dc464 567This provides some Moose specific extensions to this method, you
568almost never call this method directly unless you really know what
569you are doing.
6ba6d68c 570
571This method makes sure to handle the moose weak-ref, type-constraint
ac2dc464 572and type coercion features.
ef1d5f4b 573
093b12c2 574=item B<get_method_map>
e9ec68d6 575
ac2dc464 576This accommodates Moose::Meta::Role::Method instances, which are
577aliased, instead of added, but still need to be counted as valid
e9ec68d6 578methods.
579
78cd1d3b 580=item B<add_override_method_modifier ($name, $method)>
581
ac2dc464 582This will create an C<override> method modifier for you, and install
02a0fb52 583it in the package.
584
78cd1d3b 585=item B<add_augment_method_modifier ($name, $method)>
586
ac2dc464 587This will create an C<augment> method modifier for you, and install
02a0fb52 588it in the package.
589
2b14ac61 590=item B<calculate_all_roles>
591
ef333f17 592=item B<roles>
593
ac2dc464 594This will return an array of C<Moose::Meta::Role> instances which are
02a0fb52 595attached to this class.
596
ef333f17 597=item B<add_role ($role)>
598
ac2dc464 599This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
02a0fb52 600to the list of associated roles.
601
ef333f17 602=item B<does_role ($role_name)>
603
ac2dc464 604This will test if this class C<does> a given C<$role_name>. It will
605not only check it's local roles, but ask them as well in order to
02a0fb52 606cascade down the role hierarchy.
607
d79e62fd 608=item B<excludes_role ($role_name)>
609
ac2dc464 610This will test if this class C<excludes> a given C<$role_name>. It will
611not only check it's local roles, but ask them as well in order to
d79e62fd 612cascade down the role hierarchy.
613
9e93dd19 614=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 615
9e93dd19 616This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
617support for taking the C<$params> as a HASH ref.
ac1ef2f9 618
11c86f15 619=item B<throw_error $message, %extra>
620
621Throws the error created by C<create_error> using C<raise_error>
622
623=item B<create_error $message, %extra>
624
625Creates an error message or object.
626
627The default behavior is C<create_error_confess>.
628
629If C<error_class> is set uses C<create_error_object>. Otherwise uses
630C<error_builder> (a code reference or variant name), and calls the appropriate
631C<create_error_$builder> method.
632
633=item B<error_builder $builder_name>
634
635Get or set the error builder. Defaults to C<confess>.
636
637=item B<error_class $class_name>
638
639Get or set the error class. Has no default.
640
641=item B<create_error_confess %args>
642
643Creates an error using L<Carp/longmess>
644
645=item B<create_error_croak %args>
646
647Creates an error using L<Carp/shortmess>
648
649=item B<create_error_object %args>
650
651Calls C<new> on the C<class> parameter in C<%args>. Usable with C<error_class>
652to support custom error objects for your meta class.
653
654=item B<raise_error $error>
655
656Dies with an error object or string.
657
c0e30cf5 658=back
659
660=head1 BUGS
661
ac2dc464 662All complex software has bugs lurking in it, and this module is no
c0e30cf5 663exception. If you find a bug please either email me, or add the bug
664to cpan-RT.
665
c0e30cf5 666=head1 AUTHOR
667
668Stevan Little E<lt>stevan@iinteractive.comE<gt>
669
670=head1 COPYRIGHT AND LICENSE
671
778db3ac 672Copyright 2006-2008 by Infinity Interactive, Inc.
c0e30cf5 673
674L<http://www.iinteractive.com>
675
676This library is free software; you can redistribute it and/or modify
ac2dc464 677it under the same terms as Perl itself.
c0e30cf5 678
8a7a9c53 679=cut
1a563243 680