refactor the default type constraint message logic a bit
[gitmo/Moose.git] / lib / Moose / Meta / TypeConstraint.pm
CommitLineData
4e036ee4 1
2package Moose::Meta::TypeConstraint;
3
4use strict;
5use warnings;
6use metaclass;
7
ed90007b 8use overload '0+' => sub { refaddr(shift) }, # id an object
9 '""' => sub { shift->name }, # stringify to tc name
72e389bf 10 bool => sub { 1 },
b644e331 11 fallback => 1;
900466d6 12
477a812e 13use Eval::Closure;
dabed765 14use Scalar::Util qw(blessed refaddr);
9f2230e9 15use Sub::Name qw(subname);
5a18346b 16use Try::Tiny;
66811d63 17
e606ae5f 18use base qw(Class::MOP::Object);
19
3726f905 20__PACKAGE__->meta->add_attribute('name' => (reader => 'name'));
21__PACKAGE__->meta->add_attribute('parent' => (
22 reader => 'parent',
23 predicate => 'has_parent',
24));
baf26cc6 25
26my $null_constraint = sub { 1 };
d67145ed 27__PACKAGE__->meta->add_attribute('constraint' => (
8de73ff1 28 reader => 'constraint',
29 writer => '_set_constraint',
baf26cc6 30 default => sub { $null_constraint }
d67145ed 31));
76d37e5a 32__PACKAGE__->meta->add_attribute('message' => (
33 accessor => 'message',
34 predicate => 'has_message'
35));
92a88343 36__PACKAGE__->meta->add_attribute('_default_message' => (
37 accessor => '_default_message',
38));
39# can't make this a default because it has to close over the type name, and
40# cmop attributes don't have lazy
41my $_default_message_generator = sub {
42 my $name = shift;
43 sub {
44 my $value = shift;
45 # have to load it late like this, since it uses Moose itself
46 my $can_partialdump = try {
47 # versions prior to 0.14 had a potential infinite loop bug
48 Class::MOP::load_class('Devel::PartialDump', { -version => 0.14 });
49 1;
50 };
51 if ($can_partialdump) {
52 $value = Devel::PartialDump->new->dump($value);
53 }
54 else {
55 $value = (defined $value ? overload::StrVal($value) : 'undef');
56 }
57 return "Validation failed for '" . $name . "' with value $value";
58 }
59};
a27aa600 60__PACKAGE__->meta->add_attribute('coercion' => (
61 accessor => 'coercion',
62 predicate => 'has_coercion'
63));
70ea9161 64
c8cf9aaa 65__PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
66 init_arg => 'optimized',
67 accessor => 'hand_optimized_type_constraint',
e27dfc11 68 predicate => 'has_hand_optimized_type_constraint',
c8cf9aaa 69));
70
4e36cf24 71__PACKAGE__->meta->add_attribute('inlined' => (
7487e61c 72 init_arg => 'inlined',
4e36cf24 73 accessor => 'inlined',
7487e61c 74 predicate => '_has_inlined_type_constraint',
4e36cf24 75));
76
9c44971f 77__PACKAGE__->meta->add_attribute('inline_environment' => (
78 init_arg => 'inline_environment',
ca789903 79 accessor => '_inline_environment',
9c44971f 80 default => sub { {} },
81));
82
bd72f3c8 83sub parents {
84 my $self;
85 $self->parent;
86}
87
3726f905 88# private accessors
89
90__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
91 accessor => '_compiled_type_constraint',
92 predicate => '_has_compiled_type_constraint'
93));
22aed3c0 94__PACKAGE__->meta->add_attribute('package_defined_in' => (
95 accessor => '_package_defined_in'
96));
97
e27dfc11 98sub new {
a27aa600 99 my $class = shift;
8534c69a 100 my ($first, @rest) = @_;
101 my %args = ref $first ? %$first : $first ? ($first, @rest) : ();
102 $args{name} = $args{name} ? "$args{name}" : "__ANON__";
70ea9161 103
8534c69a 104 my $self = $class->_new(%args);
3726f905 105 $self->compile_type_constraint()
106 unless $self->_has_compiled_type_constraint;
92a88343 107 $self->_default_message($_default_message_generator->($self->name))
108 unless $self->has_message;
66811d63 109 return $self;
110}
111
8534c69a 112
113
70ea9161 114sub coerce {
115 my $self = shift;
116
117 my $coercion = $self->coercion;
118
119 unless ($coercion) {
120 require Moose;
121 Moose->throw_error("Cannot coerce without a type coercion");
122 }
123
02679ba4 124 return $_[0] if $self->check($_[0]);
125
70ea9161 126 return $coercion->coerce(@_);
127}
a1257460 128
bb6ac54a 129sub assert_coerce {
130 my $self = shift;
131
132 my $coercion = $self->coercion;
133
134 unless ($coercion) {
135 require Moose;
136 Moose->throw_error("Cannot coerce without a type coercion");
137 }
138
139 return $_[0] if $self->check($_[0]);
140
141 my $result = $coercion->coerce(@_);
142
143 $self->assert_valid($result);
144
145 return $result;
146}
147
a1257460 148sub check {
149 my ($self, @args) = @_;
150 my $constraint_subref = $self->_compiled_type_constraint;
151 return $constraint_subref->(@args) ? 1 : undef;
152}
153
e27dfc11 154sub validate {
76d37e5a 155 my ($self, $value) = @_;
156 if ($self->_compiled_type_constraint->($value)) {
157 return undef;
158 }
159 else {
688fcdda 160 $self->get_message($value);
76d37e5a 161 }
162}
163
7c047a36 164sub can_be_inlined {
7487e61c 165 my $self = shift;
166
92efe680 167 if ( $self->has_parent && $self->constraint == $null_constraint ) {
7c047a36 168 return $self->parent->can_be_inlined;
7487e61c 169 }
170
171 return $self->_has_inlined_type_constraint;
172}
173
4e36cf24 174sub _inline_check {
175 my $self = shift;
176
7c047a36 177 unless ( $self->can_be_inlined ) {
3f9c18f3 178 require Moose;
179 Moose->throw_error( 'Cannot inline a type constraint check for ' . $self->name );
180 }
4e36cf24 181
92efe680 182 if ( $self->has_parent && $self->constraint == $null_constraint ) {
7487e61c 183 return $self->parent->_inline_check(@_);
184 }
185
e6fff671 186 return '( do { ' . $self->inlined->( $self, @_ ) . ' } )';
4e36cf24 187}
188
ca789903 189sub inline_environment {
190 my $self = shift;
191
192 if ( $self->has_parent && $self->constraint == $null_constraint ) {
193 return $self->parent->inline_environment;
194 }
195
196 return $self->_inline_environment;
197}
198
c24269b3 199sub assert_valid {
200 my ($self, $value) = @_;
201
202 my $error = $self->validate($value);
203 return 1 if ! defined $error;
204
205 require Moose;
206 Moose->throw_error($error);
207}
208
688fcdda 209sub get_message {
210 my ($self, $value) = @_;
92a88343 211 my $msg = $self->message || $self->_default_message;
212 local $_ = $value;
213 return $msg->($value);
688fcdda 214}
215
3726f905 216## type predicates ...
217
d9e17f80 218sub equals {
219 my ( $self, $type_or_name ) = @_;
220
e606ae5f 221 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
dabed765 222
0677975f 223 return 1 if $self == $other;
dabed765 224
225 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
226 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
227 }
228
229 return unless $self->constraint == $other->constraint;
230
231 if ( $self->has_parent ) {
232 return unless $other->has_parent;
233 return unless $self->parent->equals( $other->parent );
234 } else {
235 return if $other->has_parent;
236 }
d9e17f80 237
05a5763c 238 return;
d9e17f80 239}
240
b26e162e 241sub is_a_type_of {
d9e17f80 242 my ($self, $type_or_name) = @_;
243
e606ae5f 244 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 245
246 ($self->equals($type) || $self->is_subtype_of($type));
b26e162e 247}
248
cce8198b 249sub is_subtype_of {
d9e17f80 250 my ($self, $type_or_name) = @_;
251
e606ae5f 252 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 253
cce8198b 254 my $current = $self;
d9e17f80 255
cce8198b 256 while (my $parent = $current->parent) {
d9e17f80 257 return 1 if $parent->equals($type);
cce8198b 258 $current = $parent;
259 }
d9e17f80 260
cce8198b 261 return 0;
262}
263
3726f905 264## compiling the type constraint
265
266sub compile_type_constraint {
267 my $self = shift;
268 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 269}
270
3726f905 271## type compilers ...
272
273sub _actually_compile_type_constraint {
274 my $self = shift;
e27dfc11 275
3726f905 276 return $self->_compile_hand_optimized_type_constraint
277 if $self->has_hand_optimized_type_constraint;
e27dfc11 278
7c047a36 279 if ( $self->can_be_inlined ) {
477a812e 280 return eval_closure(
9c44971f 281 source => 'sub { ' . $self->_inline_check('$_[0]') . ' }',
282 environment => $self->inline_environment,
477a812e 283 );
43837b8a 284 }
285
3726f905 286 my $check = $self->constraint;
70ea9161 287 unless ( defined $check ) {
288 require Moose;
289 Moose->throw_error( "Could not compile type constraint '"
e27dfc11 290 . $self->name
70ea9161 291 . "' because no constraint check" );
292 }
e27dfc11 293
3726f905 294 return $self->_compile_subtype($check)
295 if $self->has_parent;
e27dfc11 296
3726f905 297 return $self->_compile_type($check);
298}
299
300sub _compile_hand_optimized_type_constraint {
301 my $self = shift;
e27dfc11 302
3726f905 303 my $type_constraint = $self->hand_optimized_type_constraint;
e27dfc11 304
70ea9161 305 unless ( ref $type_constraint ) {
306 require Moose;
70ea9161 307 Moose->throw_error("Hand optimized type constraint is not a code reference");
308 }
42bc21a4 309
310 return $type_constraint;
3726f905 311}
312
313sub _compile_subtype {
314 my ($self, $check) = @_;
e27dfc11 315
baf26cc6 316 # gather all the parent constraintss in order
3726f905 317 my @parents;
baf26cc6 318 my $optimized_parent;
3726f905 319 foreach my $parent ($self->_collect_all_parents) {
baf26cc6 320 # if a parent is optimized, the optimized constraint already includes
321 # all of its parents tcs, so we can break the loop
3726f905 322 if ($parent->has_hand_optimized_type_constraint) {
baf26cc6 323 push @parents => $optimized_parent = $parent->hand_optimized_type_constraint;
e27dfc11 324 last;
3726f905 325 }
326 else {
baf26cc6 327 push @parents => $parent->constraint;
3726f905 328 }
329 }
e27dfc11 330
baf26cc6 331 @parents = grep { $_ != $null_constraint } reverse @parents;
332
333 unless ( @parents ) {
334 return $self->_compile_type($check);
335 } elsif( $optimized_parent and @parents == 1 ) {
336 # the case of just one optimized parent is optimized to prevent
337 # looping and the unnecessary localization
b5981f07 338 if ( $check == $null_constraint ) {
339 return $optimized_parent;
340 } else {
9f2230e9 341 return subname($self->name, sub {
b5981f07 342 return undef unless $optimized_parent->($_[0]);
a1257460 343 my (@args) = @_;
344 local $_ = $args[0];
345 $check->(@args);
b5981f07 346 });
347 }
baf26cc6 348 } else {
349 # general case, check all the constraints, from the first parent to ourselves
b5981f07 350 my @checks = @parents;
351 push @checks, $check if $check != $null_constraint;
9f2230e9 352 return subname($self->name => sub {
a1257460 353 my (@args) = @_;
354 local $_ = $args[0];
baf26cc6 355 foreach my $check (@checks) {
a1257460 356 return undef unless $check->(@args);
baf26cc6 357 }
358 return 1;
359 });
360 }
3726f905 361}
362
363sub _compile_type {
364 my ($self, $check) = @_;
baf26cc6 365
366 return $check if $check == $null_constraint; # Item, Any
367
9f2230e9 368 return subname($self->name => sub {
a1257460 369 my (@args) = @_;
370 local $_ = $args[0];
371 $check->(@args);
1b2aea39 372 });
3726f905 373}
374
375## other utils ...
376
377sub _collect_all_parents {
378 my $self = shift;
379 my @parents;
380 my $current = $self->parent;
381 while (defined $current) {
382 push @parents => $current;
383 $current = $current->parent;
384 }
385 return @parents;
386}
387
85a9908f 388sub create_child_type {
9ceb576e 389 my ($self, %opts) = @_;
390 my $class = ref $self;
391 return $class->new(%opts, parent => $self);
392}
393
4e036ee4 3941;
395
ad46f524 396# ABSTRACT: The Moose Type Constraint metaclass
397
4e036ee4 398__END__
399
400=pod
401
4e036ee4 402=head1 DESCRIPTION
403
cfd006f0 404This class represents a single type constraint. Moose's built-in type
76127c77 405constraints, as well as constraints you define, are all stored in a
cfd006f0 406L<Moose::Meta::TypeConstraint::Registry> object as objects of this
407class.
6ba6d68c 408
baf46b9e 409=head1 INHERITANCE
410
411C<Moose::Meta::TypeConstraint> is a subclass of L<Class::MOP::Object>.
412
4e036ee4 413=head1 METHODS
414
415=over 4
416
cfd006f0 417=item B<< Moose::Meta::TypeConstraint->new(%options) >>
4e036ee4 418
cfd006f0 419This creates a new type constraint based on the provided C<%options>:
4e036ee4 420
cfd006f0 421=over 8
d9e17f80 422
cfd006f0 423=item * name
e606ae5f 424
cfd006f0 425The constraint name. If a name is not provided, it will be set to
426"__ANON__".
b26e162e 427
cfd006f0 428=item * parent
b26e162e 429
cfd006f0 430A C<Moose::Meta::TypeConstraint> object which is the parent type for
431the type being created. This is optional.
cce8198b 432
cfd006f0 433=item * constraint
e606ae5f 434
cfd006f0 435This is the subroutine reference that implements the actual constraint
436check. This defaults to a subroutine which always returns true.
6ba6d68c 437
cfd006f0 438=item * message
0a5bd159 439
cfd006f0 440A subroutine reference which is used to generate an error message when
441the constraint fails. This is optional.
0a5bd159 442
cfd006f0 443=item * coercion
76d37e5a 444
cfd006f0 445A L<Moose::Meta::TypeCoercion> object representing the coercions to
446the type. This is optional.
76d37e5a 447
7142d232 448=item * inlined
449
450A subroutine which returns a string suitable for inlining this type
451constraint. It will be called as a method on the type constraint object, and
452will receive a single additional parameter, a variable name to be tested
453(usually C<"$_"> or C<"$_[0]">.
454
455This is optional.
456
457=item * inline_environment
458
459A hash reference of variables to close over. The keys are variables names, and
460the values are I<references> to the variables.
461
cfd006f0 462=item * optimized
76d37e5a 463
7142d232 464B<This option is deprecated.>
465
cfd006f0 466This is a variant of the C<constraint> parameter that is somehow
467optimized. Typically, this means incorporating both the type's
468constraint and all of its parents' constraints into a single
469subroutine reference.
6ba6d68c 470
cfd006f0 471=back
4e036ee4 472
cfd006f0 473=item B<< $constraint->equals($type_name_or_object) >>
e606ae5f 474
cfd006f0 475Returns true if the supplied name or type object is the same as the
476current type.
66811d63 477
cfd006f0 478=item B<< $constraint->is_subtype_of($type_name_or_object) >>
e606ae5f 479
cfd006f0 480Returns true if the supplied name or type object is a parent of the
481current type.
3726f905 482
cfd006f0 483=item B<< $constraint->is_a_type_of($type_name_or_object) >>
e606ae5f 484
cfd006f0 485Returns true if the given type is the same as the current type, or is
486a parent of the current type. This is a shortcut for checking
487C<equals> and C<is_subtype_of>.
d9e17f80 488
cfd006f0 489=item B<< $constraint->coerce($value) >>
2f7e4042 490
b2894aea 491This will attempt to coerce the value to the type. If the type does not
cfd006f0 492have any defined coercions this will throw an error.
66811d63 493
572b5187 494If no coercion can produce a value matching C<$constraint>, the original
495value is returned.
496
086c6b6c 497=item B<< $constraint->assert_coerce($value) >>
572b5187 498
499This method behaves just like C<coerce>, but if the result is not valid
500according to C<$constraint>, an error is thrown.
501
cfd006f0 502=item B<< $constraint->check($value) >>
2f7e4042 503
cfd006f0 504Returns true if the given value passes the constraint for the type.
76d37e5a 505
cfd006f0 506=item B<< $constraint->validate($value) >>
2f7e4042 507
cfd006f0 508This is similar to C<check>. However, if the type I<is valid> then the
509method returns an explicit C<undef>. If the type is not valid, we call
510C<< $self->get_message($value) >> internally to generate an error
511message.
76d37e5a 512
952320e0 513=item B<< $constraint->assert_valid($value) >>
514
515Like C<check> and C<validate>, this method checks whether C<$value> is
516valid under the constraint. If it is, it will return true. If it is not,
517an exception will be thrown with the results of
518C<< $self->get_message($value) >>.
519
cfd006f0 520=item B<< $constraint->name >>
2f7e4042 521
cfd006f0 522Returns the type's name, as provided to the constructor.
688fcdda 523
cfd006f0 524=item B<< $constraint->parent >>
2f7e4042 525
cfd006f0 526Returns the type's parent, as provided to the constructor, if any.
4e036ee4 527
cfd006f0 528=item B<< $constraint->has_parent >>
2f7e4042 529
cfd006f0 530Returns true if the type has a parent type.
a27aa600 531
cfd006f0 532=item B<< $constraint->parents >>
2f7e4042 533
cfd006f0 534A synonym for C<parent>. This is useful for polymorphism with types
535that can have more than one parent.
c8cf9aaa 536
cfd006f0 537=item B<< $constraint->constraint >>
c8cf9aaa 538
cfd006f0 539Returns the type's constraint, as provided to the constructor.
9ceb576e 540
cfd006f0 541=item B<< $constraint->get_message($value) >>
4e036ee4 542
cfd006f0 543This generates a method for the given value. If the type does not have
544an explicit message, we generate a default message.
3726f905 545
cfd006f0 546=item B<< $constraint->has_message >>
547
548Returns true if the type has a message.
549
550=item B<< $constraint->message >>
551
552Returns the type's message as a subroutine reference.
553
554=item B<< $constraint->coercion >>
555
556Returns the type's L<Moose::Meta::TypeCoercion> object, if one
557exists.
558
559=item B<< $constraint->has_coercion >>
560
561Returns true if the type has a coercion.
562
7142d232 563=item B<< $constraint->can_be_inlined >>
564
565Returns true if this type constraint can be inlined. A type constraint which
566subtypes an inlinable constraint and does not add an additional constraint
567"inherits" its parent type's inlining.
568
cfd006f0 569=item B<< $constraint->hand_optimized_type_constraint >>
570
7142d232 571B<This method is deprecated.>
572
cfd006f0 573Returns the type's hand optimized constraint, as provided to the
574constructor via the C<optimized> option.
575
576=item B<< $constraint->has_hand_optimized_type_constraint >>
577
7142d232 578B<This method is deprecated.>
579
cfd006f0 580Returns true if the type has an optimized constraint.
581
582=item B<< $constraint->create_child_type(%options) >>
451c8248 583
cfd006f0 584This returns a new type constraint of the same class using the
585provided C<%options>. The C<parent> option will be the current type.
3726f905 586
cfd006f0 587This method exists so that subclasses of this class can override this
588behavior and change how child types are created.
451c8248 589
590=back
591
4e036ee4 592=head1 BUGS
593
d4048ef3 594See L<Moose/BUGS> for details on reporting bugs.
4e036ee4 595
c8cf9aaa 596=cut