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