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