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