make a dev version to go with the CMOP release
[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
dabed765 11use Scalar::Util qw(blessed refaddr);
66811d63 12
e606ae5f 13use base qw(Class::MOP::Object);
14
a532c4ac 15our $VERSION = '0.71_01';
e606ae5f 16$VERSION = eval $VERSION;
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));
70ea9161 39
c8cf9aaa 40__PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
41 init_arg => 'optimized',
42 accessor => 'hand_optimized_type_constraint',
e27dfc11 43 predicate => 'has_hand_optimized_type_constraint',
c8cf9aaa 44));
45
bd72f3c8 46sub parents {
47 my $self;
48 $self->parent;
49}
50
3726f905 51# private accessors
52
53__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
54 accessor => '_compiled_type_constraint',
55 predicate => '_has_compiled_type_constraint'
56));
22aed3c0 57__PACKAGE__->meta->add_attribute('package_defined_in' => (
58 accessor => '_package_defined_in'
59));
60
e27dfc11 61sub new {
a27aa600 62 my $class = shift;
8534c69a 63 my ($first, @rest) = @_;
64 my %args = ref $first ? %$first : $first ? ($first, @rest) : ();
65 $args{name} = $args{name} ? "$args{name}" : "__ANON__";
70ea9161 66
8534c69a 67 my $self = $class->_new(%args);
3726f905 68 $self->compile_type_constraint()
69 unless $self->_has_compiled_type_constraint;
66811d63 70 return $self;
71}
72
8534c69a 73
74
70ea9161 75sub coerce {
76 my $self = shift;
77
78 my $coercion = $self->coercion;
79
80 unless ($coercion) {
81 require Moose;
82 Moose->throw_error("Cannot coerce without a type coercion");
83 }
84
85 return $coercion->coerce(@_);
86}
a1257460 87
88sub check {
89 my ($self, @args) = @_;
90 my $constraint_subref = $self->_compiled_type_constraint;
91 return $constraint_subref->(@args) ? 1 : undef;
92}
93
e27dfc11 94sub validate {
76d37e5a 95 my ($self, $value) = @_;
96 if ($self->_compiled_type_constraint->($value)) {
97 return undef;
98 }
99 else {
688fcdda 100 $self->get_message($value);
76d37e5a 101 }
102}
103
688fcdda 104sub get_message {
105 my ($self, $value) = @_;
688fcdda 106 if (my $msg = $self->message) {
107 local $_ = $value;
108 return $msg->($value);
109 }
110 else {
e606ae5f 111 $value = (defined $value ? overload::StrVal($value) : 'undef');
688fcdda 112 return "Validation failed for '" . $self->name . "' failed with value $value";
113 }
114}
115
3726f905 116## type predicates ...
117
d9e17f80 118sub equals {
119 my ( $self, $type_or_name ) = @_;
120
e606ae5f 121 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
dabed765 122
123 return 1 if refaddr($self) == refaddr($other);
124
125 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
126 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
127 }
128
129 return unless $self->constraint == $other->constraint;
130
131 if ( $self->has_parent ) {
132 return unless $other->has_parent;
133 return unless $self->parent->equals( $other->parent );
134 } else {
135 return if $other->has_parent;
136 }
d9e17f80 137
dabed765 138 return 1;
d9e17f80 139}
140
b26e162e 141sub is_a_type_of {
d9e17f80 142 my ($self, $type_or_name) = @_;
143
e606ae5f 144 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 145
146 ($self->equals($type) || $self->is_subtype_of($type));
b26e162e 147}
148
cce8198b 149sub is_subtype_of {
d9e17f80 150 my ($self, $type_or_name) = @_;
151
e606ae5f 152 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 153
cce8198b 154 my $current = $self;
d9e17f80 155
cce8198b 156 while (my $parent = $current->parent) {
d9e17f80 157 return 1 if $parent->equals($type);
cce8198b 158 $current = $parent;
159 }
d9e17f80 160
cce8198b 161 return 0;
162}
163
3726f905 164## compiling the type constraint
165
166sub compile_type_constraint {
167 my $self = shift;
168 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 169}
170
3726f905 171## type compilers ...
172
173sub _actually_compile_type_constraint {
174 my $self = shift;
e27dfc11 175
3726f905 176 return $self->_compile_hand_optimized_type_constraint
177 if $self->has_hand_optimized_type_constraint;
e27dfc11 178
3726f905 179 my $check = $self->constraint;
70ea9161 180 unless ( defined $check ) {
181 require Moose;
182 Moose->throw_error( "Could not compile type constraint '"
e27dfc11 183 . $self->name
70ea9161 184 . "' because no constraint check" );
185 }
e27dfc11 186
3726f905 187 return $self->_compile_subtype($check)
188 if $self->has_parent;
e27dfc11 189
3726f905 190 return $self->_compile_type($check);
191}
192
193sub _compile_hand_optimized_type_constraint {
194 my $self = shift;
e27dfc11 195
3726f905 196 my $type_constraint = $self->hand_optimized_type_constraint;
e27dfc11 197
70ea9161 198 unless ( ref $type_constraint ) {
199 require Moose;
200 Carp::confess ("Hand optimized type constraint for " . $self->name . " is not a code reference");
201 Moose->throw_error("Hand optimized type constraint is not a code reference");
202 }
42bc21a4 203
204 return $type_constraint;
3726f905 205}
206
207sub _compile_subtype {
208 my ($self, $check) = @_;
e27dfc11 209
baf26cc6 210 # gather all the parent constraintss in order
3726f905 211 my @parents;
baf26cc6 212 my $optimized_parent;
3726f905 213 foreach my $parent ($self->_collect_all_parents) {
baf26cc6 214 # if a parent is optimized, the optimized constraint already includes
215 # all of its parents tcs, so we can break the loop
3726f905 216 if ($parent->has_hand_optimized_type_constraint) {
baf26cc6 217 push @parents => $optimized_parent = $parent->hand_optimized_type_constraint;
e27dfc11 218 last;
3726f905 219 }
220 else {
baf26cc6 221 push @parents => $parent->constraint;
3726f905 222 }
223 }
e27dfc11 224
baf26cc6 225 @parents = grep { $_ != $null_constraint } reverse @parents;
226
227 unless ( @parents ) {
228 return $self->_compile_type($check);
229 } elsif( $optimized_parent and @parents == 1 ) {
230 # the case of just one optimized parent is optimized to prevent
231 # looping and the unnecessary localization
b5981f07 232 if ( $check == $null_constraint ) {
233 return $optimized_parent;
234 } else {
235 return Class::MOP::subname($self->name, sub {
236 return undef unless $optimized_parent->($_[0]);
a1257460 237 my (@args) = @_;
238 local $_ = $args[0];
239 $check->(@args);
b5981f07 240 });
241 }
baf26cc6 242 } else {
243 # general case, check all the constraints, from the first parent to ourselves
b5981f07 244 my @checks = @parents;
245 push @checks, $check if $check != $null_constraint;
baf26cc6 246 return Class::MOP::subname($self->name => sub {
a1257460 247 my (@args) = @_;
248 local $_ = $args[0];
baf26cc6 249 foreach my $check (@checks) {
a1257460 250 return undef unless $check->(@args);
baf26cc6 251 }
252 return 1;
253 });
254 }
3726f905 255}
256
257sub _compile_type {
258 my ($self, $check) = @_;
baf26cc6 259
260 return $check if $check == $null_constraint; # Item, Any
261
1b2aea39 262 return Class::MOP::subname($self->name => sub {
a1257460 263 my (@args) = @_;
264 local $_ = $args[0];
265 $check->(@args);
1b2aea39 266 });
3726f905 267}
268
269## other utils ...
270
271sub _collect_all_parents {
272 my $self = shift;
273 my @parents;
274 my $current = $self->parent;
275 while (defined $current) {
276 push @parents => $current;
277 $current = $current->parent;
278 }
279 return @parents;
280}
281
85a9908f 282sub create_child_type {
9ceb576e 283 my ($self, %opts) = @_;
284 my $class = ref $self;
285 return $class->new(%opts, parent => $self);
286}
287
3726f905 288## this should get deprecated actually ...
289
547dda77 290sub union { Carp::croak "DEPRECATED" }
3726f905 291
4e036ee4 2921;
293
294__END__
295
296=pod
297
298=head1 NAME
299
6ba6d68c 300Moose::Meta::TypeConstraint - The Moose Type Constraint metaclass
4e036ee4 301
302=head1 DESCRIPTION
303
e27dfc11 304For the most part, the only time you will ever encounter an
305instance of this class is if you are doing some serious deep
306introspection. This API should not be considered final, but
307it is B<highly unlikely> that this will matter to a regular
6ba6d68c 308Moose user.
309
e27dfc11 310If you wish to use features at this depth, please come to the
6ba6d68c 311#moose IRC channel on irc.perl.org and we can talk :)
312
4e036ee4 313=head1 METHODS
314
315=over 4
316
317=item B<meta>
318
319=item B<new>
320
d9e17f80 321=item B<equals ($type_name_or_object)>
322
e606ae5f 323This checks the current type against the supplied type (only).
324Returns false if the two types are not equal. It also returns false if
325you provide the type as a name, and the type name isn't found in the
326type registry.
327
d9e17f80 328=item B<is_a_type_of ($type_name_or_object)>
b26e162e 329
e606ae5f 330This checks the current type against the supplied type, or if the
331current type is a sub-type of the type name or object supplied. It
332also returns false if you provide the type as a name, and the type
333name isn't found in the type registry.
b26e162e 334
d9e17f80 335=item B<is_subtype_of ($type_name_or_object)>
cce8198b 336
e606ae5f 337This checks the current type is a sub-type of the type name or object
338supplied. It also returns false if you provide the type as a name, and
339the type name isn't found in the type registry.
340
6ba6d68c 341=item B<compile_type_constraint>
342
0a5bd159 343=item B<coerce ($value)>
344
345This will apply the type-coercion if applicable.
346
76d37e5a 347=item B<check ($value)>
348
e27dfc11 349This method will return a true (C<1>) if the C<$value> passes the
76d37e5a 350constraint, and false (C<0>) otherwise.
351
352=item B<validate ($value)>
353
e27dfc11 354This method is similar to C<check>, but it deals with the error
355message. If the C<$value> passes the constraint, C<undef> will be
356returned. If the C<$value> does B<not> pass the constraint, then
357the C<message> will be used to construct a custom error message.
6ba6d68c 358
4e036ee4 359=item B<name>
360
e606ae5f 361The name of the type in the global type registry.
362
66811d63 363=item B<parent>
364
2f7e4042 365This type's parent type.
e606ae5f 366
3726f905 367=item B<has_parent>
368
e606ae5f 369Returns true if this type has a parent type.
370
d9e17f80 371=item B<parents>
372
2f7e4042 373Synonym for C<parent>.
374
66811d63 375=item B<constraint>
376
2f7e4042 377Returns this type's constraint. This is the value of C<where> provided
378when defining a type.
379
76d37e5a 380=item B<has_message>
381
2f7e4042 382Returns true if this type has a message.
383
76d37e5a 384=item B<message>
385
2f7e4042 386Returns this type's message.
387
688fcdda 388=item B<get_message ($value)>
389
2f7e4042 390Generate message for $value.
391
4e036ee4 392=item B<has_coercion>
393
2f7e4042 394Returns true if this type has a coercion.
395
a27aa600 396=item B<coercion>
397
2f7e4042 398Returns this type's L<Moose::Meta::TypeCoercion> if one exists.
399
c8cf9aaa 400=item B<hand_optimized_type_constraint>
401
402=item B<has_hand_optimized_type_constraint>
403
85a9908f 404=item B<create_child_type>
9ceb576e 405
4e036ee4 406=back
407
3726f905 408=head2 DEPRECATED METHOD
409
451c8248 410=over 4
411
3726f905 412=item B<union>
413
414This was just bad idea on my part,.. use the L<Moose::Meta::TypeConstraint::Union>
415itself instead.
451c8248 416
417=back
418
4e036ee4 419=head1 BUGS
420
e27dfc11 421All complex software has bugs lurking in it, and this module is no
4e036ee4 422exception. If you find a bug please either email me, or add the bug
423to cpan-RT.
424
425=head1 AUTHOR
426
427Stevan Little E<lt>stevan@iinteractive.comE<gt>
428
429=head1 COPYRIGHT AND LICENSE
430
2840a3b2 431Copyright 2006-2009 by Infinity Interactive, Inc.
4e036ee4 432
433L<http://www.iinteractive.com>
434
435This library is free software; you can redistribute it and/or modify
e27dfc11 436it under the same terms as Perl itself.
4e036ee4 437
c8cf9aaa 438=cut