2 package Moose::Meta::TypeConstraint;
8 use Sub::Name 'subname';
10 use Scalar::Util 'blessed';
12 our $VERSION = '0.06';
14 __PACKAGE__->meta->add_attribute('name' => (reader => 'name' ));
15 __PACKAGE__->meta->add_attribute('parent' => (reader => 'parent' ));
16 __PACKAGE__->meta->add_attribute('constraint' => (reader => 'constraint'));
17 __PACKAGE__->meta->add_attribute('message' => (
18 accessor => 'message',
19 predicate => 'has_message'
21 __PACKAGE__->meta->add_attribute('coercion' => (
22 accessor => 'coercion',
23 predicate => 'has_coercion'
27 __PACKAGE__->meta->add_attribute('compiled_type_constraint' => (
28 accessor => '_compiled_type_constraint'
33 my $self = $class->meta->new_object(@_);
34 $self->compile_type_constraint();
39 ((shift)->coercion || confess "Cannot coerce without a type coercion")->coerce(@_)
42 sub _collect_all_parents {
45 my $current = $self->parent;
46 while (defined $current) {
47 unshift @parents => $current;
48 $current = $current->parent;
53 sub compile_type_constraint {
55 my $check = $self->constraint;
57 || confess "Could not compile type constraint '" . $self->name . "' because no constraint check";
58 my $parent = $self->parent;
59 if (defined $parent) {
60 # we have a subtype ...
61 # so we gather all the parents in order
62 # and grab their constraints ...
63 my @parents = map { $_->constraint } $self->_collect_all_parents;
64 # then we compile them to run without
65 # having to recurse as we did before
66 $self->_compiled_type_constraint(subname $self->name => sub {
68 foreach my $parent (@parents) {
69 return undef unless $parent->($_[0]);
71 return undef unless $check->($_[0]);
78 $self->_compiled_type_constraint(subname $self->name => sub {
80 return undef unless $check->($_[0]);
86 sub check { $_[0]->_compiled_type_constraint->($_[1]) }
89 my ($self, $value) = @_;
90 if ($self->_compiled_type_constraint->($value)) {
94 if ($self->has_message) {
96 return $self->message->($value);
99 return "Validation failed for '" . $self->name . "' failed";
105 my ($self, $type_name) = @_;
106 ($self->name eq $type_name || $self->is_subtype_of($type_name));
110 my ($self, $type_name) = @_;
112 while (my $parent = $current->parent) {
113 return 1 if $parent->name eq $type_name;
120 my ($class, @type_constraints) = @_;
121 (scalar @type_constraints >= 2)
122 || confess "You must pass in at least 2 Moose::Meta::TypeConstraint instances to make a union";
123 (blessed($_) && $_->isa('Moose::Meta::TypeConstraint'))
124 || confess "You must pass in only Moose::Meta::TypeConstraint instances to make unions"
125 foreach @type_constraints;
126 return Moose::Meta::TypeConstraint::Union->new(
127 type_constraints => \@type_constraints,
131 package Moose::Meta::TypeConstraint::Union;
137 our $VERSION = '0.03';
139 __PACKAGE__->meta->add_attribute('type_constraints' => (
140 accessor => 'type_constraints',
141 default => sub { [] }
146 my $self = $class->meta->new_object(@_);
150 sub name { join ' | ' => map { $_->name } @{$_[0]->type_constraints} }
153 # this should probably never be used
154 # but we include it here for completeness
157 sub { $self->check($_[0]) };
160 # conform to the TypeConstraint API
162 sub message { undef }
163 sub has_message { 0 }
166 # not sure what this should actually do here
167 sub coercion { undef }
169 # this should probably be memoized
172 foreach my $type (@{$self->type_constraints}) {
173 return 1 if $type->has_coercion
179 # this feels too simple, and may not always DWIM
180 # correctly, especially in the presence of
181 # close subtype relationships, however it should
182 # work for a fair percentage of the use cases
186 foreach my $type (@{$self->type_constraints}) {
187 if ($type->has_coercion) {
188 my $temp = $type->coerce($value);
189 return $temp if $self->check($temp);
195 sub _compiled_type_constraint {
199 foreach my $type (@{$self->type_constraints}) {
200 return 1 if $type->check($value);
209 $self->_compiled_type_constraint->($value);
216 foreach my $type (@{$self->type_constraints}) {
217 my $err = $type->validate($value);
218 return unless defined $err;
219 $message .= ($message ? ' and ' : '') . $err
222 return ($message . ' in (' . $self->name . ')') ;
226 my ($self, $type_name) = @_;
227 foreach my $type (@{$self->type_constraints}) {
228 return 1 if $type->is_a_type_of($type_name);
234 my ($self, $type_name) = @_;
235 foreach my $type (@{$self->type_constraints}) {
236 return 1 if $type->is_subtype_of($type_name);
249 Moose::Meta::TypeConstraint - The Moose Type Constraint metaclass
253 For the most part, the only time you will ever encounter an
254 instance of this class is if you are doing some serious deep
255 introspection. This API should not be considered final, but
256 it is B<highly unlikely> that this will matter to a regular
259 If you wish to use features at this depth, please come to the
260 #moose IRC channel on irc.perl.org and we can talk :)
270 =item B<is_a_type_of ($type_name)>
272 This checks the current type name, and if it does not match,
273 checks if it is a subtype of it.
275 =item B<is_subtype_of ($type_name)>
277 =item B<compile_type_constraint>
279 =item B<coerce ($value)>
281 This will apply the type-coercion if applicable.
283 =item B<check ($value)>
285 This method will return a true (C<1>) if the C<$value> passes the
286 constraint, and false (C<0>) otherwise.
288 =item B<validate ($value)>
290 This method is similar to C<check>, but it deals with the error
291 message. If the C<$value> passes the constraint, C<undef> will be
292 returned. If the C<$value> does B<not> pass the constraint, then
293 the C<message> will be used to construct a custom error message.
305 =item B<has_coercion>
313 =item B<union (@type_constraints)>
319 All complex software has bugs lurking in it, and this module is no
320 exception. If you find a bug please either email me, or add the bug
325 Stevan Little E<lt>stevan@iinteractive.comE<gt>
327 =head1 COPYRIGHT AND LICENSE
329 Copyright 2006 by Infinity Interactive, Inc.
331 L<http://www.iinteractive.com>
333 This library is free software; you can redistribute it and/or modify
334 it under the same terms as Perl itself.