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