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