stop using a package global for enums, just close over it
[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
7487e61c 138sub has_inlined_type_constraint {
139 my $self = shift;
140
92efe680 141 if ( $self->has_parent && $self->constraint == $null_constraint ) {
7487e61c 142 return $self->parent->has_inlined_type_constraint;
143 }
144
145 return $self->_has_inlined_type_constraint;
146}
147
4e36cf24 148sub _inline_check {
149 my $self = shift;
150
3f9c18f3 151 unless ( $self->has_inlined_type_constraint ) {
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
43837b8a 259 if ( $self->has_inlined_type_constraint ) {
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
cfd006f0 428=item * optimized
76d37e5a 429
cfd006f0 430This is a variant of the C<constraint> parameter that is somehow
431optimized. Typically, this means incorporating both the type's
432constraint and all of its parents' constraints into a single
433subroutine reference.
6ba6d68c 434
cfd006f0 435=back
4e036ee4 436
cfd006f0 437=item B<< $constraint->equals($type_name_or_object) >>
e606ae5f 438
cfd006f0 439Returns true if the supplied name or type object is the same as the
440current type.
66811d63 441
cfd006f0 442=item B<< $constraint->is_subtype_of($type_name_or_object) >>
e606ae5f 443
cfd006f0 444Returns true if the supplied name or type object is a parent of the
445current type.
3726f905 446
cfd006f0 447=item B<< $constraint->is_a_type_of($type_name_or_object) >>
e606ae5f 448
cfd006f0 449Returns true if the given type is the same as the current type, or is
450a parent of the current type. This is a shortcut for checking
451C<equals> and C<is_subtype_of>.
d9e17f80 452
cfd006f0 453=item B<< $constraint->coerce($value) >>
2f7e4042 454
b2894aea 455This will attempt to coerce the value to the type. If the type does not
cfd006f0 456have any defined coercions this will throw an error.
66811d63 457
572b5187 458If no coercion can produce a value matching C<$constraint>, the original
459value is returned.
460
086c6b6c 461=item B<< $constraint->assert_coerce($value) >>
572b5187 462
463This method behaves just like C<coerce>, but if the result is not valid
464according to C<$constraint>, an error is thrown.
465
cfd006f0 466=item B<< $constraint->check($value) >>
2f7e4042 467
cfd006f0 468Returns true if the given value passes the constraint for the type.
76d37e5a 469
cfd006f0 470=item B<< $constraint->validate($value) >>
2f7e4042 471
cfd006f0 472This is similar to C<check>. However, if the type I<is valid> then the
473method returns an explicit C<undef>. If the type is not valid, we call
474C<< $self->get_message($value) >> internally to generate an error
475message.
76d37e5a 476
952320e0 477=item B<< $constraint->assert_valid($value) >>
478
479Like C<check> and C<validate>, this method checks whether C<$value> is
480valid under the constraint. If it is, it will return true. If it is not,
481an exception will be thrown with the results of
482C<< $self->get_message($value) >>.
483
cfd006f0 484=item B<< $constraint->name >>
2f7e4042 485
cfd006f0 486Returns the type's name, as provided to the constructor.
688fcdda 487
cfd006f0 488=item B<< $constraint->parent >>
2f7e4042 489
cfd006f0 490Returns the type's parent, as provided to the constructor, if any.
4e036ee4 491
cfd006f0 492=item B<< $constraint->has_parent >>
2f7e4042 493
cfd006f0 494Returns true if the type has a parent type.
a27aa600 495
cfd006f0 496=item B<< $constraint->parents >>
2f7e4042 497
cfd006f0 498A synonym for C<parent>. This is useful for polymorphism with types
499that can have more than one parent.
c8cf9aaa 500
cfd006f0 501=item B<< $constraint->constraint >>
c8cf9aaa 502
cfd006f0 503Returns the type's constraint, as provided to the constructor.
9ceb576e 504
cfd006f0 505=item B<< $constraint->get_message($value) >>
4e036ee4 506
cfd006f0 507This generates a method for the given value. If the type does not have
508an explicit message, we generate a default message.
3726f905 509
cfd006f0 510=item B<< $constraint->has_message >>
511
512Returns true if the type has a message.
513
514=item B<< $constraint->message >>
515
516Returns the type's message as a subroutine reference.
517
518=item B<< $constraint->coercion >>
519
520Returns the type's L<Moose::Meta::TypeCoercion> object, if one
521exists.
522
523=item B<< $constraint->has_coercion >>
524
525Returns true if the type has a coercion.
526
527=item B<< $constraint->hand_optimized_type_constraint >>
528
529Returns the type's hand optimized constraint, as provided to the
530constructor via the C<optimized> option.
531
532=item B<< $constraint->has_hand_optimized_type_constraint >>
533
534Returns true if the type has an optimized constraint.
535
536=item B<< $constraint->create_child_type(%options) >>
451c8248 537
cfd006f0 538This returns a new type constraint of the same class using the
539provided C<%options>. The C<parent> option will be the current type.
3726f905 540
cfd006f0 541This method exists so that subclasses of this class can override this
542behavior and change how child types are created.
451c8248 543
544=back
545
4e036ee4 546=head1 BUGS
547
d4048ef3 548See L<Moose/BUGS> for details on reporting bugs.
4e036ee4 549
c8cf9aaa 550=cut