rudementary support for attribute traits
[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';
13use Scalar::Util 'blessed';
66811d63 14
28412c0b 15our $VERSION = '0.11';
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
3726f905 42# private accessors
43
44__PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
45 accessor => '_compiled_type_constraint',
46 predicate => '_has_compiled_type_constraint'
47));
22aed3c0 48__PACKAGE__->meta->add_attribute('package_defined_in' => (
49 accessor => '_package_defined_in'
50));
51
e27dfc11 52sub new {
a27aa600 53 my $class = shift;
54 my $self = $class->meta->new_object(@_);
3726f905 55 $self->compile_type_constraint()
56 unless $self->_has_compiled_type_constraint;
66811d63 57 return $self;
58}
59
3726f905 60sub coerce { ((shift)->coercion || confess "Cannot coerce without a type coercion")->coerce(@_) }
42bc21a4 61sub check { $_[0]->_compiled_type_constraint->($_[1]) ? 1 : undef }
e27dfc11 62sub validate {
76d37e5a 63 my ($self, $value) = @_;
64 if ($self->_compiled_type_constraint->($value)) {
65 return undef;
66 }
67 else {
68 if ($self->has_message) {
69 local $_ = $value;
70 return $self->message->($value);
71 }
72 else {
451c8248 73 return "Validation failed for '" . $self->name . "' failed";
76d37e5a 74 }
75 }
76}
77
3726f905 78## type predicates ...
79
b26e162e 80sub is_a_type_of {
81 my ($self, $type_name) = @_;
82 ($self->name eq $type_name || $self->is_subtype_of($type_name));
83}
84
cce8198b 85sub is_subtype_of {
86 my ($self, $type_name) = @_;
87 my $current = $self;
88 while (my $parent = $current->parent) {
89 return 1 if $parent->name eq $type_name;
90 $current = $parent;
91 }
92 return 0;
93}
94
3726f905 95## compiling the type constraint
96
97sub compile_type_constraint {
98 my $self = shift;
99 $self->_compiled_type_constraint($self->_actually_compile_type_constraint);
451c8248 100}
101
3726f905 102## type compilers ...
103
104sub _actually_compile_type_constraint {
105 my $self = shift;
e27dfc11 106
3726f905 107 return $self->_compile_hand_optimized_type_constraint
108 if $self->has_hand_optimized_type_constraint;
e27dfc11 109
3726f905 110 my $check = $self->constraint;
111 (defined $check)
e27dfc11 112 || confess "Could not compile type constraint '"
113 . $self->name
3726f905 114 . "' because no constraint check";
e27dfc11 115
3726f905 116 return $self->_compile_subtype($check)
117 if $self->has_parent;
e27dfc11 118
3726f905 119 return $self->_compile_type($check);
120}
121
122sub _compile_hand_optimized_type_constraint {
123 my $self = shift;
e27dfc11 124
3726f905 125 my $type_constraint = $self->hand_optimized_type_constraint;
e27dfc11 126
42bc21a4 127 confess unless ref $type_constraint;
128
129 return $type_constraint;
3726f905 130}
131
132sub _compile_subtype {
133 my ($self, $check) = @_;
e27dfc11 134
3726f905 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;
e27dfc11 141 last;
3726f905 142 }
143 else {
144 unshift @parents => $parent->constraint;
145 }
146 }
e27dfc11 147
3726f905 148 # then we compile them to run without
149 # having to recurse as we did before
6f9ff1af 150 return subname $self->name => sub {
151 local $_ = $_[0];
3726f905 152 foreach my $parent (@parents) {
153 return undef unless $parent->($_[0]);
154 }
6f9ff1af 155 return undef unless $check->($_[0]);
156 1;
157 };
3726f905 158}
159
160sub _compile_type {
161 my ($self, $check) = @_;
6f9ff1af 162 return subname $self->name => sub {
163 local $_ = $_[0];
164 return undef unless $check->($_[0]);
165 1;
166 };
3726f905 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
e27dfc11 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
6ba6d68c 202Moose user.
203
e27dfc11 204If you wish to use features at this depth, please come to the
6ba6d68c 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
e27dfc11 217This checks the current type name, and if it does not match,
b26e162e 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
e27dfc11 230This method will return a true (C<1>) if the C<$value> passes the
76d37e5a 231constraint, and false (C<0>) otherwise.
232
233=item B<validate ($value)>
234
e27dfc11 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
e27dfc11 275All complex software has bugs lurking in it, and this module is no
4e036ee4 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
778db3ac 285Copyright 2006-2008 by Infinity Interactive, Inc.
4e036ee4 286
287L<http://www.iinteractive.com>
288
289This library is free software; you can redistribute it and/or modify
e27dfc11 290it under the same terms as Perl itself.
4e036ee4 291
c8cf9aaa 292=cut