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