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