1 package Mouse::Meta::TypeConstraint;
2 use Mouse::Util qw(:meta); # enables strict and warnings
5 'bool' => sub { 1 }, # always true
7 '""' => sub { $_[0]->name }, # stringify to tc name
9 '|' => sub { # or-combination
10 require Mouse::Util::TypeConstraints;
11 return Mouse::Util::TypeConstraints::find_or_parse_type_constraint(
20 my $null_check = sub { 1 };
23 my($class, %args) = @_;
25 $args{name} = '__ANON__' if !defined $args{name};
27 my $check = delete $args{optimized};
29 if($args{_compiled_type_constraint}){
30 Carp::cluck("'_compiled_type_constraint' has been deprecated, use 'optimized' instead")
33 $check = $args{_compiled_type_constraint};
37 $args{hand_optimized_type_constraint} = $check;
38 $args{compiled_type_constraint} = $check;
41 $check = $args{constraint};
43 if(defined($check) && ref($check) ne 'CODE'){
44 Carp::confess("Constraint for $args{name} is not a CODE reference");
47 $args{package_defined_in} ||= caller;
49 my $self = bless \%args, $class;
50 $self->compile_type_constraint() if !$self->{hand_optimized_type_constraint};
52 if($self->{type_constraints}){ # Union
54 foreach my $type(@{$self->{type_constraints}}){
55 if($type->has_coercion){
56 push @coercions, $type;
60 $self->{_compiled_type_coercion} = sub {
62 foreach my $type(@coercions){
63 my $value = $type->coerce($thing);
64 return $value if $self->check($value);
74 sub create_child_type{
77 return ref($self)->new(
78 # a child inherits its parent's attributes
81 # but does not inherit 'compiled_type_constraint' and 'hand_optimized_type_constraint'
82 compiled_type_constraint => undef,
83 hand_optimized_type_constraint => undef,
85 # and is given child-specific args, of course.
94 sub compile_type_constraint{
99 for(my $parent = $self->{parent}; defined $parent; $parent = $parent->{parent}){
100 if($parent->{hand_optimized_type_constraint}){
101 unshift @checks, $parent->{hand_optimized_type_constraint};
102 last; # a hand optimized constraint must include all the parents
104 elsif($parent->{constraint}){
105 unshift @checks, $parent->{constraint};
110 if($self->{constraint}){
111 push @checks, $self->{constraint};
114 if($self->{type_constraints}){ # Union
115 my @types = map{ $_->{compiled_type_constraint} } @{ $self->{type_constraints} };
117 foreach my $c(@types){
118 return 1 if $c->($_[0]);
125 $self->{compiled_type_constraint} = $null_check;
128 $self->{compiled_type_constraint} = sub{
131 foreach my $c(@checks){
132 return undef if !$c->(@args);
140 sub _add_type_coercions{
143 my $coercions = ($self->{_coercion_map} ||= []);
144 my %has = map{ $_->[0] => undef } @{$coercions};
146 for(my $i = 0; $i < @_; $i++){
148 my $action = $_[++$i];
150 if(exists $has{$from}){
151 Carp::confess("A coercion action already exists for '$from'");
154 my $type = Mouse::Util::TypeConstraints::find_or_parse_type_constraint($from)
155 or Carp::confess("Could not find the type constraint ($from) to coerce from");
157 push @{$coercions}, [ $type => $action ];
161 if(exists $self->{type_constraints}){ # union type
162 Carp::confess("Cannot add additional type coercions to Union types");
165 $self->{_compiled_type_coercion} = sub {
167 foreach my $pair (@{$coercions}) {
\r
168 #my ($constraint, $converter) = @$pair;
\r
169 if ($pair->[0]->check($thing)) {
\r
171 return $pair->[1]->($thing);
182 return $self->_compiled_type_constraint->(@_);
188 return $_[0] if $self->_compiled_type_constraint->(@_);
190 my $coercion = $self->_compiled_type_coercion;
191 return $coercion ? $coercion->(@_) : $_[0];
195 my ($self, $value) = @_;
196 if ( my $msg = $self->message ) {
198 return $msg->($value);
201 $value = ( defined $value ? overload::StrVal($value) : 'undef' );
202 return "Validation failed for '$self' failed with value $value";
207 my($self, $other) = @_;
209 # ->is_a_type_of('__ANON__') is always false
210 return 0 if !ref($other) && $other eq '__ANON__';
212 (my $other_name = $other) =~ s/\s+//g;
214 return 1 if $self->name eq $other_name;
216 if(exists $self->{type_constraints}){ # union
217 foreach my $type(@{$self->{type_constraints}}){
218 return 1 if $type->name eq $other_name;
222 for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
223 return 1 if $parent->name eq $other_name;
229 # See also Moose::Meta::TypeConstraint::Parameterizable
231 my($self, $param, $name) = @_;
234 require Mouse::Util::TypeConstraints;
235 $param = Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($param);
238 $name ||= sprintf '%s[%s]', $self->name, $param->name;
240 my $generator = $self->{constraint_generator}
241 || Carp::confess("The $name constraint cannot be used, because $param doesn't subtype from a parameterizable type");
243 return Mouse::Meta::TypeConstraint->new(
247 constraint => $generator->($param), # must be 'constraint', not 'optimized'
249 type => 'Parameterized',
258 Mouse::Meta::TypeConstraint - The Mouse Type Constraint metaclass
262 This document describes Mouse version 0.40_03
266 For the most part, the only time you will ever encounter an
267 instance of this class is if you are doing some serious deep
268 introspection. This API should not be considered final, but
269 it is B<highly unlikely> that this will matter to a regular
286 L<Moose::Meta::TypeConstraint>