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