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