File Coverage

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


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::AdditionalValidationChecks;
2              
3             # ABSTRACT: Add additional validation checks
4              
5 28     28   22311 use Mojo::Base 'Mojolicious::Plugin';
  28         76  
  28         227  
6              
7             our $VERSION = '0.17';
8              
9 28     28   23884 use Email::Valid;
  28         1686558  
  28         3624  
10 28     28   559 use Scalar::Util qw(looks_like_number);
  28         224  
  28         4362  
11 28     28   17327 use Time::Piece;
  28         230290  
  28         257  
12 28     28   2844 use Mojo::URL;
  28         165  
  28         1283  
13              
14             sub register {
15 28     28 1 5823 my ($self, $app) = @_;
16              
17 28         491 my $email = Email::Valid->new(
18             allow_ip => 1,
19             );
20              
21 28         4494 my $validator = $app->validator;
22             $validator->add_check( email => sub {
23 3     3   44724 my ($self, $field, $value, @params) = @_;
24 3         47 my $address = $email->address( @params, -address => $value );
25 3 100       12161 return $address ? 0 : 1;
26 28         18254 });
27              
28             $validator->add_check( int => sub {
29 22     22   73930 my ($nr) = $_[2] =~ m{\A ([\+-]? [0-9]+) \z}x;
30 22 100       110 my $return = defined $nr ? 0 : 1;
31 22         64 return $return;
32 28         1641 });
33              
34             $validator->add_check( min => sub {
35 6 100   6   75351 return 1 if !looks_like_number( $_[2] );
36 5 100       53 return if !length $_[3];
37 4         27 return $_[2] < $_[3];
38 28         1935 });
39              
40             $validator->add_check( max => sub {
41 6 100   6   79042 return 1 if !looks_like_number( $_[2] );
42 5 100       59 return if !length $_[3];
43 4         25 return $_[2] > $_[3];
44 28         1371 });
45              
46             $validator->add_check( phone => sub {
47 16 100   16   161275 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 28         1703 });
55              
56             $validator->add_check( length => sub {
57 8     8   90064 my ($self, $field, $value, $min, $max) = @_;
58              
59 8         47 my $length = length $value;
60              
61 8 100 100     49 return 0 if !$min && !$max;
62              
63 7 100 100     48 return 1 if $min && $length < $min;
64 4 100 100     19 return 1 if $max && $length > $max;
65              
66 3         10 return 0;
67 28         1399 });
68              
69             $validator->add_check( http_url => sub {
70 7     7   85583 my $url = Mojo::URL->new( $_[2] );
71 7 100       1260 return 1 if !$url->is_abs;
72 4 100       42 return 1 if !grep{ $url->scheme eq $_ }qw(http https);
  8         32  
73 3         28 return 0;
74 28         1262 });
75              
76             $validator->add_check( not => sub {
77 5     5   63207 my ($validation, @tmp) = (shift, shift, shift);
78 5 100       86 return 0 if !@_;
79              
80 4         12 my $field = $validation->topic;
81 4         32 $validation->in( @_ );
82              
83 4 100       239 if ( $validation->has_error($field) ) {
84 2         21 delete $validation->{error}->{$field};
85 2         8 return 0;
86             }
87              
88 2         17 return 1;
89 28         1493 });
90              
91             $validator->add_check( color => sub {
92 83     83   897972 my ($validation, $field, $value, $type) = @_;
93              
94 83         384 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 83         195 state $rgb_percent = qr{
99             \s* (?: 100 | [1-9][0-] | [0-9] ) \%
100             }x;
101              
102 83         173 state $alpha = qr{
103             \s* (?: (?: 0 (?:\.[0-9]+)? )| (?: 1 (?:\.0)? ) )
104             }x;
105              
106 83         1297 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             };
142              
143 83 100       389 return 1 if !$types->{$type};
144              
145 82         975 my $found = $value =~ $types->{$type};
146 82         334 return !$found;
147 28         1898 });
148              
149             $validator->add_check( uuid => sub {
150 47     47   529166 my ($validation, $field, $value, $type) = @_;
151              
152 47         648 my %regexes = (
153             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,
154             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,
155             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,
156             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,
157             );
158              
159 47   100     240 $type ||= 'all';
160 47         125 my $regex = $regexes{$type};
161 47 100       165 $regex = $regexes{all} if !$regex;
162              
163 47         605 return $value !~ $regex;
164 28         1421 });
165              
166             $validator->add_check( hex => sub {
167 9     9   106139 my ($validation, $field, $value, $type) = @_;
168              
169 9         89 return $value !~ m{\A [0-9A-Fa-f]+ \z}xms;
170 28         1420 });
171              
172             $validator->add_check( float => sub {
173 27     27   158117 my ($validation, $field, $value) = @_;
174              
175 27         225 return $value !~ m{
176             \A
177             (?:
178             [+-]?
179             (?:[0-9]+)
180             )?
181             (?:
182             \.
183             [0-9]*
184             )
185             (?:
186             [eE]
187             [\+\-]?
188             (?:[0-9]+)
189             )?
190             \z
191             }xms;
192 28         1715 });
193              
194             $validator->add_check( ip => sub {
195 61     61   556893 my ($validation, $field, $value, $type) = @_;
196              
197 61   100     345 $type //= 4;
198              
199 61         127 state $octett = qr{
200             (?: 25[0-5] | 2[0-4][0-9] | 1[0-9][0-9] | [1-9][0-9] | [0-9] )
201             }xms;
202              
203 61         121 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}))";
204 61         102 my $g = "[0-9a-fA-F]{1,4}";
205            
206 61         516 my @tail = (
207             ":",
208             "(:($g)?|$ipv4)",
209             ":($ipv4|$g(:$g)?|)",
210             "(:$ipv4|:$g(:$ipv4|(:$g){0,2})|:)",
211             "((:$g){0,2}(:$ipv4|(:$g){1,2})|:)",
212             "((:$g){0,3}(:$ipv4|(:$g){1,2})|:)",
213             "((:$g){0,4}(:$ipv4|(:$g){1,2})|:)",
214             );
215            
216 61         120 my $ipv6 = $g;
217 61         534 $ipv6 = "$g:($ipv6|$_)" for @tail;
218 61         218 $ipv6 = qq/:(:$g){0,5}((:$g){1,2}|:$ipv4)|$ipv6/;
219 61         1139 $ipv6 =~ s/\(/(?:/g;
220              
221 61         2808 my %regexes = (
222             4 => qr/\A (?: $octett \. ){3} $octett \z/xms,
223             6 => qr/\A$ipv6\z/,
224             );
225              
226 61         221 my $regex = $regexes{$type};
227 61 100       178 $regex = $regexes{4} if !$regex;
228              
229 61         749 return $value !~ $regex;
230 28         1616 });
231              
232             $validator->add_check( between => sub {
233 12 100 100 12   137172 my $op = (looks_like_number( $_[3] ) and looks_like_number( $_[4] ) ) ? 'n' : 's';
234              
235 12         64 my $result;
236 12 100       49 if ( $op eq 'n' ) {
237 3   100     31 $result = $_[2] < $_[3] || $_[2] > $_[4];
238             }
239             else {
240 9   100     49 $result = (
241             ( ( $_[2] cmp $_[3] ) == -1 ) ||
242             ( ( $_[4] cmp $_[2] ) == -1 )
243             );
244             }
245              
246 12         42 return $result;
247 28         1697 });
248              
249             $validator->add_check( number => sub {
250 19     19   193533 my ($validation, @tmp) = (shift, shift, shift);
251              
252 19 100       150 return 1 if !looks_like_number( $tmp[1] );
253              
254 16         50 my $field = $validation->topic;
255 16         111 $validation->check( 'int', $tmp[1] );
256              
257 16 100       274 return 0 if !$validation->has_error($field);
258              
259 12         89 $validation->output->{$field} = $tmp[1];
260 12         74 delete $validation->{error}->{$field};
261              
262 12         59 $validation->check( 'float', $tmp[1] );
263              
264 12 100       91 return 0 if !$validation->has_error($field);
265              
266 2         14 delete $validation->{error}->{$field};
267            
268 2         7 return 1;
269 28         1534 });
270              
271             $validator->add_check( date => sub {
272 22 100   22   238021 return 1 if !$_[2];
273 21 100       191 return 1 if $_[2] !~ m{\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z};
274              
275 13         33 my $date;
276 13 100       35 eval {
277 13         92 $date = Time::Piece->strptime( $_[2], '%Y-%m-%d' );
278 11         1181 1;
279             } or return 1;
280              
281             # this is needed as 2013-02-31 is parsed, but will return 2013-03-03 for ymd()
282 11 100       59 return 1 if $_[2] ne $date->ymd;
283              
284 6         243 return 0;
285 28         1895 });
286             }
287              
288             1;
289              
290             __END__