File Coverage

blib/lib/MooseX/TypeArray.pm
Criterion Covered Total %
statement 21 52 40.3
branch 4 14 28.5
condition 0 3 0.0
subroutine 6 9 66.6
pod 1 1 100.0
total 32 79 40.5


line stmt bran cond sub pod time code
1 1     1   827 use strict;
  1         2  
  1         237  
2 1     1   7 use warnings;
  1         3  
  1         58  
3              
4             package MooseX::TypeArray;
5             BEGIN {
6 1     1   34 $MooseX::TypeArray::VERSION = '0.1.0';
7             }
8              
9             # ABSTRACT: Create composite types where all subtypes must be satisfied
10              
11              
12 1         12 use Sub::Exporter -setup => {
13             exports => [qw( typearray )],
14             groups => [ default => [qw( typearray )] ],
15 1     1   1457 };
  1         16762  
16              
17             my $sugarmap = {
18              
19             # '' => sub {},
20             'ARRAY' => sub { { name => undef, combining => $_[0], } },
21             'HASH' => sub { { name => undef, %{ $_[0] }, combining => [ @{ $_[0]->{combining} || [] } ], } },
22              
23             # '_string' => sub {},
24             '_string,ARRAY' => sub { { name => $_[0], combining => $_[1], } },
25             '_string,HASH' => sub { { name => $_[0], %{ $_[1] }, combining => [ @{ $_[1]->{combining} || [] } ], } },
26             'ARRAY,HASH' => sub { { name => undef, %{ $_[1] }, combining => [ @{ $_[1]->{combining} || [] }, @{ $_[0] } ], } },
27             '_string,ARRAY,HASH' => sub { { name => $_[0], %{ $_[2] }, combining => [ @{ $_[2]->{combining} || [] }, @{ $_[1] } ], } },
28              
29             };
30              
31             sub _desugar_typearray {
32 10     10   19156 my (@args) = @_;
33 10 100       16 my (@argtypes) = map { ref $_ ? ref $_ : '_string' } @args;
  16         48  
34 10         19 my $signature = join q{,}, @argtypes;
35              
36 10 100       110 if ( exists $sugarmap->{$signature} ) {
37 6         14 return $sugarmap->{$signature}->(@args);
38             }
39 24         57 return __PACKAGE__->_throw_error( 'Unexpected parameters types passed: <'
40             . $signature . '>,' . qq{\n}
41             . 'Expected one from [ '
42 4         14 . ( join q{, }, map { '<' . $_ . '>' } sort keys %{$sugarmap} )
  4         22  
43             . ' ] ' );
44             }
45              
46             sub _check_conflict_names {
47 0     0   0 my ( $name, $package ) = @_;
48 0         0 require Moose::Util::TypeConstraints;
49              
50 0         0 my $type = Moose::Util::TypeConstraints::find_type_constraint($name);
51              
52 0 0 0     0 if ( defined $type and $type->_package_defined_in eq $package ) {
53 0         0 __PACKAGE__->_throw_error( "The type constraint '$name' has already been created in "
54             . $type->_package_defined_in
55             . " and cannot be created again in $package " );
56             }
57 0 0       0 if ( $name !~ /\A[[:word:]:.]+\z/sxm ) {
58 0         0 __PACKAGE__->_throw_error(
59             sprintf q{%s contains invalid characters for a type name. Names can contain alphanumeric character, ":", and "."%s},
60             $name, qq{\n} );
61             }
62 0         0 return 1;
63             }
64              
65             sub _convert_type_names {
66 0     0   0 my ( $name, @types ) = @_;
67 0         0 require Moose::Util::TypeConstraints;
68 0         0 my @out;
69 0         0 for my $type (@types) {
70 0         0 my $translated_type = Moose::Util::TypeConstraints::find_or_parse_type_constraint($type);
71 0 0       0 if ( not $translated_type ) {
72 0         0 __PACKAGE__->_throw_error("Could not locate type constraint ($type) for the TypeArray");
73             }
74 0         0 push @out, $translated_type;
75             }
76 0         0 return @out;
77             }
78              
79             ## no critic ( RequireArgUnpacking )
80              
81              
82             sub typearray {
83 0     0 1 0 my $config = _desugar_typearray(@_);
84              
85 0         0 $config->{package_defined_in} = scalar caller 0;
86              
87 0 0       0 _check_conflict_names( $config->{name}, $config->{package_defined_in} ) if defined $config->{name};
88              
89 0         0 $config->{combining} = [ _convert_type_names( $config->{name}, @{ $config->{combining} } ) ];
  0         0  
90              
91 0         0 require Moose::Meta::TypeConstraint::TypeArray;
92              
93 0         0 my $constraint = Moose::Meta::TypeConstraint::TypeArray->new( %{$config} );
  0         0  
94              
95 0 0       0 if ( defined $config->{name} ) {
96 0         0 require Moose::Util::TypeConstraints;
97 0         0 Moose::Util::TypeConstraints::register_type_constraint($constraint);
98             }
99              
100 0         0 return $constraint;
101              
102             }
103              
104             ## no critic ( RequireArgUnpacking )
105             sub _throw_error {
106 4     4   7 shift;
107 4         1651 require Moose;
108 0           unshift @_, 'Moose';
109 0           goto &Moose::throw_error;
110             }
111              
112             1;
113              
114             __END__
115             =pod
116              
117             =head1 NAME
118              
119             MooseX::TypeArray - Create composite types where all subtypes must be satisfied
120              
121             =head1 VERSION
122              
123             version 0.1.0
124              
125             =head1 SYNOPSIS
126              
127             {
128             package #
129             Foo;
130             use Moose::Util::TypeConstraint;
131             use MooseX::TypeArray;
132             subtype 'Natural',
133             as 'Int',
134             where { $_ > 0 };
135             message { "This number ($_) is not bigger then 0" };
136              
137             subtype 'BiggerThanTen',
138             as 'Int',
139             where { $_ > 10 },
140             message { "This number ($_) is not bigger than ten!" };
141              
142              
143             typearray NaturalAndBiggerThanTen => [ 'Natural', 'BiggerThanTen' ];
144              
145             # or this , which is the same thing.
146              
147             typearray NaturalAndBiggerThanTen => {
148             combining => [qw( Natural BiggerThanTen )],
149             };
150              
151              
152             ...
153              
154             has field => (
155             isa => 'NaturalAndBiggerThanTen',
156             ...
157             );
158              
159             ...
160             }
161             use Try::Tiny;
162             use Data::Dumper qw( Dumper );
163              
164             try {
165             Foo->new( field => 0 );
166             } catch {
167             print Dumper( $_ );
168             #
169             # bless({ errors => {
170             # Natural => "This number (0) is not bigger then 0",
171             # BiggerThanTen => "This number (0) is not bigger than ten!"
172             # }}, 'MooseX::TypeArray::Error' );
173             #
174             print $_;
175              
176             # Validation failed for TypeArray NaturalAndBiggerThanTen with value "0" :
177             # 1. Validation failed for Natural:
178             # This number (0) is not bigger than 0
179             # 2. Validation failed for BiggerThanTen:
180             # This number (0) is not bigger than ten!
181             #
182             }
183              
184             =head1 DESCRIPTION
185              
186             This type constraint is much like the "Union" type constraint, except the union
187             type constraint validates when any of its members are valid. This type
188             constraint requires B<ALL> of its members to be valid.
189              
190             This type constraint also returns an Object with a breakdown of the composite
191             failed constraints on error, which you should be able to use if you work with
192             this type constraint directly.
193              
194             Alas, Moose itself currently doesn't support propagation of objects as
195             validation methods, so you will only get the stringified version of this object
196             until that is solved.
197              
198             Alternatively, you can use L<MooseX::Attribute::ValidateWithException> until
199             Moose natively supports exceptions.
200              
201             =head1 FUNCTIONS
202              
203             =head2 typearray
204              
205             This function has 2 forms, anonymous and named.
206              
207             =head2 typearray $NAME, \@CONSTRAINTS
208              
209             typearray 'foo', [ 'SubTypeA', 'SubTypeB' ];
210             # the same as
211             typearray { name => 'foo', combining => [ 'SubTypeA', 'SubTypeB' ] };
212              
213             =head2 typearray $NAME, \@CONSTRAINTS, \%CONFIG
214              
215             typearray 'foo', [ 'SubTypeA', 'SubTypeB' ], { blah => "blah" };
216             # the same as
217             typearray { name => 'foo', combining => [ 'SubTypeA', 'SubTypeB' ], blah => "blah" };
218              
219             =head2 typearray $NAME, \%CONFIG
220              
221             typearray 'foo', { blah => "blah" };
222             # the same as
223             typearray { name => 'foo', blah => "blah" };
224              
225             =head2 typearray \@CONSTRAINTS
226              
227             typearray [ 'SubTypeA', 'SubTypeB' ];
228             # the same as
229             typearray { combining => [ 'SubTypeA', 'SubTypeB' ] };
230              
231             =head2 typearray \@CONSTRAINTS, \%CONFIG
232              
233             typearray [ 'SubTypeA', 'SubTypeB' ], { blah => "blah};
234             # the same as
235             typearray { combining => [ 'SubTypeA', 'SubTypeB' ] , blah => "blah" };
236              
237             =head2 typearray \%CONFIG
238              
239             typearray {
240             name => $name # the name of the type ( ie: 'MyType' or 'NaturalBigInt' )
241             combining => $arrayref # the subtypes which must be satisfied for this constraint
242             };
243              
244             No other keys are recognised at this time.
245              
246             =head1 AUTHOR
247              
248             Kent Fredric <kentnl@cpan.org>
249              
250             =head1 COPYRIGHT AND LICENSE
251              
252             This software is copyright (c) 2011 by Kent Fredric <kentnl@cpan.org>.
253              
254             This is free software; you can redistribute it and/or modify it under
255             the same terms as the Perl 5 programming language system itself.
256              
257             =cut
258