File Coverage

blib/lib/Poz/Types/array.pm
Criterion Covered Total %
statement 99 104 95.1
branch 27 30 90.0
condition 3 3 100.0
subroutine 23 24 95.8
pod 9 11 81.8
total 161 172 93.6


line stmt bran cond sub pod time code
1             package Poz::Types::array;
2 11     11   220 use 5.032;
  11         42  
3 11     11   55 use strict;
  11         20  
  11         229  
4 11     11   44 use warnings;
  11         17  
  11         476  
5 11     11   67 use Carp ();
  11         20  
  11         278  
6 11     11   99 use Try::Tiny;
  11         168  
  11         875  
7 11     11   82 use parent 'Poz::Types';
  11         20  
  11         90  
8              
9             sub new {
10 14     14 1 34 my ($class, $validator) = @_;
11 14 50       91 if (!$validator->isa('Poz::Types')) {
12 0         0 Carp::croak("Invalid validator: is not a subclass of Poz::Types");
13             }
14 14         99 my $self = bless {
15             __validator__ => $validator,
16             __as__ => undef,
17             __rules__ => [],
18             __optional__ => 0,
19             __default__ => undef,
20             transform => [],
21             }, $class;
22 14         76 return $self;
23             }
24              
25             sub as {
26 1     1 1 3 my ($self, $typename) = @_;
27 1         44 $self->{__as__} = $typename;
28 1         7 return $self;
29             }
30              
31             sub parse {
32 22     22 1 430 my ($self, $data) = @_;
33 22         63 my ($valid, $errors) = $self->safe_parse($data);
34 22 100       80 if ($errors) {
35 6         39 my $error_message = _errors_to_string($errors);
36 6         79 Carp::croak($error_message);
37             }
38 16         93 return $valid;
39             }
40              
41             sub safe_parse {
42 25 100   25 1 2476 Carp::croak "Must handle error" unless wantarray;
43              
44 24         49 my ($self, $data) = @_;
45 24         46 my @parsed = ();
46 24         39 my @errors = ();
47 24 100 100     118 if (defined $self->{__default__} && !defined $data) {
48 2         5 $data = $self->{__default__};
49             }
50 24 100       112 if (!defined $data) {
    50          
51 2 50       10 if ($self->{__optional__}) {
52 2         8 return (undef, undef);
53             } else {
54 0         0 push @errors, {
55             key => undef,
56             error => "Required"
57             };
58             }
59             } elsif (ref($data) ne 'ARRAY') {
60 0         0 push @errors, {
61             key => undef,
62             error => "Invalid data: is not arrayref"
63             };
64             } else {
65 22         35 for my $rule (@{$self->{__rules__}}) {
  22         64  
66 8         19 my $err = $rule->($self, $data);
67 8 100       26 if (defined $err) {
68 4         22 push @errors, {
69             key => undef,
70             error => $err,
71             };
72             }
73             }
74 22         40 for my $i (0 .. $#{$data}) {
  22         67  
75 66         973 my $v = $self->{__validator__};
76 66         130 my $val = $data->[$i];
77             try {
78 66     66   5798 my $_parsed = $v->parse($val);
79 63         174 push @parsed, $_parsed;
80             } catch {
81 3     3   2589 my $error_message = $_;
82 3         34 $error_message =~ s/ at .+ line [0-9]+\.\n//;
83 3         55 push @errors, {
84             key => $i,
85             error => $error_message,
86             };
87             }
88 66         535 }
89             }
90 22 100       368 if (scalar(@errors) > 0) {
91 7         38 return (undef, [@errors])
92             }
93 15         72 my $classname = $self->{__as__};
94 15 100       63 my $valid = $classname ? bless [@parsed], $classname : [@parsed];
95 15         58 return ($valid, undef);
96             }
97              
98             sub _errors_to_string {
99 6     6   10 my $errors = shift;
100 6         15 my @error_strings = ();
101 6         14 for my $error (@$errors) {
102             my $message = $error->{key} ?
103             sprintf("%s on key `%s`", $error->{error}, $error->{key}) :
104 6 100       30 sprintf("%s", $error->{error});
105 6         26 push @error_strings, $message;
106             }
107 6         24 return join(", and ", @error_strings);
108             }
109              
110             sub min {
111 1     1 1 3 my ($self, $min) = @_;
112 1         9 push @{$self->{__rules__}}, sub {
113 2     2   5 my ($self, $value) = @_;
114 2 100       8 return "Array is too short" if scalar(@$value) < $min;
115 1         3 return;
116 1         2 };
117 1         4 return $self;
118             }
119              
120             sub max {
121 1     1 1 4 my ($self, $max) = @_;
122 1         7 push @{$self->{__rules__}}, sub {
123 2     2   6 my ($self, $value) = @_;
124 2 100       9 return "Array is too long" if scalar(@$value) > $max;
125 1         3 return;
126 1         3 };
127 1         4 return $self;
128             }
129              
130             sub length {
131 1     1 1 4 my ($self, $length) = @_;
132 1         7 push @{$self->{__rules__}}, sub {
133 2     2   4 my ($self, $value) = @_;
134 2 100       11 return "Array is not of length $length" if scalar(@$value) != $length;
135 1         3 return;
136 1         3 };
137 1         4 return $self;
138             }
139              
140             sub nonempty {
141 1     1 1 3 my ($self) = @_;
142 1         7 push @{$self->{__rules__}}, sub {
143 2     2   5 my ($self, $value) = @_;
144 2 100       24 return "Array is empty" if scalar(@$value) == 0;
145 1         3 return;
146 1         2 };
147 1         4 return $self;
148             }
149              
150             sub element {
151 0     0 1 0 my ($self) = @_;
152 0         0 return $self->{__validator__};
153             }
154              
155             sub optional {
156 3     3 0 9 my ($self) = @_;
157 3         13 $self->{__optional__} = 1;
158 3         14 return $self;
159             }
160              
161             sub default {
162 2     2 0 6 my ($self, $default) = @_;
163 2         5 $self->{__default__} = $default;
164 2         9 return $self;
165             }
166              
167             1;
168              
169             =head1 NAME
170              
171             Poz::Types::array - Array type validation for Poz::Types
172              
173             =head1 SYNOPSIS
174              
175             use Poz qw/z/;
176              
177             my $array_validator = z->array(z->number);
178              
179             $array_validator->min(1)->max(5)->nonempty();
180              
181             my $data = [1, 2, 3];
182             my $validated_data = $array_validator->parse($data);
183              
184             =head1 DESCRIPTION
185              
186             Poz::Types::array provides a way to validate arrays with various rules. It is designed to work with the Poz.
187              
188             =head1 METHODS
189              
190             =head2 as
191              
192             $array_validator->as('ArrayClass');
193              
194             Sets the class name to bless the validated array into.
195              
196             =head2 parse
197              
198             my $validated_data = $array_validator->parse($data);
199              
200             Parses and validates the data. Throws an exception if validation fails.
201              
202             =head2 safe_parse
203              
204             my ($validated_data, $errors) = $array_validator->safe_parse($data);
205              
206             Parses and validates the data. Returns the validated data and any errors.
207              
208             =head2 min
209              
210             $array_validator->min($min_length);
211              
212             Sets a minimum length for the array.
213              
214             =head2 max
215              
216             $array_validator->max($max_length);
217              
218             Sets a maximum length for the array.
219              
220             =head2 length
221              
222             $array_validator->length($exact_length);
223              
224             Sets an exact length for the array.
225              
226             =head2 nonempty
227              
228             $array_validator->nonempty();
229              
230             Ensures the array is not empty.
231              
232             =head2 element
233              
234             my $element_validator = $array_validator->element();
235              
236             Returns the element validator.
237              
238             =head1 LICENSE
239              
240             Copyright (C) ytnobody.
241              
242             This library is free software; you can redistribute it and/or modify
243             it under the same terms as Perl itself.
244              
245             =head1 AUTHOR
246              
247             ytnobody E<lt>ytnobody@gmail.comE<gt>
248              
249             =cut