File Coverage

blib/lib/FormValidator/Tiny.pm
Criterion Covered Total %
statement 360 364 99.1
branch 176 194 90.7
condition 66 80 82.5
subroutine 75 75 100.0
pod 8 8 100.0
total 685 721 95.1


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