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