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