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