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