2 package Moose::Meta::TypeConstraint;
8 use overload '0+' => sub { refaddr(shift) }, # id an object
9 '""' => sub { shift->name }, # stringify to tc name
14 use Scalar::Util qw(blessed refaddr);
15 use Sub::Name qw(subname);
18 use base qw(Class::MOP::Object);
20 __PACKAGE__->meta->add_attribute('name' => (
22 Class::MOP::_definition_context(),
24 __PACKAGE__->meta->add_attribute('parent' => (
26 predicate => 'has_parent',
27 Class::MOP::_definition_context(),
30 my $null_constraint = sub { 1 };
31 __PACKAGE__->meta->add_attribute('constraint' => (
32 reader => 'constraint',
33 writer => '_set_constraint',
34 default => sub { $null_constraint },
35 Class::MOP::_definition_context(),
38 __PACKAGE__->meta->add_attribute('message' => (
39 accessor => 'message',
40 predicate => 'has_message',
41 Class::MOP::_definition_context(),
44 __PACKAGE__->meta->add_attribute('_default_message' => (
45 accessor => '_default_message',
46 Class::MOP::_definition_context(),
49 # can't make this a default because it has to close over the type name, and
50 # cmop attributes don't have lazy
51 my $_default_message_generator = sub {
55 # have to load it late like this, since it uses Moose itself
56 my $can_partialdump = try {
57 # versions prior to 0.14 had a potential infinite loop bug
58 Class::MOP::load_class('Devel::PartialDump', { -version => 0.14 });
61 if ($can_partialdump) {
62 $value = Devel::PartialDump->new->dump($value);
65 $value = (defined $value ? overload::StrVal($value) : 'undef');
67 return "Validation failed for '" . $name . "' with value $value";
70 __PACKAGE__->meta->add_attribute('coercion' => (
71 accessor => 'coercion',
72 predicate => 'has_coercion',
73 Class::MOP::_definition_context(),
76 __PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
77 init_arg => 'optimized',
78 accessor => 'hand_optimized_type_constraint',
79 predicate => 'has_hand_optimized_type_constraint',
80 Class::MOP::_definition_context(),
83 __PACKAGE__->meta->add_attribute('inlined' => (
84 init_arg => 'inlined',
85 accessor => 'inlined',
86 predicate => '_has_inlined_type_constraint',
87 Class::MOP::_definition_context(),
90 __PACKAGE__->meta->add_attribute('inline_environment' => (
91 init_arg => 'inline_environment',
92 accessor => '_inline_environment',
93 default => sub { {} },
94 Class::MOP::_definition_context(),
104 __PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
105 accessor => '_compiled_type_constraint',
106 predicate => '_has_compiled_type_constraint',
107 Class::MOP::_definition_context(),
110 __PACKAGE__->meta->add_attribute('package_defined_in' => (
111 accessor => '_package_defined_in',
112 Class::MOP::_definition_context(),
117 my ($first, @rest) = @_;
118 my %args = ref $first ? %$first : $first ? ($first, @rest) : ();
119 $args{name} = $args{name} ? "$args{name}" : "__ANON__";
121 my $self = $class->_new(%args);
122 $self->compile_type_constraint()
123 unless $self->_has_compiled_type_constraint;
124 $self->_default_message($_default_message_generator->($self->name))
125 unless $self->has_message;
134 my $coercion = $self->coercion;
138 Moose->throw_error("Cannot coerce without a type coercion");
141 return $_[0] if $self->check($_[0]);
143 return $coercion->coerce(@_);
149 my $coercion = $self->coercion;
153 Moose->throw_error("Cannot coerce without a type coercion");
156 return $_[0] if $self->check($_[0]);
158 my $result = $coercion->coerce(@_);
160 $self->assert_valid($result);
166 my ($self, @args) = @_;
167 my $constraint_subref = $self->_compiled_type_constraint;
168 return $constraint_subref->(@args) ? 1 : undef;
172 my ($self, $value) = @_;
173 if ($self->_compiled_type_constraint->($value)) {
177 $self->get_message($value);
184 if ( $self->has_parent && $self->constraint == $null_constraint ) {
185 return $self->parent->can_be_inlined;
188 return $self->_has_inlined_type_constraint;
194 unless ( $self->can_be_inlined ) {
196 Moose->throw_error( 'Cannot inline a type constraint check for ' . $self->name );
199 if ( $self->has_parent && $self->constraint == $null_constraint ) {
200 return $self->parent->_inline_check(@_);
203 return '( do { ' . $self->inlined->( $self, @_ ) . ' } )';
206 sub inline_environment {
209 if ( $self->has_parent && $self->constraint == $null_constraint ) {
210 return $self->parent->inline_environment;
213 return $self->_inline_environment;
217 my ($self, $value) = @_;
219 my $error = $self->validate($value);
220 return 1 if ! defined $error;
223 Moose->throw_error($error);
227 my ($self, $value) = @_;
228 my $msg = $self->message || $self->_default_message;
230 return $msg->($value);
233 ## type predicates ...
236 my ( $self, $type_or_name ) = @_;
238 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
240 return 1 if $self == $other;
242 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
243 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
246 return unless $self->constraint == $other->constraint;
248 if ( $self->has_parent ) {
249 return unless $other->has_parent;
250 return unless $self->parent->equals( $other->parent );
252 return if $other->has_parent;
259 my ($self, $type_or_name) = @_;
261 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
263 ($self->equals($type) || $self->is_subtype_of($type));
267 my ($self, $type_or_name) = @_;
269 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
273 while (my $parent = $current->parent) {
274 return 1 if $parent->equals($type);
281 ## compiling the type constraint
283 sub compile_type_constraint {
285 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
288 ## type compilers ...
290 sub _actually_compile_type_constraint {
293 return $self->_compile_hand_optimized_type_constraint
294 if $self->has_hand_optimized_type_constraint;
296 if ( $self->can_be_inlined ) {
298 source => 'sub { ' . $self->_inline_check('$_[0]') . ' }',
299 environment => $self->inline_environment,
303 my $check = $self->constraint;
304 unless ( defined $check ) {
306 Moose->throw_error( "Could not compile type constraint '"
308 . "' because no constraint check" );
311 return $self->_compile_subtype($check)
312 if $self->has_parent;
314 return $self->_compile_type($check);
317 sub _compile_hand_optimized_type_constraint {
320 my $type_constraint = $self->hand_optimized_type_constraint;
322 unless ( ref $type_constraint ) {
324 Moose->throw_error("Hand optimized type constraint is not a code reference");
327 return $type_constraint;
330 sub _compile_subtype {
331 my ($self, $check) = @_;
333 # gather all the parent constraintss in order
335 my $optimized_parent;
336 foreach my $parent ($self->_collect_all_parents) {
337 # if a parent is optimized, the optimized constraint already includes
338 # all of its parents tcs, so we can break the loop
339 if ($parent->has_hand_optimized_type_constraint) {
340 push @parents => $optimized_parent = $parent->hand_optimized_type_constraint;
344 push @parents => $parent->constraint;
348 @parents = grep { $_ != $null_constraint } reverse @parents;
350 unless ( @parents ) {
351 return $self->_compile_type($check);
352 } elsif( $optimized_parent and @parents == 1 ) {
353 # the case of just one optimized parent is optimized to prevent
354 # looping and the unnecessary localization
355 if ( $check == $null_constraint ) {
356 return $optimized_parent;
358 return subname($self->name, sub {
359 return undef unless $optimized_parent->($_[0]);
366 # general case, check all the constraints, from the first parent to ourselves
367 my @checks = @parents;
368 push @checks, $check if $check != $null_constraint;
369 return subname($self->name => sub {
372 foreach my $check (@checks) {
373 return undef unless $check->(@args);
381 my ($self, $check) = @_;
383 return $check if $check == $null_constraint; # Item, Any
385 return subname($self->name => sub {
394 sub _collect_all_parents {
397 my $current = $self->parent;
398 while (defined $current) {
399 push @parents => $current;
400 $current = $current->parent;
405 sub create_child_type {
406 my ($self, %opts) = @_;
407 my $class = ref $self;
408 return $class->new(%opts, parent => $self);
413 # ABSTRACT: The Moose Type Constraint metaclass
421 This class represents a single type constraint. Moose's built-in type
422 constraints, as well as constraints you define, are all stored in a
423 L<Moose::Meta::TypeConstraint::Registry> object as objects of this
428 C<Moose::Meta::TypeConstraint> is a subclass of L<Class::MOP::Object>.
434 =item B<< Moose::Meta::TypeConstraint->new(%options) >>
436 This creates a new type constraint based on the provided C<%options>:
442 The constraint name. If a name is not provided, it will be set to
447 A C<Moose::Meta::TypeConstraint> object which is the parent type for
448 the type being created. This is optional.
452 This is the subroutine reference that implements the actual constraint
453 check. This defaults to a subroutine which always returns true.
457 A subroutine reference which is used to generate an error message when
458 the constraint fails. This is optional.
462 A L<Moose::Meta::TypeCoercion> object representing the coercions to
463 the type. This is optional.
467 A subroutine which returns a string suitable for inlining this type
468 constraint. It will be called as a method on the type constraint object, and
469 will receive a single additional parameter, a variable name to be tested
470 (usually C<"$_"> or C<"$_[0]">.
474 =item * inline_environment
476 A hash reference of variables to close over. The keys are variables names, and
477 the values are I<references> to the variables.
481 B<This option is deprecated.>
483 This is a variant of the C<constraint> parameter that is somehow
484 optimized. Typically, this means incorporating both the type's
485 constraint and all of its parents' constraints into a single
486 subroutine reference.
490 =item B<< $constraint->equals($type_name_or_object) >>
492 Returns true if the supplied name or type object is the same as the
495 =item B<< $constraint->is_subtype_of($type_name_or_object) >>
497 Returns true if the supplied name or type object is a parent of the
500 =item B<< $constraint->is_a_type_of($type_name_or_object) >>
502 Returns true if the given type is the same as the current type, or is
503 a parent of the current type. This is a shortcut for checking
504 C<equals> and C<is_subtype_of>.
506 =item B<< $constraint->coerce($value) >>
508 This will attempt to coerce the value to the type. If the type does not
509 have any defined coercions this will throw an error.
511 If no coercion can produce a value matching C<$constraint>, the original
514 =item B<< $constraint->assert_coerce($value) >>
516 This method behaves just like C<coerce>, but if the result is not valid
517 according to C<$constraint>, an error is thrown.
519 =item B<< $constraint->check($value) >>
521 Returns true if the given value passes the constraint for the type.
523 =item B<< $constraint->validate($value) >>
525 This is similar to C<check>. However, if the type I<is valid> then the
526 method returns an explicit C<undef>. If the type is not valid, we call
527 C<< $self->get_message($value) >> internally to generate an error
530 =item B<< $constraint->assert_valid($value) >>
532 Like C<check> and C<validate>, this method checks whether C<$value> is
533 valid under the constraint. If it is, it will return true. If it is not,
534 an exception will be thrown with the results of
535 C<< $self->get_message($value) >>.
537 =item B<< $constraint->name >>
539 Returns the type's name, as provided to the constructor.
541 =item B<< $constraint->parent >>
543 Returns the type's parent, as provided to the constructor, if any.
545 =item B<< $constraint->has_parent >>
547 Returns true if the type has a parent type.
549 =item B<< $constraint->parents >>
551 A synonym for C<parent>. This is useful for polymorphism with types
552 that can have more than one parent.
554 =item B<< $constraint->constraint >>
556 Returns the type's constraint, as provided to the constructor.
558 =item B<< $constraint->get_message($value) >>
560 This generates a method for the given value. If the type does not have
561 an explicit message, we generate a default message.
563 =item B<< $constraint->has_message >>
565 Returns true if the type has a message.
567 =item B<< $constraint->message >>
569 Returns the type's message as a subroutine reference.
571 =item B<< $constraint->coercion >>
573 Returns the type's L<Moose::Meta::TypeCoercion> object, if one
576 =item B<< $constraint->has_coercion >>
578 Returns true if the type has a coercion.
580 =item B<< $constraint->can_be_inlined >>
582 Returns true if this type constraint can be inlined. A type constraint which
583 subtypes an inlinable constraint and does not add an additional constraint
584 "inherits" its parent type's inlining.
586 =item B<< $constraint->hand_optimized_type_constraint >>
588 B<This method is deprecated.>
590 Returns the type's hand optimized constraint, as provided to the
591 constructor via the C<optimized> option.
593 =item B<< $constraint->has_hand_optimized_type_constraint >>
595 B<This method is deprecated.>
597 Returns true if the type has an optimized constraint.
599 =item B<< $constraint->create_child_type(%options) >>
601 This returns a new type constraint of the same class using the
602 provided C<%options>. The C<parent> option will be the current type.
604 This method exists so that subclasses of this class can override this
605 behavior and change how child types are created.
611 See L<Moose/BUGS> for details on reporting bugs.