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