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.2206';
3              
4 390     390   2787 use strict;
  390         902  
  390         11771  
5 390     390   2043 use warnings;
  390         937  
  390         9137  
6 390     390   2087 use metaclass;
  390         998  
  390         2283  
7              
8 390     390   186710 use Moose::Meta::TypeCoercion::Union;
  390         1086  
  390         14165  
9              
10 390     390   2750 use List::Util 1.33 qw(first all);
  390         7327  
  390         25467  
11              
12 390     390   2670 use parent 'Moose::Meta::TypeConstraint';
  390         924  
  390         1780  
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 298 my ($class, %options) = @_;
22              
23 77         478 my $name = join '|' => sort { $a cmp $b }
24 73         183 map { $_->name } @{ $options{type_constraints} };
  149         4073  
  73         196  
25              
26 73         525 my $self = $class->SUPER::new(
27             name => $name,
28             %options,
29             );
30              
31 73         2577 $self->_set_constraint( $self->_compiled_type_constraint );
32              
33 73         465 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 143 my $self = shift;
42              
43 75 100       356 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       116 if ( grep { $_->has_coercion } @{ $self->type_constraints } ) {
  83         2517  
  41         1193  
48 2         16 return $self->{coercion} = Moose::Meta::TypeCoercion::Union->new(
49             type_constraint => $self );
50             }
51             else {
52 39         281 return $self->{coercion} = undef;
53             }
54             }
55              
56             sub has_coercion {
57 72     72 1 265 return defined $_[0]->coercion;
58             }
59              
60             sub _actually_compile_type_constraint {
61 73     73   151 my $self = shift;
62              
63 73         151 my @constraints = @{ $self->type_constraints };
  73         2294  
64              
65             return sub {
66 1976     1976   6615 my $value = shift;
67 1976         3577 foreach my $type (@constraints) {
68 3375 100       19976 return 1 if $type->check($value);
69             }
70 1326         15242 return undef;
71 73         3745 };
72             }
73              
74             sub can_be_inlined {
75 232     232 1 587 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         373 for my $tc ( @{ $self->type_constraints } ) {
  232         6638  
80 460 100       1162 return 0 unless $tc->can_be_inlined;
81             }
82              
83 223         1022 return 1;
84             }
85              
86             sub _inline_check {
87 108     108   394 my $self = shift;
88 108         202 my $val = shift;
89              
90             return '('
91             . (
92 218         707 join ' || ', map { '(' . $_->_inline_check($val) . ')' }
93 108         201 @{ $self->type_constraints }
  108         3093  
94             )
95             . ')';
96             }
97              
98             sub inline_environment {
99 163     163 1 322 my $self = shift;
100              
101 327         547 return { map { %{ $_->inline_environment } }
  327         1095  
102 163         296 @{ $self->type_constraints } };
  163         4441  
103             }
104              
105             sub equals {
106 8     8 1 24 my ( $self, $type_or_name ) = @_;
107              
108 8         25 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
109              
110 8 100       63 return unless $other->isa(__PACKAGE__);
111              
112 3         7 my @self_constraints = @{ $self->type_constraints };
  3         105  
113 3         7 my @other_constraints = @{ $other->type_constraints };
  3         127  
114              
115 3 50       10 return unless @self_constraints == @other_constraints;
116              
117             # FIXME presort type constraints for efficiency?
118 3         9 constraint: foreach my $constraint ( @self_constraints ) {
119 6         17 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         20 return @other_constraints == 0;
128             }
129              
130             sub parent {
131 13     13 1 34 my $self = shift;
132              
133 13         24 my ($first, @rest) = @{ $self->type_constraints };
  13         431  
134              
135 13         72 for my $parent ( $first->_collect_all_parents ) {
136 30 100   30   149 return $parent if all { $_->is_a_type_of($parent) } @rest;
  30         105  
137             }
138              
139 1         9 return;
140             }
141              
142             sub validate {
143 6     6 1 18 my ($self, $value) = @_;
144 6         9 my $message;
145 6         14 foreach my $type (@{$self->type_constraints}) {
  6         223  
146 10         37 my $err = $type->validate($value);
147 10 100       39 return unless defined $err;
148 7 100       28 $message .= ($message ? ' and ' : '') . $err
    50          
149             if defined $err;
150             }
151 3         105 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         34  
  3         116  
158             }
159              
160             sub is_a_type_of {
161 13     13 1 47 my ($self, $type_name) = @_;
162              
163 13     21   52 return all { $_->is_a_type_of($type_name) } @{ $self->type_constraints };
  21         64  
  13         464  
164             }
165              
166             sub is_subtype_of {
167 13     13 1 40 my ($self, $type_name) = @_;
168              
169 13     22   61 return all { $_->is_subtype_of($type_name) } @{ $self->type_constraints };
  22         71  
  13         440  
170             }
171              
172             sub create_child_type {
173 5     5 1 24 my ( $self, %opts ) = @_;
174              
175 5         24 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     38 if ( not( defined $opts{constraint} )
184             && $self->has_coercion ) {
185 1         3 $constraint->coercion(
186             Moose::Meta::TypeCoercion::Union->new(
187             type_constraint => $self,
188             )
189             );
190             }
191              
192 5         23 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.2206
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