2 package Moose::Meta::TypeConstraint;
8 use Sub::Name 'subname';
10 use Scalar::Util 'blessed';
12 our $VERSION = '0.05';
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 compile_type_constraint {
44 my $check = $self->constraint;
46 || confess "Could not compile type constraint '" . $self->name . "' because no constraint check";
47 my $parent = $self->parent;
48 if (defined $parent) {
49 # we have a subtype ...
50 $parent = $parent->_compiled_type_constraint;
51 $self->_compiled_type_constraint(subname $self->name => sub {
53 return undef unless defined $parent->($_[0]) && $check->($_[0]);
59 $self->_compiled_type_constraint(subname $self->name => sub {
61 return undef unless $check->($_[0]);
67 sub check { $_[0]->_compiled_type_constraint->($_[1]) }
70 my ($self, $value) = @_;
71 if ($self->_compiled_type_constraint->($value)) {
75 if ($self->has_message) {
77 return $self->message->($value);
80 return "Validation failed for '" . $self->name . "' failed";
86 my ($self, $type_name) = @_;
87 ($self->name eq $type_name || $self->is_subtype_of($type_name));
91 my ($self, $type_name) = @_;
93 while (my $parent = $current->parent) {
94 return 1 if $parent->name eq $type_name;
101 my ($class, @type_constraints) = @_;
102 (scalar @type_constraints >= 2)
103 || confess "You must pass in at least 2 Moose::Meta::TypeConstraint instances to make a union";
104 (blessed($_) && $_->isa('Moose::Meta::TypeConstraint'))
105 || confess "You must pass in only Moose::Meta::TypeConstraint instances to make unions"
106 foreach @type_constraints;
107 return Moose::Meta::TypeConstraint::Union->new(
108 type_constraints => \@type_constraints,
112 package Moose::Meta::TypeConstraint::Union;
118 our $VERSION = '0.02';
120 __PACKAGE__->meta->add_attribute('type_constraints' => (
121 accessor => 'type_constraints',
122 default => sub { [] }
127 my $self = $class->meta->new_object(@_);
131 sub name { join ' | ' => map { $_->name } @{$_[0]->type_constraints} }
134 # this should probably never be used
135 # but we include it here for completeness
138 sub { $self->check($_[0]) };
141 # conform to the TypeConstraint API
143 sub message { undef }
144 sub has_message { 0 }
147 # not sure what this should actually do here
148 sub coercion { undef }
150 # this should probably be memoized
153 foreach my $type (@{$self->type_constraints}) {
154 return 1 if $type->has_coercion
160 # this feels too simple, and may not always DWIM
161 # correctly, especially in the presence of
162 # close subtype relationships, however it should
163 # work for a fair percentage of the use cases
167 foreach my $type (@{$self->type_constraints}) {
168 if ($type->has_coercion) {
169 my $temp = $type->coerce($value);
170 return $temp if $self->check($temp);
179 foreach my $type (@{$self->type_constraints}) {
180 return 1 if $type->check($value);
189 foreach my $type (@{$self->type_constraints}) {
190 my $err = $type->validate($value);
191 return unless defined $err;
192 $message .= ($message ? ' and ' : '') . $err
195 return ($message . ' in (' . $self->name . ')') ;
199 my ($self, $type_name) = @_;
200 foreach my $type (@{$self->type_constraints}) {
201 return 1 if $type->is_a_type_of($type_name);
207 my ($self, $type_name) = @_;
208 foreach my $type (@{$self->type_constraints}) {
209 return 1 if $type->is_subtype_of($type_name);
222 Moose::Meta::TypeConstraint - The Moose Type Constraint metaclass
226 For the most part, the only time you will ever encounter an
227 instance of this class is if you are doing some serious deep
228 introspection. This API should not be considered final, but
229 it is B<highly unlikely> that this will matter to a regular
232 If you wish to use features at this depth, please come to the
233 #moose IRC channel on irc.perl.org and we can talk :)
243 =item B<is_a_type_of ($type_name)>
245 This checks the current type name, and if it does not match,
246 checks if it is a subtype of it.
248 =item B<is_subtype_of ($type_name)>
250 =item B<compile_type_constraint>
252 =item B<coerce ($value)>
254 This will apply the type-coercion if applicable.
256 =item B<check ($value)>
258 This method will return a true (C<1>) if the C<$value> passes the
259 constraint, and false (C<0>) otherwise.
261 =item B<validate ($value)>
263 This method is similar to C<check>, but it deals with the error
264 message. If the C<$value> passes the constraint, C<undef> will be
265 returned. If the C<$value> does B<not> pass the constraint, then
266 the C<message> will be used to construct a custom error message.
278 =item B<has_coercion>
286 =item B<union (@type_constraints)>
292 All complex software has bugs lurking in it, and this module is no
293 exception. If you find a bug please either email me, or add the bug
298 Stevan Little E<lt>stevan@iinteractive.comE<gt>
300 =head1 COPYRIGHT AND LICENSE
302 Copyright 2006 by Infinity Interactive, Inc.
304 L<http://www.iinteractive.com>
306 This library is free software; you can redistribute it and/or modify
307 it under the same terms as Perl itself.