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