Add comment to 810-isa-or.t
[gitmo/Mouse.git] / lib / Mouse / Meta / TypeConstraint.pm
CommitLineData
684db121 1package Mouse::Meta::TypeConstraint;
bc69ee88 2use Mouse::Util qw(:meta); # enables strict and warnings
9c85e9dc 3
f5ee065f 4use overload
93540011 5 '""' => sub { $_[0]->name }, # stringify to tc name
8adc0b23 6
7 '|' => sub { # or-combination
8 require Mouse::Util::TypeConstraints;
9 return Mouse::Util::TypeConstraints::find_or_parse_type_constraint(
10 "$_[0] | $_[1]",
11 );
12 },
13
f5ee065f 14 fallback => 1;
684db121 15
ca352580 16use Carp ();
6d28c5cf 17
f5ee065f 18my $null_check = sub { 1 };
19
684db121 20sub new {
f5ee065f 21 my($class, %args) = @_;
22
23 $args{name} = '__ANON__' if !defined $args{name};
684db121 24
3b89ea91 25 my $check = delete $args{optimized};
26
27 if($args{_compiled_type_constraint}){
0126c27c 28 Carp::cluck("'_compiled_type_constraint' has been deprecated, use 'optimized' instead")
29 if _MOUSE_VERBOSE;
3b89ea91 30
0126c27c 31 $check = $args{_compiled_type_constraint};
3b89ea91 32 }
33
34 if($check){
35 $args{hand_optimized_type_constraint} = $check;
36 $args{compiled_type_constraint} = $check;
37 }
38
39 $check = $args{constraint};
f5ee065f 40
f5ee065f 41 if(defined($check) && ref($check) ne 'CODE'){
ca352580 42 Carp::confess("Constraint for $args{name} is not a CODE reference");
f5ee065f 43 }
44
3b89ea91 45 $args{package_defined_in} ||= caller;
46
f5ee065f 47 my $self = bless \%args, $class;
3b89ea91 48 $self->compile_type_constraint() if !$self->{hand_optimized_type_constraint};
f5ee065f 49
ffbbf459 50 if($self->{type_constraints}){ # Union
51 my @coercions;
52 foreach my $type(@{$self->{type_constraints}}){
53 if($type->has_coercion){
54 push @coercions, $type;
55 }
56 }
57 if(@coercions){
58 $self->{_compiled_type_coercion} = sub {
59 my($thing) = @_;
60 foreach my $type(@coercions){
61 my $value = $type->coerce($thing);
62 return $value if $self->check($value);
63 }
64 return $thing;
65 };
66 }
67 }
68
f5ee065f 69 return $self;
70}
71
72sub create_child_type{
73 my $self = shift;
e98220ab 74 # XXX: FIXME
75 return ref($self)->new(
3b89ea91 76 # a child inherits its parent's attributes
77 %{$self},
78
79 # but does not inherit 'compiled_type_constraint' and 'hand_optimized_type_constraint'
80 compiled_type_constraint => undef,
81 hand_optimized_type_constraint => undef,
82
83 # and is given child-specific args, of course.
84 @_,
85
86 # and its parent
87 parent => $self,
e98220ab 88 );
684db121 89}
90
f5ee065f 91
3b89ea91 92sub compile_type_constraint{
f5ee065f 93 my($self) = @_;
94
95 # add parents first
96 my @checks;
97 for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
3b89ea91 98 if($parent->{hand_optimized_type_constraint}){
c4b28dd2 99 unshift @checks, $parent->{hand_optimized_type_constraint};
3b89ea91 100 last; # a hand optimized constraint must include all the parents
101 }
102 elsif($parent->{constraint}){
c4b28dd2 103 unshift @checks, $parent->{constraint};
f5ee065f 104 }
105 }
3b89ea91 106
f5ee065f 107 # then add child
108 if($self->{constraint}){
109 push @checks, $self->{constraint};
110 }
111
3b89ea91 112 if($self->{type_constraints}){ # Union
113 my @types = map{ $_->_compiled_type_constraint } @{ $self->{type_constraints} };
114 push @checks, sub{
115 foreach my $c(@types){
116 return 1 if $c->($_[0]);
117 }
118 return 0;
119 };
120 }
121
f5ee065f 122 if(@checks == 0){
3b89ea91 123 $self->{compiled_type_constraint} = $null_check;
f5ee065f 124 }
125 elsif(@checks == 1){
126 my $c = $checks[0];
3b89ea91 127 $self->{compiled_type_constraint} = sub{
f5ee065f 128 my(@args) = @_;
129 local $_ = $args[0];
130 return $c->(@args);
131 };
132 }
133 else{
3b89ea91 134 $self->{compiled_type_constraint} = sub{
f5ee065f 135 my(@args) = @_;
136 local $_ = $args[0];
137 foreach my $c(@checks){
138 return undef if !$c->(@args);
139 }
140 return 1;
141 };
142 }
3b89ea91 143 return;
90fe520e 144}
145
ffbbf459 146sub _add_type_coercions{
147 my $self = shift;
148
149 my $coercions = ($self->{_coercion_map} ||= []);
150 my %has = map{ $_->[0] => undef } @{$coercions};
151
152 for(my $i = 0; $i < @_; $i++){
153 my $from = $_[ $i];
154 my $action = $_[++$i];
155
156 if(exists $has{$from}){
ca352580 157 Carp::confess("A coercion action already exists for '$from'");
ffbbf459 158 }
159
160 my $type = Mouse::Util::TypeConstraints::find_or_parse_type_constraint($from)
ca352580 161 or Carp::confess("Could not find the type constraint ($from) to coerce from");
ffbbf459 162
163 push @{$coercions}, [ $type => $action ];
164 }
165
166 # compile
167 if(exists $self->{type_constraints}){ # union type
ca352580 168 Carp::confess("Cannot add additional type coercions to Union types");
ffbbf459 169 }
170 else{
171 $self->{_compiled_type_coercion} = sub {
172 my($thing) = @_;\r
173 foreach my $pair (@{$coercions}) {\r
174 #my ($constraint, $converter) = @$pair;\r
175 if ($pair->[0]->check($thing)) {\r
176 local $_ = $thing;
177 return $pair->[1]->($thing);
178 }\r
179 }\r
180 return $thing;\r
181 };
182 }
183 return;
184}
185
feb0e21b 186sub check {
187 my $self = shift;
ffbbf459 188 return $self->_compiled_type_constraint->(@_);
189}
190
191sub coerce {
192 my $self = shift;
ffbbf459 193
194 return $_[0] if $self->_compiled_type_constraint->(@_);
195
93540011 196 my $coercion = $self->_compiled_type_coercion;
197 return $coercion ? $coercion->(@_) : $_[0];
feb0e21b 198}
199
200sub get_message {
201 my ($self, $value) = @_;
202 if ( my $msg = $self->message ) {
203 local $_ = $value;
204 return $msg->($value);
205 }
206 else {
207 $value = ( defined $value ? overload::StrVal($value) : 'undef' );
208 return "Validation failed for '$self' failed with value $value";
209 }
210}
211
212sub is_a_type_of{
213 my($self, $other) = @_;
214
215 # ->is_a_type_of('__ANON__') is always false
ca352580 216 return 0 if !ref($other) && $other eq '__ANON__';
feb0e21b 217
218 (my $other_name = $other) =~ s/\s+//g;
219
220 return 1 if $self->name eq $other_name;
221
222 if(exists $self->{type_constraints}){ # union
223 foreach my $type(@{$self->{type_constraints}}){
224 return 1 if $type->name eq $other_name;
225 }
226 }
227
228 for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
229 return 1 if $parent->name eq $other_name;
230 }
231
232 return 0;
233}
234
235
684db121 2361;
237__END__
238
239=head1 NAME
240
1820fffe 241Mouse::Meta::TypeConstraint - The Mouse Type Constraint metaclass
684db121 242
a25ca8d6 243=head1 VERSION
244
065f79e7 245This document describes Mouse version 0.40_02
a25ca8d6 246
684db121 247=head1 DESCRIPTION
248
249For the most part, the only time you will ever encounter an
250instance of this class is if you are doing some serious deep
251introspection. This API should not be considered final, but
252it is B<highly unlikely> that this will matter to a regular
253Mouse user.
254
255Don't use this.
256
257=head1 METHODS
258
259=over 4
260
261=item B<new>
262
263=item B<name>
264
265=back
266
1820fffe 267=head1 SEE ALSO
268
269L<Moose::Meta::TypeConstraint>
270
684db121 271=cut
272