ohloh link
[gitmo/Moose.git] / lib / Moose / Meta / TypeConstraint.pm
CommitLineData
4e036ee4 1
2package Moose::Meta::TypeConstraint;
3
4use strict;
5use warnings;
6use metaclass;
7
b644e331 8use overload '""' => sub { shift->name }, # stringify to tc name
9 fallback => 1;
900466d6 10
c07af9d2 11use Sub::Name 'subname';
12use Carp 'confess';
dabed765 13use Scalar::Util qw(blessed refaddr);
66811d63 14
c14746bc 15our $VERSION = '0.12';
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' => (
8de73ff1 24 reader => 'constraint',
25 writer => '_set_constraint',
26 default => sub { sub { 1 } }
d67145ed 27));
76d37e5a 28__PACKAGE__->meta->add_attribute('message' => (
29 accessor => 'message',
30 predicate => 'has_message'
31));
a27aa600 32__PACKAGE__->meta->add_attribute('coercion' => (
33 accessor => 'coercion',
34 predicate => 'has_coercion'
35));
c8cf9aaa 36__PACKAGE__->meta->add_attribute('hand_optimized_type_constraint' => (
37 init_arg => 'optimized',
38 accessor => 'hand_optimized_type_constraint',
e27dfc11 39 predicate => 'has_hand_optimized_type_constraint',
c8cf9aaa 40));
41
bd72f3c8 42sub parents {
43 my $self;
44 $self->parent;
45}
46
3726f905 47# private accessors
48
49__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
50 accessor => '_compiled_type_constraint',
51 predicate => '_has_compiled_type_constraint'
52));
22aed3c0 53__PACKAGE__->meta->add_attribute('package_defined_in' => (
54 accessor => '_package_defined_in'
55));
56
e27dfc11 57sub new {
a27aa600 58 my $class = shift;
59 my $self = $class->meta->new_object(@_);
3726f905 60 $self->compile_type_constraint()
61 unless $self->_has_compiled_type_constraint;
66811d63 62 return $self;
63}
64
3726f905 65sub coerce { ((shift)->coercion || confess "Cannot coerce without a type coercion")->coerce(@_) }
42bc21a4 66sub check { $_[0]->_compiled_type_constraint->($_[1]) ? 1 : undef }
e27dfc11 67sub validate {
76d37e5a 68 my ($self, $value) = @_;
69 if ($self->_compiled_type_constraint->($value)) {
70 return undef;
71 }
72 else {
688fcdda 73 $self->get_message($value);
76d37e5a 74 }
75}
76
688fcdda 77sub get_message {
78 my ($self, $value) = @_;
79 $value = (defined $value ? overload::StrVal($value) : 'undef');
80 if (my $msg = $self->message) {
81 local $_ = $value;
82 return $msg->($value);
83 }
84 else {
85 return "Validation failed for '" . $self->name . "' failed with value $value";
86 }
87}
88
3726f905 89## type predicates ...
90
d9e17f80 91sub equals {
92 my ( $self, $type_or_name ) = @_;
93
dabed765 94 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
95
96 return 1 if refaddr($self) == refaddr($other);
97
98 if ( $self->has_hand_optimized_type_constraint and $other->has_hand_optimized_type_constraint ) {
99 return 1 if $self->hand_optimized_type_constraint == $other->hand_optimized_type_constraint;
100 }
101
102 return unless $self->constraint == $other->constraint;
103
104 if ( $self->has_parent ) {
105 return unless $other->has_parent;
106 return unless $self->parent->equals( $other->parent );
107 } else {
108 return if $other->has_parent;
109 }
d9e17f80 110
dabed765 111 return 1;
d9e17f80 112}
113
b26e162e 114sub is_a_type_of {
d9e17f80 115 my ($self, $type_or_name) = @_;
116
117 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
118
119 ($self->equals($type) || $self->is_subtype_of($type));
b26e162e 120}
121
cce8198b 122sub is_subtype_of {
d9e17f80 123 my ($self, $type_or_name) = @_;
124
125 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
126
cce8198b 127 my $current = $self;
d9e17f80 128
cce8198b 129 while (my $parent = $current->parent) {
d9e17f80 130 return 1 if $parent->equals($type);
cce8198b 131 $current = $parent;
132 }
d9e17f80 133
cce8198b 134 return 0;
135}
136
3726f905 137## compiling the type constraint
138
139sub compile_type_constraint {
140 my $self = shift;
141 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 142}
143
3726f905 144## type compilers ...
145
146sub _actually_compile_type_constraint {
147 my $self = shift;
e27dfc11 148
3726f905 149 return $self->_compile_hand_optimized_type_constraint
150 if $self->has_hand_optimized_type_constraint;
e27dfc11 151
3726f905 152 my $check = $self->constraint;
153 (defined $check)
e27dfc11 154 || confess "Could not compile type constraint '"
155 . $self->name
3726f905 156 . "' because no constraint check";
e27dfc11 157
3726f905 158 return $self->_compile_subtype($check)
159 if $self->has_parent;
e27dfc11 160
3726f905 161 return $self->_compile_type($check);
162}
163
164sub _compile_hand_optimized_type_constraint {
165 my $self = shift;
e27dfc11 166
3726f905 167 my $type_constraint = $self->hand_optimized_type_constraint;
e27dfc11 168
42bc21a4 169 confess unless ref $type_constraint;
170
171 return $type_constraint;
3726f905 172}
173
174sub _compile_subtype {
175 my ($self, $check) = @_;
e27dfc11 176
3726f905 177 # so we gather all the parents in order
178 # and grab their constraints ...
179 my @parents;
180 foreach my $parent ($self->_collect_all_parents) {
181 if ($parent->has_hand_optimized_type_constraint) {
182 unshift @parents => $parent->hand_optimized_type_constraint;
e27dfc11 183 last;
3726f905 184 }
185 else {
186 unshift @parents => $parent->constraint;
187 }
188 }
e27dfc11 189
3726f905 190 # then we compile them to run without
191 # having to recurse as we did before
6f9ff1af 192 return subname $self->name => sub {
193 local $_ = $_[0];
3726f905 194 foreach my $parent (@parents) {
195 return undef unless $parent->($_[0]);
196 }
6f9ff1af 197 return undef unless $check->($_[0]);
198 1;
199 };
3726f905 200}
201
202sub _compile_type {
203 my ($self, $check) = @_;
6f9ff1af 204 return subname $self->name => sub {
205 local $_ = $_[0];
206 return undef unless $check->($_[0]);
207 1;
208 };
3726f905 209}
210
211## other utils ...
212
213sub _collect_all_parents {
214 my $self = shift;
215 my @parents;
216 my $current = $self->parent;
217 while (defined $current) {
218 push @parents => $current;
219 $current = $current->parent;
220 }
221 return @parents;
222}
223
224## this should get deprecated actually ...
225
547dda77 226sub union { Carp::croak "DEPRECATED" }
3726f905 227
4e036ee4 2281;
229
230__END__
231
232=pod
233
234=head1 NAME
235
6ba6d68c 236Moose::Meta::TypeConstraint - The Moose Type Constraint metaclass
4e036ee4 237
238=head1 DESCRIPTION
239
e27dfc11 240For the most part, the only time you will ever encounter an
241instance of this class is if you are doing some serious deep
242introspection. This API should not be considered final, but
243it is B<highly unlikely> that this will matter to a regular
6ba6d68c 244Moose user.
245
e27dfc11 246If you wish to use features at this depth, please come to the
6ba6d68c 247#moose IRC channel on irc.perl.org and we can talk :)
248
4e036ee4 249=head1 METHODS
250
251=over 4
252
253=item B<meta>
254
255=item B<new>
256
d9e17f80 257=item B<equals ($type_name_or_object)>
258
259=item B<is_a_type_of ($type_name_or_object)>
b26e162e 260
e27dfc11 261This checks the current type name, and if it does not match,
b26e162e 262checks if it is a subtype of it.
263
d9e17f80 264=item B<is_subtype_of ($type_name_or_object)>
cce8198b 265
6ba6d68c 266=item B<compile_type_constraint>
267
0a5bd159 268=item B<coerce ($value)>
269
270This will apply the type-coercion if applicable.
271
76d37e5a 272=item B<check ($value)>
273
e27dfc11 274This method will return a true (C<1>) if the C<$value> passes the
76d37e5a 275constraint, and false (C<0>) otherwise.
276
277=item B<validate ($value)>
278
e27dfc11 279This method is similar to C<check>, but it deals with the error
280message. If the C<$value> passes the constraint, C<undef> will be
281returned. If the C<$value> does B<not> pass the constraint, then
282the C<message> will be used to construct a custom error message.
6ba6d68c 283
4e036ee4 284=item B<name>
285
66811d63 286=item B<parent>
287
3726f905 288=item B<has_parent>
289
d9e17f80 290=item B<parents>
291
66811d63 292=item B<constraint>
293
76d37e5a 294=item B<has_message>
295
296=item B<message>
297
688fcdda 298=item B<get_message ($value)>
299
4e036ee4 300=item B<has_coercion>
301
a27aa600 302=item B<coercion>
303
c8cf9aaa 304=item B<hand_optimized_type_constraint>
305
306=item B<has_hand_optimized_type_constraint>
307
4e036ee4 308=back
309
3726f905 310=head2 DEPRECATED METHOD
311
451c8248 312=over 4
313
3726f905 314=item B<union>
315
316This was just bad idea on my part,.. use the L<Moose::Meta::TypeConstraint::Union>
317itself instead.
451c8248 318
319=back
320
4e036ee4 321=head1 BUGS
322
e27dfc11 323All complex software has bugs lurking in it, and this module is no
4e036ee4 324exception. If you find a bug please either email me, or add the bug
325to cpan-RT.
326
327=head1 AUTHOR
328
329Stevan Little E<lt>stevan@iinteractive.comE<gt>
330
331=head1 COPYRIGHT AND LICENSE
332
778db3ac 333Copyright 2006-2008 by Infinity Interactive, Inc.
4e036ee4 334
335L<http://www.iinteractive.com>
336
337This library is free software; you can redistribute it and/or modify
e27dfc11 338it under the same terms as Perl itself.
4e036ee4 339
c8cf9aaa 340=cut