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