File Coverage

blib/lib/Mojolicious/Plugin/AdditionalValidationChecks.pm
Criterion Covered Total %
statement 125 125 100.0
branch 52 52 100.0
condition 22 22 100.0
subroutine 22 22 100.0
pod 1 1 100.0
total 222 222 100.0


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::AdditionalValidationChecks;
2              
3             # ABSTRACT: Add additional validation checks
4              
5 29     29   24197 use Mojo::Base 'Mojolicious::Plugin';
  29         89  
  29         253  
6              
7             our $VERSION = '0.18';
8              
9 29     29   25467 use Email::Valid;
  29         1906876  
  29         3905  
10 29     29   625 use Scalar::Util qw(looks_like_number);
  29         267  
  29         4634  
11 29     29   20481 use Time::Piece;
  29         262098  
  29         279  
12 29     29   3047 use Mojo::URL;
  29         134  
  29         1165  
13              
14             sub register {
15 29     29 1 5887 my ($self, $app) = @_;
16              
17 29         546 my $email = Email::Valid->new(
18             allow_ip => 1,
19             );
20              
21 29         4568 my $validator = $app->validator;
22             $validator->add_check( email => sub {
23 3     3   47988 my ($self, $field, $value, @params) = @_;
24 3         47 my $address = $email->address( @params, -address => $value );
25 3 100       95855 return $address ? 0 : 1;
26 29         19521 });
27              
28             $validator->add_check( int => sub {
29 22     22   70293 my ($nr) = $_[2] =~ m{\A ([\+-]? [0-9]+) \z}x;
30 22 100       95 my $return = defined $nr ? 0 : 1;
31 22         69 return $return;
32 29         2066 });
33              
34             $validator->add_check( min => sub {
35 6 100   6   74374 return 1 if !looks_like_number( $_[2] );
36 5 100       47 return if !length $_[3];
37 4         22 return $_[2] < $_[3];
38 29         1981 });
39              
40             $validator->add_check( max => sub {
41 6 100   6   71044 return 1 if !looks_like_number( $_[2] );
42 5 100       38 return if !length $_[3];
43 4         18 return $_[2] > $_[3];
44 29         1427 });
45              
46             $validator->add_check( phone => sub {
47 16 100   16   215456 return 0 if $_[2] =~ m{\A
48             ((?: \+ | 00 ) [1-9]{1}[0-9]{0,2})? # country
49             \s*? [0-9]{2,5} \s*? # local
50             [/-]?
51             \s*? [0-9]{2,12} # phone
52             \z}x;
53 5         45 return 1;
54 29         1734 });
55              
56             $validator->add_check( length => sub {
57 8     8   89959 my ($self, $field, $value, $min, $max) = @_;
58              
59 8         54 my $length = length $value;
60              
61 8 100 100     39 return 0 if !$min && !$max;
62              
63 7 100 100     37 return 1 if $min && $length < $min;
64 4 100 100     18 return 1 if $max && $length > $max;
65              
66 3         16 return 0;
67 29         1446 });
68              
69             $validator->add_check( http_url => sub {
70 7     7   79320 my $url = Mojo::URL->new( $_[2] );
71 7 100       1234 return 1 if !$url->is_abs;
72 4 100       41 return 1 if !grep{ $url->scheme eq $_ }qw(http https);
  8         33  
73 3         29 return 0;
74 29         1342 });
75              
76             $validator->add_check( not => sub {
77 5     5   60673 my ($validation, @tmp) = (shift, shift, shift);
78 5 100       49 return 0 if !@_;
79              
80 4         12 my $field = $validation->topic;
81 4         29 $validation->in( @_ );
82              
83 4 100       248 if ( $validation->has_error($field) ) {
84 2         15 delete $validation->{error}->{$field};
85 2         5 return 0;
86             }
87              
88 2         17 return 1;
89 29         1583 });
90              
91             $validator->add_check( color => sub {
92 149     149   1601193 my ($validation, $field, $value, $type) = @_;
93              
94 149         592 state $rgb_int = qr{
95             \s* (?: 25[0-5] | 2[0-4][0-9] | 1[0-9][0-9] | [1-9][0-9] | [0-9] )
96             }x;
97              
98 149         333 state $rgb_percent = qr{
99             \s* (?: 100 | [1-9][0-9] | [0-9] ) \%
100             }x;
101              
102 149         304 state $alpha = qr{
103             \s* (?: 0 | 0?\.[0-9]+ | 1(?:\.0)? )
104             }x;
105              
106             state $types = {
107             rgb => qr{
108             \A
109             rgb\(
110             (?:
111             (?:
112             (?:$rgb_int,){2} $rgb_int
113             ) |
114             (?:
115             (?:$rgb_percent,){2} $rgb_percent
116             )
117             )
118             \)
119             \z
120             }x,
121             rgba => qr{
122             \A
123             rgba\(
124             (?:
125             (?:
126             (?:$rgb_int,){3} $alpha
127             ) |
128             (?:
129             (?:$rgb_percent,){3} $alpha
130             )
131             )
132             \)
133             \z
134             }xms,
135             hex => qr{
136             \A
137             \#
138             (?: (?:[0-9A-Fa-f]){3} ){1,2}
139             \z
140             }xms,
141             hsl => qr{
142             \A
143             hsla?\(
144             (?:
145             -? [0-9]+ \s* (?:deg|g?rad|turn)?
146             (?[, ]) \s*
147             $rgb_percent \g{sep} \s*
148             $rgb_percent \s*
149             (?:
150 48         680 (?(?{$+{sep} eq ','}) , | / ) \s*
151             (?:$alpha|$rgb_percent)
152             )?
153             )
154             \)
155             \z
156             }xms,
157              
158 149         2203 };
159              
160 149 100       683 return 1 if !$types->{$type};
161              
162 148         2093 my $found = $value =~ $types->{$type};
163 148         687 return !$found;
164 29         1917 });
165              
166             $validator->add_check( uuid => sub {
167 47     47   507752 my ($validation, $field, $value, $type) = @_;
168              
169 47         550 my %regexes = (
170             3 => qr/\A[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}\z/i,
171             4 => qr/\A[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i,
172             5 => qr/\A[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i,
173             all => qr/\A[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\z/i,
174             );
175              
176 47   100     217 $type ||= 'all';
177 47         112 my $regex = $regexes{$type};
178 47 100       131 $regex = $regexes{all} if !$regex;
179              
180 47         524 return $value !~ $regex;
181 29         1951 });
182              
183             $validator->add_check( hex => sub {
184 9     9   103010 my ($validation, $field, $value, $type) = @_;
185              
186 9         89 return $value !~ m{\A [0-9A-Fa-f]+ \z}xms;
187 29         1672 });
188              
189             $validator->add_check( float => sub {
190 27     27   214452 my ($validation, $field, $value) = @_;
191              
192 27         274 return $value !~ m{
193             \A
194             (?:
195             [+-]?
196             (?:[0-9]+)
197             )?
198             (?:
199             \.
200             [0-9]*
201             )
202             (?:
203             [eE]
204             [\+\-]?
205             (?:[0-9]+)
206             )?
207             \z
208             }xms;
209 29         1597 });
210              
211             $validator->add_check( ip => sub {
212 61     61   668544 my ($validation, $field, $value, $type) = @_;
213              
214 61   100     368 $type //= 4;
215              
216 61         145 state $octett = qr{
217             (?: 25[0-5] | 2[0-4][0-9] | 1[0-9][0-9] | [1-9][0-9] | [0-9] )
218             }xms;
219              
220 61         153 my $ipv4 = "((25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))";
221 61         135 my $g = "[0-9a-fA-F]{1,4}";
222            
223 61         657 my @tail = (
224             ":",
225             "(:($g)?|$ipv4)",
226             ":($ipv4|$g(:$g)?|)",
227             "(:$ipv4|:$g(:$ipv4|(:$g){0,2})|:)",
228             "((:$g){0,2}(:$ipv4|(:$g){1,2})|:)",
229             "((:$g){0,3}(:$ipv4|(:$g){1,2})|:)",
230             "((:$g){0,4}(:$ipv4|(:$g){1,2})|:)",
231             );
232            
233 61         132 my $ipv6 = $g;
234 61         617 $ipv6 = "$g:($ipv6|$_)" for @tail;
235 61         279 $ipv6 = qq/:(:$g){0,5}((:$g){1,2}|:$ipv4)|$ipv6/;
236 61         1346 $ipv6 =~ s/\(/(?:/g;
237              
238 61         3397 my %regexes = (
239             4 => qr/\A (?: $octett \. ){3} $octett \z/xms,
240             6 => qr/\A$ipv6\z/,
241             );
242              
243 61         259 my $regex = $regexes{$type};
244 61 100       216 $regex = $regexes{4} if !$regex;
245              
246 61         907 return $value !~ $regex;
247 29         2112 });
248              
249             $validator->add_check( between => sub {
250 12 100 100 12   160321 my $op = (looks_like_number( $_[3] ) and looks_like_number( $_[4] ) ) ? 'n' : 's';
251              
252 12         65 my $result;
253 12 100       63 if ( $op eq 'n' ) {
254 3   100     33 $result = $_[2] < $_[3] || $_[2] > $_[4];
255             }
256             else {
257 9   100     59 $result = (
258             ( ( $_[2] cmp $_[3] ) == -1 ) ||
259             ( ( $_[4] cmp $_[2] ) == -1 )
260             );
261             }
262              
263 12         60 return $result;
264 29         1728 });
265              
266             $validator->add_check( number => sub {
267 19     19   190261 my ($validation, @tmp) = (shift, shift, shift);
268              
269 19 100       142 return 1 if !looks_like_number( $tmp[1] );
270              
271 16         51 my $field = $validation->topic;
272 16         111 $validation->check( 'int', $tmp[1] );
273              
274 16 100       256 return 0 if !$validation->has_error($field);
275              
276 12         76 $validation->output->{$field} = $tmp[1];
277 12         62 delete $validation->{error}->{$field};
278              
279 12         44 $validation->check( 'float', $tmp[1] );
280              
281 12 100       98 return 0 if !$validation->has_error($field);
282              
283 2         13 delete $validation->{error}->{$field};
284            
285 2         6 return 1;
286 29         1643 });
287              
288             $validator->add_check( date => sub {
289 22 100   22   281249 return 1 if !$_[2];
290 21 100       193 return 1 if $_[2] !~ m{\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z};
291              
292 13         36 my $date;
293 13 100       36 eval {
294 13         128 $date = Time::Piece->strptime( $_[2], '%Y-%m-%d' );
295 11         1221 1;
296             } or return 1;
297              
298             # this is needed as 2013-02-31 is parsed, but will return 2013-03-03 for ymd()
299 11 100       72 return 1 if $_[2] ne $date->ymd;
300              
301 6         276 return 0;
302 29         1812 });
303             }
304              
305             1;
306              
307             __END__