File Coverage

blib/lib/Mojolicious/Plugin/Validate/Tiny.pm
Criterion Covered Total %
statement 35 109 32.1
branch 2 36 5.5
condition 1 19 5.2
subroutine 9 14 64.2
pod 1 1 100.0
total 48 179 26.8


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Validate::Tiny;
2 1     1   736 use Mojo::Base 'Mojolicious::Plugin';
  1         3  
  1         8  
3              
4 1     1   229 use v5.10;
  1         14  
5 1     1   5 use strict;
  1         3  
  1         18  
6 1     1   4 use warnings;
  1         2  
  1         31  
7 1     1   5 use Carp qw/croak/;
  1         16  
  1         59  
8 1     1   593 use Validate::Tiny;
  1         16954  
  1         97  
9 1     1   738 no if $] >= 5.017011, warnings => 'experimental::smartmatch';
  1         15  
  1         7  
10              
11             our $VERSION = '0.18';
12              
13             sub register {
14 1     1 1 70 my ( $self, $app, $conf ) = @_;
15 1         14 my $log = $app->log;
16              
17             # Processing config
18             $conf = {
19             explicit => 0,
20             autofields => 1,
21             exclude => [],
22 1 50       227 %{ $conf || {} } };
  1         9  
23              
24             # Helper do_validation
25             $app->helper(
26             do_validation => sub {
27 0     0   0 my ( $c, $rules, $params ) = @_;
28 0 0       0 croak "ValidateTiny: Wrong validatation rules"
29             unless ref($rules) ~~ [ 'ARRAY', 'HASH' ];
30              
31 0         0 $c->stash('validate_tiny.was_called', 1);
32              
33 0 0       0 $rules = { checks => $rules } if ref $rules eq 'ARRAY';
34 0   0     0 $rules->{fields} ||= [];
35              
36             # Validate GET+POST parameters by default
37 0   0     0 $params ||= $c->req->params->to_hash();
38              
39             # Validate Uploaded files by default
40 0   0     0 $params->{ $_->name } ||= $_ for (@{ $c->req->uploads });
  0         0  
41              
42             #Latest mojolicious has an issue in that it doesn't include route supplied parameters so we need to hack that in.
43 0         0 $params = { %{$params}, %{$c->stash->{'mojo.captures'}} };
  0         0  
  0         0  
44              
45             # Autofill fields
46 0 0       0 if ( $conf->{autofields} ) {
47 0         0 push @{$rules->{fields}}, keys %$params;
  0         0  
48 0         0 for ( my $i = 0; $i< @{$rules->{checks}}; $i += 2 ){
  0         0  
49 0         0 my $field = $rules->{checks}[$i];
50 0 0       0 next if ref $field eq 'Regexp';
51 0         0 push @{$rules->{fields}}, $field;
  0         0  
52             }
53             }
54              
55             # Remove fields duplications
56 0         0 my %h;
57 0         0 @{$rules->{fields}} = grep { !$h{$_}++ } @{$rules->{fields}};
  0         0  
  0         0  
  0         0  
58              
59             # Check that there is an individual rule for every field
60 0 0       0 if ( $conf->{explicit} ) {
61 0         0 my %h = @{ $rules->{checks} };
  0         0  
62 0         0 my @fields_wo_rules;
63              
64 0         0 foreach my $f ( @{ $rules->{fields} } ) {
  0         0  
65 0 0       0 next if $f ~~ $conf->{exclude};
66 0 0       0 push @fields_wo_rules, $f unless exists $h{$f};
67             }
68              
69 0 0       0 if (@fields_wo_rules) {
70             my $err_msg = 'ValidateTiny: No validation rules for '
71 0         0 . join( ', ', map { qq'"$_"' } @fields_wo_rules );
  0         0  
72              
73 0         0 my $errors = {};
74 0         0 foreach my $f (@fields_wo_rules) {
75 0         0 $errors->{$f} = "No validation rules for field \"$f\"";
76             }
77 0         0 $c->stash( 'validate_tiny.errors' => $errors);
78 0         0 $log->debug($err_msg);
79 0         0 return 0;
80             }
81             }
82              
83             # Do validation, Validate::Tiny made a breaking change and we need to support old and new users
84 0         0 my $result;
85 0 0       0 if(Validate::Tiny->can('check')) {
86 0         0 $result = Validate::Tiny->check( $params, $rules );
87             }
88             else { # Fall back for old Validate::Tiny version
89 0         0 $result = Validate::Tiny->new( $params, $rules );
90             }
91            
92 0         0 $c->stash( 'validate_tiny.result' => $result );
93              
94 0 0       0 if ( $result->success ) {
95 0         0 $log->debug('ValidateTiny: Successful');
96 0         0 return $result->data;
97             } else {
98 0         0 $log->debug( 'ValidateTiny: Failed: ' . join( ', ', keys %{ $result->error } ) );
  0         0  
99 0         0 $c->stash( 'validate_tiny.errors' => $result->error );
100 0         0 return;
101             }
102 1         14 } );
103              
104             # Helper validator_has_errors
105             $app->helper(
106             validator_has_errors => sub {
107 0     0   0 my $c = shift;
108 0         0 my $errors = $c->stash('validate_tiny.errors');
109              
110 0 0 0     0 return 0 if !$errors || !keys %$errors;
111 0         0 return 1;
112 1         175 } );
113              
114             # Helper validator_error
115             $app->helper(
116             validator_error => sub {
117 0     0   0 my ( $c, $name ) = @_;
118 0         0 my $errors = $c->stash('validate_tiny.errors');
119              
120 0 0       0 return $errors unless defined $name;
121              
122 0 0 0     0 if ( $errors && defined $errors->{$name} ) {
123 0         0 return $errors->{$name};
124             }
125 1         90 } );
126              
127             # Helper validator_error_string
128             $app->helper(
129             validator_error_string => sub {
130 0     0   0 my ( $c, $params ) = @_;
131 0 0       0 return '' unless $c->validator_has_errors();
132 0   0     0 $params //= {};
133              
134 0         0 return $c->stash('validate_tiny.result')->error_string(%$params);
135 1         86 } );
136              
137             # Helper validator_any_error
138             $app->helper(
139             validator_any_error => sub {
140 0     0   0 my ( $c ) = @_;
141 0         0 my $errors = $c->stash('validate_tiny.errors');
142              
143 0 0       0 if ( $errors ) {
144 0         0 return ( ( values %$errors )[0] );
145             }
146              
147 0         0 return;
148 1         83 } );
149              
150              
151             # Print info about actions without validation
152             $app->hook(
153             after_dispatch => sub {
154 1     1   13166 my ($c) = @_;
155 1         6 my $stash = $c->stash;
156 1 50       11 return 1 if $stash->{'validate_tiny.was_called'};
157              
158 1 0 33     6 if ( $stash->{controller} && $stash->{action} ) {
159 0         0 $log->debug("ValidateTiny: No validation in [$stash->{controller}#$stash->{action}]");
160 0         0 return 0;
161             }
162              
163 1         2 return 1;
164 1         103 } );
165              
166             }
167              
168             1;
169              
170              
171             =head1 NAME
172              
173             Mojolicious::Plugin::Validate::Tiny - Lightweight validator for Mojolicious
174              
175             =head1 SEE
176              
177             This plugin is a copy of L, with the intent to have a plugin that it's maintained
178              
179             =head1 SYNOPSIS
180              
181             # Mojolicious
182             $self->plugin('Validate::Tiny');
183            
184             # Mojolicious::Lite
185             plugin 'Validate::Tiny';
186            
187             sub action {
188             my $self = shift;
189             my $validate_rules = [
190             # All of these are required
191             [qw/name email pass pass2/] => is_required(),
192              
193             # pass2 must be equal to pass
194             pass2 => is_equal('pass'),
195              
196             # custom sub validates an email address
197             email => sub {
198             my ( $value, $params ) = @_;
199             Email::Valid->address($value) ? undef : 'Invalid email';
200             }
201             ];
202             return unless $self->do_validation($validate_rules);
203            
204             ... Do something ...
205             }
206            
207            
208             sub action2 {
209             my $self = shift;
210              
211             my $validate_rules = {
212             checks => [...],
213             fields => [...],
214             filters => [...]
215             };
216             if ( my $filtered_params = $self->do_validation($validate_rules) ) {
217             # all $params are validated and filters are applyed
218             ... do your action ...
219              
220            
221             } else {
222             my $errors = $self->validator_error; # hash with errors
223             my $pass_error = $self->validator_error('password'); # password error text
224             my $any_error = $self->validator_any_error; # any error text
225            
226             $self->render( status => '403', text => $any_error );
227             }
228            
229             }
230            
231             __DATA__
232            
233             @@ user.html.ep
234             %= if (validator_has_errors) {
235            
Please, correct the errors below.
236             % }
237             %= form_for 'user' => begin
238            
239             <%= input_tag 'username' %>
240             <%= validator_error 'username' %>
241            
242             <%= submit_button %>
243             % end
244              
245            
246             =head1 DESCRIPTION
247              
248             L is a L support for L.
249              
250             =head1 OPTIONS
251              
252             =head2 C (default 0)
253              
254             If "explicit" is true then for every field must be provided check rule
255              
256             =head2 C (default 1)
257              
258             If "autofields" then validator will automatically create fields list based on passed checks.
259             So, you can pass:
260             [
261             user => is_required(),
262             pass => is_required(),
263             ]
264              
265             instead of
266              
267             {
268             fields => ['user', 'pass'],
269             checks => [
270             user => is_required(),
271             pass => is_required(),
272             ]
273             }
274              
275             =head2 C (default [])
276              
277             Is an arrayref with a list of fields that will be never checked.
278              
279             For example, if you use "Mojolicious::Plugin::CSRFProtect" then you should add "csrftoken" to exclude list.
280              
281             =head1 HELPERS
282              
283             =head2 C
284              
285             Validates parameters with provided rules and automatically set errors.
286              
287             $VALIDATE_RULES - Validate::Tiny rules in next form
288              
289             {
290             checks => $CHECKS, # Required
291             fields => [], # Optional (will check all GET+POST parameters)
292             filters => [], # Optional
293             }
294              
295             You can pass only "checks" arrayref to "do_validation".
296             In this case validator will take all GET+POST parameters as "fields"
297              
298             returns false if validation failed
299             returns true if validation succeded
300              
301             $self->do_validation($VALIDATE_RULES)
302             $self->do_validation($CHECKS);
303              
304              
305             =head2 C
306              
307             Check if there are any errors.
308              
309             if ($self->validator_has_errors) {
310             $self->render_text( $self->validator_any_error );
311             }
312              
313             %= if (validator_has_errors) {
314            
Please, correct the errors below.
315             % }
316              
317             =head2 C
318              
319             Returns the appropriate error.
320              
321             my $errors_hash = $self->validator_error();
322             my $username_error = $self->validator_error('username');
323              
324             <%= validator_error 'username' %>
325              
326             =head2 C
327              
328             Returns a string with all errors (an empty string in case of no errors).
329             Helper maps directly to Validate::Tiny::error_string method ( see L )
330              
331             my $error_str = $self->validator_error_string();
332              
333             <%= validator_error_string %>
334            
335             =head2 C
336            
337             Returns any of the existing errors. This method is usefull if you want return only one error.
338              
339             =head1 AUTHOR
340              
341             Viktor Turskyi
342             and this copy is maintained by Adrian Crisan
343              
344             =head1 BUGS
345              
346             Please report any bugs or feature requests to Github L
347              
348             =head1 SEE ALSO
349              
350             L, L, L, L
351              
352             =cut