fix up the inlining test
[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
ca789903 89sub inline_environment {
90 my $self = shift;
91
92 return { map { %{ $_->inline_environment } }
93 @{ $self->type_constraints } };
94}
95
dabed765 96sub equals {
97 my ( $self, $type_or_name ) = @_;
98
99 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
100
101 return unless $other->isa(__PACKAGE__);
102
103 my @self_constraints = @{ $self->type_constraints };
104 my @other_constraints = @{ $other->type_constraints };
105
106 return unless @self_constraints == @other_constraints;
107
108 # FIXME presort type constraints for efficiency?
109 constraint: foreach my $constraint ( @self_constraints ) {
110 for ( my $i = 0; $i < @other_constraints; $i++ ) {
111 if ( $constraint->equals($other_constraints[$i]) ) {
112 splice @other_constraints, $i, 1;
113 next constraint;
114 }
115 }
116 }
117
118 return @other_constraints == 0;
119}
120
121sub parents {
122 my $self = shift;
123 $self->type_constraints;
124}
125
8ee73eeb 126sub validate {
3726f905 127 my ($self, $value) = @_;
8ee73eeb 128 my $message;
129 foreach my $type (@{$self->type_constraints}) {
130 my $err = $type->validate($value);
131 return unless defined $err;
132 $message .= ($message ? ' and ' : '') . $err
133 if defined $err;
134 }
d03bd989 135 return ($message . ' in (' . $self->name . ')') ;
8ee73eeb 136}
137
1aae641c 138sub find_type_for {
139 my ($self, $value) = @_;
140
141 return first { $_->check($value) } @{ $self->type_constraints };
142}
143
8ee73eeb 144sub is_a_type_of {
145 my ($self, $type_name) = @_;
146 foreach my $type (@{$self->type_constraints}) {
147 return 1 if $type->is_a_type_of($type_name);
148 }
d03bd989 149 return 0;
8ee73eeb 150}
151
152sub is_subtype_of {
153 my ($self, $type_name) = @_;
154 foreach my $type (@{$self->type_constraints}) {
155 return 1 if $type->is_subtype_of($type_name);
156 }
157 return 0;
158}
159
85a9908f 160sub create_child_type {
622c9332 161 my ( $self, %opts ) = @_;
162
163 my $constraint
164 = Moose::Meta::TypeConstraint->new( %opts, parent => $self );
165
9ceb576e 166 # if we have a type constraint union, and no
167 # type check, this means we are just aliasing
168 # the union constraint, which means we need to
169 # handle this differently.
170 # - SL
622c9332 171 if ( not( defined $opts{constraint} )
172 && $self->has_coercion ) {
173 $constraint->coercion(
174 Moose::Meta::TypeCoercion::Union->new(
175 type_constraint => $self,
176 )
177 );
9ceb576e 178 }
622c9332 179
9ceb576e 180 return $constraint;
181}
182
8ee73eeb 1831;
184
ad46f524 185# ABSTRACT: A union of Moose type constraints
186
8ee73eeb 187__END__
188
189=pod
190
39b3bc94 191=head1 DESCRIPTION
192
ae2f99ea 193This metaclass represents a union of type constraints. A union takes
194multiple type constraints, and is true if any one of its member
195constraints is true.
ecb59493 196
ae2f99ea 197=head1 INHERITANCE
ecb59493 198
ae2f99ea 199C<Moose::Meta::TypeConstraint::Union> is a subclass of
200L<Moose::Meta::TypeConstraint>.
ecb59493 201
39b3bc94 202=over 4
203
ae2f99ea 204=item B<< Moose::Meta::TypeConstraint::Union->new(%options) >>
39b3bc94 205
ae2f99ea 206This creates a new class type constraint based on the given
207C<%options>.
1b58cb9f 208
ae2f99ea 209It takes the same options as its parent. It also requires an
210additional option, C<type_constraints>. This is an array reference
211containing the L<Moose::Meta::TypeConstraint> objects that are the
212members of the union type. The C<name> option defaults to the names
213all of these member types sorted and then joined by a pipe (|).
dabed765 214
ae2f99ea 215The constructor sets the implementation of the constraint so that is
216simply calls C<check> on the newly created object.
ecb59493 217
ae2f99ea 218Finally, the constructor also makes sure that the object's C<coercion>
219attribute is a L<Moose::Meta::TypeCoercion::Union> object.
ecb59493 220
ae2f99ea 221=item B<< $constraint->type_constraints >>
ecb59493 222
ae2f99ea 223This returns the array reference of C<type_constraints> provided to
224the constructor.
39b3bc94 225
ae2f99ea 226=item B<< $constraint->parents >>
39b3bc94 227
ae2f99ea 228This returns the same constraint as the C<type_constraints> method.
39b3bc94 229
ae2f99ea 230=item B<< $constraint->check($value) >>
39b3bc94 231
ae2f99ea 232=item B<< $constraint->validate($value) >>
39b3bc94 233
ae2f99ea 234These two methods simply call the relevant method on each of the
235member type constraints in the union. If any type accepts the value,
236the value is valid.
39b3bc94 237
ae2f99ea 238With C<validate> the error message returned includes all of the error
239messages returned by the member type constraints.
39b3bc94 240
ae2f99ea 241=item B<< $constraint->equals($type_name_or_object) >>
39b3bc94 242
ae2f99ea 243A type is considered equal if it is also a union type, and the two
244unions have the same member types.
39b3bc94 245
1aae641c 246=item B<< $constraint->find_type_for($value) >>
247
248This returns the first member type constraint for which C<check($value)> is
249true, allowing you to determine which of the Union's member type constraints
250a given value matches.
251
ae2f99ea 252=item B<< $constraint->is_a_type_of($type_name_or_object) >>
ecb59493 253
ae2f99ea 254This returns true if any of the member type constraints return true
255for the C<is_a_type_of> method.
ecb59493 256
ae2f99ea 257=item B<< $constraint->is_subtype_of >>
39b3bc94 258
ae2f99ea 259This returns true if any of the member type constraints return true
260for the C<is_a_subtype_of> method.
0eec94be 261
ae2f99ea 262=item B<< $constraint->create_child_type(%options) >>
0eec94be 263
ae2f99ea 264This returns a new L<Moose::Meta::TypeConstraint> object with the type
265as its parent.
9ceb576e 266
39b3bc94 267=back
268
269=head1 BUGS
270
d4048ef3 271See L<Moose/BUGS> for details on reporting bugs.
39b3bc94 272
8ee73eeb 273=cut