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