bump version to 0.59
[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
2351f08e 15our $VERSION = '0.59';
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
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
e606ae5f 284This checks the current type against the supplied type (only).
285Returns false if the two types are not equal. It also returns false if
286you provide the type as a name, and the type name isn't found in the
287type registry.
288
d9e17f80 289=item B<is_a_type_of ($type_name_or_object)>
b26e162e 290
e606ae5f 291This checks the current type against the supplied type, or if the
292current type is a sub-type of the type name or object supplied. It
293also returns false if you provide the type as a name, and the type
294name isn't found in the type registry.
b26e162e 295
d9e17f80 296=item B<is_subtype_of ($type_name_or_object)>
cce8198b 297
e606ae5f 298This checks the current type is a sub-type of the type name or object
299supplied. It also returns false if you provide the type as a name, and
300the type name isn't found in the type registry.
301
6ba6d68c 302=item B<compile_type_constraint>
303
0a5bd159 304=item B<coerce ($value)>
305
306This will apply the type-coercion if applicable.
307
76d37e5a 308=item B<check ($value)>
309
e27dfc11 310This method will return a true (C<1>) if the C<$value> passes the
76d37e5a 311constraint, and false (C<0>) otherwise.
312
313=item B<validate ($value)>
314
e27dfc11 315This method is similar to C<check>, but it deals with the error
316message. If the C<$value> passes the constraint, C<undef> will be
317returned. If the C<$value> does B<not> pass the constraint, then
318the C<message> will be used to construct a custom error message.
6ba6d68c 319
4e036ee4 320=item B<name>
321
e606ae5f 322The name of the type in the global type registry.
323
66811d63 324=item B<parent>
325
e606ae5f 326This type's parent type.
327
3726f905 328=item B<has_parent>
329
e606ae5f 330Returns true if this type has a parent type.
331
d9e17f80 332=item B<parents>
333
66811d63 334=item B<constraint>
335
76d37e5a 336=item B<has_message>
337
338=item B<message>
339
688fcdda 340=item B<get_message ($value)>
341
4e036ee4 342=item B<has_coercion>
343
a27aa600 344=item B<coercion>
345
c8cf9aaa 346=item B<hand_optimized_type_constraint>
347
348=item B<has_hand_optimized_type_constraint>
349
4e036ee4 350=back
351
3726f905 352=head2 DEPRECATED METHOD
353
451c8248 354=over 4
355
3726f905 356=item B<union>
357
358This was just bad idea on my part,.. use the L<Moose::Meta::TypeConstraint::Union>
359itself instead.
451c8248 360
361=back
362
4e036ee4 363=head1 BUGS
364
e27dfc11 365All complex software has bugs lurking in it, and this module is no
4e036ee4 366exception. If you find a bug please either email me, or add the bug
367to cpan-RT.
368
369=head1 AUTHOR
370
371Stevan Little E<lt>stevan@iinteractive.comE<gt>
372
373=head1 COPYRIGHT AND LICENSE
374
778db3ac 375Copyright 2006-2008 by Infinity Interactive, Inc.
4e036ee4 376
377L<http://www.iinteractive.com>
378
379This library is free software; you can redistribute it and/or modify
e27dfc11 380it under the same terms as Perl itself.
4e036ee4 381
c8cf9aaa 382=cut