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