make more things immutable
[gitmo/Moose.git] / lib / Moose / Meta / TypeConstraint.pm
CommitLineData
4e036ee4 1
2package Moose::Meta::TypeConstraint;
3
4use strict;
5use warnings;
6use metaclass;
7
b644e331 8use overload '""' => sub { shift->name }, # stringify to tc name
9 fallback => 1;
900466d6 10
c07af9d2 11use Carp 'confess';
dabed765 12use Scalar::Util qw(blessed refaddr);
66811d63 13
0779da92 14use base qw(Class::MOP::Object);
15
a94188ac 16our $VERSION = '0.56';
d44714be 17our $AUTHORITY = 'cpan:STEVAN';
66811d63 18
3726f905 19__PACKAGE__->meta->add_attribute('name' => (reader => 'name'));
20__PACKAGE__->meta->add_attribute('parent' => (
21 reader => 'parent',
22 predicate => 'has_parent',
23));
baf26cc6 24
25my $null_constraint = sub { 1 };
d67145ed 26__PACKAGE__->meta->add_attribute('constraint' => (
8de73ff1 27 reader => 'constraint',
28 writer => '_set_constraint',
baf26cc6 29 default => sub { $null_constraint }
d67145ed 30));
76d37e5a 31__PACKAGE__->meta->add_attribute('message' => (
32 accessor => 'message',
33 predicate => 'has_message'
34));
a27aa600 35__PACKAGE__->meta->add_attribute('coercion' => (
36 accessor => 'coercion',
37 predicate => 'has_coercion'
38));
c8cf9aaa 39__PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
40 init_arg => 'optimized',
41 accessor => 'hand_optimized_type_constraint',
e27dfc11 42 predicate => 'has_hand_optimized_type_constraint',
c8cf9aaa 43));
44
bd72f3c8 45sub parents {
46 my $self;
47 $self->parent;
48}
49
3726f905 50# private accessors
51
52__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
53 accessor => '_compiled_type_constraint',
54 predicate => '_has_compiled_type_constraint'
55));
22aed3c0 56__PACKAGE__->meta->add_attribute('package_defined_in' => (
57 accessor => '_package_defined_in'
58));
59
e27dfc11 60sub new {
a27aa600 61 my $class = shift;
0779da92 62 my $self = $class->_new(@_);
3726f905 63 $self->compile_type_constraint()
64 unless $self->_has_compiled_type_constraint;
66811d63 65 return $self;
66}
67
3726f905 68sub coerce { ((shift)->coercion || confess "Cannot coerce without a type coercion")->coerce(@_) }
42bc21a4 69sub check { $_[0]->_compiled_type_constraint->($_[1]) ? 1 : undef }
e27dfc11 70sub validate {
76d37e5a 71 my ($self, $value) = @_;
72 if ($self->_compiled_type_constraint->($value)) {
73 return undef;
74 }
75 else {
688fcdda 76 $self->get_message($value);
76d37e5a 77 }
78}
79
688fcdda 80sub get_message {
81 my ($self, $value) = @_;
688fcdda 82 if (my $msg = $self->message) {
83 local $_ = $value;
84 return $msg->($value);
85 }
86 else {
d5598ade 87 $value = (defined $value ? overload::StrVal($value) : 'undef');
688fcdda 88 return "Validation failed for '" . $self->name . "' failed with value $value";
89 }
90}
91
3726f905 92## type predicates ...
93
d9e17f80 94sub equals {
95 my ( $self, $type_or_name ) = @_;
96
dabed765 97 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
98
99 return 1 if refaddr($self) == refaddr($other);
100
101 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
102 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
103 }
104
105 return unless $self->constraint == $other->constraint;
106
107 if ( $self->has_parent ) {
108 return unless $other->has_parent;
109 return unless $self->parent->equals( $other->parent );
110 } else {
111 return if $other->has_parent;
112 }
d9e17f80 113
dabed765 114 return 1;
d9e17f80 115}
116
b26e162e 117sub is_a_type_of {
d9e17f80 118 my ($self, $type_or_name) = @_;
119
120 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
121
122 ($self->equals($type) || $self->is_subtype_of($type));
b26e162e 123}
124
cce8198b 125sub is_subtype_of {
d9e17f80 126 my ($self, $type_or_name) = @_;
127
128 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
129
cce8198b 130 my $current = $self;
d9e17f80 131
cce8198b 132 while (my $parent = $current->parent) {
d9e17f80 133 return 1 if $parent->equals($type);
cce8198b 134 $current = $parent;
135 }
d9e17f80 136
cce8198b 137 return 0;
138}
139
3726f905 140## compiling the type constraint
141
142sub compile_type_constraint {
143 my $self = shift;
144 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 145}
146
3726f905 147## type compilers ...
148
149sub _actually_compile_type_constraint {
150 my $self = shift;
e27dfc11 151
3726f905 152 return $self->_compile_hand_optimized_type_constraint
153 if $self->has_hand_optimized_type_constraint;
e27dfc11 154
3726f905 155 my $check = $self->constraint;
156 (defined $check)
e27dfc11 157 || confess "Could not compile type constraint '"
158 . $self->name
3726f905 159 . "' because no constraint check";
e27dfc11 160
3726f905 161 return $self->_compile_subtype($check)
162 if $self->has_parent;
e27dfc11 163
3726f905 164 return $self->_compile_type($check);
165}
166
167sub _compile_hand_optimized_type_constraint {
168 my $self = shift;
e27dfc11 169
3726f905 170 my $type_constraint = $self->hand_optimized_type_constraint;
e27dfc11 171
42bc21a4 172 confess unless ref $type_constraint;
173
174 return $type_constraint;
3726f905 175}
176
177sub _compile_subtype {
178 my ($self, $check) = @_;
e27dfc11 179
baf26cc6 180 # gather all the parent constraintss in order
3726f905 181 my @parents;
baf26cc6 182 my $optimized_parent;
3726f905 183 foreach my $parent ($self->_collect_all_parents) {
baf26cc6 184 # if a parent is optimized, the optimized constraint already includes
185 # all of its parents tcs, so we can break the loop
3726f905 186 if ($parent->has_hand_optimized_type_constraint) {
baf26cc6 187 push @parents => $optimized_parent = $parent->hand_optimized_type_constraint;
e27dfc11 188 last;
3726f905 189 }
190 else {
baf26cc6 191 push @parents => $parent->constraint;
3726f905 192 }
193 }
e27dfc11 194
baf26cc6 195 @parents = grep { $_ != $null_constraint } reverse @parents;
196
197 unless ( @parents ) {
198 return $self->_compile_type($check);
199 } elsif( $optimized_parent and @parents == 1 ) {
200 # the case of just one optimized parent is optimized to prevent
201 # looping and the unnecessary localization
b5981f07 202 if ( $check == $null_constraint ) {
203 return $optimized_parent;
204 } else {
205 return Class::MOP::subname($self->name, sub {
206 return undef unless $optimized_parent->($_[0]);
207 local $_ = $_[0];
208 $check->($_[0]);
209 });
210 }
baf26cc6 211 } else {
212 # general case, check all the constraints, from the first parent to ourselves
b5981f07 213 my @checks = @parents;
214 push @checks, $check if $check != $null_constraint;
baf26cc6 215 return Class::MOP::subname($self->name => sub {
216 local $_ = $_[0];
217 foreach my $check (@checks) {
218 return undef unless $check->($_[0]);
219 }
220 return 1;
221 });
222 }
3726f905 223}
224
225sub _compile_type {
226 my ($self, $check) = @_;
baf26cc6 227
228 return $check if $check == $null_constraint; # Item, Any
229
1b2aea39 230 return Class::MOP::subname($self->name => sub {
6f9ff1af 231 local $_ = $_[0];
baf26cc6 232 $check->($_[0]);
1b2aea39 233 });
3726f905 234}
235
236## other utils ...
237
238sub _collect_all_parents {
239 my $self = shift;
240 my @parents;
241 my $current = $self->parent;
242 while (defined $current) {
243 push @parents => $current;
244 $current = $current->parent;
245 }
246 return @parents;
247}
248
249## this should get deprecated actually ...
250
547dda77 251sub union { Carp::croak "DEPRECATED" }
3726f905 252
4e036ee4 2531;
254
255__END__
256
257=pod
258
259=head1 NAME
260
6ba6d68c 261Moose::Meta::TypeConstraint - The Moose Type Constraint metaclass
4e036ee4 262
263=head1 DESCRIPTION
264
e27dfc11 265For the most part, the only time you will ever encounter an
266instance of this class is if you are doing some serious deep
267introspection. This API should not be considered final, but
268it is B<highly unlikely> that this will matter to a regular
6ba6d68c 269Moose user.
270
e27dfc11 271If you wish to use features at this depth, please come to the
6ba6d68c 272#moose IRC channel on irc.perl.org and we can talk :)
273
4e036ee4 274=head1 METHODS
275
276=over 4
277
278=item B<meta>
279
280=item B<new>
281
d9e17f80 282=item B<equals ($type_name_or_object)>
283
284=item B<is_a_type_of ($type_name_or_object)>
b26e162e 285
e27dfc11 286This checks the current type name, and if it does not match,
b26e162e 287checks if it is a subtype of it.
288
d9e17f80 289=item B<is_subtype_of ($type_name_or_object)>
cce8198b 290
6ba6d68c 291=item B<compile_type_constraint>
292
0a5bd159 293=item B<coerce ($value)>
294
295This will apply the type-coercion if applicable.
296
76d37e5a 297=item B<check ($value)>
298
e27dfc11 299This method will return a true (C<1>) if the C<$value> passes the
76d37e5a 300constraint, and false (C<0>) otherwise.
301
302=item B<validate ($value)>
303
e27dfc11 304This method is similar to C<check>, but it deals with the error
305message. If the C<$value> passes the constraint, C<undef> will be
306returned. If the C<$value> does B<not> pass the constraint, then
307the C<message> will be used to construct a custom error message.
6ba6d68c 308
4e036ee4 309=item B<name>
310
66811d63 311=item B<parent>
312
3726f905 313=item B<has_parent>
314
d9e17f80 315=item B<parents>
316
66811d63 317=item B<constraint>
318
76d37e5a 319=item B<has_message>
320
321=item B<message>
322
688fcdda 323=item B<get_message ($value)>
324
4e036ee4 325=item B<has_coercion>
326
a27aa600 327=item B<coercion>
328
c8cf9aaa 329=item B<hand_optimized_type_constraint>
330
331=item B<has_hand_optimized_type_constraint>
332
4e036ee4 333=back
334
3726f905 335=head2 DEPRECATED METHOD
336
451c8248 337=over 4
338
3726f905 339=item B<union>
340
341This was just bad idea on my part,.. use the L<Moose::Meta::TypeConstraint::Union>
342itself instead.
451c8248 343
344=back
345
4e036ee4 346=head1 BUGS
347
e27dfc11 348All complex software has bugs lurking in it, and this module is no
4e036ee4 349exception. If you find a bug please either email me, or add the bug
350to cpan-RT.
351
352=head1 AUTHOR
353
354Stevan Little E<lt>stevan@iinteractive.comE<gt>
355
356=head1 COPYRIGHT AND LICENSE
357
778db3ac 358Copyright 2006-2008 by Infinity Interactive, Inc.
4e036ee4 359
360L<http://www.iinteractive.com>
361
362This library is free software; you can redistribute it and/or modify
e27dfc11 363it under the same terms as Perl itself.
4e036ee4 364
c8cf9aaa 365=cut