File Coverage

lib/Mojolicious/Plugin/Vparam.pm
Criterion Covered Total %
statement 244 255 95.6
branch 196 226 86.7
condition 106 145 73.1
subroutine 14 14 100.0
pod 1 1 100.0
total 561 641 87.5


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Vparam;
2 74     74   27487750 use Mojo::Base 'Mojolicious::Plugin';
  74         124540  
  74         454  
3 74     74   66377 use Mojolicious::Plugin::Vparam::Common qw(:all);
  74         172  
  74         11424  
4              
5 74     74   18189 use version;
  74         108231  
  74         425  
6 74     74   30808 use List::MoreUtils qw(firstval natatime mesh);
  74         711336  
  74         518  
7              
8             our $VERSION = '3.03';
9              
10             # Regext for shortcut parser
11             our $SHORTCUT_REGEXP = qr{
12             ^
13             (
14             [!?@~%] # symbols shortcut
15             |
16             (?:array|maybe|optional|required?|skipundef)\[ # text[] shortcut start
17             )
18             (.*?) # value
19             \]? # text[] shortcut end
20             $
21             }xi;
22              
23             sub register {
24 75     75 1 729262 my ($self, $app, $conf) = @_;
25              
26 75   50     336 $conf ||= {};
27              
28 75   50     567 $conf->{class} ||= 'field-with-error';
29 75   50     433 $conf->{types} ||= {};
30 75   50     413 $conf->{filters} ||= {};
31              
32 75   50     1130 $conf->{vsort_page} ||= 'page';
33 75   50     404 $conf->{vsort_rws} ||= 'rws';
34 75   50     469 $conf->{rows} ||= 25;
35 75   50     430 $conf->{vsort_oby} ||= 'oby';
36 75   50     404 $conf->{vsort_ods} ||= 'ods';
37 75   50     369 $conf->{ods} ||= 'ASC';
38              
39 75   100     373 $conf->{phone_country} //= '';
40 75   100     352 $conf->{phone_region} //= '';
41 75   100     343 $conf->{phone_fix} //= '';
42              
43 75 50       278 $conf->{date} = '%F' unless exists $conf->{date};
44 75 50       285 $conf->{time} = '%T' unless exists $conf->{time};
45 75 100       265 $conf->{datetime} = '%F %T %z' unless exists $conf->{datetime};
46              
47 75   50     432 $conf->{blessed} //= 1;
48 75   50     325 $conf->{optional} //= 0;
49 75   50     369 $conf->{skipundef} //= 0;
50 75   50     316 $conf->{multiline} //= 0;
51              
52 75   100     321 $conf->{address_secret} //= '';
53              
54 75   50     324 $conf->{password_min} //= 8;
55              
56 75   50     447 $conf->{hash_delimiter} //= '=>';
57              
58             # Enable Mojolicious::Validator::Validation integration if available
59             $conf->{mojo_validator} //=
60 75 50 33     2160 version->new($Mojolicious::VERSION) < version->new(4.42) ? 0 : 1;
61              
62             # Get or set type
63             $app->helper(vtype => sub {
64 2027     2027   174241 my ($self, $name, %opts) = @_;
65 2027 100       8784 return $conf->{types}{$name} = \%opts if @_ > 2;
66 1         7 return $conf->{types}{$name};
67 75         690 });
68              
69             # Get or set filter
70             $app->helper(vfilter => sub {
71 1127     1127   86633 my ($self, $name, $sub) = @_;
72 1127 100       4114 return $conf->{filters}{$name} = $sub if @_ > 2;
73 1         6 return $conf->{filters}{$name};
74 75         1559 });
75              
76             # Get or set config parameters
77             $app->helper(vconf => sub {
78 7     7   35793 my ($self, $name, $value) = @_;
79 7 100       43 return $conf->{$name} = $value if @_ > 2;
80 1         5 return $conf->{$name};
81 75         1149 });
82              
83             # Get or set error for parameter $name
84             $app->helper(verror => sub{
85 576     576   44194 my ($self, $name, @opts) = @_;
86              
87 576   100     1478 my $errors = $self->stash->{'vparam-verrors'} //= {};
88              
89 576 100       5996 if( @_ <= 2 ) {
    100          
90 391 100       1825 return 0 unless exists $errors->{$name};
91              
92             return 'ARRAY' eq ref $errors->{$name}
93 5         31 ? scalar @{$errors->{$name}}
94 142 100 50     1358 : $errors->{$name}{message} // 0
95             ;
96             } elsif( @_ == 3 ) {
97 5 50       19 return 0 unless exists $errors->{$name};
98              
99             my $error = 'ARRAY' eq ref $errors->{$name}
100 8         22 ? firstval {$_->{index} == $opts[0]} @{$errors->{$name}}
  5         27  
101 5 50       39 : $errors->{$name}
102             ;
103 5   100     65 return $error->{message} // 0;
104             } else {
105              
106 180         408 my %attr = %{{@opts}};
  180         2141  
107 180 100       886 if( $attr{array} ) {
108 13 100       54 $errors->{ $name } = [] unless exists $errors->{ $name };
109 13         24 push @{$errors->{ $name }}, \%attr;
  13         40  
110             } else {
111 167         375 $errors->{ $name } = \%attr;
112             }
113              
114 180 50       484 if( $conf->{mojo_validator} ) {
115 180         530 $self->validation->error($name => [$attr{message}]);
116             }
117              
118 180         26113 return $errors;
119             }
120 75         1189 });
121              
122             # Return string if parameter have error, else empty string.
123             $app->helper(vclass => sub{
124 6     6   1327 my ($self, $name, @classes) = @_;
125 6 100       22 return '' unless $self->verror( $name );
126              
127 3         49 my @class;
128             push @class, $conf->{class}
129 3 50 33     22 if defined($conf->{class}) && length($conf->{class});
130 3         5 push @class, @classes;
131              
132 3         19 return join ' ', @class;
133 75         1153 });
134              
135             $app->helper(vvalue => sub{
136 6     6   13899 my ($self, $name, $default) = @_;
137              
138 6         27 my @input = params($self, $name);
139              
140 6         990 my $value;
141 6 100       21 if( not @input ) {
    100          
142 3         5 $value = $default;
143             } elsif( @input > 1 ) {
144 1         2 $value = \@input;
145             } else {
146 2         4 $value = $input[0];
147             }
148              
149 6         37 return $value;
150 75         1125 });
151              
152             # Return all errors as Hash or errors count in scalar context.
153             $app->helper(verrors => sub{
154 20     20   5282 my ($self) = @_;
155 20   100     65 my $errors = $self->stash->{'vparam-verrors'} //= {};
156 20 100       335 return wantarray ? %$errors : scalar keys %$errors;
157 75         1080 });
158              
159             # Many parameters
160             $app->helper(vparams => sub{
161 475     475   210334 my ($self, %params) = @_;
162              
163             # Result
164 475         677 my %result;
165              
166             # Get default optional
167             my $optional = exists $params{-optional}
168             ? delete $params{-optional}
169             : $conf->{optional}
170 475 100       1411 ;
171             my $skipundef = exists $params{-skipundef}
172             ? delete $params{-skipundef}
173             : $conf->{skipundef}
174 475 50       1094 ;
175             my $multiline = exists $params{-multiline}
176             ? delete $params{-multiline}
177             : $conf->{multiline}
178 475 50       1008 ;
179             my $blessed = exists $params{-blessed}
180             ? delete $params{-blessed}
181             : $conf->{blessed}
182 475 50       992 ;
183              
184             # Internal variables
185 475   100     1127 my $vars = $self->stash->{'vparam-vars'} //= {};
186              
187 475         5108 for my $name (keys %params) {
188             # Param definition
189 548         4475 my $def = $params{$name};
190              
191             # Get attibutes
192 548         674 my %attr;
193 548 100       2100 if( 'HASH' eq ref $def ) {
    100          
    100          
    100          
    50          
194 238         833 %attr = %$def;
195             } elsif( 'Regexp' eq ref $def ) {
196 3         7 $attr{regexp} = $def;
197             } elsif( 'CODE' eq ref $def ) {
198 3         6 $attr{post} = $def;
199             } elsif( 'ARRAY' eq ref $def ) {
200 3         7 $attr{in} = $def;
201             } elsif( !ref $def ) {
202 301         650 $attr{type} = $def;
203             }
204              
205             # Skip
206 548 100       1294 if( exists $attr{skip} ) {
207 5 100       14 if( 'CODE' eq ref $attr{skip} ) {
    50          
208             # Skip by sub result
209 1 50       4 next if $attr{skip}->($self, $name);
210             } elsif( $attr{skip} ) {
211             # Skip by flag
212 4         10 next;
213             }
214             }
215              
216             # Set defaults
217 543   100     2447 $attr{optional} //= $optional;
218 543   66     1858 $attr{skipundef} //= $skipundef;
219 543   66     1739 $attr{multiline} //= $multiline;
220 543   66     1763 $attr{blessed} //= $blessed;
221              
222             # Apply type
223 543   100     1172 my $type = $attr{type} // $attr{isa};
224 543 100       1024 if( defined $type ) {
225             # Parse shortcut
226 530         4124 while( my ($mod, $inner) = $type =~ $SHORTCUT_REGEXP ) {
227 50 50       138 last unless $inner;
228 50         85 $type = $inner;
229              
230 50 100 100     383 if( $mod eq '?' || $mod =~ m{^optional\[}i) {
    100 100        
    100 100        
    100          
    50          
    100          
231 17         128 $attr{optional} = 1;
232             } elsif( $mod eq '!' || $mod =~ m{^required?\[}i) {
233 6         36 $attr{optional} = 0;
234             } elsif( $mod eq '@' || $mod =~ m{^array\[}i) {
235 22         154 $attr{array} = 1;
236             } elsif( $mod eq '%' ) {
237 2   33     25 $attr{hash} //= $conf->{hash_delimiter};
238             } elsif( $mod =~ m{^skipundef\[}i) {
239 0         0 $attr{skipundef}= 1;
240             } elsif( $mod eq '~' ) {
241 2         4 $attr{skipundef}= 1;
242 2         15 $attr{optional} = 1;
243             }
244             }
245              
246 530 100       1439 if( exists $conf->{types}{ $type } ) {
247 528         690 for my $key ( keys %{$conf->{types}{ $type }} ) {
  528         1690  
248 1468 100       2361 next if defined $attr{ $key };
249 1458         2699 $attr{ $key } = $conf->{types}{ $type }{ $key };
250             }
251             } else {
252 2         53 die sprintf 'Type "%s" is not defined', $type;
253             }
254             }
255              
256             # Preload module if required
257 541 100       1340 if( my $load = $attr{load} ) {
258 98 100       295 if( 'CODE' eq ref $load ) {
    100          
259 1         4 $load->($self, $name);
260             } elsif( 'ARRAY' eq ref $load ) {
261 54         105 for my $module ( @$load ) {
262 108         259 my $e = load_class( $module );
263 108 50       363866 die $e if $e;
264             }
265             } else {
266 43         163 my $e = load_class( $load );
267 43 50       358896 die $e if $e;
268             }
269             }
270              
271             # Get value
272 541         373938 my @input;
273 541 100       2088 if ($attr{'jpath?'}) {
    100          
    100          
    100          
274             # JSON Pointer
275 12 100       23 unless (exists $vars->{json}) {
276             $vars->{json} =
277 8   50     21 Mojolicious::Plugin::Vparam::JSON::parse_json(
278             $self->req->body // ''
279             );
280             }
281 12 100       6526 if( $vars->{json} ) {
282             $vars->{pointer} //=
283 6   66     36 Mojo::JSON::Pointer->new( $vars->{json} );
284 6 100       64 if( $vars->{pointer}->contains( $attr{'jpath?'} ) ) {
285 4         132 my $value = $vars->{pointer}->get( $attr{'jpath?'} );
286 4 50       104 @input = 'ARRAY' eq ref $value ? @$value : $value;
287             }
288             } else {
289             # POST parameters
290 6         23 @input = params($self, $name);
291             }
292             } elsif ($attr{jpath}) {
293             # JSON Pointer
294 20 100       46 unless (exists $vars->{json}) {
295             $vars->{json} =
296 9   50     28 Mojolicious::Plugin::Vparam::JSON::parse_json(
297             $self->req->body // ''
298             );
299             }
300 20 100       3613 if( $vars->{json} ) {
301             $vars->{pointer} //=
302 18   66     85 Mojo::JSON::Pointer->new( $vars->{json} );
303 18 100       166 if( $vars->{pointer}->contains( $attr{jpath} ) ) {
304 14         465 my $value = $vars->{pointer}->get( $attr{jpath} );
305 14 100       366 @input = 'ARRAY' eq ref $value ? @$value : $value;
306             }
307             }
308             } elsif ($attr{cpath}) {
309             # CSS
310 6 100       12 unless (exists $vars->{dom}) {
311             $vars->{dom} =
312 4   50     12 Mojolicious::Plugin::Vparam::DOM::parse_dom(
313             $self->req->body // ''
314             );
315             }
316 6 100       20 if( $vars->{dom} ) {
317             @input =
318 5         30 $vars->{dom}->find($attr{cpath})->map('text')->each;
319             }
320             } elsif ($attr{xpath}) {
321 5 50       21 unless (exists $vars->{xml}) {
322 5   50     24 $vars->{xml} = Mojolicious::Plugin::Vparam::XML::parse_xml(
323             $self->req->body // ''
324             );
325             }
326 1 50       7 if( $vars->{xml} ) {
327 0         0 @input = map {$_->textContent}
328 0         0 $vars->{xml}->findnodes($attr{xpath});
329             }
330             } else {
331             # POST parameters
332 498         1408 @input = params($self, $name);
333             }
334              
335             # Set undefined value if parameter not set
336             # if array or hash then keep it empty
337             @input = (undef)
338             if
339             not @input
340             and not $attr{array}
341             and not $attr{hash}
342 537 100 100     77861 ;
      100        
343              
344             # Set array if values more that one
345 537 100 100     1504 $attr{array} = 1 if @input > 1 and not $attr{hash};
346              
347 537 100       1191 if( $attr{multiline} ) {
348 9 50       19 if( $attr{array} ) {
349 0         0 die 'Array of arrays not supported';
350             } else {
351             my $regexp = 'Regexp' eq ref $attr{multiline}
352             ? $attr{multiline}
353 9 100       28 : qr{\r?\n}
354             ;
355             # Apply multiline
356             @input =
357 17         56 grep { $_ =~ m{\S} }
358 9         18 map { split $regexp, $_ } @input;
  9         81  
359             }
360              
361             # Multiline force array
362 9         21 $attr{array} = 1;
363             }
364              
365             # Normalize hash key delimiter
366             $attr{hash} = $conf->{hash_delimiter}
367 537 100 100     1345 if $attr{hash} && $attr{hash} eq '1';
368              
369             # Process on all input values
370 537         791 my @keys;
371             my @output;
372 537         1382 for my $index ( 0 .. $#input ) {
373 583         1039 my $in = my $out = $input[$index];
374 583         673 my $key;
375              
376 583 100       1083 if( $attr{hash} ) {
377 34         368 my @splitted = split $attr{hash}, $out, 2;
378 34 100       90 unless( @splitted == 2 ) {
379 3         24 $self->verror(
380             $name,
381             %attr,
382             index => $index,
383             in => $in,
384             out => $out,
385             message => 'Not a hash',
386             );
387 3         8 next;
388             }
389              
390 31         44 $key = $splitted[0];
391 31         51 $in = $out = $splitted[1];
392             }
393              
394             # Apply pre filter
395 580 100       2192 $out = $attr{pre}->( $self, $out, \%attr ) if $attr{pre};
396              
397             # Apply validator
398 580 100       12704 if( $attr{valid} ) {
399 567 100       1451 if( my $error = $attr{valid}->($self, $out, \%attr) ) {
400             # Set default value if error
401 188         1549 $out = $attr{default};
402              
403             # Default value always supress error
404 188 100       445 $error = 0 if exists $attr{default};
405              
406             # Disable error on optional
407 188 100       479 if( $attr{optional} ) {
408             # Only if input param not set
409 19 100       51 $error = 0 if not defined $in;
410 19 100 100     115 $error = 0 if defined($in) and $in =~ m{^\s*$};
411             }
412              
413             $self->verror(
414 188 100       1390 $name,
415             %attr,
416             index => $index,
417             in => $in,
418             out => $out,
419             message => $error,
420             ) if $error;
421             }
422             }
423              
424             # Hack for bool values:
425             # HTML forms do not transmit if checkbox off
426 580 100 100     2291 if( $type and not defined $in ) {
427 64 100       136 if( $type eq 'bool' ) {
428             $out = exists $attr{default}
429             ? $attr{default}
430 6 100       13 : 0
431             ;
432             }
433 64 100       125 if( $type eq 'logic' ) {
434 6         9 $out = $attr{default};
435             }
436             }
437              
438             # Apply post filter
439 580 100       1648 $out = $attr{post}->( $self, $out, \%attr ) if $attr{post};
440              
441             # Apply other filters
442 580         5036 for my $key ( keys %attr ) {
443             # Skip unknown attribute
444 4798 100       7504 next unless $conf->{filters}{ $key };
445              
446             my $error = $conf->{filters}{ $key }->(
447 77         231 $self, $out, $attr{ $key }
448             );
449 77 100       168 if( $error ) {
450             # Set default value if error
451 38         70 $out = $attr{default};
452              
453             # Default value always supress error
454 38 100       87 $error = 0 if defined $attr{default};
455             # Disable error on optional
456 38 100       72 if( $attr{optional} ) {
457             # Only if input param not set
458 1 50       4 $error = 0 if not defined $in;
459 1 50 33     55 $error = 0 if defined($in) and $in =~ m{^\s*$};
460             }
461              
462             $self->verror(
463 38 100       249 $name,
464             %attr,
465             index => $index,
466             in => $in,
467             out => $out,
468             message => $error,
469             ) if $error;
470             }
471             }
472              
473             # Add output
474 580 100 100     1846 if( defined($out) or not $attr{skipundef} ) {
475 572         986 push @output, $out;
476 572 100 66     2076 push @keys, $key if $attr{hash} and defined $key;
477             }
478             }
479              
480             # Error for required empty array
481             $self->verror( $name, %attr, message => 'Empty array' )
482 537 100 100     1635 if $attr{array} and not $attr{optional} and not @input;
      100        
483             # Error for required empty hash
484             $self->verror( $name, %attr, message => 'Empty hash' )
485 537 100 100     1217 if $attr{hash} and not $attr{optional} and not @input;
      100        
486              
487             # Rename for model
488 537   66     1595 my $as = $attr{as} // $name;
489              
490 537 100       1243 if( $attr{hash} ) {
    100          
491 14         97 $result{ $as } = { mesh @keys, @output };
492             } elsif( $attr{array} ) {
493 42 100       90 if( defined $attr{multijoin} ) {
494             $result{ $as } = @output
495 7 100       19 ? join $attr{multijoin}, grep {defined} @output
  15         87  
496             : undef
497             ;
498             } else {
499 35         84 $result{ $as } = \@output;
500             }
501             } else {
502             $result{ $as } = $output[0]
503 481 100 66     1607 unless $attr{skipundef} and not defined($output[0]);
504             }
505             # Mojolicious::Validator::Validation
506             $self->validation->output->{$name} = $result{ $as }
507 537 50       2199 if $conf->{mojo_validator};
508             }
509              
510 469 100       28413 return wantarray ? %result : \%result;
511 75         1578 });
512              
513             # One parameter
514             $app->helper(vparam => sub{
515 444     444   1285294 my ($self, $name, $def, %attr) = @_;
516              
517 444 50       1249 die 'Parameter name required' unless defined $name;
518 444 50       900 die 'Parameter type or definition required' unless defined $def;
519              
520 444         625 my $result;
521              
522 444 50       1208 unless( %attr ) {
    50          
    100          
    50          
    100          
523 319         1514 $result = $self->vparams( $name => $def );
524 0         0 } elsif( 'HASH' eq ref $def ) {
525             # Ignore attrs not in HashRef
526 0         0 $result = $self->vparams( $name => $def );
527 0         0 } elsif( 'Regexp' eq ref $def ) {
528 2         11 $result = $self->vparams( $name => { regexp => $def, %attr } );
529 0         0 } elsif('CODE' eq ref $def) {
530 0         0 $result = $self->vparams( $name => { post => $def, %attr } );
531 0         0 } elsif('ARRAY' eq ref $def) {
532 0         0 $result = $self->vparams( $name => { in => $def, %attr } );
533             } else {
534 123         922 $result = $self->vparams( $name => { type => $def, %attr } );
535             }
536              
537 439   33     3697 return $result->{ $attr{as} // $name };
538 75         1266 });
539              
540             # Load extensions: types, filters etc.
541 75         1083 for my $module (find_modules 'Mojolicious::Plugin::Vparam') {
542 1275         249699 my $e = load_class( $module );
543 1275 50       25423 die $e if $e;
544              
545 1275 100       14212 next unless $module->can('register');
546 1200         3521 $module->register($self, $app, $conf);
547             }
548              
549 75         725 return;
550             }
551              
552             1;
553              
554             __END__
555              
556             =encoding utf-8
557              
558             =head1 NAME
559              
560             Mojolicious::Plugin::Vparam - Mojolicious plugin validator for GET/POST data.
561              
562             =head1 DESCRIPTION
563              
564             Features:
565              
566             =over
567              
568             =item *
569              
570             Simple syntax or full featured
571              
572             =item *
573              
574             Many predefined types
575              
576             =item *
577              
578             Shortcuts for the most common uses
579              
580             =item *
581              
582             Filters complementary types
583              
584             =item *
585              
586             Support arrays and hashes of values
587              
588             =item *
589              
590             Support HTML checkbox as bool
591              
592             =item *
593              
594             Simple JSON values extraction and validation using JSON Pointer from
595             L<Mojo::JSON::Pointer>.
596              
597             =item *
598              
599             Simple XML/HTML values extraction and validation using CSS selector engine
600             from L<Mojo::DOM::CSS> or XPath from L<XML::LibXML>.
601              
602             =item *
603              
604             Validate all parameters at once and get hash to simple use in any Model
605              
606             =item *
607              
608             Manage validation errors
609              
610             =item *
611              
612             Full Mojolicious::Validator::Validation integration
613              
614             =back
615              
616             This module use simple parameters types B<str>, B<int>, B<email>, B<bool>,
617             etc. to validate.
618             Instead of many other modules you mostly not need add specific validation
619             subs or rules.
620             Just set parameter type. But if you want sub or regexp you can do it too.
621              
622             =head1 SYNOPSIS
623              
624             # Add plugin in startup
625             $self->plugin('Vparam');
626              
627             # Use in controller
628             $login = $self->vparam(login => 'str');
629             $passw = $self->vparam(password => 'password', size => [8, 100]);
630             $email = $self->vparam(email => 'email', optional => 1);
631             $session = $self->vparam(session => 'bool', default => 1);
632              
633             $ids = $self->vparam(ids => '@int');
634              
635             =head1 METHODS
636              
637             =head2 vparam
638              
639             Get one parameter. By default parameter is required.
640              
641             # Simple get one parameter
642             $param1 = $self->vparam(date => 'datetime');
643              
644             # Or more syntax
645             $param2 = $self->vparam(count => {type => 'int', default => 1});
646             # Or more simple syntax
647             $param2 = $self->vparam(count => 'int', default => 1);
648              
649             =head2 vparams
650              
651             Get many parameters as hash. By default all parameters are required.
652              
653             %params = $self->vparams(
654             # Simple syntax
655             name => 'str',
656             password => qr{^\w{8,32}$},
657             myparam => sub { $_[1] && $_[1] eq 'ok' ? 1 : 0 } },
658             someone => ['one', 'two', 'tree'],
659              
660             # More syntax
661             from => { type => 'date', default => '' },
662             to => { type => 'date', default => '' },
663             id => { type => 'int' },
664             money => { regexp => qr{^\d+(?:\.\d{2})?$} },
665             myparam => { post => sub { $_[1] && $_[1] eq 'ok' ? 1 : 0 } },
666              
667             # Checkbox
668             isa => { type => 'bool', default => 0 },
669             );
670              
671             =head2 vsort
672              
673             Like I<vparams> but add some keys to simple use with tables. Example:
674              
675             # HTML - table with controls and filters
676             Order by:
677             <select name="oby">
678             <option value="0">Name</option>
679             <option value="1">Date</option>
680             </select>
681             Order direction:
682             <select name="ods">
683             <option value="asc">Low to High</option>
684             <option value="desc">High to Low</option>
685             </select>
686             Count per page:
687             <select name="rws">
688             <option value="10">10</option>
689             <option value="100">100</option>
690             </select>
691             Filter by name:
692             <input type="text" name="name" value="">
693             Any other filters ...
694              
695              
696             # Controller
697             %params = $self->vsort(
698             -sort => ['name', 'date', ...],
699              
700             # next as vparams
701             name => 'text',
702             ...
703             );
704              
705             =over
706              
707             =item page
708              
709             Page number. Default: 1.
710              
711             You can set different name by I<vsort_page> config parameter.
712             If you set undef then parameter is not apply.
713              
714             =item rws
715              
716             Rows on page. Default: 25.
717              
718             You can set different name by I<vsort_rws> config parameter.
719             You can set different default by I<vsort_rows> config parameter.
720             If you set undef then parameter is not apply.
721              
722             =item oby
723              
724             Column number for sorting. Default: 1 - in many cases first database
725             column is primary key.
726              
727             You can set different name by I<vsort_oby> config parameter.
728             If you set undef then parameter is not apply.
729              
730             Value of B<oby> The value will be automatically mapped to the column name
731             using the L</-sort> attribute.
732             Also, the value will be checked for proper mapping.
733             So you do not need to worry about it.
734              
735             =item ods
736              
737             Sort order ASC|DESC. Default: ASC.
738              
739             You can set different name by I<vsort_ods> config parameter.
740             If you set undef then parameter is not apply.
741              
742             =back
743              
744             =head2 verror $name
745              
746             Get parameter error string. Return 0 if no error.
747              
748             # Get error
749             print $self->verror('myparam');
750              
751             # Get error for first element in array
752             print $self->verror('myparam' => 0);
753              
754             # Set error
755             $self->verror('myparam', message => 'Error message')
756              
757             =head2 verrors
758              
759             Return errors count in scalar context. In list context return errors hash.
760              
761             # List context get hash
762             my %errors = $self->verrors;
763              
764             # Scalar context get count
765             die 'Errors!' if $self->verrors;
766              
767             =head2 vclass $name, @classes
768              
769             Get classes for invalid input. Return empty string if no error.
770              
771             # Form example
772             <input name="myparam" class="<%= vclass 'myparam' %>">
773             # Return next code for invalid I<myparam>:
774             # <input name="myparam" class="field-with-error">
775              
776             You can set additional I<@classes> to set if field invalid.
777              
778             =head2 vvalue $name, $default
779              
780             Get raw input value after validation. Return I<$default> value or empty
781             string before validation.
782              
783             # Form example:
784             <input name="myparam" value="<%= vvalue 'myparam' %>">
785              
786             # Return next code if user just open form without submit and validation:
787             # <input name="myparam" value="">
788              
789             # Then user submit form and you validate id. For example user submit "abc":
790             # <input name="myparam" value="abc">
791              
792             =head2 vtype $name, %opts
793              
794             Set new type $name if defined %opts. Else return type $name definition.
795              
796             # Get type
797             $self->vtype('mytype');
798              
799             # Set type
800             # load - requires modules
801             # pre - get int
802             # valid - check for not empty
803             # post - force number
804             $self->vtype('mytype',
805             pre => sub {
806             my ($self, $param) = @_;
807             return int $param // '';
808             },
809             valid => sub {
810             my ($self, $param) = @_;
811             return length $param ? 0 : 'Invalid'
812             },
813             post => sub {
814             my ($self, $param) = @_;
815             return 0 + $param;
816             }
817             );
818              
819             =head2 vfilter $name, &sub
820              
821             Set new filter $name if defined %opts. Else return filter $name definition.
822              
823             # Get filter
824             $self->vfilter('myfilter');
825              
826             # Set filter
827             $self->vfilter('myfilter', sub {
828             my ($self, $param, $expression) = @_;
829             return $param eq $expression ? 0 : 'Invalid';
830             });
831              
832             Filter sub must return 0 if parameter value is valid. Or error string if not.
833              
834             =head1 SIMPLE SYNTAX
835              
836             You can use the simplified syntax instead of specifying the type,
837             simply by using an expression instead.
838              
839             =over
840              
841             =item I<REGEXP>
842              
843             Apply as L</regexp> filter. No type verification, just match.
844              
845             $self->vparam(myparam => qr{^(abc|cde)$});
846              
847             =item I<CODE> $mojo, $value
848              
849             Apply as L</post> function. You need manual verify and set error.
850              
851             $self->vparam(myparam => sub { $_[1] && $_[1] eq 'good' ? 1 : 0 });
852              
853             =item I<ARRAY>
854              
855             Apply as L</in> filter. No type verification, just match.
856              
857             $self->vparam(myparam => [qw(abc cde)]);
858              
859             =back
860              
861             =head1 CONFIGURATION
862              
863             =over
864              
865             =item class
866              
867             CSS class for invalid parameters. Default: field-with-error.
868              
869             =item types
870              
871             You can simple add you own types.
872             Just set this parameters as HashRef with new types definition.
873              
874             =item filters
875              
876             You can simple add you own filters.
877             Just set this parameters as HashRef with new filters definition.
878              
879              
880             =item vsort_page
881              
882             Parameter name for current page number in I<vsort>. Default: page.
883              
884             =item vsort_rws
885              
886             Parameter name for current page rows count in I<vsort>. Default: rws.
887              
888             =item rows
889              
890             Default rows count for I<vsort_rws>. Default: 25.
891              
892             =item vsort_oby
893              
894             Parameter name for current order by I<vsort>. Default: oby.
895              
896             =item vsort_ods
897              
898             Parameter name for current order destination I<vsort>. Default: ods.
899              
900             =item ods
901              
902             Default order destination for I<vsort_rws>. Default: ASC.
903              
904             =item phone_country
905              
906             Phone country. Default: empty.
907              
908             =item phone_region
909              
910             Phone region. Default: empty.
911              
912             =item phone_fix
913              
914             Name of algorithm to fix phone, typicallty country code. Default: empty.
915              
916             =item date
917              
918             Date format for strftime. Default: %F.
919             if no format specified, return L<DateTime> object.
920              
921             =item time
922              
923             Time format for strftime. Default: %T.
924             if no format specified, return L<DateTime> object.
925              
926             =item datetime
927              
928             Datetime format for strftime. Default: '%F %T %z'.
929             if no format specified, return L<DateTime> object.
930              
931             =item blessed
932              
933             By default return objects used for parse or validation:
934             L<Mojo::URL>, L<DateTime>, etc.
935              
936             =item optional
937              
938             By default all parameters are required. You can change this by set this
939             parameter as true.
940              
941             =item address_secret
942              
943             Secret for address:points signing. Format: "ADDRESS:LATITUDE,LONGITUDE[MD5]".
944             MD5 ensures that the coordinates belong to address.
945              
946             =item password_min
947              
948             Minimum password length. Default: 8.
949              
950             =item hash_delimiter
951              
952             Delimiter to split input parameter on two parts: key and value.
953             Default: => - like a perl hash.
954              
955             =item mojo_validator
956              
957             Enable L<Mojolicious::Validator::Validation> integration.
958              
959             =back
960              
961             =cut
962              
963             =head1 TYPES
964              
965             List of supported types:
966              
967             =head2 int
968              
969             Signed integer. Use L</min> filter for unsigned.
970              
971             =head2 numeric or number
972              
973             Signed number. Use L</min> filter for unsigned.
974              
975             =head2 money
976              
977             Get money. Use L</min> filter for unsigned.
978              
979             =head2 percent
980              
981             Unsigned number: 0 <= percent <= 100.
982              
983              
984             =head2 str
985              
986             Trimmed text. Must be non empty if required.
987              
988             =head2 text
989              
990             Any text. No errors.
991              
992             =head2 password
993              
994             String with minimum length from I<password_min>.
995             Must content characters and digits.
996              
997             =head2 uuid
998              
999             Standart 32 length UUID. Return in lower case.
1000              
1001             =head2 date
1002              
1003             Get date. Parsed from many formats.
1004             See I<date> configuration parameter for result format.
1005             See L<DateTime::Format::DateParse> and even more.
1006              
1007             =head2 time
1008              
1009             Get time. Parsed from many formats.
1010             See I<time> configuration parameter for result format.
1011             See L<DateTime::Format::DateParse> and even more.
1012              
1013             =head2 datetime
1014              
1015             Get full date and time. Parsed from many formats.
1016             See I<datetime> configuration parameter for result format.
1017              
1018             Input formats:
1019              
1020             =over
1021              
1022             =item *
1023              
1024             Timestamp.
1025              
1026             =item *
1027              
1028             Relative from now in format C<[+-] DD HH:MM:SS>. First sign required.
1029              
1030             =over
1031              
1032             =item *
1033              
1034             Minutes by default. Example: C<+15> or C<-6>.
1035              
1036             =item *
1037              
1038             Minutes and seconds. Example: C<+15:44>.
1039              
1040             =item *
1041              
1042             Hours. Example: C<+3:15:44>.
1043              
1044             =item *
1045              
1046             Days. Example: C<+8 3:15:44>.
1047              
1048             =back
1049              
1050             Values are given in arbitrary range.
1051             For example you can add 400 minutes and 300 seconds: C<+400:300>.
1052              
1053             =item *
1054              
1055             All that can be obtained L<DateTime::Format::DateParse>.
1056              
1057             =item *
1058              
1059             Russian date format like C<DD.MM.YYYY>
1060              
1061             =back
1062              
1063             =head2 bool
1064              
1065             Boolean value. Can be used to get value from checkbox or another sources.
1066              
1067             HTML forms do not send checbox if it checked off. So you don`t get error,
1068             but get false for it.
1069              
1070             $self->vparam(mybox => 'bool');
1071              
1072             Valid values are:
1073              
1074             =over
1075              
1076             =item
1077              
1078             I<TRUE> can be 1, yes, true, ok
1079              
1080             =item
1081              
1082             I<FALSE> can be 0, no, false, fail
1083              
1084             =item
1085              
1086             Empty string is I<FALSE>
1087              
1088             =back
1089              
1090             Other values get error.
1091              
1092             Example:
1093              
1094             <input type="checkbox" name="bool1" value="yes">
1095             <input type="checkbox" name="bool2" value="1">
1096             <input type="checkbox" name="bool3" value="ok">
1097              
1098             =head2 logic
1099              
1100             Three-valued logic. Same as I<bool> but undef state if empty string. Example:
1101              
1102             <select name="logic1">
1103             <option value=""></option>
1104             <option value="1">True</option>
1105             <option value="0">False</option>
1106             </select>
1107              
1108             =head2 email
1109              
1110             Email adress.
1111              
1112             =head2 url
1113              
1114             Get url as L<Mojo::URL> object.
1115              
1116             =head2 phone
1117              
1118             Phone in international format. Support B<wait>, B<pause> and B<additional>.
1119              
1120             You can set default country I<phone_country> and region I<phone_country> codes.
1121             Then you users can input shortest number.
1122             But this is not work if you site has i18n.
1123              
1124             =head2 json
1125              
1126             JSON incapsulated as form parameter.
1127              
1128             =head2 address
1129              
1130             Location address. Two forms are parsed: string and json.
1131             Can verify adress sign to trust source.
1132              
1133             =head2 lon
1134              
1135             Longitude.
1136              
1137             =head2 lat
1138              
1139             Latilude.
1140              
1141             =head2 isin
1142              
1143             International Securities Identification Number:
1144             Mir, American Express, Diners Club, JCB, Visa,
1145             MasterCard, Maestro, etc.
1146              
1147             You can check for ISIN type like:
1148              
1149             # Mir
1150             $self->vparam(card => 'isin', regexp => qr{^2});
1151              
1152             # American Express, Diners Club, JCB
1153             $self->vparam(card => 'isin', regexp => qr{^3});
1154              
1155             # Visa
1156             $self->vparam(card => 'isin', regexp => qr{^4});
1157              
1158             # MasterCard
1159             $self->vparam(card => 'isin', regexp => qr{^5});
1160              
1161             # Maestro
1162             $self->vparam(card => 'isin', regexp => qr{^6});
1163              
1164             =head2 maestro
1165              
1166             Some local country, not 16 numbers cards: Maestro, Visa Electron, etc.
1167              
1168             =head3 creditcard
1169              
1170             Aggregate any creditcard: ISIN, Maestro, etc.
1171              
1172             =head2 barcode
1173              
1174             Barcode: EAN-13, EAN-8, EAN 5, EAN 2, UPC-12, ITF-14, JAN, UPC, etc.
1175              
1176             $self->vparam(barcode => 'goods');
1177              
1178             =head2 inn
1179              
1180             RU: Taxpayer Identification Number
1181              
1182             =head2 kpp
1183              
1184             RU: Code of reason for registration
1185              
1186             =head1 ATTRIBUTES
1187              
1188             You can set a simple mode as in example or full mode. Full mode keys:
1189              
1190              
1191             =head2 default
1192              
1193             Default value. Default: undef.
1194              
1195             # Supress myparam to be undefined and error
1196             $self->vparam(myparam => 'str', default => '');
1197              
1198             =head2 load
1199              
1200             Autoload module for this type.
1201             Can be module name, array of module names or sub.
1202              
1203             =head2 pre $mojo, &sub
1204              
1205             Incoming filter sub. Used for primary filtration: string length and trim, etc.
1206             Result will be used as new param value.
1207              
1208             Usually, if you need this attribute, you need to create a new type.
1209              
1210             =head2 valid $mojo, &sub
1211              
1212             Validation sub. Return 0 if valid, else string of error.
1213              
1214             Usually, if you need this attribute, you need to create a new type.
1215              
1216             =head2 post $mojo, &sub
1217              
1218             Out filter sub. Used to modify value for use in you program. Usually used to
1219             bless in some object.
1220             Result will be used as new param value.
1221              
1222             =head2 type
1223              
1224             Parameter type. If set then some filters will be apply. See L</TYPES>.
1225              
1226             $self->vparam(myparam => 'datetime');
1227             $self->vparam(myparam => {type => 'datetime'});
1228             $self->vparams(
1229             myparam1 => {type => 'datetime'},
1230             myparam2 => {isa => 'datetime'},
1231             );
1232              
1233             After the application of the type used filters.
1234              
1235             You can use B<isa> alias instead of B<type>.
1236              
1237             =head2 as
1238              
1239             Rename output key for I<vparams> to simple use in models.
1240              
1241             # Return {login => '...', password => '...'}
1242             $self->vparams(
1243             myparam1 => {type => 'str', as => 'login'},
1244             myparam2 => {type => 'str', as => 'password'},
1245             );
1246              
1247             =head2 array
1248              
1249             Force value will array. Default: false.
1250              
1251             You can force values will arrays by B<@> prefix or case insensive B<array[...]>.
1252              
1253             # Arrays shortcut syntax
1254             $param1 = $self->vparam(array1 => '@int');
1255             $param2 = $self->vparam(array2 => 'array[int]');
1256              
1257             # Array attribute syntax
1258             $param3 = $self->vparam(array3 => 'int', array => 1);
1259              
1260             # The array will come if more than one value incoming
1261             # Example: http://mysite.us?array4=123&array4=456...
1262             $param4 = $self->vparam(array4 => 'int');
1263              
1264             =head2 hash
1265              
1266             Get value as hash. Default: false.
1267             You can get values as hash by B<%> prefix.
1268              
1269             To make hash vparam split value by predefined delimiter
1270             (see I<hash_delimiter> configuration parameter). It`s important to make
1271             value format.
1272              
1273             # Get param1 as hash. Parameter value need to be like 'a=>1'.
1274             $param1 = $self->vparam(myparam1 => '%int');
1275              
1276             # Same, but custom delimiter "::"
1277             $param2 = $self->vparam(myparam2 => 'int', hash => '::');
1278              
1279             # Multiple parameters supported.
1280             # You can send many myparam3 with different values list like:
1281             # ?myparam3=a_1&myparam3=b_2&myparam3=c_3
1282             # and get perl hash like $param3 = {a => 1, b => 3, c => 3}
1283             $param3 = $self->vparam(myparam3 => '%int', hash => '_');
1284              
1285             =head2 optional
1286              
1287             By default all parameters are required. You can change this for parameter by
1288             set I<optional>.
1289             Then true and value is not passed validation don`t set verrors.
1290              
1291             # Simple vparam
1292             # myparam is undef but no error.
1293             $param1 = $self->vparam(param1 => 'int', optional => 1);
1294              
1295             # Set one in vparams
1296             %params = $self->vparams(
1297             myparam => { type => 'int', optional => 1 },
1298             );
1299              
1300             # Set all in vparams
1301             %params = $self->vparams(
1302             -optional => 1,
1303             param1 => 'int',
1304             param2 => 'str',
1305             );
1306              
1307             # Shortcut optional syntax
1308             $param2 = $self->vparam(param2 => '?int');
1309             $param3 = $self->vparam(param3 => 'maybe[int]');
1310             $param4 = $self->vparam(param4 => 'optional[int]');
1311              
1312             # Shortcut required syntax
1313             $param5 = $self->vparam(param5 => '!int');
1314             $param6 = $self->vparam(param6 => 'require[int]');
1315             $param7 = $self->vparam(param7 => 'required[int]');
1316              
1317             =head2 skip
1318              
1319             So as not to smear the validation code you can use the I<skip> parameter
1320             to skip on the condition.
1321             This attribute is useful for controlling access to the form fields.
1322              
1323             # This example don`t get param1 in production mode.
1324              
1325             # HTML
1326             % unless( $self->app->mode eq 'production' ) {
1327             %= number_field 'param1'
1328             % }
1329              
1330             # Simple flag
1331             $param1 = $self->vparam(
1332             param1 => 'int', skip => $self->app->mode eq 'production',
1333             );
1334              
1335             # Same as by use sub.
1336             $param1 = $self->vparam(
1337             param1 => 'int', skip => sub { $_[0]->app->mode eq 'production' },
1338             );
1339              
1340             If you use sub then first parameter is controller.
1341              
1342             =head2 skipundef
1343              
1344             By default all parameters are in output hash. You can skip parameter in result
1345             if it`s undefined by set I<skipundef>.
1346              
1347             # Simple vparam
1348             # myparam is undef.
1349             $param1 = $self->vparam(param1 => 'int', optional => 1, skipundef => 1);
1350              
1351             # Simple flag
1352             # The %params hash is empty if myparam value is not integer.
1353             %params = $self->vparams(
1354             myparam => { type => 'int', optional => 1, skipundef => 1 },
1355             );
1356              
1357             # Set all in vparams
1358             # The %params hash is empty if all parameters are not valid.
1359             %params = $self->vparams(
1360             -skipundef => 1,
1361             param1 => 'int',
1362             param2 => 'str',
1363             );
1364              
1365             # Shortcut syntax: skipundef and optional is on
1366             $param2 = $self->vparam(param2 => '~int');
1367              
1368             Arrays always return as arrayref. But undefined values will be skipped:
1369              
1370             # This vparam return [1,2,3] for ?param3=1&param3=&param3=2&param3=3
1371             $param2 = $self->vparam(param3 => '~int');
1372              
1373             =head2 multiline
1374              
1375             You can simple split I<textarea> to values:
1376              
1377             # This vparam return [1,2,3] for input "1\n2\n3\n"
1378             $param1 = $self->vparam(param1 => 'int', multiline => 1);
1379              
1380             # Or by custom regexp
1381             # This vparam return [1,2,3] for input "1,2,3"
1382             $param1 = $self->vparam(param1 => 'int', multiline => qr{\s*,\s*});
1383              
1384             Empty lines ignored.
1385              
1386             =head2 multijoin
1387              
1388             Any array values can be joined in string:
1389              
1390             # This vparam return "1,2,3" for input ?param1=1&param1=2&param1=3
1391             $param1 = $self->vparam(param1 => 'int', multijoin => ',');
1392              
1393             # This vparam return "1,2,3" for input "1\n2\n3\n"
1394             $param2 = $self->vparam(param2 => 'int', multiline => 1, multijoin => ',');
1395              
1396             =head2 blessed
1397              
1398             Keep and return blessed object for parsed parameters if available.
1399             Vparam always return scalars if disabled.
1400              
1401             Note: if defined I<date>, I<time>, I<datetime> then always return
1402             formatted scalar.
1403              
1404             =head2 jpath or jpath?
1405              
1406             If you POST data not form but raw JSON you can use JSON Pointer selectors
1407             from L<Mojo::JSON::Pointer> to get and validate parameters.
1408              
1409             # POST data contains:
1410             # {"point":{"address":"some", "lon": 45.123456, "lat": 38.23452}}
1411              
1412             %opts = $self->vparams(
1413             address => { type => 'str', jpath => '/point/address' },
1414             lon => { type => 'lon', jpath => '/point/lon' },
1415             lat => { type => 'lat', jpath => '/point/lat' },
1416             );
1417              
1418             Note: we don`t support multikey in json. Use hash or die.
1419              
1420             If You use C<jpath?> instead C<jpath>, vparam tries parse input json, if
1421             json is invalid vparam tries fetch param from input form:
1422              
1423              
1424             ######################## works:
1425             # {"point":{"address":"some", "lon": 45.123456, "lat": 38.23452}}
1426              
1427             # #######################works:
1428             # address=some&lon=45.123456&lat=38.23452
1429              
1430             ################# doesn't work:
1431             # query: address=some
1432             # body: {"point":{"lon": 45.123456, "lat": 38.23452}}
1433              
1434             %opts = $self->vparams(
1435             address => { type => 'str', 'jpath?' => '/point/address' },
1436             lon => { type => 'lon', 'jpath?' => '/point/lon' },
1437             lat => { type => 'lat', 'jpath?' => '/point/lat' },
1438             );
1439              
1440             Note: You cant mix C<jpath> and C<jpath?>: If body contains valid JSON, vparam
1441             doesn't try check form params.
1442              
1443              
1444             =head2 cpath
1445              
1446             Same as jpath but parse XML/HTML using CSS selectors from L<Mojo::DOM::CSS>.
1447              
1448             # POST data contains:
1449             # <Point>
1450             # <Address>some</Address>
1451             # <Lon>45.123456</Lon>
1452             # <Lat>38.23452</Lat>
1453             # </Point>
1454              
1455             %opts = $self->vparams(
1456             address => { type => 'str', cpath => 'Point > Address' },
1457             lon => { type => 'lon', cpath => 'Point > Lon' },
1458             lat => { type => 'lat', cpath => 'Point > Lat' },
1459             );
1460              
1461              
1462             =head2 xpath
1463              
1464             Same as cpath but parse XML/HTML using XPath selectors from L<XML::LibXML>.
1465              
1466             # POST data contains:
1467             # <Point time="2016-11-25 14:39:00 +0300">
1468             # <Address>some</Address>
1469             # <Lon>45.123456</Lon>
1470             # <Lat>38.23452</Lat>
1471             # </Point>
1472              
1473             %opts = $self->vparams(
1474             address => { type => 'str', xpath => '/Point/Address' },
1475             lon => { type => 'lon', xpath => '/Point/Lon' },
1476             lat => { type => 'lat', xpath => '/Point/Lat' },
1477             time => { type => 'datetime', xpath => '/Point/@time' },
1478             );
1479              
1480             =head1 RESERVED ATTRIBUTES
1481              
1482             =head2 -sort
1483              
1484             List of column names for I<vsort>. Usually not all columns visible for users and
1485             you need convert column numbers in names. This also protect you SQL queries
1486             from set too much or too low column number.
1487              
1488             =head2 -optional
1489              
1490             Set default I<optional> flag for all params in L</vparams> and L</vsort>.
1491              
1492             =head2 -skipundef
1493              
1494             Set default I<skipundef> flag for all params in L</vparams> and L</vsort>.
1495              
1496             =head1 FILTERS
1497              
1498             Filters are used in conjunction with types for additional verification.
1499              
1500             =head2 range
1501              
1502             Check parameter value to be in range.
1503              
1504             # Error if myparam less than 10 or greater than 100
1505             $self->vparam(myparam => 'int', range => [10, 100]);
1506              
1507             =head2 regexp
1508              
1509             Check parameter to be match for regexp
1510              
1511             # Error if myparam not equal "abc" or "cde"
1512             $self->vparam(myparam => 'str', regexp => qr{^(abc|cde)$});
1513              
1514             =head2 in
1515              
1516             Check parameter value to be in list of defined values.
1517              
1518             # Error if myparam not equal "abc" or "cde"
1519             $self->vparam(myparam => 'str', in => [qw(abc cde)]);
1520              
1521             =head2 size
1522              
1523             Check maximum length in utf8.
1524              
1525             # Error if value is an empty string
1526             $self->vparam(myparam => 'str', size => [1, 100]);
1527              
1528             =head2 Numbers comparation
1529              
1530             I<min>, I<max>, I<equal>, I<not>
1531              
1532             =head2 Strings comparation
1533              
1534             I<lt>, I<gt>, I<le>, I<ge>, I<cmp>, I<eq>, I<ne>
1535              
1536             =head1 RESTRICTIONS
1537              
1538             =over
1539              
1540             =item *
1541              
1542             Version 1.0 invert I<valid> behavior: now checker return 0 if no error
1543             or description string if has.
1544              
1545             =item *
1546              
1547             New errors keys: orig => in, pre => out
1548              
1549             =back
1550              
1551             =head1 SEE ALSO
1552              
1553             L<Mojolicious::Validator::Validation>, L<Mojolicious::Plugin::Human>.
1554              
1555             =head1 AUTHORS
1556              
1557             Dmitry E. Oboukhov <unera@debian.org>,
1558             Roman V. Nikolaev <rshadow@rambler.ru>
1559              
1560             =head1 COPYRIGHT
1561              
1562             Copyright (C) 2011 Dmitry E. Oboukhov <unera@debian.org>
1563             Copyright (C) 2011 Roman V. Nikolaev <rshadow@rambler.ru>
1564              
1565             This library is free software; you can redistribute it and/or modify
1566             it under the same terms as Perl itself, either Perl version 5.8.8 or,
1567             at your option, any later version of Perl 5 you may have available.
1568              
1569             =cut