Fixed a couple of typos.
[gitmo/Moose.git] / lib / Moose / Meta / TypeConstraint / Union.pm
CommitLineData
8ee73eeb 1
2package Moose::Meta::TypeConstraint::Union;
3
4use strict;
5use warnings;
6use metaclass;
7
3726f905 8use Moose::Meta::TypeCoercion::Union;
9
1aae641c 10use List::Util qw(first);
11
bb8ef151 12our $VERSION = '1.9900';
75b95414 13$VERSION = eval $VERSION;
d44714be 14our $AUTHORITY = 'cpan:STEVAN';
8ee73eeb 15
d67145ed 16use base 'Moose::Meta::TypeConstraint';
17
8ee73eeb 18__PACKAGE__->meta->add_attribute('type_constraints' => (
19 accessor => 'type_constraints',
20 default => sub { [] }
21));
22
d03bd989 23sub new {
3726f905 24 my ($class, %options) = @_;
816ef2e2 25
26 my $name = join '|' => sort { $a cmp $b }
27 map { $_->name } @{ $options{type_constraints} };
28
3726f905 29 my $self = $class->SUPER::new(
816ef2e2 30 name => $name,
31 %options,
3726f905 32 );
816ef2e2 33
3726f905 34 $self->_set_constraint(sub { $self->check($_[0]) });
4beacd39 35
70f676ca 36 return $self;
37}
38
39# XXX - this is a rather gross implementation of laziness for the benefit of
40# MX::Types. If we try to call ->has_coercion on the objects during object
41# construction, this does not work when defining a recursive constraint with
42# MX::Types.
43sub coercion {
44 my $self = shift;
45
46 return $self->{coercion} if exists $self->{coercion};
47
5c56b608 48 # Using any instead of grep here causes a weird error with some corner
49 # cases when MX::Types is in use. See RT #61001.
50 if ( grep { $_->has_coercion } @{ $self->type_constraints } ) {
70f676ca 51 return $self->{coercion} = Moose::Meta::TypeCoercion::Union->new(
52 type_constraint => $self );
53 }
54 else {
55 return $self->{coercion} = undef;
4beacd39 56 }
70f676ca 57}
4beacd39 58
70f676ca 59sub has_coercion {
60 return defined $_[0]->coercion;
8ee73eeb 61}
62
816ef2e2 63sub _actually_compile_type_constraint {
64 my $self = shift;
65
66 my @constraints = @{ $self->type_constraints };
67
68 return sub {
69 my $value = shift;
70 foreach my $type (@constraints) {
71 return 1 if $type->check($value);
72 }
73 return undef;
74 };
75}
76
77
dabed765 78sub equals {
79 my ( $self, $type_or_name ) = @_;
80
81 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
82
83 return unless $other->isa(__PACKAGE__);
84
85 my @self_constraints = @{ $self->type_constraints };
86 my @other_constraints = @{ $other->type_constraints };
87
88 return unless @self_constraints == @other_constraints;
89
90 # FIXME presort type constraints for efficiency?
91 constraint: foreach my $constraint ( @self_constraints ) {
92 for ( my $i = 0; $i < @other_constraints; $i++ ) {
93 if ( $constraint->equals($other_constraints[$i]) ) {
94 splice @other_constraints, $i, 1;
95 next constraint;
96 }
97 }
98 }
99
100 return @other_constraints == 0;
101}
102
103sub parents {
104 my $self = shift;
105 $self->type_constraints;
106}
107
8ee73eeb 108sub validate {
3726f905 109 my ($self, $value) = @_;
8ee73eeb 110 my $message;
111 foreach my $type (@{$self->type_constraints}) {
112 my $err = $type->validate($value);
113 return unless defined $err;
114 $message .= ($message ? ' and ' : '') . $err
115 if defined $err;
116 }
d03bd989 117 return ($message . ' in (' . $self->name . ')') ;
8ee73eeb 118}
119
1aae641c 120sub find_type_for {
121 my ($self, $value) = @_;
122
123 return first { $_->check($value) } @{ $self->type_constraints };
124}
125
8ee73eeb 126sub is_a_type_of {
127 my ($self, $type_name) = @_;
128 foreach my $type (@{$self->type_constraints}) {
129 return 1 if $type->is_a_type_of($type_name);
130 }
d03bd989 131 return 0;
8ee73eeb 132}
133
134sub is_subtype_of {
135 my ($self, $type_name) = @_;
136 foreach my $type (@{$self->type_constraints}) {
137 return 1 if $type->is_subtype_of($type_name);
138 }
139 return 0;
140}
141
85a9908f 142sub create_child_type {
622c9332 143 my ( $self, %opts ) = @_;
144
145 my $constraint
146 = Moose::Meta::TypeConstraint->new( %opts, parent => $self );
147
9ceb576e 148 # if we have a type constraint union, and no
149 # type check, this means we are just aliasing
150 # the union constraint, which means we need to
151 # handle this differently.
152 # - SL
622c9332 153 if ( not( defined $opts{constraint} )
154 && $self->has_coercion ) {
155 $constraint->coercion(
156 Moose::Meta::TypeCoercion::Union->new(
157 type_constraint => $self,
158 )
159 );
9ceb576e 160 }
622c9332 161
9ceb576e 162 return $constraint;
163}
164
8ee73eeb 1651;
166
167__END__
168
169=pod
170
39b3bc94 171=head1 NAME
172
ecb59493 173Moose::Meta::TypeConstraint::Union - A union of Moose type constraints
39b3bc94 174
175=head1 DESCRIPTION
176
ae2f99ea 177This metaclass represents a union of type constraints. A union takes
178multiple type constraints, and is true if any one of its member
179constraints is true.
ecb59493 180
ae2f99ea 181=head1 INHERITANCE
ecb59493 182
ae2f99ea 183C<Moose::Meta::TypeConstraint::Union> is a subclass of
184L<Moose::Meta::TypeConstraint>.
ecb59493 185
39b3bc94 186=over 4
187
ae2f99ea 188=item B<< Moose::Meta::TypeConstraint::Union->new(%options) >>
39b3bc94 189
ae2f99ea 190This creates a new class type constraint based on the given
191C<%options>.
1b58cb9f 192
ae2f99ea 193It takes the same options as its parent. It also requires an
194additional option, C<type_constraints>. This is an array reference
195containing the L<Moose::Meta::TypeConstraint> objects that are the
196members of the union type. The C<name> option defaults to the names
197all of these member types sorted and then joined by a pipe (|).
dabed765 198
ae2f99ea 199The constructor sets the implementation of the constraint so that is
200simply calls C<check> on the newly created object.
ecb59493 201
ae2f99ea 202Finally, the constructor also makes sure that the object's C<coercion>
203attribute is a L<Moose::Meta::TypeCoercion::Union> object.
ecb59493 204
ae2f99ea 205=item B<< $constraint->type_constraints >>
ecb59493 206
ae2f99ea 207This returns the array reference of C<type_constraints> provided to
208the constructor.
39b3bc94 209
ae2f99ea 210=item B<< $constraint->parents >>
39b3bc94 211
ae2f99ea 212This returns the same constraint as the C<type_constraints> method.
39b3bc94 213
ae2f99ea 214=item B<< $constraint->check($value) >>
39b3bc94 215
ae2f99ea 216=item B<< $constraint->validate($value) >>
39b3bc94 217
ae2f99ea 218These two methods simply call the relevant method on each of the
219member type constraints in the union. If any type accepts the value,
220the value is valid.
39b3bc94 221
ae2f99ea 222With C<validate> the error message returned includes all of the error
223messages returned by the member type constraints.
39b3bc94 224
ae2f99ea 225=item B<< $constraint->equals($type_name_or_object) >>
39b3bc94 226
ae2f99ea 227A type is considered equal if it is also a union type, and the two
228unions have the same member types.
39b3bc94 229
1aae641c 230=item B<< $constraint->find_type_for($value) >>
231
232This returns the first member type constraint for which C<check($value)> is
233true, allowing you to determine which of the Union's member type constraints
234a given value matches.
235
ae2f99ea 236=item B<< $constraint->is_a_type_of($type_name_or_object) >>
ecb59493 237
ae2f99ea 238This returns true if any of the member type constraints return true
239for the C<is_a_type_of> method.
ecb59493 240
ae2f99ea 241=item B<< $constraint->is_subtype_of >>
39b3bc94 242
ae2f99ea 243This returns true if any of the member type constraints return true
244for the C<is_a_subtype_of> method.
0eec94be 245
ae2f99ea 246=item B<< $constraint->create_child_type(%options) >>
0eec94be 247
ae2f99ea 248This returns a new L<Moose::Meta::TypeConstraint> object with the type
249as its parent.
9ceb576e 250
39b3bc94 251=back
252
253=head1 BUGS
254
d4048ef3 255See L<Moose/BUGS> for details on reporting bugs.
39b3bc94 256
257=head1 AUTHOR
258
259Stevan Little E<lt>stevan@iinteractive.comE<gt>
260
39b3bc94 261=head1 COPYRIGHT AND LICENSE
262
7e0492d3 263Copyright 2006-2010 by Infinity Interactive, Inc.
39b3bc94 264
265L<http://www.iinteractive.com>
266
267This library is free software; you can redistribute it and/or modify
268it under the same terms as Perl itself.
269
8ee73eeb 270=cut