File Coverage

blib/lib/Catmandu/Validator.pm
Criterion Covered Total %
statement 54 55 98.1
branch 21 22 95.4
condition 11 11 100.0
subroutine 8 8 100.0
pod 2 2 100.0
total 96 98 97.9


line stmt bran cond sub pod time code
1             package Catmandu::Validator;
2              
3 7     7   4232 use Catmandu::Sane;
  7         21  
  7         47  
4              
5             our $VERSION = '1.2020';
6              
7 7     7   58 use Catmandu::Util qw(:is);
  7         15  
  7         2054  
8 7     7   56 use Moo::Role;
  7         26  
  7         54  
9 7     7   3850 use namespace::clean;
  7         18  
  7         44  
10              
11             requires 'validate_data';
12              
13             has 'last_errors' =>
14             (is => 'rwp', clearer => '_clear_last_errors', init_arg => undef,);
15              
16             has 'after_callback' => (is => 'ro', clearer => 1,);
17              
18             has 'error_callback' => (is => 'ro', clearer => 1,);
19              
20             has 'error_field' => (is => 'ro', clearer => 1,);
21              
22             has ['valid_count', 'invalid_count'] =>
23             (is => 'rwp', init_arg => undef, default => sub {0},);
24              
25             sub is_valid {
26 10     10 1 28 my ($self, $data) = @_;
27              
28 10 50       40 if (!is_hash_ref($data)) {
29 0         0 Catmandu::BadArg->throw('Cannot validate data of this type');
30             }
31              
32 10         295 $self->_clear_last_errors;
33 10         86 $self->_set_valid_count(0);
34 10         24 $self->_set_invalid_count(0);
35              
36 10         33 my $errors = $self->validate_data($data);
37              
38 10 100       37 if ($errors) {
39 6         38 $self->_set_last_errors($errors);
40 6         17 $self->_set_invalid_count(1);
41 6         39 return 0;
42             }
43             else {
44 4         16 $self->_set_valid_count(1);
45             }
46              
47 4         39 1;
48             }
49              
50             sub validate {
51 12     12 1 4197 my ($self, $data) = @_;
52              
53 12         44 $self->_set_valid_count(0);
54 12         28 $self->_set_invalid_count(0);
55              
56             # handle a single record
57 12 100       60 if (is_hash_ref($data)) {
58 5         16 return $self->_process_record($data);
59             }
60              
61             # handle arrayref, returns a new arrayref
62 7 100       24 if (is_array_ref($data)) {
63 5         13 return [grep {defined} map {$self->_process_record($_)} @$data];
  16         63  
  16         59  
64             }
65              
66             # handle iterators, returns a new iterator
67 2 100       12 if (is_invocant($data)) {
68 1     3   12 return $data->select(sub {$self->_process_record($_[0])});
  3         8  
69             }
70              
71 1         20 Catmandu::BadArg->throw('Cannot validate data of this type');
72             }
73              
74             sub _process_record {
75 24     24   44 my ($self, $data) = @_;
76              
77 24 100 100     111 my $error_field
78             = ($self->error_field || 0) eq '1'
79             ? '_validation_errors'
80             : $self->error_field;
81              
82 24         459 $self->_clear_last_errors;
83 24         145 my $errors = $self->validate_data($data);
84 24         164 $self->_set_last_errors($errors);
85              
86 24 100       50 if ($errors) {
87 14         37 $self->_set_invalid_count(1 + $self->invalid_count);
88             }
89             else {
90 10         30 $self->_set_valid_count(1 + $self->valid_count);
91             }
92              
93 24 100 100     77 if ($errors && $error_field) {
94 4         9 $data->{$error_field} = $errors;
95             }
96              
97 24 100       59 if ($self->after_callback) {
98 8         20 return $self->after_callback->($data, $errors);
99             }
100              
101 16 100 100     54 if ($errors && $self->error_callback) {
102 2         11 $self->error_callback->($data, $errors);
103 2         10 return;
104             }
105              
106 14 100 100     50 return if $errors && !$error_field;
107              
108 11         45 $data;
109             }
110              
111             1;
112              
113             __END__
114              
115             =pod
116              
117             =head1 NAME
118              
119             Catmandu::Validator - Namespace for packages that can validate items in Catmandu
120              
121             =head1 SYNOPSIS
122              
123             use Catmandu::Validator::Simple;
124              
125             my $validator = Catmandu::Validator::Simple->new(
126             handler => sub {
127             $data = shift;
128             return "error" unless $data->{title} =~ m/good title/;
129             return;
130             }
131             );
132              
133             if ( $validator->is_valid($hashref) ) {
134             save_record_in_database($hashref);
135             } else {
136             reject_form($validator->last_errors);
137             }
138              
139             my $validator = Catmandu::Validator::Simple->new(
140             handler => sub { ...},
141             error_callback => sub {
142             my ($data, $errors) = @_;
143             print "Validation errors for record $data->{_id}:\n";
144             print "$_\n" for @{$errors};
145             }
146             };
147              
148             my $validated_arrayref = $validator->validate($arrayref);
149              
150             $validator->validate($iterator, {
151             after_callback => sub {
152             my ($record, $errors) = @_;
153             if ($errors) {
154             add_to_failure_report($rec, $errors);
155             #omit the invalid record from the result
156             return undef;
157             }
158             return $rec;
159             }
160             })->each( sub {
161             my $record = shift;
162             publish_record($record);
163             });
164              
165             See L<Catmandu::Fix::validate> and L<Catmandu::Fix::Condition::valid> to use validators in fixes (L<Catmandu::Fix>).
166              
167             =head1 DESCRIPTION
168              
169             A Catmandu::Validator is a base class for Perl packages that can validate data.
170              
171             =head1 METHODS
172              
173             =head2 new()
174              
175             Create a new Catmandu::Validator.
176              
177             =head2 new( after_callback => \&callback )
178              
179             The after_callback is called after each record has been validated.
180             The callback function should take $hashref to each data record, and $arrayref to list of validation errors
181             for the record as arguments.
182              
183             =head2 new( error_field => $field_name )
184              
185             If the error_field parameter is set, then during validation each record that
186             fails validation will get an extra field added containing an
187             arrayref to the validation errors. The name of the key will be the
188             value passed or '_validation_errors' if 1 is passed. By default it is not set.
189              
190             =head2 is_valid( \%hash )
191              
192             Validates a single record. Returns 1 success and 0 on failure. Information about the validation errors
193             can be retrieved with the L</"last_errors()"> method.
194              
195             =head2 validate( \%hash )
196              
197             =head2 validate( $iterator )
198              
199             =head2 validate( \@array )
200              
201             Validates a single record or multiple records in an iterator or an array. Returns validated records in the same type of
202             container for multiple records or the record itself for a single record. The default behaviour is to return the records that passed validation unchanged and omit the invalid records.
203             This behaviour can be changed by setting the I<after_callback> or the I<error_field> in the constructor. Returns undef on validation failure for single records.
204              
205             =head2 last_errors()
206              
207             Returns arrayref of errors from the record that was last validated if that record failed validation
208             or undef if there were no errors.
209              
210             =head2 valid_count()
211              
212             Returns the number of valid records from last validate operation.
213              
214             =head2 invalid_count()
215              
216             Returns the number of invalid records from the last validate operation.
217              
218             =head1 SEE ALSO
219              
220             L<Catmandu::Validator::Env> and L<Catmandu::Validator::Simple>.
221              
222             L<Catmandu::Iterable>
223              
224             =cut