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