Convert Moose->throw_error to Moose::Util::throw
[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
b5ae7c00 13use Class::Load qw(load_class);
477a812e 14use Eval::Closure;
dabed765 15use Scalar::Util qw(blessed refaddr);
9f2230e9 16use Sub::Name qw(subname);
5a18346b 17use Try::Tiny;
606ada8f 18use Moose::Util ();
66811d63 19
e606ae5f 20use base qw(Class::MOP::Object);
21
dc2b7cc8 22__PACKAGE__->meta->add_attribute('name' => (
23 reader => 'name',
24 Class::MOP::_definition_context(),
25));
3726f905 26__PACKAGE__->meta->add_attribute('parent' => (
27 reader => 'parent',
28 predicate => 'has_parent',
dc2b7cc8 29 Class::MOP::_definition_context(),
3726f905 30));
baf26cc6 31
32my $null_constraint = sub { 1 };
d67145ed 33__PACKAGE__->meta->add_attribute('constraint' => (
8de73ff1 34 reader => 'constraint',
35 writer => '_set_constraint',
dc2b7cc8 36 default => sub { $null_constraint },
37 Class::MOP::_definition_context(),
d67145ed 38));
dc2b7cc8 39
76d37e5a 40__PACKAGE__->meta->add_attribute('message' => (
41 accessor => 'message',
dc2b7cc8 42 predicate => 'has_message',
43 Class::MOP::_definition_context(),
76d37e5a 44));
dc2b7cc8 45
92a88343 46__PACKAGE__->meta->add_attribute('_default_message' => (
47 accessor => '_default_message',
dc2b7cc8 48 Class::MOP::_definition_context(),
92a88343 49));
dc2b7cc8 50
92a88343 51# can't make this a default because it has to close over the type name, and
52# cmop attributes don't have lazy
53my $_default_message_generator = sub {
54 my $name = shift;
55 sub {
56 my $value = shift;
57 # have to load it late like this, since it uses Moose itself
58 my $can_partialdump = try {
59 # versions prior to 0.14 had a potential infinite loop bug
b5ae7c00 60 load_class('Devel::PartialDump', { -version => 0.14 });
92a88343 61 1;
62 };
63 if ($can_partialdump) {
64 $value = Devel::PartialDump->new->dump($value);
65 }
66 else {
67 $value = (defined $value ? overload::StrVal($value) : 'undef');
68 }
69 return "Validation failed for '" . $name . "' with value $value";
70 }
71};
a27aa600 72__PACKAGE__->meta->add_attribute('coercion' => (
73 accessor => 'coercion',
dc2b7cc8 74 predicate => 'has_coercion',
75 Class::MOP::_definition_context(),
a27aa600 76));
70ea9161 77
c8cf9aaa 78__PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
79 init_arg => 'optimized',
80 accessor => 'hand_optimized_type_constraint',
e27dfc11 81 predicate => 'has_hand_optimized_type_constraint',
dc2b7cc8 82 Class::MOP::_definition_context(),
c8cf9aaa 83));
84
4e36cf24 85__PACKAGE__->meta->add_attribute('inlined' => (
7487e61c 86 init_arg => 'inlined',
4e36cf24 87 accessor => 'inlined',
7487e61c 88 predicate => '_has_inlined_type_constraint',
dc2b7cc8 89 Class::MOP::_definition_context(),
4e36cf24 90));
91
9c44971f 92__PACKAGE__->meta->add_attribute('inline_environment' => (
93 init_arg => 'inline_environment',
ca789903 94 accessor => '_inline_environment',
9c44971f 95 default => sub { {} },
dc2b7cc8 96 Class::MOP::_definition_context(),
9c44971f 97));
98
bd72f3c8 99sub parents {
143cc5ab 100 my $self = shift;
bd72f3c8 101 $self->parent;
102}
103
3726f905 104# private accessors
105
106__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
107 accessor => '_compiled_type_constraint',
dc2b7cc8 108 predicate => '_has_compiled_type_constraint',
109 Class::MOP::_definition_context(),
3726f905 110));
dc2b7cc8 111
22aed3c0 112__PACKAGE__->meta->add_attribute('package_defined_in' => (
dc2b7cc8 113 accessor => '_package_defined_in',
114 Class::MOP::_definition_context(),
22aed3c0 115));
116
e27dfc11 117sub new {
a27aa600 118 my $class = shift;
8534c69a 119 my ($first, @rest) = @_;
120 my %args = ref $first ? %$first : $first ? ($first, @rest) : ();
121 $args{name} = $args{name} ? "$args{name}" : "__ANON__";
70ea9161 122
7ec88c15 123 if ( $args{optimized} ) {
124 Moose::Deprecated::deprecated(
125 feature => 'optimized type constraint sub ref',
126 message =>
127 'Providing an optimized subroutine ref for type constraints is deprecated.'
128 . ' Use the inlining feature (inline_as) instead.'
129 );
130 }
131
7e206b74 132 if ( exists $args{message}
133 && (!ref($args{message}) || ref($args{message}) ne 'CODE') ) {
8dfdf793 134 Moose::Util::throw("The 'message' parameter must be a coderef");
7e206b74 135 }
136
8534c69a 137 my $self = $class->_new(%args);
3726f905 138 $self->compile_type_constraint()
139 unless $self->_has_compiled_type_constraint;
92a88343 140 $self->_default_message($_default_message_generator->($self->name))
141 unless $self->has_message;
66811d63 142 return $self;
143}
144
8534c69a 145
146
70ea9161 147sub coerce {
148 my $self = shift;
149
150 my $coercion = $self->coercion;
151
152 unless ($coercion) {
b1a85073 153 Moose::Util::throw("Cannot coerce without a type coercion");
70ea9161 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) {
b1a85073 167 Moose::Util::throw("Cannot coerce without a type coercion");
bb6ac54a 168 }
169
170 return $_[0] if $self->check($_[0]);
171
172 my $result = $coercion->coerce(@_);
173
174 $self->assert_valid($result);
175
176 return $result;
177}
178
a1257460 179sub check {
180 my ($self, @args) = @_;
181 my $constraint_subref = $self->_compiled_type_constraint;
182 return $constraint_subref->(@args) ? 1 : undef;
183}
184
e27dfc11 185sub validate {
76d37e5a 186 my ($self, $value) = @_;
187 if ($self->_compiled_type_constraint->($value)) {
188 return undef;
189 }
190 else {
688fcdda 191 $self->get_message($value);
76d37e5a 192 }
193}
194
7c047a36 195sub can_be_inlined {
7487e61c 196 my $self = shift;
197
92efe680 198 if ( $self->has_parent && $self->constraint == $null_constraint ) {
7c047a36 199 return $self->parent->can_be_inlined;
7487e61c 200 }
201
202 return $self->_has_inlined_type_constraint;
203}
204
4e36cf24 205sub _inline_check {
206 my $self = shift;
207
7c047a36 208 unless ( $self->can_be_inlined ) {
b1a85073 209 Moose::Util::throw( 'Cannot inline a type constraint check for ' . $self->name );
3f9c18f3 210 }
4e36cf24 211
92efe680 212 if ( $self->has_parent && $self->constraint == $null_constraint ) {
7487e61c 213 return $self->parent->_inline_check(@_);
214 }
215
e6fff671 216 return '( do { ' . $self->inlined->( $self, @_ ) . ' } )';
4e36cf24 217}
218
ca789903 219sub inline_environment {
220 my $self = shift;
221
222 if ( $self->has_parent && $self->constraint == $null_constraint ) {
223 return $self->parent->inline_environment;
224 }
225
226 return $self->_inline_environment;
227}
228
c24269b3 229sub assert_valid {
230 my ($self, $value) = @_;
231
232 my $error = $self->validate($value);
233 return 1 if ! defined $error;
234
606ada8f 235 Moose::Util::throw(
236 message => $error,
237 class => 'Moose::Exception::TypeConstraint',
238 type_name => $self->name,
239 value => $value,
240 );
c24269b3 241}
242
688fcdda 243sub get_message {
244 my ($self, $value) = @_;
7e206b74 245 my $msg = $self->has_message
246 ? $self->message
247 : $self->_default_message;
92a88343 248 local $_ = $value;
249 return $msg->($value);
688fcdda 250}
251
3726f905 252## type predicates ...
253
d9e17f80 254sub equals {
255 my ( $self, $type_or_name ) = @_;
256
e606ae5f 257 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
dabed765 258
0677975f 259 return 1 if $self == $other;
dabed765 260
261 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
262 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
263 }
264
265 return unless $self->constraint == $other->constraint;
266
267 if ( $self->has_parent ) {
268 return unless $other->has_parent;
269 return unless $self->parent->equals( $other->parent );
270 } else {
271 return if $other->has_parent;
272 }
d9e17f80 273
05a5763c 274 return;
d9e17f80 275}
276
b26e162e 277sub is_a_type_of {
d9e17f80 278 my ($self, $type_or_name) = @_;
279
e606ae5f 280 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 281
282 ($self->equals($type) || $self->is_subtype_of($type));
b26e162e 283}
284
cce8198b 285sub is_subtype_of {
d9e17f80 286 my ($self, $type_or_name) = @_;
287
e606ae5f 288 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 289
cce8198b 290 my $current = $self;
d9e17f80 291
cce8198b 292 while (my $parent = $current->parent) {
c0841d0c 293 return 1 if $parent->equals($type);
cce8198b 294 $current = $parent;
295 }
d9e17f80 296
cce8198b 297 return 0;
298}
299
3726f905 300## compiling the type constraint
301
302sub compile_type_constraint {
303 my $self = shift;
304 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 305}
306
3726f905 307## type compilers ...
308
309sub _actually_compile_type_constraint {
310 my $self = shift;
e27dfc11 311
3726f905 312 return $self->_compile_hand_optimized_type_constraint
313 if $self->has_hand_optimized_type_constraint;
e27dfc11 314
7c047a36 315 if ( $self->can_be_inlined ) {
477a812e 316 return eval_closure(
9c44971f 317 source => 'sub { ' . $self->_inline_check('$_[0]') . ' }',
318 environment => $self->inline_environment,
477a812e 319 );
43837b8a 320 }
321
3726f905 322 my $check = $self->constraint;
70ea9161 323 unless ( defined $check ) {
b1a85073 324 Moose::Util::throw( "Could not compile type constraint '"
e27dfc11 325 . $self->name
70ea9161 326 . "' because no constraint check" );
327 }
e27dfc11 328
3726f905 329 return $self->_compile_subtype($check)
330 if $self->has_parent;
e27dfc11 331
3726f905 332 return $self->_compile_type($check);
333}
334
335sub _compile_hand_optimized_type_constraint {
336 my $self = shift;
e27dfc11 337
3726f905 338 my $type_constraint = $self->hand_optimized_type_constraint;
e27dfc11 339
70ea9161 340 unless ( ref $type_constraint ) {
b1a85073 341 Moose::Util::throw("Hand optimized type constraint is not a code reference");
70ea9161 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
5b3b59ff 568Returns all of the types parents as an list of type constraint objects.
c8cf9aaa 569
cfd006f0 570=item B<< $constraint->constraint >>
c8cf9aaa 571
cfd006f0 572Returns the type's constraint, as provided to the constructor.
9ceb576e 573
cfd006f0 574=item B<< $constraint->get_message($value) >>
4e036ee4 575
cfd006f0 576This generates a method for the given value. If the type does not have
577an explicit message, we generate a default message.
3726f905 578
cfd006f0 579=item B<< $constraint->has_message >>
580
581Returns true if the type has a message.
582
583=item B<< $constraint->message >>
584
585Returns the type's message as a subroutine reference.
586
587=item B<< $constraint->coercion >>
588
589Returns the type's L<Moose::Meta::TypeCoercion> object, if one
590exists.
591
592=item B<< $constraint->has_coercion >>
593
594Returns true if the type has a coercion.
595
7142d232 596=item B<< $constraint->can_be_inlined >>
597
598Returns true if this type constraint can be inlined. A type constraint which
599subtypes an inlinable constraint and does not add an additional constraint
600"inherits" its parent type's inlining.
601
cfd006f0 602=item B<< $constraint->hand_optimized_type_constraint >>
603
7142d232 604B<This method is deprecated.>
605
cfd006f0 606Returns the type's hand optimized constraint, as provided to the
607constructor via the C<optimized> option.
608
609=item B<< $constraint->has_hand_optimized_type_constraint >>
610
7142d232 611B<This method is deprecated.>
612
cfd006f0 613Returns true if the type has an optimized constraint.
614
615=item B<< $constraint->create_child_type(%options) >>
451c8248 616
cfd006f0 617This returns a new type constraint of the same class using the
618provided C<%options>. The C<parent> option will be the current type.
3726f905 619
cfd006f0 620This method exists so that subclasses of this class can override this
621behavior and change how child types are created.
451c8248 622
623=back
624
4e036ee4 625=head1 BUGS
626
d4048ef3 627See L<Moose/BUGS> for details on reporting bugs.
4e036ee4 628
c8cf9aaa 629=cut