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