File Coverage

blib/lib/Moose/Meta/TypeConstraint/Union.pm
Criterion Covered Total %
statement 103 103 100.0
branch 22 24 91.6
condition 3 3 100.0
subroutine 25 25 100.0
pod 12 12 100.0
total 165 167 98.8


line stmt bran cond sub pod time code
1             package Moose::Meta::TypeConstraint::Union;
2             our $VERSION = '2.2203';
3              
4 401     401   2595 use strict;
  401         907  
  401         10816  
5 401     401   1853 use warnings;
  401         753  
  401         8210  
6 401     401   1822 use metaclass;
  401         790  
  401         2136  
7              
8 401     401   164325 use Moose::Meta::TypeCoercion::Union;
  401         963  
  401         12934  
9              
10 401     401   2507 use List::Util 1.33 qw(first all);
  401         6688  
  401         22894  
11              
12 401     401   2639 use parent 'Moose::Meta::TypeConstraint';
  401         795  
  401         1612  
13              
14             __PACKAGE__->meta->add_attribute('type_constraints' => (
15             accessor => 'type_constraints',
16             default => sub { [] },
17             Class::MOP::_definition_context(),
18             ));
19              
20             sub new {
21 73     73 1 259 my ($class, %options) = @_;
22              
23 77         371 my $name = join '|' => sort { $a cmp $b }
24 73         162 map { $_->name } @{ $options{type_constraints} };
  149         3652  
  73         188  
25              
26 73         443 my $self = $class->SUPER::new(
27             name => $name,
28             %options,
29             );
30              
31 73         2348 $self->_set_constraint( $self->_compiled_type_constraint );
32              
33 73         390 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.
40             sub coercion {
41 75     75 1 125 my $self = shift;
42              
43 75 100       346 return $self->{coercion} if exists $self->{coercion};
44              
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 41 100       86 if ( grep { $_->has_coercion } @{ $self->type_constraints } ) {
  83         2205  
  41         1087  
48 2         17 return $self->{coercion} = Moose::Meta::TypeCoercion::Union->new(
49             type_constraint => $self );
50             }
51             else {
52 39         199 return $self->{coercion} = undef;
53             }
54             }
55              
56             sub has_coercion {
57 72     72 1 202 return defined $_[0]->coercion;
58             }
59              
60             sub _actually_compile_type_constraint {
61 73     73   137 my $self = shift;
62              
63 73         192 my @constraints = @{ $self->type_constraints };
  73         2164  
64              
65             return sub {
66 1976     1976   6309 my $value = shift;
67 1976         3789 foreach my $type (@constraints) {
68 3375 100       17933 return 1 if $type->check($value);
69             }
70 1326         14293 return undef;
71 73         2667 };
72             }
73              
74             sub can_be_inlined {
75 232     232 1 505 my $self = shift;
76              
77             # This was originally done with all() from List::MoreUtils, but that
78             # caused some sort of bizarro parsing failure under 5.10.
79 232         332 for my $tc ( @{ $self->type_constraints } ) {
  232         6159  
80 460 100       1070 return 0 unless $tc->can_be_inlined;
81             }
82              
83 223         837 return 1;
84             }
85              
86             sub _inline_check {
87 108     108   312 my $self = shift;
88 108         166 my $val = shift;
89              
90             return '('
91             . (
92 218         575 join ' || ', map { '(' . $_->_inline_check($val) . ')' }
93 108         208 @{ $self->type_constraints }
  108         2781  
94             )
95             . ')';
96             }
97              
98             sub inline_environment {
99 163     163 1 294 my $self = shift;
100              
101 327         435 return { map { %{ $_->inline_environment } }
  327         696  
102 163         257 @{ $self->type_constraints } };
  163         4141  
103             }
104              
105             sub equals {
106 8     8 1 21 my ( $self, $type_or_name ) = @_;
107              
108 8         24 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
109              
110 8 100       47 return unless $other->isa(__PACKAGE__);
111              
112 3         6 my @self_constraints = @{ $self->type_constraints };
  3         104  
113 3         5 my @other_constraints = @{ $other->type_constraints };
  3         89  
114              
115 3 50       11 return unless @self_constraints == @other_constraints;
116              
117             # FIXME presort type constraints for efficiency?
118 3         7 constraint: foreach my $constraint ( @self_constraints ) {
119 6         14 for ( my $i = 0; $i < @other_constraints; $i++ ) {
120 7 100       19 if ( $constraint->equals($other_constraints[$i]) ) {
121 6         11 splice @other_constraints, $i, 1;
122 6         13 next constraint;
123             }
124             }
125             }
126              
127 3         16 return @other_constraints == 0;
128             }
129              
130             sub parent {
131 13     13 1 32 my $self = shift;
132              
133 13         25 my ($first, @rest) = @{ $self->type_constraints };
  13         498  
134              
135 13         63 for my $parent ( $first->_collect_all_parents ) {
136 30 100   30   150 return $parent if all { $_->is_a_type_of($parent) } @rest;
  30         100  
137             }
138              
139 1         7 return;
140             }
141              
142             sub validate {
143 6     6 1 17 my ($self, $value) = @_;
144 6         10 my $message;
145 6         12 foreach my $type (@{$self->type_constraints}) {
  6         257  
146 10         36 my $err = $type->validate($value);
147 10 100       38 return unless defined $err;
148 7 100       30 $message .= ($message ? ' and ' : '') . $err
    50          
149             if defined $err;
150             }
151 3         101 return ($message . ' in (' . $self->name . ')') ;
152             }
153              
154             sub find_type_for {
155 3     3 1 9 my ($self, $value) = @_;
156              
157 3     5   14 return first { $_->check($value) } @{ $self->type_constraints };
  5         33  
  3         151  
158             }
159              
160             sub is_a_type_of {
161 13     13 1 39 my ($self, $type_name) = @_;
162              
163 13     21   52 return all { $_->is_a_type_of($type_name) } @{ $self->type_constraints };
  21         66  
  13         440  
164             }
165              
166             sub is_subtype_of {
167 13     13 1 32 my ($self, $type_name) = @_;
168              
169 13     22   54 return all { $_->is_subtype_of($type_name) } @{ $self->type_constraints };
  22         67  
  13         398  
170             }
171              
172             sub create_child_type {
173 5     5 1 22 my ( $self, %opts ) = @_;
174              
175 5         22 my $constraint
176             = Moose::Meta::TypeConstraint->new( %opts, parent => $self );
177              
178             # if we have a type constraint union, and no
179             # type check, this means we are just aliasing
180             # the union constraint, which means we need to
181             # handle this differently.
182             # - SL
183 5 100 100     34 if ( not( defined $opts{constraint} )
184             && $self->has_coercion ) {
185 1         12 $constraint->coercion(
186             Moose::Meta::TypeCoercion::Union->new(
187             type_constraint => $self,
188             )
189             );
190             }
191              
192 5         44 return $constraint;
193             }
194              
195             1;
196              
197             # ABSTRACT: A union of Moose type constraints
198              
199             __END__
200              
201             =pod
202              
203             =encoding UTF-8
204              
205             =head1 NAME
206              
207             Moose::Meta::TypeConstraint::Union - A union of Moose type constraints
208              
209             =head1 VERSION
210              
211             version 2.2203
212              
213             =head1 DESCRIPTION
214              
215             This metaclass represents a union of type constraints. A union takes
216             multiple type constraints, and is true if any one of its member
217             constraints is true.
218              
219             =head1 INHERITANCE
220              
221             C<Moose::Meta::TypeConstraint::Union> is a subclass of
222             L<Moose::Meta::TypeConstraint>.
223              
224             =head1 METHODS
225              
226             =head2 Moose::Meta::TypeConstraint::Union->new(%options)
227              
228             This creates a new class type constraint based on the given
229             C<%options>.
230              
231             It takes the same options as its parent. It also requires an
232             additional option, C<type_constraints>. This is an array reference
233             containing the L<Moose::Meta::TypeConstraint> objects that are the
234             members of the union type. The C<name> option defaults to the names
235             all of these member types sorted and then joined by a pipe (|).
236              
237             The constructor sets the implementation of the constraint so that is
238             simply calls C<check> on the newly created object.
239              
240             Finally, the constructor also makes sure that the object's C<coercion>
241             attribute is a L<Moose::Meta::TypeCoercion::Union> object.
242              
243             =head2 $constraint->type_constraints
244              
245             This returns the array reference of C<type_constraints> provided to
246             the constructor.
247              
248             =head2 $constraint->parent
249              
250             This returns the nearest common ancestor of all the components of the union.
251              
252             =head2 $constraint->check($value)
253              
254             =head2 $constraint->validate($value)
255              
256             These two methods simply call the relevant method on each of the
257             member type constraints in the union. If any type accepts the value,
258             the value is valid.
259              
260             With C<validate> the error message returned includes all of the error
261             messages returned by the member type constraints.
262              
263             =head2 $constraint->equals($type_name_or_object)
264              
265             A type is considered equal if it is also a union type, and the two
266             unions have the same member types.
267              
268             =head2 $constraint->find_type_for($value)
269              
270             This returns the first member type constraint for which C<check($value)> is
271             true, allowing you to determine which of the Union's member type constraints
272             a given value matches.
273              
274             =head2 $constraint->is_a_type_of($type_name_or_object)
275              
276             This returns true if all of the member type constraints return true
277             for the C<is_a_type_of> method.
278              
279             =head2 $constraint->is_subtype_of
280              
281             This returns true if all of the member type constraints return true
282             for the C<is_subtype_of> method.
283              
284             =head2 $constraint->create_child_type(%options)
285              
286             This returns a new L<Moose::Meta::TypeConstraint> object with the type
287             as its parent.
288              
289             =head1 BUGS
290              
291             See L<Moose/BUGS> for details on reporting bugs.
292              
293             =head1 AUTHORS
294              
295             =over 4
296              
297             =item *
298              
299             Stevan Little <stevan@cpan.org>
300              
301             =item *
302              
303             Dave Rolsky <autarch@urth.org>
304              
305             =item *
306              
307             Jesse Luehrs <doy@cpan.org>
308              
309             =item *
310              
311             Shawn M Moore <sartak@cpan.org>
312              
313             =item *
314              
315             יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
316              
317             =item *
318              
319             Karen Etheridge <ether@cpan.org>
320              
321             =item *
322              
323             Florian Ragwitz <rafl@debian.org>
324              
325             =item *
326              
327             Hans Dieter Pearcey <hdp@cpan.org>
328              
329             =item *
330              
331             Chris Prather <chris@prather.org>
332              
333             =item *
334              
335             Matt S Trout <mstrout@cpan.org>
336              
337             =back
338              
339             =head1 COPYRIGHT AND LICENSE
340              
341             This software is copyright (c) 2006 by Infinity Interactive, Inc.
342              
343             This is free software; you can redistribute it and/or modify it under
344             the same terms as the Perl 5 programming language system itself.
345              
346             =cut