Fix tool/create-moose-compatibility-tests.pl to replace class_of()
[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
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
4c99ea95 84sub name;
85sub parent;
86sub message;
87sub has_coercion;
88sub _compiled_type_constraint;
89sub _compiled_type_coercion;
90
91sub compile_type_constraint;
92
ffbbf459 93sub _add_type_coercions{
94 my $self = shift;
95
96 my $coercions = ($self->{_coercion_map} ||= []);
97 my %has = map{ $_->[0] => undef } @{$coercions};
98
99 for(my $i = 0; $i < @_; $i++){
100 my $from = $_[ $i];
101 my $action = $_[++$i];
102
103 if(exists $has{$from}){
ca352580 104 Carp::confess("A coercion action already exists for '$from'");
ffbbf459 105 }
106
107 my $type = Mouse::Util::TypeConstraints::find_or_parse_type_constraint($from)
ca352580 108 or Carp::confess("Could not find the type constraint ($from) to coerce from");
ffbbf459 109
110 push @{$coercions}, [ $type => $action ];
111 }
112
113 # compile
114 if(exists $self->{type_constraints}){ # union type
ca352580 115 Carp::confess("Cannot add additional type coercions to Union types");
ffbbf459 116 }
117 else{
118 $self->{_compiled_type_coercion} = sub {
278448b8 119 my($thing) = @_;
120 foreach my $pair (@{$coercions}) {
121 #my ($constraint, $converter) = @$pair;
122 if ($pair->[0]->check($thing)) {
ffbbf459 123 local $_ = $thing;
124 return $pair->[1]->($thing);
278448b8 125 }
126 }
127 return $thing;
ffbbf459 128 };
129 }
130 return;
131}
132
feb0e21b 133sub check {
134 my $self = shift;
ffbbf459 135 return $self->_compiled_type_constraint->(@_);
136}
137
138sub coerce {
139 my $self = shift;
ffbbf459 140
de0d4152 141 my $coercion = $self->_compiled_type_coercion;
142 if(!$coercion){
143 Carp::confess("Cannot coerce without a type coercion");
144 }
145
ffbbf459 146 return $_[0] if $self->_compiled_type_constraint->(@_);
147
de0d4152 148 return $coercion->(@_);
feb0e21b 149}
150
151sub get_message {
152 my ($self, $value) = @_;
153 if ( my $msg = $self->message ) {
154 local $_ = $value;
155 return $msg->($value);
156 }
157 else {
158 $value = ( defined $value ? overload::StrVal($value) : 'undef' );
159 return "Validation failed for '$self' failed with value $value";
160 }
161}
162
163sub is_a_type_of{
164 my($self, $other) = @_;
165
166 # ->is_a_type_of('__ANON__') is always false
ca352580 167 return 0 if !ref($other) && $other eq '__ANON__';
feb0e21b 168
169 (my $other_name = $other) =~ s/\s+//g;
170
171 return 1 if $self->name eq $other_name;
172
173 if(exists $self->{type_constraints}){ # union
174 foreach my $type(@{$self->{type_constraints}}){
175 return 1 if $type->name eq $other_name;
176 }
177 }
178
179 for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
180 return 1 if $parent->name eq $other_name;
181 }
182
183 return 0;
184}
185
b4d791ba 186# See also Moose::Meta::TypeConstraint::Parameterizable
187sub parameterize{
188 my($self, $param, $name) = @_;
189
190 if(!ref $param){
191 require Mouse::Util::TypeConstraints;
192 $param = Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($param);
193 }
194
195 $name ||= sprintf '%s[%s]', $self->name, $param->name;
196
197 my $generator = $self->{constraint_generator}
198 || Carp::confess("The $name constraint cannot be used, because $param doesn't subtype from a parameterizable type");
199
200 return Mouse::Meta::TypeConstraint->new(
5a363f78 201 name => $name,
202 parent => $self,
203 parameter => $param,
204 constraint => $generator->($param), # must be 'constraint', not 'optimized'
b4d791ba 205
5a363f78 206 type => 'Parameterized',
b4d791ba 207 );
208}
feb0e21b 209
684db121 2101;
211__END__
212
213=head1 NAME
214
1820fffe 215Mouse::Meta::TypeConstraint - The Mouse Type Constraint metaclass
684db121 216
a25ca8d6 217=head1 VERSION
218
e9148d13 219This document describes Mouse version 0.50
a25ca8d6 220
684db121 221=head1 DESCRIPTION
222
223For the most part, the only time you will ever encounter an
224instance of this class is if you are doing some serious deep
225introspection. This API should not be considered final, but
226it is B<highly unlikely> that this will matter to a regular
227Mouse user.
228
229Don't use this.
230
231=head1 METHODS
232
233=over 4
234
235=item B<new>
236
237=item B<name>
238
239=back
240
1820fffe 241=head1 SEE ALSO
242
243L<Moose::Meta::TypeConstraint>
244
684db121 245=cut
246