test with Test::Deep::eq_deeply
[gitmo/Moose.git] / lib / Moose / Meta / TypeConstraint.pm
CommitLineData
4e036ee4 1
2package Moose::Meta::TypeConstraint;
3
4use strict;
5use warnings;
6use metaclass;
7
c07af9d2 8use Sub::Name 'subname';
9use Carp 'confess';
10use Scalar::Util 'blessed';
66811d63 11
c8cf9aaa 12our $VERSION = '0.07';
66811d63 13
8ee73eeb 14use Moose::Meta::TypeConstraint::Union;
15
66811d63 16__PACKAGE__->meta->add_attribute('name' => (reader => 'name' ));
17__PACKAGE__->meta->add_attribute('parent' => (reader => 'parent' ));
18__PACKAGE__->meta->add_attribute('constraint' => (reader => 'constraint'));
76d37e5a 19__PACKAGE__->meta->add_attribute('message' => (
20 accessor => 'message',
21 predicate => 'has_message'
22));
a27aa600 23__PACKAGE__->meta->add_attribute('coercion' => (
24 accessor => 'coercion',
25 predicate => 'has_coercion'
26));
66811d63 27
28# private accessor
29__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
30 accessor => '_compiled_type_constraint'
31));
32
c8cf9aaa 33__PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
34 init_arg => 'optimized',
35 accessor => 'hand_optimized_type_constraint',
36 predicate => 'has_hand_optimized_type_constraint',
37));
38
66811d63 39sub new {
a27aa600 40 my $class = shift;
41 my $self = $class->meta->new_object(@_);
66811d63 42 $self->compile_type_constraint();
43 return $self;
44}
45
0a5bd159 46sub coerce {
47 ((shift)->coercion || confess "Cannot coerce without a type coercion")->coerce(@_)
48}
49
43123819 50sub _collect_all_parents {
51 my $self = shift;
52 my @parents;
53 my $current = $self->parent;
54 while (defined $current) {
c8cf9aaa 55 push @parents => $current;
43123819 56 $current = $current->parent;
57 }
58 return @parents;
59}
60
451c8248 61sub compile_type_constraint {
a27aa600 62 my $self = shift;
c8cf9aaa 63
64 if ($self->has_hand_optimized_type_constraint) {
65 my $type_constraint = $self->hand_optimized_type_constraint;
66 $self->_compiled_type_constraint(sub {
67 return undef unless $type_constraint->($_[0]);
68 return 1;
69 });
70 return;
71 }
72
a27aa600 73 my $check = $self->constraint;
66811d63 74 (defined $check)
75 || confess "Could not compile type constraint '" . $self->name . "' because no constraint check";
76 my $parent = $self->parent;
77 if (defined $parent) {
43123819 78 # we have a subtype ...
79 # so we gather all the parents in order
80 # and grab their constraints ...
c8cf9aaa 81 my @parents;
82 foreach my $parent ($self->_collect_all_parents) {
83 if ($parent->has_hand_optimized_type_constraint) {
84 unshift @parents => $parent->hand_optimized_type_constraint;
85 last;
86 }
87 else {
88 unshift @parents => $parent->constraint;
89 }
90 }
91
43123819 92 # then we compile them to run without
93 # having to recurse as we did before
66811d63 94 $self->_compiled_type_constraint(subname $self->name => sub {
95 local $_ = $_[0];
43123819 96 foreach my $parent (@parents) {
97 return undef unless $parent->($_[0]);
98 }
99 return undef unless $check->($_[0]);
5a4c5493 100 1;
c8cf9aaa 101 });
66811d63 102 }
103 else {
a27aa600 104 # we have a type ....
66811d63 105 $self->_compiled_type_constraint(subname $self->name => sub {
106 local $_ = $_[0];
107 return undef unless $check->($_[0]);
5a4c5493 108 1;
66811d63 109 });
110 }
111}
112
a27aa600 113sub check { $_[0]->_compiled_type_constraint->($_[1]) }
4e036ee4 114
76d37e5a 115sub validate {
116 my ($self, $value) = @_;
117 if ($self->_compiled_type_constraint->($value)) {
118 return undef;
119 }
120 else {
121 if ($self->has_message) {
122 local $_ = $value;
123 return $self->message->($value);
124 }
125 else {
451c8248 126 return "Validation failed for '" . $self->name . "' failed";
76d37e5a 127 }
128 }
129}
130
b26e162e 131sub is_a_type_of {
132 my ($self, $type_name) = @_;
133 ($self->name eq $type_name || $self->is_subtype_of($type_name));
134}
135
cce8198b 136sub is_subtype_of {
137 my ($self, $type_name) = @_;
138 my $current = $self;
139 while (my $parent = $current->parent) {
140 return 1 if $parent->name eq $type_name;
141 $current = $parent;
142 }
143 return 0;
144}
145
451c8248 146sub union {
147 my ($class, @type_constraints) = @_;
c07af9d2 148 (scalar @type_constraints >= 2)
149 || confess "You must pass in at least 2 Moose::Meta::TypeConstraint instances to make a union";
150 (blessed($_) && $_->isa('Moose::Meta::TypeConstraint'))
151 || confess "You must pass in only Moose::Meta::TypeConstraint instances to make unions"
152 foreach @type_constraints;
451c8248 153 return Moose::Meta::TypeConstraint::Union->new(
0a5bd159 154 type_constraints => \@type_constraints,
451c8248 155 );
156}
157
4e036ee4 1581;
159
160__END__
161
162=pod
163
164=head1 NAME
165
6ba6d68c 166Moose::Meta::TypeConstraint - The Moose Type Constraint metaclass
4e036ee4 167
168=head1 DESCRIPTION
169
6ba6d68c 170For the most part, the only time you will ever encounter an
171instance of this class is if you are doing some serious deep
172introspection. This API should not be considered final, but
173it is B<highly unlikely> that this will matter to a regular
174Moose user.
175
176If you wish to use features at this depth, please come to the
177#moose IRC channel on irc.perl.org and we can talk :)
178
4e036ee4 179=head1 METHODS
180
181=over 4
182
183=item B<meta>
184
185=item B<new>
186
b26e162e 187=item B<is_a_type_of ($type_name)>
188
189This checks the current type name, and if it does not match,
190checks if it is a subtype of it.
191
192=item B<is_subtype_of ($type_name)>
cce8198b 193
6ba6d68c 194=item B<compile_type_constraint>
195
0a5bd159 196=item B<coerce ($value)>
197
198This will apply the type-coercion if applicable.
199
76d37e5a 200=item B<check ($value)>
201
202This method will return a true (C<1>) if the C<$value> passes the
203constraint, and false (C<0>) otherwise.
204
205=item B<validate ($value)>
206
207This method is similar to C<check>, but it deals with the error
208message. If the C<$value> passes the constraint, C<undef> will be
209returned. If the C<$value> does B<not> pass the constraint, then
210the C<message> will be used to construct a custom error message.
6ba6d68c 211
4e036ee4 212=item B<name>
213
66811d63 214=item B<parent>
215
66811d63 216=item B<constraint>
217
76d37e5a 218=item B<has_message>
219
220=item B<message>
221
4e036ee4 222=item B<has_coercion>
223
a27aa600 224=item B<coercion>
225
c8cf9aaa 226=item B<hand_optimized_type_constraint>
227
228=item B<has_hand_optimized_type_constraint>
229
4e036ee4 230=back
231
451c8248 232=over 4
233
234=item B<union (@type_constraints)>
235
236=back
237
4e036ee4 238=head1 BUGS
239
240All complex software has bugs lurking in it, and this module is no
241exception. If you find a bug please either email me, or add the bug
242to cpan-RT.
243
244=head1 AUTHOR
245
246Stevan Little E<lt>stevan@iinteractive.comE<gt>
247
248=head1 COPYRIGHT AND LICENSE
249
b77fdbed 250Copyright 2006, 2007 by Infinity Interactive, Inc.
4e036ee4 251
252L<http://www.iinteractive.com>
253
254This library is free software; you can redistribute it and/or modify
255it under the same terms as Perl itself.
256
c8cf9aaa 257=cut