Fix Union->parent to return the nearest common ancestor
[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);
b5ae7c00 14use Class::Load qw(load_class);
477a812e 15use Eval::Closure;
dabed765 16use Scalar::Util qw(blessed refaddr);
9f2230e9 17use Sub::Name qw(subname);
5a18346b 18use Try::Tiny;
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 {
100 my $self;
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') ) {
134 confess("The 'message' parameter must be a coderef");
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) {
153 require Moose;
154 Moose->throw_error("Cannot coerce without a type coercion");
155 }
156
02679ba4 157 return $_[0] if $self->check($_[0]);
158
70ea9161 159 return $coercion->coerce(@_);
160}
a1257460 161
bb6ac54a 162sub assert_coerce {
163 my $self = shift;
164
165 my $coercion = $self->coercion;
166
167 unless ($coercion) {
168 require Moose;
169 Moose->throw_error("Cannot coerce without a type coercion");
170 }
171
172 return $_[0] if $self->check($_[0]);
173
174 my $result = $coercion->coerce(@_);
175
176 $self->assert_valid($result);
177
178 return $result;
179}
180
a1257460 181sub check {
182 my ($self, @args) = @_;
183 my $constraint_subref = $self->_compiled_type_constraint;
184 return $constraint_subref->(@args) ? 1 : undef;
185}
186
e27dfc11 187sub validate {
76d37e5a 188 my ($self, $value) = @_;
189 if ($self->_compiled_type_constraint->($value)) {
190 return undef;
191 }
192 else {
688fcdda 193 $self->get_message($value);
76d37e5a 194 }
195}
196
7c047a36 197sub can_be_inlined {
7487e61c 198 my $self = shift;
199
92efe680 200 if ( $self->has_parent && $self->constraint == $null_constraint ) {
7c047a36 201 return $self->parent->can_be_inlined;
7487e61c 202 }
203
204 return $self->_has_inlined_type_constraint;
205}
206
4e36cf24 207sub _inline_check {
208 my $self = shift;
209
7c047a36 210 unless ( $self->can_be_inlined ) {
3f9c18f3 211 require Moose;
212 Moose->throw_error( 'Cannot inline a type constraint check for ' . $self->name );
213 }
4e36cf24 214
92efe680 215 if ( $self->has_parent && $self->constraint == $null_constraint ) {
7487e61c 216 return $self->parent->_inline_check(@_);
217 }
218
e6fff671 219 return '( do { ' . $self->inlined->( $self, @_ ) . ' } )';
4e36cf24 220}
221
ca789903 222sub inline_environment {
223 my $self = shift;
224
225 if ( $self->has_parent && $self->constraint == $null_constraint ) {
226 return $self->parent->inline_environment;
227 }
228
229 return $self->_inline_environment;
230}
231
c24269b3 232sub assert_valid {
233 my ($self, $value) = @_;
234
235 my $error = $self->validate($value);
236 return 1 if ! defined $error;
237
238 require Moose;
239 Moose->throw_error($error);
240}
241
688fcdda 242sub get_message {
243 my ($self, $value) = @_;
7e206b74 244 my $msg = $self->has_message
245 ? $self->message
246 : $self->_default_message;
92a88343 247 local $_ = $value;
248 return $msg->($value);
688fcdda 249}
250
3726f905 251## type predicates ...
252
d9e17f80 253sub equals {
254 my ( $self, $type_or_name ) = @_;
255
e606ae5f 256 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
dabed765 257
0677975f 258 return 1 if $self == $other;
dabed765 259
260 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
261 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
262 }
263
264 return unless $self->constraint == $other->constraint;
265
266 if ( $self->has_parent ) {
267 return unless $other->has_parent;
268 return unless $self->parent->equals( $other->parent );
269 } else {
270 return if $other->has_parent;
271 }
d9e17f80 272
05a5763c 273 return;
d9e17f80 274}
275
b26e162e 276sub is_a_type_of {
d9e17f80 277 my ($self, $type_or_name) = @_;
278
e606ae5f 279 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 280
281 ($self->equals($type) || $self->is_subtype_of($type));
b26e162e 282}
283
cce8198b 284sub is_subtype_of {
d9e17f80 285 my ($self, $type_or_name) = @_;
286
e606ae5f 287 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 288
cce8198b 289 my $current = $self;
d9e17f80 290
cce8198b 291 while (my $parent = $current->parent) {
c0841d0c 292 return 1 if $parent->equals($type);
cce8198b 293 $current = $parent;
294 }
d9e17f80 295
cce8198b 296 return 0;
297}
298
3726f905 299## compiling the type constraint
300
301sub compile_type_constraint {
302 my $self = shift;
303 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 304}
305
3726f905 306## type compilers ...
307
308sub _actually_compile_type_constraint {
309 my $self = shift;
e27dfc11 310
3726f905 311 return $self->_compile_hand_optimized_type_constraint
312 if $self->has_hand_optimized_type_constraint;
e27dfc11 313
7c047a36 314 if ( $self->can_be_inlined ) {
477a812e 315 return eval_closure(
9c44971f 316 source => 'sub { ' . $self->_inline_check('$_[0]') . ' }',
317 environment => $self->inline_environment,
477a812e 318 );
43837b8a 319 }
320
3726f905 321 my $check = $self->constraint;
70ea9161 322 unless ( defined $check ) {
323 require Moose;
324 Moose->throw_error( "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 ) {
341 require Moose;
70ea9161 342 Moose->throw_error("Hand optimized type constraint is not a code reference");
343 }
42bc21a4 344
345 return $type_constraint;
3726f905 346}
347
348sub _compile_subtype {
349 my ($self, $check) = @_;
e27dfc11 350
baf26cc6 351 # gather all the parent constraintss in order
3726f905 352 my @parents;
baf26cc6 353 my $optimized_parent;
3726f905 354 foreach my $parent ($self->_collect_all_parents) {
baf26cc6 355 # if a parent is optimized, the optimized constraint already includes
356 # all of its parents tcs, so we can break the loop
3726f905 357 if ($parent->has_hand_optimized_type_constraint) {
baf26cc6 358 push @parents => $optimized_parent = $parent->hand_optimized_type_constraint;
e27dfc11 359 last;
3726f905 360 }
361 else {
baf26cc6 362 push @parents => $parent->constraint;
3726f905 363 }
364 }
e27dfc11 365
baf26cc6 366 @parents = grep { $_ != $null_constraint } reverse @parents;
367
368 unless ( @parents ) {
369 return $self->_compile_type($check);
370 } elsif( $optimized_parent and @parents == 1 ) {
371 # the case of just one optimized parent is optimized to prevent
372 # looping and the unnecessary localization
b5981f07 373 if ( $check == $null_constraint ) {
374 return $optimized_parent;
375 } else {
9f2230e9 376 return subname($self->name, sub {
b5981f07 377 return undef unless $optimized_parent->($_[0]);
a1257460 378 my (@args) = @_;
379 local $_ = $args[0];
380 $check->(@args);
b5981f07 381 });
382 }
baf26cc6 383 } else {
384 # general case, check all the constraints, from the first parent to ourselves
b5981f07 385 my @checks = @parents;
386 push @checks, $check if $check != $null_constraint;
9f2230e9 387 return subname($self->name => sub {
a1257460 388 my (@args) = @_;
389 local $_ = $args[0];
baf26cc6 390 foreach my $check (@checks) {
a1257460 391 return undef unless $check->(@args);
baf26cc6 392 }
393 return 1;
394 });
395 }
3726f905 396}
397
398sub _compile_type {
399 my ($self, $check) = @_;
baf26cc6 400
401 return $check if $check == $null_constraint; # Item, Any
402
9f2230e9 403 return subname($self->name => sub {
a1257460 404 my (@args) = @_;
405 local $_ = $args[0];
406 $check->(@args);
1b2aea39 407 });
3726f905 408}
409
410## other utils ...
411
412sub _collect_all_parents {
413 my $self = shift;
414 my @parents;
415 my $current = $self->parent;
416 while (defined $current) {
417 push @parents => $current;
418 $current = $current->parent;
419 }
420 return @parents;
421}
422
c0841d0c 423sub _ancestor_count {
424 my $self = shift;
425 return scalar $self->_collect_all_parents;
426}
427
85a9908f 428sub create_child_type {
9ceb576e 429 my ($self, %opts) = @_;
430 my $class = ref $self;
431 return $class->new(%opts, parent => $self);
432}
433
4e036ee4 4341;
435
ad46f524 436# ABSTRACT: The Moose Type Constraint metaclass
437
4e036ee4 438__END__
439
440=pod
441
4e036ee4 442=head1 DESCRIPTION
443
cfd006f0 444This class represents a single type constraint. Moose's built-in type
76127c77 445constraints, as well as constraints you define, are all stored in a
cfd006f0 446L<Moose::Meta::TypeConstraint::Registry> object as objects of this
447class.
6ba6d68c 448
baf46b9e 449=head1 INHERITANCE
450
451C<Moose::Meta::TypeConstraint> is a subclass of L<Class::MOP::Object>.
452
4e036ee4 453=head1 METHODS
454
455=over 4
456
cfd006f0 457=item B<< Moose::Meta::TypeConstraint->new(%options) >>
4e036ee4 458
cfd006f0 459This creates a new type constraint based on the provided C<%options>:
4e036ee4 460
cfd006f0 461=over 8
d9e17f80 462
cfd006f0 463=item * name
e606ae5f 464
cfd006f0 465The constraint name. If a name is not provided, it will be set to
466"__ANON__".
b26e162e 467
cfd006f0 468=item * parent
b26e162e 469
cfd006f0 470A C<Moose::Meta::TypeConstraint> object which is the parent type for
471the type being created. This is optional.
cce8198b 472
cfd006f0 473=item * constraint
e606ae5f 474
cfd006f0 475This is the subroutine reference that implements the actual constraint
476check. This defaults to a subroutine which always returns true.
6ba6d68c 477
cfd006f0 478=item * message
0a5bd159 479
cfd006f0 480A subroutine reference which is used to generate an error message when
481the constraint fails. This is optional.
0a5bd159 482
cfd006f0 483=item * coercion
76d37e5a 484
cfd006f0 485A L<Moose::Meta::TypeCoercion> object representing the coercions to
486the type. This is optional.
76d37e5a 487
7142d232 488=item * inlined
489
490A subroutine which returns a string suitable for inlining this type
491constraint. It will be called as a method on the type constraint object, and
492will receive a single additional parameter, a variable name to be tested
493(usually C<"$_"> or C<"$_[0]">.
494
495This is optional.
496
497=item * inline_environment
498
499A hash reference of variables to close over. The keys are variables names, and
500the values are I<references> to the variables.
501
cfd006f0 502=item * optimized
76d37e5a 503
7142d232 504B<This option is deprecated.>
505
cfd006f0 506This is a variant of the C<constraint> parameter that is somehow
507optimized. Typically, this means incorporating both the type's
508constraint and all of its parents' constraints into a single
509subroutine reference.
6ba6d68c 510
cfd006f0 511=back
4e036ee4 512
cfd006f0 513=item B<< $constraint->equals($type_name_or_object) >>
e606ae5f 514
cfd006f0 515Returns true if the supplied name or type object is the same as the
516current type.
66811d63 517
cfd006f0 518=item B<< $constraint->is_subtype_of($type_name_or_object) >>
e606ae5f 519
cfd006f0 520Returns true if the supplied name or type object is a parent of the
521current type.
3726f905 522
cfd006f0 523=item B<< $constraint->is_a_type_of($type_name_or_object) >>
e606ae5f 524
cfd006f0 525Returns true if the given type is the same as the current type, or is
526a parent of the current type. This is a shortcut for checking
527C<equals> and C<is_subtype_of>.
d9e17f80 528
cfd006f0 529=item B<< $constraint->coerce($value) >>
2f7e4042 530
b2894aea 531This will attempt to coerce the value to the type. If the type does not
cfd006f0 532have any defined coercions this will throw an error.
66811d63 533
572b5187 534If no coercion can produce a value matching C<$constraint>, the original
535value is returned.
536
086c6b6c 537=item B<< $constraint->assert_coerce($value) >>
572b5187 538
539This method behaves just like C<coerce>, but if the result is not valid
540according to C<$constraint>, an error is thrown.
541
cfd006f0 542=item B<< $constraint->check($value) >>
2f7e4042 543
cfd006f0 544Returns true if the given value passes the constraint for the type.
76d37e5a 545
cfd006f0 546=item B<< $constraint->validate($value) >>
2f7e4042 547
cfd006f0 548This is similar to C<check>. However, if the type I<is valid> then the
549method returns an explicit C<undef>. If the type is not valid, we call
550C<< $self->get_message($value) >> internally to generate an error
551message.
76d37e5a 552
952320e0 553=item B<< $constraint->assert_valid($value) >>
554
555Like C<check> and C<validate>, this method checks whether C<$value> is
556valid under the constraint. If it is, it will return true. If it is not,
557an exception will be thrown with the results of
558C<< $self->get_message($value) >>.
559
cfd006f0 560=item B<< $constraint->name >>
2f7e4042 561
cfd006f0 562Returns the type's name, as provided to the constructor.
688fcdda 563
cfd006f0 564=item B<< $constraint->parent >>
2f7e4042 565
cfd006f0 566Returns the type's parent, as provided to the constructor, if any.
4e036ee4 567
cfd006f0 568=item B<< $constraint->has_parent >>
2f7e4042 569
cfd006f0 570Returns true if the type has a parent type.
a27aa600 571
cfd006f0 572=item B<< $constraint->parents >>
2f7e4042 573
cfd006f0 574A synonym for C<parent>. This is useful for polymorphism with types
575that can have more than one parent.
c8cf9aaa 576
cfd006f0 577=item B<< $constraint->constraint >>
c8cf9aaa 578
cfd006f0 579Returns the type's constraint, as provided to the constructor.
9ceb576e 580
cfd006f0 581=item B<< $constraint->get_message($value) >>
4e036ee4 582
cfd006f0 583This generates a method for the given value. If the type does not have
584an explicit message, we generate a default message.
3726f905 585
cfd006f0 586=item B<< $constraint->has_message >>
587
588Returns true if the type has a message.
589
590=item B<< $constraint->message >>
591
592Returns the type's message as a subroutine reference.
593
594=item B<< $constraint->coercion >>
595
596Returns the type's L<Moose::Meta::TypeCoercion> object, if one
597exists.
598
599=item B<< $constraint->has_coercion >>
600
601Returns true if the type has a coercion.
602
7142d232 603=item B<< $constraint->can_be_inlined >>
604
605Returns true if this type constraint can be inlined. A type constraint which
606subtypes an inlinable constraint and does not add an additional constraint
607"inherits" its parent type's inlining.
608
cfd006f0 609=item B<< $constraint->hand_optimized_type_constraint >>
610
7142d232 611B<This method is deprecated.>
612
cfd006f0 613Returns the type's hand optimized constraint, as provided to the
614constructor via the C<optimized> option.
615
616=item B<< $constraint->has_hand_optimized_type_constraint >>
617
7142d232 618B<This method is deprecated.>
619
cfd006f0 620Returns true if the type has an optimized constraint.
621
622=item B<< $constraint->create_child_type(%options) >>
451c8248 623
cfd006f0 624This returns a new type constraint of the same class using the
625provided C<%options>. The C<parent> option will be the current type.
3726f905 626
cfd006f0 627This method exists so that subclasses of this class can override this
628behavior and change how child types are created.
451c8248 629
630=back
631
4e036ee4 632=head1 BUGS
633
d4048ef3 634See L<Moose/BUGS> for details on reporting bugs.
4e036ee4 635
c8cf9aaa 636=cut