File Coverage

blib/lib/Mojolicious/Plugin/Validate/Tiny.pm
Criterion Covered Total %
statement 27 104 25.9
branch 2 38 5.2
condition 1 19 5.2
subroutine 6 11 54.5
pod 1 1 100.0
total 37 173 21.3


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Validate::Tiny;
2 1     1   605 use Mojo::Base 'Mojolicious::Plugin';
  1         2  
  1         6  
3              
4 1     1   179 use Carp qw/croak/;
  1         9  
  1         47  
5 1     1   6 use List::Util qw(any none);
  1         1  
  1         61  
6 1     1   494 use Validate::Tiny;
  1         13430  
  1         1055  
7              
8             our $VERSION = '1.0.2';
9              
10              
11             sub register {
12 1     1 1 43 my ( $self, $app, $conf ) = @_;
13 1         7 my $log = $app->log;
14              
15             # Processing config
16             $conf = {
17             explicit => 0,
18             autofields => 1,
19             exclude => [],
20 1 50       176 %{ $conf || {} }
  1         7  
21             };
22              
23             # Helper do_validation
24             $app->helper(
25             do_validation => sub {
26 0     0   0 my ( $c, $rules, $params ) = @_;
27            
28             croak "ValidateTiny: Wrong validatation rules"
29 0 0       0 if (none {$_ eq ref($rules)} ('ARRAY', 'HASH'));
  0         0  
30              
31 0         0 $c->flash('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       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 (any {$_ eq $f} @{$conf->{exclude}});
  0         0  
  0         0  
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->flash('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->flash('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->flash('validate_tiny.errors' => $result->error);
100 0         0 return;
101             }
102 1         11 } );
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->flash('validate_tiny.errors');
109              
110 0 0 0     0 return 0 if !$errors || !keys %$errors;
111 0         0 return 1;
112 1         133 } );
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->flash('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         78 } );
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->flash('validate_tiny.result')->error_string(%$params);
135 1         73 } );
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->flash('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         68 } );
149              
150              
151             # Print info about actions without validation
152             $app->hook(
153             after_dispatch => sub {
154 1     1   13742 my ($c) = @_;
155              
156 1 50       8 return 1 if $c->flash('validate_tiny.was_called');
157              
158 1         212 my $stash = $c->stash;
159              
160 1 0 33     8 if ( $stash->{controller} && $stash->{action} ) {
161 0         0 $log->debug("ValidateTiny: No validation in [$stash->{controller}#$stash->{action}]");
162 0         0 return 0;
163             }
164              
165 1         3 return 1;
166 1         74 } );
167              
168             }
169              
170             1;
171              
172              
173             =head1 NAME
174              
175             Mojolicious::Plugin::Validate::Tiny - Lightweight validator for Mojolicious
176              
177             =head1 SEE
178              
179             This plugin is a copy of L, with the intent to have a plugin that it's maintained
180              
181             =head1 SYNOPSIS
182              
183             # Mojolicious
184             $self->plugin('Validate::Tiny');
185            
186             # Mojolicious::Lite
187             plugin 'Validate::Tiny';
188            
189             sub action {
190             my $self = shift;
191             my $validate_rules = [
192             # All of these are required
193             [qw/name email pass pass2/] => is_required(),
194              
195             # pass2 must be equal to pass
196             pass2 => is_equal('pass'),
197              
198             # custom sub validates an email address
199             email => sub {
200             my ( $value, $params ) = @_;
201             Email::Valid->address($value) ? undef : 'Invalid email';
202             }
203             ];
204             return unless $self->do_validation($validate_rules);
205            
206             ... Do something ...
207             }
208            
209            
210             sub action2 {
211             my $self = shift;
212              
213             my $validate_rules = {
214             checks => [...],
215             fields => [...],
216             filters => [...]
217             };
218             if ( my $filtered_params = $self->do_validation($validate_rules) ) {
219             # all $params are validated and filters are applyed
220             ... do your action ...
221              
222            
223             } else {
224             my $errors = $self->validator_error; # hash with errors
225             my $pass_error = $self->validator_error('password'); # password error text
226             my $any_error = $self->validator_any_error; # any error text
227            
228             $self->render( status => '403', text => $any_error );
229             }
230            
231             }
232            
233             __DATA__
234            
235             @@ user.html.ep
236             %= if (validator_has_errors) {
237            
Please, correct the errors below.
238             % }
239             %= form_for 'user' => begin
240            
241             <%= input_tag 'username' %>
242             <%= validator_error 'username' %>
243            
244             <%= submit_button %>
245             % end
246              
247            
248             =head1 DESCRIPTION
249              
250             L is a L support for L.
251              
252             =head1 OPTIONS
253              
254             =head2 C (default 0)
255              
256             If "explicit" is true then for every field must be provided check rule
257              
258             =head2 C (default 1)
259              
260             If "autofields" then validator will automatically create fields list based on passed checks.
261             So, you can pass:
262             [
263             user => is_required(),
264             pass => is_required(),
265             ]
266              
267             instead of
268              
269             {
270             fields => ['user', 'pass'],
271             checks => [
272             user => is_required(),
273             pass => is_required(),
274             ]
275             }
276              
277             =head2 C (default [])
278              
279             Is an arrayref with a list of fields that will be never checked.
280              
281             For example, if you use "Mojolicious::Plugin::CSRFProtect" then you should add "csrftoken" to exclude list.
282              
283             =head1 HELPERS
284              
285             =head2 C
286              
287             Validates parameters with provided rules and automatically set errors.
288              
289             $VALIDATE_RULES - Validate::Tiny rules in next form
290              
291             {
292             checks => $CHECKS, # Required
293             fields => [], # Optional (will check all GET+POST parameters)
294             filters => [], # Optional
295             }
296              
297             You can pass only "checks" arrayref to "do_validation".
298             In this case validator will take all GET+POST parameters as "fields"
299              
300             returns false if validation failed
301             returns true if validation succeded
302              
303             $self->do_validation($VALIDATE_RULES)
304             $self->do_validation($CHECKS);
305              
306              
307             =head2 C
308              
309             Check if there are any errors.
310              
311             if ($self->validator_has_errors) {
312             $self->render_text( $self->validator_any_error );
313             }
314              
315             %= if (validator_has_errors) {
316            
Please, correct the errors below.
317             % }
318              
319             =head2 C
320              
321             Returns the appropriate error.
322              
323             my $errors_hash = $self->validator_error();
324             my $username_error = $self->validator_error('username');
325              
326             <%= validator_error 'username' %>
327              
328             =head2 C
329              
330             Returns a string with all errors (an empty string in case of no errors).
331             Helper maps directly to Validate::Tiny::error_string method ( see L )
332              
333             my $error_str = $self->validator_error_string();
334              
335             <%= validator_error_string %>
336            
337             =head2 C
338            
339             Returns any of the existing errors. This method is usefull if you want return only one error.
340              
341             =head1 AUTHOR
342              
343             Viktor Turskyi
344             and this copy is maintained by Adrian Crisan
345              
346             =head1 BUGS
347              
348             Please report any bugs or feature requests to Github L
349              
350             =head1 SEE ALSO
351              
352             L, L, L, L
353              
354             =cut