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