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