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