bump all versions to 0.60
[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
f5bc97e5 15our $VERSION = '0.60';
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;
e606ae5f 62 my $self = $class->_new(@_);
3726f905 63 $self->compile_type_constraint()
64 unless $self->_has_compiled_type_constraint;
66811d63 65 return $self;
66}
67
c245d69b 68sub coerce { ((shift)->coercion || Moose->throw_error("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 {
e606ae5f 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
e606ae5f 97 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
dabed765 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
e606ae5f 120 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 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
e606ae5f 128 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name) or return;
d9e17f80 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)
c245d69b 157 || Moose->throw_error("Could not compile type constraint '"
e27dfc11 158 . $self->name
4c0b3599 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
c245d69b 172 Moose->throw_error("Hand optimized type constraint is not a code reference") unless ref $type_constraint;
42bc21a4 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
85a9908f 249sub create_child_type {
9ceb576e 250 my ($self, %opts) = @_;
251 my $class = ref $self;
252 return $class->new(%opts, parent => $self);
253}
254
3726f905 255## this should get deprecated actually ...
256
547dda77 257sub union { Carp::croak "DEPRECATED" }
3726f905 258
4e036ee4 2591;
260
261__END__
262
263=pod
264
265=head1 NAME
266
6ba6d68c 267Moose::Meta::TypeConstraint - The Moose Type Constraint metaclass
4e036ee4 268
269=head1 DESCRIPTION
270
e27dfc11 271For the most part, the only time you will ever encounter an
272instance of this class is if you are doing some serious deep
273introspection. This API should not be considered final, but
274it is B<highly unlikely> that this will matter to a regular
6ba6d68c 275Moose user.
276
e27dfc11 277If you wish to use features at this depth, please come to the
6ba6d68c 278#moose IRC channel on irc.perl.org and we can talk :)
279
4e036ee4 280=head1 METHODS
281
282=over 4
283
284=item B<meta>
285
286=item B<new>
287
d9e17f80 288=item B<equals ($type_name_or_object)>
289
e606ae5f 290This checks the current type against the supplied type (only).
291Returns false if the two types are not equal. It also returns false if
292you provide the type as a name, and the type name isn't found in the
293type registry.
294
d9e17f80 295=item B<is_a_type_of ($type_name_or_object)>
b26e162e 296
e606ae5f 297This checks the current type against the supplied type, or if the
298current type is a sub-type of the type name or object supplied. It
299also returns false if you provide the type as a name, and the type
300name isn't found in the type registry.
b26e162e 301
d9e17f80 302=item B<is_subtype_of ($type_name_or_object)>
cce8198b 303
e606ae5f 304This checks the current type is a sub-type of the type name or object
305supplied. It also returns false if you provide the type as a name, and
306the type name isn't found in the type registry.
307
6ba6d68c 308=item B<compile_type_constraint>
309
0a5bd159 310=item B<coerce ($value)>
311
312This will apply the type-coercion if applicable.
313
76d37e5a 314=item B<check ($value)>
315
e27dfc11 316This method will return a true (C<1>) if the C<$value> passes the
76d37e5a 317constraint, and false (C<0>) otherwise.
318
319=item B<validate ($value)>
320
e27dfc11 321This method is similar to C<check>, but it deals with the error
322message. If the C<$value> passes the constraint, C<undef> will be
323returned. If the C<$value> does B<not> pass the constraint, then
324the C<message> will be used to construct a custom error message.
6ba6d68c 325
4e036ee4 326=item B<name>
327
e606ae5f 328The name of the type in the global type registry.
329
66811d63 330=item B<parent>
331
e606ae5f 332This type's parent type.
333
3726f905 334=item B<has_parent>
335
e606ae5f 336Returns true if this type has a parent type.
337
d9e17f80 338=item B<parents>
339
66811d63 340=item B<constraint>
341
76d37e5a 342=item B<has_message>
343
344=item B<message>
345
688fcdda 346=item B<get_message ($value)>
347
4e036ee4 348=item B<has_coercion>
349
a27aa600 350=item B<coercion>
351
c8cf9aaa 352=item B<hand_optimized_type_constraint>
353
354=item B<has_hand_optimized_type_constraint>
355
85a9908f 356=item B<create_child_type>
9ceb576e 357
4e036ee4 358=back
359
3726f905 360=head2 DEPRECATED METHOD
361
451c8248 362=over 4
363
3726f905 364=item B<union>
365
366This was just bad idea on my part,.. use the L<Moose::Meta::TypeConstraint::Union>
367itself instead.
451c8248 368
369=back
370
4e036ee4 371=head1 BUGS
372
e27dfc11 373All complex software has bugs lurking in it, and this module is no
4e036ee4 374exception. If you find a bug please either email me, or add the bug
375to cpan-RT.
376
377=head1 AUTHOR
378
379Stevan Little E<lt>stevan@iinteractive.comE<gt>
380
381=head1 COPYRIGHT AND LICENSE
382
778db3ac 383Copyright 2006-2008 by Infinity Interactive, Inc.
4e036ee4 384
385L<http://www.iinteractive.com>
386
387This library is free software; you can redistribute it and/or modify
e27dfc11 388it under the same terms as Perl itself.
4e036ee4 389
c8cf9aaa 390=cut