File Coverage

blib/lib/FormValidator/Tiny.pm
Criterion Covered Total %
statement 368 372 99.1
branch 180 198 90.9
condition 66 80 82.5
subroutine 77 77 100.0
pod 9 9 100.0
total 700 736 95.2


line stmt bran cond sub pod time code
1             package FormValidator::Tiny;
2             $FormValidator::Tiny::VERSION = '0.003';
3 23     23   3371654 use v5.18;
  23         90  
4 23     23   132 use warnings;
  23         49  
  23         707  
5              
6 23     23   131 use List::Util qw( any pairs pairgrep pairmap );
  23         47  
  23         1901  
7 23     23   151 use Scalar::Util qw( blessed looks_like_number );
  23         52  
  23         1248  
8 23     23   6756 use experimental qw( regex_sets );
  23         61862  
  23         135  
9              
10 23     23   1647 use Exporter;
  23         53  
  23         2819  
11              
12             BEGIN {
13 23     23   298 our @ISA = qw( Exporter );
14 23         65 our @EXPORT = qw( validation_spec validate_form );
15 23         83 my @export_predicates = qw(
16             limit_character_set
17             length_in_range
18             equal_to
19             number_in_range
20             one_of
21             );
22 23         43 my @export_filters = qw(
23             split_by
24             trim
25             );
26 23         56 our @EXPORT_OK = (@export_predicates, @export_filters);
27 23         24009 our %EXPORT_TAGS = (
28             validation => \@EXPORT,
29             predicates => \@export_predicates,
30             filters => \@export_filters,
31             all => [ @EXPORT, @export_predicates, @export_filters ],
32             );
33             }
34              
35             # ABSTRACT: A tiny form validator
36              
37             my %coercer = (
38             '+' => sub { (1, '', 0+$_[0]) },
39             '?' => sub { (1, '', length($_[0]) > 0) },
40             '?+' => sub { (1, '', !!(0+$_[0])) },
41             '?perl' => sub { (1, '', !!$_[0]) },
42             '[]' => sub { (1, '', [ _listy($_[0]) ]) },
43             '{}' => sub { (1, '', ref $_[0] ? +{ _listy($_[0]) } : { $_[0] => $_[0] }) },
44             );
45              
46             sub _sub_coercer {
47 1     1   3 my ($sub) = @_;
48             sub {
49 1     1   2 local $_ = $_[0];
50 1         3 my $into = $sub->(@_);
51 1         6 (1, '', $into);
52             }
53 1         4 }
54              
55             sub _yes_no_coercer {
56 3     3   10 my ($yes, $no) = @_;
57 3         7 my $fcyes = fc $yes;
58 3         6 my $fcno = fc $no;
59             sub {
60 3     3   16 my $fc_ = fc $_[0];
61 3 100       8 my $truth = $fc_ eq $fcyes ? 1
    100          
62             : $fc_ eq $fcno ? 0
63             : undef;
64              
65 3         13 return (defined $truth, qq[Enter "$yes" or "$no".], $truth);
66 3         11 };
67             }
68              
69             sub _package_coercer {
70 1     1   4 my ($package) = @_;
71 1     1   10 sub { (1, '', $package->new($_[0])) }
72 1         6 }
73              
74             sub _type_coercer {
75 1     1   4 my ($type) = @_;
76 1     1   5 sub { (1, '', $type->coerce($_[0])) }
77 1         5 }
78              
79             sub _sub_validator {
80 17     17   38 my ($sub) = @_;
81             sub {
82 56 100   56   152 return (1, '', $_[0]) unless defined $_[0];
83 55         130 local $_ = $_[0];
84 55         181 my ($valid, $error) = $sub->(@_);
85 55         402 ($valid, $error, $_[0]);
86 17         72 };
87             }
88              
89             sub _re_validator {
90 6     6   17 my ($re) = @_;
91             sub {
92 6     6   14 my ($value) = @_;
93 6 50       15 return (1, '', $value) unless defined $value;
94 6         33 my $valid = $value =~ /$re/;
95 6         19 ($valid, 'Incorrect.', $value);
96 6         36 };
97             }
98              
99             sub _type_validator {
100 3     3   7 my ($type) = @_;
101 3 100       15 if ($type->can('check')) {
    50          
102 1     1   822 my $message = $type->can('get_message') ? sub { $type->get_message($_[0]) }
103 2 100       12 : 'Incorrect.';
104              
105             return sub {
106 4     4   7 my ($value) = @_;
107 4 50       7 return (1, '', $value) unless defined $value;
108 4         13 my $valid = $type->check($value);
109 4         18 ($valid, $message, $value);
110             }
111 2         13 }
112             elsif ($type->can('validate')) {
113             return sub {
114 2     2   4 my ($value) = @_;
115 2 50       5 return (1, '', $value) unless defined $value;
116 2         6 my $message = $type->validate($value);
117 2   100     18 (!defined $message, $message//'', $value);
118             }
119 1         5 }
120              
121 0         0 die "bad type encountered"; # uncoverable statement
122             }
123              
124             sub _with_error {
125 2     2   5 my ($decl, $with_error) = @_;
126             sub {
127 5     5   10 my ($valid, $decl_message, $value) = $decl->(@_);
128 5         16 ($valid, $with_error, $value);
129             }
130 2         11 }
131              
132             sub _listy {
133 78     78   187 my ($stuff) = @_;
134 78 100       457 return @$stuff if 'ARRAY' eq ref $stuff;
135 28 100       207 return %$stuff if 'HASH' eq ref $stuff;
136 1         4 return ($stuff);
137             }
138              
139             sub _ytsil {
140 30     30   80 my ($old_stuff, $new_stuff) = @_;
141 30 100       140 return $new_stuff if 'ARRAY' eq ref $old_stuff;
142 15 50       93 return { @$new_stuff } if 'HASH' eq ref $old_stuff;
143 0         0 return $new_stuff->[0];
144             }
145              
146             sub _for_each {
147 6     6   16 my ($element, $decl_sub) = @_;
148              
149 6 100   10   27 my $lister = $element eq 'each' ? sub { _listy($_[0]) } : sub { pairs(_listy($_[0])) };
  10         33  
  20         63  
150              
151 6         16 my ($puller, $pusher);
152 6 100       15 if ($element eq 'key') {
    100          
153 2     8   7 $puller = sub { $_[0][0] }; # pull key from pairs()
  8         26  
154 2     8   14 $pusher = sub { ($_[1], $_[0][1]) }; # push updated key, original value from pairs()
  8         43  
155             }
156             elsif ($element eq 'value') {
157 2     8   7 $puller = sub { $_[0][1] }; # pull value from pairs()
  8         26  
158 2     8   7 $pusher = sub { ($_[0][0], $_[1]) }; # push original key from pairs(), updated value
  8         46  
159             }
160             else { # $element eq 'each'
161 2     16   5 $puller = sub { $_[0] }; # pull value from _listy()
  16         45  
162 2     16   7 $pusher = sub { $_[1] }; # push updated element
  16         65  
163             }
164              
165             sub {
166 30     30   76 my ($stuff) = @_;
167              
168 30         71 my $valid = 1;
169 30         61 my $error = '';
170             my @new_stuff = map {
171 30         90 my $update_value = $puller->($_);
  32         105  
172 32         96 my ($element_valid, $element_error, $element_value) = $decl_sub->($update_value);
173 32 100       104 unless ($element_valid) {
174 16         35 $valid = 0;
175 16   66     72 $error ||= $element_error;
176             }
177 32         87 $pusher->($_, $element_value);
178             } $lister->($stuff);
179              
180 30         124 return ($valid, $error, _ytsil($stuff, \@new_stuff));
181             }
182 6         32 }
183              
184             # lifted from perldoc perldata
185             my $NAME_RE = qr/ (?[ ( \p{Word} & \p{XID_Start} ) + [_] ])
186             (?[ ( \p{Word} & \p{XID_Continue} ) ]) * /x;
187             my $PACKAGE_RE = qr/^ $NAME_RE (?: ('|::) $NAME_RE )* $/x;
188              
189             sub _locate_package_name {
190 39     39   108 my ($spec_name, $depth) = @_;
191 39   50     242 $depth //= 1;
192              
193 39 50       372 die "name must be a valid Perl identifier"
194             unless $spec_name =~ /$PACKAGE_RE/;
195              
196 39         104 my ($package, $name);
197 39 100       215 if ($spec_name =~ /\b(::|')\b/) {
198 1         15 my @parts = split /::|'/, $spec_name;
199 1         4 $name = pop @parts;
200 1         7 $package = join '::', @parts;
201             }
202             else {
203 38         244 ($package) = caller($depth);
204 38   50     152 $package //='main';
205 38         83 $name = $spec_name;
206             }
207              
208 39         128 $package .= '::FORM_VALIDATOR_TINY_SPECIFICATION';
209             {
210 23     23   60070 no strict 'refs';
  23         189  
  23         19390  
  39         77  
211 39   100     78 ${ $package } //= {};
  39         250  
212             }
213              
214 39         159 ($package, $name);
215             }
216              
217             sub validation_spec($;$) {
218 40     40 1 34748 my ($name, $spec) = @_;
219 40 100       175 if (ref $name) {
220 23         53 $spec = $name;
221 23         55 undef $name;
222             }
223              
224 40         91 my $error;
225 40 100       135 if (defined $name) {
226 17     1   112 $error = sub { die "spec [$name] ", @_ };
  1         14  
227             }
228             else {
229 23 100       77 if (!defined wantarray) {
230 1         16 die "useless call to validation_spec with no name in void context";
231             }
232 22     21   109 $error = sub { die "spec ", @_ };
  21         391  
233             }
234              
235 39 100       183 $error->("must be an array reference")
236             unless 'ARRAY' eq ref $spec;
237              
238 38 100       165 $error->("contains odd number of elements")
239             unless scalar @$spec % 2 == 0;
240              
241 37         118 my @decl_spec;
242             my %encountered_fields;
243 37         406 for my $field_pair (pairs @$spec) {
244 58         243 my ($field, $decls) = @$field_pair;
245              
246 58     20   266 my $error = sub { $error->("input declaration for [$field] ", @_) };
  20         91  
247              
248 58 100       204 $error->("has been defined twice") if $encountered_fields{ $field };
249 57         169 $encountered_fields{ $field }++;
250              
251 57 100       205 $error->("must be in an array reference")
252             unless 'ARRAY' eq ref $decls;
253              
254 56 100       167 $error->("contains odd number of elements")
255             unless scalar @$decls % 2 == 0;
256              
257 55         104 my %options;
258 55         126 my @decl = (\%options);
259 55         301 for my $decl_pair (pairs @$decls) {
260 82         215 my ($op, $arg) = @$decl_pair;
261              
262 82 100 100 229   415 if (any { $op eq $_ } qw( from multiple trim )) {
  229 100       890  
    100          
    100          
    100          
263 17 100       64 $error->("found [$op] after filter or validation declarations")
264             if @decl > 1;
265              
266             $error->("has more than one [$op] declaration")
267 14 100       55 if defined $options{ $op };
268              
269 11         48 $options{ $op } = $arg;
270             }
271              
272             elsif ($op =~ /^ (?: (each|key|value)_ )? into $/x) {
273 24         71 my $element = $1;
274              
275 24         144 my $into_sub;
276 24 100 66     238 if ('CODE' eq ref $arg) {
    100          
    100          
    100          
    100          
277 1         7 $into_sub = _sub_coercer($arg);
278             }
279             elsif (blessed $arg && $arg->can('coerce')) {
280 1         9 $into_sub = _type_coercer($arg);
281             }
282             elsif (defined $coercer{ $arg }) {
283 14         50 $into_sub = $coercer{ $arg };
284             }
285             elsif ($arg =~ /\?([^!]+)!(.+)/) {
286 3         14 $into_sub = _yes_no_coercer($1, $2);
287             }
288             elsif ($arg =~ $PACKAGE_RE) {
289 1         14 $into_sub = _package_coercer($arg);
290             }
291             else {
292 4         22 $error->("has unknown [$op] declaration argument [$arg]");
293             }
294              
295 20 50       55 $into_sub = _for_each($element, $into_sub) if $element;
296 20         84 push @decl, $into_sub;
297             }
298              
299             elsif ($op eq 'required' || $op eq 'optional') {
300 6 100       25 $arg = !$arg if $op eq 'optional';
301              
302             # Validate on required
303 6 100       13 if ($arg) {
304             push @decl, sub {
305 10   66 10   38 my $valid = (defined $_[0] && $_[0] =~ /./);
306 10         32 ($valid, 'Required.', $_[0])
307 3         14 };
308             }
309              
310             # Shortcircuit on optional
311             else {
312             push @decl, sub {
313 10 100 66 10   42 my $valid = (defined $_[0] && $_[0] =~ /./) ? 1 : undef;
314 10         25 ($valid, '', $_[0])
315 3         12 };
316             }
317             }
318              
319             elsif ($op =~ /^ (?: (each|key|value)_ )? must $/x) {
320 31         98 my $element = $1;
321              
322 31         120 my $must_sub;
323 31 100 100     179 if ('CODE' eq ref $arg) {
    100 66        
    100          
324 17         62 $must_sub = _sub_validator($arg);
325             }
326             elsif ('Regexp' eq ref $arg) {
327 6         38 $must_sub = _re_validator($arg);
328             }
329             elsif (blessed $arg && ($arg->can('check') || $arg->can('validate'))) {
330 3         15 $must_sub = _type_validator($arg);
331             }
332             else {
333 5         30 $error->("has unknown [$op] declaration argument [$arg]");
334             }
335              
336 26 100       83 $must_sub = _for_each($element, $must_sub) if $element;
337 26         114 push @decl, $must_sub;
338             }
339              
340             elsif ($op eq 'with_error') {
341 3 100       23 $error->("has [$op] before a declaration in which it may modify")
342             unless @decl > 1;
343              
344 2         4 my $last_decl = pop @decl;
345 2         7 push @decl, _with_error($last_decl, $arg);
346             }
347              
348             else {
349 1         10 $error->("has unknown [$op]");
350             }
351             }
352              
353 38         287 push @decl_spec, $field, \@decl;
354             }
355              
356 17         69 my $finished_spec = \@decl_spec;
357 17         83 bless $finished_spec, __PACKAGE__;
358              
359 17 100       72 if (defined $name) {
360 16         36 my $package;
361 16         74 ($package, $name) = _locate_package_name($name);
362              
363             {
364 23     23   262 no strict 'refs';
  23         50  
  23         4545  
  16         40  
365 16         40 ${ $package }->{ $name } = $finished_spec;
  16         74  
366             }
367             }
368              
369 17         117 return $finished_spec;
370             }
371              
372             sub validate_form($$) {
373 28     28 1 63318 my ($name, $input) = @_;
374 28         70 my @input;
375 28 50 33     183 if (blessed $input && $input->can('flatten')) {
376 0         0 @input = $input->flatten;
377             }
378             else {
379 28         119 @input = _listy($input);
380             }
381              
382 28         79 my $spec = $name;
383 28 100 66     182 unless (blessed $spec && $spec->isa(__PACKAGE__)) {
384 23         48 my $package;
385 23         83 ($package, $name) = _locate_package_name($name);
386              
387             {
388 23     23   143 no strict 'refs';
  23         49  
  23         45228  
  23         87  
389 23         51 $spec = ${ $package }->{ $name };
  23         86  
390              
391 23 50       95 die "no spec with name [$name] found in package [$package]"
392             unless defined $spec;
393             }
394             }
395              
396 28 50       91 die "no spec provided to validate with" unless defined $spec;
397              
398 28         72 my (%params, %errors);
399 28         345 FIELD: for my $field_pair (pairs @$spec) {
400 91         247 my ($field, $decls) = @$field_pair;
401              
402 91         154 my $field_input;
403 91         235 DECL_FOR_FIELD: for my $decl (@$decls) {
404 221 100       579 if ('HASH' eq ref $decl) {
405 91   33     412 my $from = $decl->{from} // $field;
406 91   100     333 my $multiple = $decl->{multiple} // 0;
407 91   50     320 my $trim = $decl->{trim} // 1;
408              
409 91     476   777 my @values = pairmap { $b } pairgrep { $a eq $from } @input;
  108         315  
  524         1200  
410 91 100       507 @values = map { if (defined) { s/^\s+//; s/\s+$// } $_ } @values if $trim;
  108 50       275  
  104         318  
  104         323  
  108         380  
411              
412 91 100       232 if ($multiple) {
413 32         107 $field_input = \@values;
414             }
415             else {
416 59         157 $field_input = pop @values;
417             }
418             }
419              
420             else {
421 130         351 my ($valid, $error, $new_value) = $decl->($field_input, \%params);
422              
423 130 100       462 if (!defined $valid) {
    100          
424 2         3 $field_input = undef;
425 2         4 last DECL_FOR_FIELD;
426             }
427             elsif ($valid) {
428 97         273 $field_input = $new_value;
429             }
430             else {
431 31         63 $field_input = undef;
432 31         69 push @{ $errors{ $field } }, $error;
  31         103  
433 31         98 last DECL_FOR_FIELD;
434             }
435             }
436             }
437              
438 91         287 $params{ $field } = $field_input;
439             }
440              
441 28 100       155 my $errors = scalar keys %errors ? \%errors : undef;
442 28         144 return (\%params, $errors);
443             }
444              
445             sub _comma_and {
446 10 50   10   43 if (@_ == 0) {
    100          
    100          
447 0         0 return '';
448             }
449             elsif (@_ == 1) {
450 5         13 return $_[0];
451             }
452             elsif (@_ == 2) {
453 2         14 return "$_[0] and $_[1]";
454             }
455             else {
456 3         7 my $last = pop @_;
457 3         16 return join(", ", @_) . ", and " . $last
458             }
459             }
460              
461             sub limit_character_set {
462             my $_build_class = sub {
463             my @class_parts = map {
464 13 100 100 13   24 if (1 == length $_) {
  20 100       131  
    100          
465 2         9 [ "[$_]", qq["$_"] ]
466             }
467             elsif (/^(.)-(.)$/ && ord($1) < ord($2)) {
468 13         58 [ "[$_]", qq["$1" through "$2"] ]
469             }
470             elsif (/^\[([^\]]+)\]$/) {
471 1         4 my $name = my $prop = $1;
472 1         3 $name =~ s/_/ /g;
473 1         7 [ "\\p{$prop}", qq[\L$name\E characters] ]
474             }
475             else {
476 4         63 die "invalid character set [$_]";
477             }
478             } @_;
479              
480 9         16 my $classes = join ' + ', map { $_->[0] } @class_parts;
  16         38  
481 9         225 my $re = qr/(?[ $classes ])/x;
482              
483 9         910 my $error = _comma_and(map { $_->[1] } @class_parts);
  16         37  
484              
485 9         30 return ($re, $error);
486 11     11 1 11225 };
487              
488 11 100 100     65 if (@_ == 2 && 'ARRAY' eq ref $_[0] && 'ARRAY' eq ref $_[1]) {
      66        
489 3         6 my ($first_re, $first_error) = $_build_class->(@{ $_[0] });
  3         7  
490 2         5 my ($rest_re, $rest_error) = $_build_class->(@{ $_[1] });
  2         4  
491              
492 1         5 my $error = "First character only permits: "
493             . $first_error . ". Remaining only permits: "
494             . $rest_error;
495              
496             sub {
497 3     3   1310 my ($value) = @_;
498 3         41 my $valid = ($value =~ /^(?:$first_re$rest_re*)?$/);
499 3         12 ($valid, $error);
500 1         11 };
501             }
502             else {
503 8         19 my ($re, $error) = $_build_class->(@_);
504              
505 6         14 $error = "Only permits: "
506             . $error;
507              
508             sub {
509 12     12   1606 my ($value) = @_;
510 12         209 my $valid = ($value =~ /^$re*$/);
511 12         325 ($valid, $error);
512 6         69 };
513             }
514             }
515              
516             sub length_in_range {
517 9     9 1 7595 my ($start, $stop) = @_;
518              
519 9 100       65 die "minimum length in length_in_range must be a positive integer, got [$start] instead"
520             unless $start =~ /^(?:[0-9]+|\*)$/;
521              
522 7 100       31 die "maximum length in length_in_range must be a positive integer, got [$stop] instead"
523             unless $stop =~ /^(?:[0-9]+|\*)$/;
524              
525 6 100 100     47 die "minimum length must be less than or equal to maximum length in length_in_range, got [$start>$stop] instead"
      100        
526             if $start ne '*' && $stop ne '*' && $start > $stop;
527              
528 5 100 100     42 if ($start eq '*' && $stop eq '*') {
    100          
    100          
529 1     3   7 return sub { (1, '') };
  3         552  
530             }
531             elsif ($start eq '*') {
532             return sub {
533 3     3   562 my $valid = length $_[0] <= $stop;
534 3         12 ($valid, "Must be no longer than $stop characters.")
535 1         8 };
536             }
537             elsif ($stop eq '*') {
538             return sub {
539 3     3   1031 my $valid = length $_[0] >= $start;
540 3         13 ($valid, "Must be at least $start characters long.")
541             }
542 1         8 }
543             else {
544             return sub {
545 5 50   5   1021 return (1, '') unless defined $_[0];
546 5 100       12 if (length $_[0] >= $start) {
547 4         11 my $valid = length $_[0] <= $stop;
548 4         16 return ($valid, "Must be no longer than $stop characters.");
549             }
550             else {
551 1         5 return ('', "Must be at least $start characters in length.")
552             }
553             }
554 2         18 }
555             }
556              
557             sub equal_to {
558 1     1 1 83 my ($field_name) = @_;
559              
560             sub {
561 2     2   4180 ($_[0] eq $_[1]{ $field_name }, "The value must match $field_name.")
562             }
563 1         10 }
564              
565             sub number_in_range {
566 9     9 1 9225 my $start = shift;
567 9         14 my $stop = shift;
568 9         17 my $starti = 1;
569 9         15 my $stopi = 1;
570              
571 9 100       25 if ($start eq 'exclusive') {
572 1         5 $starti = 0;
573 1         4 $start = $stop;
574 1         4 $stop = shift;
575             }
576              
577 9 100       25 if ($stop eq 'exclusive') {
578 1         3 $stopi = 0;
579 1         20 $stop = shift;
580             }
581              
582 9 100 100     61 die "minimum length in length_in_range must be a positive integer, got [$start] instead"
583             unless $start eq '*' || looks_like_number($start);
584              
585 8 100 100     40 die "maximum length in length_in_range must be a positive integer, got [$stop] instead"
586             unless $stop eq '*' || looks_like_number($stop);
587              
588 7 100 100     40 die "minimum length must be less than or equal to maximum length in length_in_range, got [$start>$stop] instead"
      100        
589             if $start ne '*' && $stop ne '*' && $start > $stop;
590              
591 12     12   1148 my $check_start = $starti ? sub { (($_[0] >= $start), "Number must be at least $start.") }
592 6 100   2   40 : sub { (($_[0] > $start), "Number must be greater than $start.") };
  2         9  
593 7     7   676 my $check_stop = $stopi ? sub { (($_[0] <= $stop), "Number must be no more than $stop.") }
594 6 100   1   24 : sub { (($_[0] < $stop), "Number must be less than $stop.") };
  1         13  
595              
596 6 100 100     31 if ($start eq '*' && $stop eq '*') {
    100          
    100          
597 1     3   10 return sub { (1, '') };
  3         873  
598             }
599             elsif ($start eq '*') {
600 1         5 return $check_stop;
601             }
602             elsif ($stop eq '*') {
603 2         14 return $check_start;
604             }
605             else {
606             return sub {
607 7     7   2301 my ($v, $e) = $check_start->(@_);
608 7 100       22 return ($v, $e) unless $v;
609 5         10 return $check_stop->(@_);
610             }
611 2         9 }
612             }
613              
614             sub one_of {
615 2 100   2 1 7004 die "at least one value must be provided to one_of"
616             unless @_ > 0;
617              
618 1         7 my @enum = @_;
619             return sub {
620 4     4   774 my ($value) = @_;
621 4         8 for my $allowed (@enum) {
622 9 100       28 return (1, '') if $value eq $allowed;
623             }
624              
625             return (0, 'Must be one of: '
626 1         4 . _comma_and( map { qq["$_"] } @enum )
  3         10  
627             );
628 1         11 };
629             }
630              
631             sub split_by {
632 4     4 1 5185 my ($by, $count) = @_;
633              
634 4 100       21 die "missing string or regex to split by"
635             unless defined $by;
636              
637 3 100 100     27 die "count must be greater than 1 if present"
638             if defined $count && $count <= 1;
639              
640 2 100       5 if ($count) {
641 1 50   1   18 sub { defined $_[0] ? [ split $by, $_[0], $count ] : [] }
642 1         6 }
643             else {
644 1 50   1   22 sub { defined $_[0] ? [ split $by, $_[0] ] : [] }
645 1         6 }
646             }
647              
648             sub trim {
649 5   100 5 1 5852 my $only = shift // 'both';
650 5 100       25 if ($only eq 'both') {
    100          
    100          
651             return sub {
652 2 50   2   13 return unless defined $_;
653 2         9 s/\A\s+//;
654 2         12 s/\s+\Z//r;
655 2         11 };
656             }
657             elsif ($only eq 'left') {
658             return sub {
659 1 50   1   10 return unless defined $_;
660 1         6 s/\A\s+//r;
661             }
662 1         10 }
663             elsif ($only eq 'right') {
664             return sub {
665 1 50   1   7 return unless defined $_;
666 1         6 s/\s+\Z//r;
667             }
668 1         5 }
669             else {
670 1         14 die qq[unknown trim option [$only], expected "both" or "left" or "right"];
671             }
672             }
673              
674             1;
675              
676             =pod
677              
678             =encoding UTF-8
679              
680             =head1 NAME
681              
682             FormValidator::Tiny - A tiny form validator
683              
684             =head1 VERSION
685              
686             version 0.003
687              
688             =head1 SYNOPSIS
689              
690             use FormValidator::Tiny qw( :validation :predicates :filtesr );
691             use Email::Valid; # <-- for demonstration, not required
692             use Email::Address; # <-- for demonstration, not required
693             use Types::Standard qw( Int ); # <-- for demonstration, not required
694              
695             validation_spec edit_user => [
696             login_name => [
697             required => 1,
698             must => limit_character_set('_', 'a-z', 'A-Z', '0-9'),
699             must => length_in_range(5, 16),
700             ],
701             name => [
702             required => 1,
703             must => length_in_range(1, 100),
704             ],
705             age => [
706             optional => 1,
707             into => '+',
708             must => Int,
709             must => number_in_range(13, '*'),
710             ],
711             password => [
712             required => 1,
713             must => length_in_range(8, 72),
714             ],
715             confirm_password => [
716             required => 1,
717             must => equal_to('password'),
718             ],
719             email => [
720             required => 1,
721             must => length_in_range(5, 250),
722             must => sub { (
723             !!Email::Valid->address($_),
724             "That is not a well-formed email address."
725             ) },
726             into => 'Email::Address',
727             ],
728             groups => [
729             optional => 1,
730             into => split_by(' '),
731             into => '[]',
732             each_must => length_in_range(3, 20),
733             each_must => limit_character_set(
734             ['_', 'a-z', 'A-Z'],
735             ['_', '-', 'a-z', 'A-Z', '0-9'],
736             ),
737             ],
738             tags => [
739             optional => 1,
740             into => split_by(/\s*,\s*/),
741             each_into => split_by(/\s\*:\s*/, 2),
742             into => '{}',
743             key_must => length_in_range(3, 20),
744             key_must => qr/^(?:[A-Z][a-z0-9]*)(?:-[A-Z][a-z0-9]*)*)$/,
745             with_error => 'Tags keys must be of a form like "Favorite" or "Welcome-Message".',
746             value_must => length_in_range(1, 500),
747             value_must => limit_character_set('_', '-', 'a-z', 'A-Z', '0-9'),
748             ],
749             ];
750              
751             # Somehow your web framework gets you a set of form parameters submitted by
752             # POST or whatever. GO!
753             my $params = web_framework_params_method();
754             my ($parsed_params, $errors) = validate_form edit_user => $params;
755              
756             # You probably want better error handling
757             if ($errors) {
758             for my $field (keys %$errors) {
759             print "Error in $field: $_\n" for @{ $errors->{$field} };
760             }
761             }
762              
763             # Form fields are valid, take action!
764             else {
765             do_the_thing(%$parased_params);
766             }
767              
768             =head1 DESCRIPTION
769              
770             The API of this module is still under development and could change, but probably won't.
771              
772             There are lots for form validators, but this one aims to be the one that just
773             does one thing and does it well without involving anything else if it can. If you
774             just need a small form validator without installing all of CPAN, this will do
775             that. If you want to install all of CPAN and use a readable form validation spec
776             syntax, I hope this will do that too.
777              
778             This module requires Perl 5.18 or better as of this writing.
779              
780             =head1 EXPORTS
781              
782             This module exports three sets of functions, each with their own export tag:
783              
784             =over
785              
786             =item :validation
787              
788             This is exported by default. It includes the two central functions provided by this interface, C and C.
789              
790             =item :predicates
791              
792             This includes the built-in predicate helpers, used with C and C-like directives.
793              
794             =over
795              
796             =item limit_character_set
797              
798             =item length_in_range
799              
800             =item equal_to
801              
802             =item number_in_range
803              
804             =item one_of
805              
806             =back
807              
808             =item :filters
809              
810             This includes the build-in filter helpers, used with C and C-like directives.
811              
812             =over
813              
814             =item split_by
815              
816             =item trim
817              
818             =back
819              
820             =item :all
821              
822             All of the above.
823              
824             =back
825              
826             =head1 FUNCTIONS
827              
828             =head2 validation_spec
829              
830             validation_spec $spec_name => \@spec;
831              
832             This defines a validation specification. It associates a specification named
833             C<$spec_name> with the current package. Any use of C within the
834             current package will use specifications named within the current package. The
835             following example would work fine as the "edit" spec defined in each controller
836             is in their respective package namespaces.
837              
838             package MyApp::Controller::User;
839             validation_spec edit => [ ... ];
840             sub process_edits {
841             my ($self, $c) = @_;
842             my ($p, $e) = validate_form edit => $c->req->body_parameters;
843             ...
844             }
845              
846             package MyApp::Controller::Page;
847             validation_spec edit => [ ... ];
848             sub process_edits {
849             my ($self, $c) = @_;
850             my ($p, $e) = validate_form edit => $c->req->body_parameters;
851             ...
852             }
853              
854             If you want to define them into a different package, name the package as part of
855             the spec. Similarly, you can validate_form using a spec defined in a different
856             package by naming the package when calling L:
857              
858             package MyApp::Forms;
859             validation_spec MyApp::Controller::User::edit => [ ... ];
860              
861             package MyApp::Controller::User;
862             sub process_groups {
863             my ($self, $c) = @_;
864             my ($p, $e) = validate_form MyApp::Controller::UserGroup::edit => $c->req->body_parameters;
865             ...
866             }
867              
868             You can also define your validation specification as lexical variables instead:
869              
870             my $spec = validation_spec [ ... ];
871             my ($p, $e) = validate_form $spec, $c->req->body_parameters;
872              
873             For information about how to craft a spec, see the L
874             section.
875              
876             =head2 validate_form
877              
878             my ($params, $errors) = validate_form $spec, $input_parameters;
879              
880             Compares the given parameters against the named spec. The C<$input_parameters>
881             may be provided as either a hash or an array of alternating key-value pairs. All
882             keys and values must be provided as strings.
883              
884             The method returns two values. The first, C<$params>, is the parameters as far
885             as they have been validated so far. The second, C<$errors> is the errors that
886             have been detected.
887              
888             The C<$params> will be provided as a hash. The keys of this hash will match the
889             keys given in the spec. Some keys may be missing if the provided
890             C<$input_parameters> did not contain values or those values are invalid.
891              
892             If there are no errors, the C<$errors> value will be set to C. With
893             errors, this will be hash of arrays. The keys of the hash will also match the
894             keys in the spec. Only fields with a validation error will be set. Each value
895             is an array of strings, with each string being an error message describing a
896             validation failure.
897              
898             =head2 limit_character_set
899              
900             must => limit_character_set(@sets)
901             must => limit_character_set(\@fc_sets, \@rc_sets);
902              
903             This returns a subroutine that limits the allowed characters for an input. In
904             the first form, the character set limits are applied to all characters in the
905             value. In the second, the first array limits the characters permitted for the
906             first character and the second limits the characters permitted for the rest.
907              
908             Character sets may be provided as single letters (e.g., "_"), as named unicode
909             character properties wrapped in square brackets (e.g., "[Uppercase_Letter]"), or
910             as ranges connected by a hyphen (e.g., "a-z").
911              
912             =head2 length_in_range
913              
914             must => length_in_range('*', 10)
915             must => length_in_range(10, '*')
916             must => length_in_range(10, 100)
917              
918             This returns a subroutine for use with C declarations that asserts the
919             minimum and maximum string character length permitted for a value. Use an
920             asterisk to define no limit.
921              
922             =head2 equal_to
923              
924             must => equal_to('field')
925              
926             This returns a subroutine for use with C declarations that asserts that
927             the value must be exactly equal to another field in the input.
928              
929             =head2 number_in_range
930              
931             must => number_in_range('*', 100)
932             must => number_in_range(100, '*')
933             must => number_in_range(100, 500)
934             must => number_in_range(exclusive => 100, exclusive => 500)
935              
936             Returns a predicate for must that requires the integer to be within the given
937             range. The endpoints are inclusive by default. You can add the word "exclusive"
938             before a value to make the comparison exclusive instead. Using a '*' indicates
939             no limit at that end of the range.
940              
941             =head2 one_of
942              
943             must => one_of(qw( a b c )),
944              
945             Returns a predicate that requires the value to exactly match one of the
946             enumerated values.
947              
948             =head2 split_by
949              
950             into => split_by(' ')
951             into => split_by(qr/,\s*/)
952             into => split_by(' ', 2)
953             into => split_by(qr/,\s*/, 10)
954              
955             Returns an into filter that splits the string into an array. The arguments are
956             similar to those accepted by Perl's built-in C.
957              
958             =head2 trim
959              
960             into => trim
961             into => trim('left')
962             into => trim('right')
963              
964             Returns an into filter that trims whitespace from the input value. You can
965             provide an argument to trim only the left whitespace or the right whitespace.
966              
967             =head1 VALIDATION SPECIFICATIONS
968              
969             The validation specification is an array reference. Each key names a field to
970             validate. The value is an array of processing declarations. Each processing
971             declaration is a key-value pair. The inputs will be processed in the order they
972             appear in the spec. The key names the type of processing. The value describes
973             arguments for the processing. The processing declarations will each be executed
974             in the order they appear. The same processor may be applied multiple times.
975              
976             =head2 Input Declarations
977              
978             Input declarations modify the initial value and must be given at the very top of
979             the list of declarations for a field before all others.
980              
981             =head3 from
982              
983             from => 'input_parameter_name'
984              
985             Without this declaration, the validator pulls input from the parameter with the
986             same name as the key named in the validation spec. This input declaration
987             changes the key used for input.
988              
989             =head3 as
990              
991             multiple => 1
992              
993             The multiple input declaration tells the validator whether to interpret the
994             input parameter as a multiple input or not. Without this declaration or with it
995             set to 0, the validator will interpret multiple inputs as a single value,
996             ignoring all but the last. With this declaration, it treat the input as multiple
997             items, even if there are 0 or 1.
998              
999             =head3 trim
1000              
1001             trim => 0
1002              
1003             The default behavior of L is to trim whitespace from the beginning
1004             and end of a value before processing. You can use the C declaration to
1005             disable that.
1006              
1007             =head2 Filtering Declarations
1008              
1009             Filtering declarations inserted into the validation spec will replace the input
1010             value with the newly filtered value at the point at which the declaration is
1011             encountered.
1012              
1013             =head3 into
1014              
1015             into => '+'
1016             into => '?'
1017             into => '?+'
1018             into => '?perl'
1019             into => '?yes!no',
1020             into => '[]'
1021             into => '{}'
1022             into => 'Package::Name'
1023             into => sub { ... }
1024             into => TypeObject
1025              
1026             This is a filter declaration that transforms the input using the named coercion.
1027              
1028             =over
1029              
1030             =item Numeric
1031              
1032             Numeric coercion is performed using the '+' argument. This will convert the
1033             value using Perl's built-in string-to-number conversion.
1034              
1035             =item Boolean
1036              
1037             Boolean coercion is performed using the '?' argument. This will convert the
1038             value to boolean. It does not use Perl's normal mechanism, though. Instead, it
1039             converts the string to boolean based on string length alone. If the string is
1040             empty, it is false. If it is not empty it is true.
1041              
1042             =item Boolean by Numeric
1043              
1044             Boolean by Numeric coercion is performed using the '?+' argument. This will
1045             first convert the string input to a number and then the number will be collapsed
1046             to a boolean value such that 0 is false and any other value is true.
1047              
1048             =item Boolean via Perl
1049              
1050             Boolean via Perl coercion is performed using the '?perl' argument. This will
1051             convert to boolean using Perl's usual boolean logic.
1052              
1053             =item Boolean via Enumeration
1054              
1055             Boolean via Enumeration coercion is performed using an argument that starts with
1056             a question mark, '?', and contains an exclamation mark, '!'. The value between
1057             the question mark and exclamation mark is the value that must be provided for a
1058             true value. The value provided between the exclamation mark and the end of
1059             string is the false value. Anything else will be treated as invalid and cause a
1060             validation error.
1061              
1062             =item Array
1063              
1064             Using a value of '[]' will make sure the value is treated as an array. This is a
1065             noop if the L declaration is set or if a L returns an array.
1066             If the value is still a single, though, this will make sure the input value is
1067             placed inside an array references. This will also turn a hash value into an array.
1068              
1069             =item Hash
1070              
1071             Setting the declaration to '{}" will coerce the value to a hash. The even indexed
1072             values in the array will become keys and the odd indexed values in the array
1073             will become their respective values. If the value is not an array, it will turn
1074             a single value into a key/value pair with the key and the pair both being equal
1075             to the original value.
1076              
1077             =item Package
1078              
1079             A package coercion happens when the string given is a package name. This assumes
1080             that passing the input value to the C constructor of the named package will
1081             do the right thing. If you need anything more complicated than that, you should
1082             use a subroutine coercion.
1083              
1084             =item Subroutine
1085              
1086             A subroutine coercion converts the value using the given subroutine. The current
1087             input value is passed as the single argument to the coercion (and also set as
1088             the localized copy of C<$_>). The return value of the subroutine becomes the new
1089             input value.
1090              
1091             =item Type::Tiny Coercion
1092              
1093             If an object is passed that provides a C method. That method will be
1094             called on the current input value and the result will be used as the new input
1095             value.
1096              
1097             =back
1098              
1099             =head3 each_into
1100              
1101             each_into => '+'
1102             each_into => '?'
1103             each_into => '?+'
1104             each_into => '?perl'
1105             each_into => '?yes!no',
1106             each_into => '[]'
1107             each_into => '{}'
1108             each_into => 'Package::Name'
1109             each_into => sub { ... }
1110             each_into => TypeObject
1111              
1112             Performs the same coercion as L, but also works with arrays and hashes.
1113             It will apply the filter to a single value or to all elements of an array or to
1114             all keys and values of a hash.
1115              
1116             =head3 key_into
1117              
1118             key_into => '+'
1119             key_into => '?'
1120             key_into => '?+'
1121             key_into => '?perl'
1122             key_into => '?yes!no',
1123             key_into => '[]'
1124             key_into => '{}'
1125             key_into => 'Package::Name'
1126             key_into => sub { ... }
1127             key_into => TypeObject
1128              
1129             Performs the same coercion as L, but also works with arrays and hashes.
1130             It will apply the filter to a single value or to all even index elements of an
1131             array or to all keys of a hash.
1132              
1133             =head3 value_into
1134              
1135             value_into => '+'
1136             value_into => '?'
1137             value_into => '?+'
1138             value_into => '?perl'
1139             value_into => '?yes!no',
1140             value_into => '[]'
1141             value_into => '{}'
1142             value_into => 'Package::Name'
1143             value_into => sub { ... }
1144             value_into => TypeObject
1145              
1146             Performs the same coercion as L, but also works with arrays and hashes.
1147             It will apply the filter to a single value or to all odd index elements of an
1148             array or to all values of a hash.
1149              
1150             =head2 Validation Declarations
1151              
1152             =head3 required
1153              
1154             =head3 optional
1155              
1156             required => 1
1157             required => 0
1158             optional => 1
1159             optional => 0
1160              
1161             It is strongly recommended that all fields add this declaratoi immediately after
1162             the input declarations, if any.
1163              
1164             When required is set (or optional is set to 0), an initial validation check is
1165             inserted that will fail if a value is not provided for this field. That value
1166             must contain at least one character (after trimming, if trimming is not
1167             disabled).
1168              
1169             When optional is set (or required is set to 0), an initial validaiton check is
1170             inserted that will shortcircuit the rest of the validation if no value is
1171             provided.
1172              
1173             =head3 must
1174              
1175             must => qr/.../
1176             must => sub { ... }
1177             must => TypeObject
1178              
1179             This declaration states that the input given must match the described predicate.
1180             The module supports three kinds of predicates:
1181              
1182             =over
1183              
1184             =item Regular Expression
1185              
1186             This will match the given regular expression against the input. It is
1187             recommended that the regular expression start with "^" or "\A" and end with "$"
1188             or "\z" to force a total string match.
1189              
1190             The error message for these validates is not very good, so you probably want to
1191             combine use of this kind of predicate with a following L
1192             declaration.
1193              
1194             =item Subroutine
1195              
1196             ($valid, $message) = $code->($value, \%fields);
1197              
1198             The subroutine will be passed two values. The first is the input to test
1199             (which will also be set in the localalized copy of C<$_>). This second value
1200             passed is rest of the input as processing currently stands.
1201              
1202             The return value must be a two elements list.
1203              
1204             =over
1205              
1206             =item 1.
1207              
1208             The first value returned is a boolean indicating whether the validation has
1209             passed. A true value (like 1) means validation passes and there's no error. A
1210             false value (like 0) means validation does not pass and an error has occured.
1211              
1212             There is a third option, which is to return C. This indicates that
1213             validaton should stop here. This is neither a success nor a failure. The value
1214             processed so far will be ignored, but no error message is returned either. Any
1215             further declarations for the field will be ignored as well.
1216              
1217             Returning C allows custom code to shortcircuit validation in exactly the
1218             same was as setting C.
1219              
1220             =item 2.
1221              
1222             The second value is the error message to use. It is acceptable to return an
1223             error message even if the first value is a true or undefined value. In that
1224             case, the error message will be ignored.
1225              
1226             =back
1227              
1228             =item Type::Tiny Object
1229              
1230             The third option is to use a L-style type object. The
1231             L routine merely checks to see if it is an object that provides
1232             a C method or a C method. If it provides a C
1233             method, that method will be called and the boolean value returned will be
1234             treated as the success or failure to validate. In this case, the error message
1235             will be pulled from a call to C, if such a method is provided. In
1236             the C case, it will be called and a true value will be treated as
1237             the error message and a false value as validation success.
1238              
1239             It is my experience that the error messages provided by L and
1240             similar type systems are not friendly for use with end-uers. As such, it is
1241             recommended that you provide a nicer error message with a following
1242             L declaration.
1243              
1244             =back
1245              
1246             =head3 each_must
1247              
1248             each_must => qr/.../
1249             each_must => sub { ... }
1250             each_must => TypeObject
1251              
1252             This declaration establishes validation rules just like L, but applies
1253             the test to every value. If the input is an array, that will apply to every
1254             value. If the input is a hash, it will apply to every key and every value of the
1255             hash. If it is a single scalar, it will apply to that single value.
1256              
1257             =head3 key_must
1258              
1259             key_must => qr/.../
1260             key_must => sub { ... }
1261             key_must => TypeObject
1262              
1263             This is very similar to C, but only applies to keys. It will apply to
1264             a single value, or to the even index values of an array, or to the keys of a
1265             hash.
1266              
1267             =head3 value_must
1268              
1269             value_must => qr/.../
1270             value_must => sub { ... }
1271             value_must => TypeObject
1272              
1273             This is very similar to C and complement of C. It will
1274             apply to a single value, or to the odd index values of an array, or to the
1275             values of a hash.
1276              
1277             =head3 with_error
1278              
1279             with_error => 'Error message.'
1280             with_error => sub { ... }
1281              
1282             This defines the error message to associate with the previous C,
1283             C, C, C, C, C, and C
1284             declaration. This will override any other associated message.
1285              
1286             If you would like to provide a different message based on the input, you may
1287             provide a subroutine.
1288              
1289             =head1 SPECIAL VARIABLES
1290              
1291             The validation specifications are defined in each packages where
1292             L is called. This is done through a package variable named
1293             C<%FORM_VALIDATOR_TINY_SPECIFICATION>. If you really need to use that variable
1294             for something else or if defining global package variables offends you, you can
1295             use the return value form of C, which will avoid creating this
1296             variable.
1297              
1298             If you stick to the regular interface, however, this variable will be
1299             established the first time C is called. The spec names are the
1300             keys and the values have no documented definition. If you want to see what they
1301             are, you must the read the code, but there's no guarantee that the internal
1302             representation of this variable will stay the same in future releases.
1303              
1304             =head1 SEE ALSO
1305              
1306             L is very similar to this module in purpose and goals, but with
1307             a different API.
1308              
1309             =head1 AUTHOR
1310              
1311             Andrew Sterling Hanenkamp
1312              
1313             =head1 COPYRIGHT AND LICENSE
1314              
1315             This software is copyright (c) 2017 by Qubling Software LLC.
1316              
1317             This is free software; you can redistribute it and/or modify it under
1318             the same terms as the Perl 5 programming language system itself.
1319              
1320             =cut
1321              
1322             __END__