File Coverage

blib/lib/App/Manoc/Utils/Validate.pm
Criterion Covered Total %
statement 73 79 92.4
branch 37 42 88.1
condition 7 9 77.7
subroutine 8 8 100.0
pod 1 1 100.0
total 126 139 90.6


line stmt bran cond sub pod time code
1             package App::Manoc::Utils::Validate;
2              
3             # ABSTRACT: Helpers for data validation.
4              
5 1     1   15852 use strict;
  1         4  
  1         38  
6 1     1   10 use warnings;
  1         2  
  1         61  
7              
8             our $VERSION = '2.99.2'; ##TRIAL VERSION
9              
10 1     1   10 use Carp 'croak';
  1         3  
  1         88  
11              
12             BEGIN {
13 1     1   9 use Exporter 'import';
  1         3  
  1         53  
14 1     1   727 our @EXPORT_OK = qw/
15             validate
16             /;
17             }
18              
19              
20              
21             sub validate {
22 26     26 1 9982 my $value = shift;
23 26         46 my $rule = shift;
24 26         60 my %options = @_;
25              
26             # get value type
27 26         55 my $ref = ref($value);
28 26         40 my $type;
29 26 100       78 if ( !$ref ) {
    100          
    50          
30 10         18 $type = 'scalar';
31             }
32             elsif ( $ref eq 'HASH' ) {
33 9         19 $type = 'hash';
34             }
35             elsif ( $ref eq 'ARRAY' ) {
36 7         12 $type = 'array';
37             }
38             else {
39             return {
40 0         0 valid => 0,
41             error => "Unsupported data type",
42             };
43             }
44              
45 26 50 66     75 if ( !exists( $rule->{type} ) and $rule->{arrayof} ) {
46 1         3 $rule->{type} = 'array';
47             }
48              
49             # check type if required, return immediately on error
50 26 50       58 if ( $rule->{type} ) {
51 26         46 my $expected_type = $rule->{type};
52 26 100 66     130 if ( $expected_type ne 'any' && $expected_type ne $type ) {
53             return {
54 7         32 valid => 0,
55             error => "Expected $expected_type",
56             };
57             }
58             }
59             else {
60 0         0 croak 'type rule is required';
61             }
62              
63             # recurse if required
64 19         34 my $validation;
65 19 100       53 if ( $type eq 'array' ) {
    100          
66 4         47 $validation = _validate_array( $value, $rule, %options );
67             }
68             elsif ( $type eq 'hash' ) {
69 7         26 $validation = _validate_hash( $value, $rule, %options );
70             }
71              
72 19 100 100     83 if ( $validation && $validation->{valid} == 0 ) {
73 5         21 return $validation;
74             }
75 14         52 return { valid => 1 };
76             }
77              
78             # recurse into elements
79             sub _validate_array {
80 4     4   9 my $data = shift;
81 4         8 my $rule = shift;
82 4         8 my %options = @_;
83              
84 4         9 my $errors = [];
85              
86             # loop on elements if required by items
87 4 100       12 if ( my $item_rule = $rule->{arrayof} ) {
88 2         4 my $i = 0;
89              
90 2         5 foreach my $element (@$data) {
91 5         15 my $validation = validate( $element, $item_rule, %options );
92 5 100       36 if ( !$validation->{valid} ) {
93 2 50       7 if ( $validation->{error} ) {
94 2         15 push @$errors, { field => $i, error => $validation->{error} };
95             }
96             else {
97 0         0 foreach my $e ( @{ $validation->{errors} } ) {
  0         0  
98 0         0 $e->{field} = $i . "." . $e->{field};
99 0         0 push @$errors, $e;
100             }
101             }
102             }
103 5         20 $i++;
104             }
105             }
106              
107 4 100       12 if ( scalar(@$errors) ) {
108 1         5 return { valid => 0, errors => $errors };
109             }
110             else {
111 3         10 return { valid => 1 };
112             }
113             }
114              
115             # recurse into hash
116             sub _validate_hash {
117 7     7   12 my $data = shift;
118 7         12 my $rule = shift;
119 7         15 my %options = @_;
120              
121 7         14 my $errors = [];
122              
123 7 100       23 if ( my $item_rules = $rule->{items} ) {
124              
125             ITEM:
126 5         25 while ( my ( $field, $item_rule ) = each(%$item_rules) ) {
127 12 100       33 if ( !exists $data->{$field} ) {
128             # give error if it is required
129 2 100       11 if ( $item_rule->{required} ) {
130 1         8 push @$errors,
131             {
132             field => $field,
133             error => "Missing required field",
134             };
135             }
136              
137             }
138             else {
139             # check value
140 10         17 my $value = $data->{$field};
141              
142 10         25 my $validation = validate( $value, $item_rule, %options );
143 10 100       46 if ( !$validation->{valid} ) {
144 3 100       15 if ( $validation->{error} ) {
145 2         10 push @$errors, { field => $field, error => $validation->{error} };
146             }
147             else {
148 1         3 foreach my $e ( @{ $validation->{errors} } ) {
  1         5  
149 2         8 $e->{field} = $field . "." . $e->{field};
150 2         11 push @$errors, $e;
151             }
152             }
153             }
154             }
155             } # end ITEM loop
156              
157             # check for unknown items
158 5 50       17 if ( !$rule->{ignore_extra_items} ) {
159 5         18 for my $k ( keys(%$data) ) {
160              
161 11 100       30 next if exists( $item_rules->{$k} );
162              
163 1         6 push @$errors,
164             {
165             field => $k,
166             error => "Unexpected field",
167             };
168             }
169             }
170             }
171              
172 7 100       37 return { valid => scalar(@$errors) == 0 ? 1 : 0, errors => $errors };
173             }
174              
175             1;
176             # Local Variables:
177             # mode: cperl
178             # indent-tabs-mode: nil
179             # cperl-indent-level: 4
180             # cperl-indent-parens-as-block: t
181             # End:
182              
183             __END__
184              
185             =pod
186              
187             =head1 NAME
188              
189             App::Manoc::Utils::Validate - Helpers for data validation.
190              
191             =head1 VERSION
192              
193             version 2.99.2
194              
195             =head1 DESCRIPTION
196              
197             These package contains helpers for data validation.
198              
199             =head1 FUNCTIONS
200              
201             =head2 validate($value, \%rule, %options)
202              
203             Validate $value using %rule. Rule can have the following clauses:
204              
205             =over 4
206              
207             =item type
208              
209             On of scalar, array, hash.
210              
211             =item arrayof
212              
213             A rule to valudidate each element of an array value. No effects for other values of other types.
214              
215             =item items
216              
217             An hash reference to validate hash values, made of ('name_of_the_key', \%rule) pairs. See also required.
218              
219             =item required
220              
221             For rules used to validate hash values, set the element as required.
222              
223             =item ignore_extra_items
224              
225             When validating an hash values with items rules, do not return errors for unrecognized keys.
226              
227             =back
228              
229             =head1 AUTHORS
230              
231             =over 4
232              
233             =item *
234              
235             Gabriele Mambrini <gmambro@cpan.org>
236              
237             =item *
238              
239             Enrico Liguori
240              
241             =back
242              
243             =head1 COPYRIGHT AND LICENSE
244              
245             This software is copyright (c) 2017 by Gabriele Mambrini.
246              
247             This is free software; you can redistribute it and/or modify it under
248             the same terms as the Perl 5 programming language system itself.
249              
250             =cut