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