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 addtional validation checks
4              
5 28     28   22272 use Mojo::Base 'Mojolicious::Plugin';
  28         73  
  28         216  
6              
7             our $VERSION = '0.16';
8              
9 28     28   23715 use Email::Valid;
  28         1700214  
  28         4222  
10 28     28   646 use Scalar::Util qw(looks_like_number);
  28         274  
  28         4092  
11 28     28   18300 use Time::Piece;
  28         238683  
  28         213  
12 28     28   2946 use Mojo::URL;
  28         146  
  28         1311  
13              
14             sub register {
15 28     28 1 5829 my ($self, $app) = @_;
16              
17 28         481 my $email = Email::Valid->new(
18             allow_ip => 1,
19             );
20              
21 28         4706 my $validator = $app->validator;
22             $validator->add_check( email => sub {
23 3     3   44518 my ($self, $field, $value, @params) = @_;
24 3         55 my $address = $email->address( @params, -address => $value );
25 3 100       143369 return $address ? 0 : 1;
26 28         18903 });
27              
28             $validator->add_check( int => sub {
29 22     22   69820 my ($nr) = $_[2] =~ m{\A ([\+-]? [0-9]+) \z}x;
30 22 100       98 my $return = defined $nr ? 0 : 1;
31 22         61 return $return;
32 28         1709 });
33              
34             $validator->add_check( min => sub {
35 6 100   6   69692 return 1 if !looks_like_number( $_[2] );
36 5 100       54 return if !length $_[3];
37 4         28 return $_[2] < $_[3];
38 28         1864 });
39              
40             $validator->add_check( max => sub {
41 6 100   6   70080 return 1 if !looks_like_number( $_[2] );
42 5 100       46 return if !length $_[3];
43 4         23 return $_[2] > $_[3];
44 28         1328 });
45              
46             $validator->add_check( phone => sub {
47 16 100   16   168863 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         48 return 1;
54 28         1609 });
55              
56             $validator->add_check( length => sub {
57 8     8   87382 my ($self, $field, $value, $min, $max) = @_;
58              
59 8         58 my $length = length $value;
60              
61 8 100 100     44 return 0 if !$min && !$max;
62              
63 7 100 100     39 return 1 if $min && $length < $min;
64 4 100 100     20 return 1 if $max && $length > $max;
65              
66 3         11 return 0;
67 28         1471 });
68              
69             $validator->add_check( http_url => sub {
70 7     7   78389 my $url = Mojo::URL->new( $_[2] );
71 7 100       1179 return 1 if !$url->is_abs;
72 4 100       35 return 1 if !grep{ $url->scheme eq $_ }qw(http https);
  8         45  
73 3         28 return 0;
74 28         1414 });
75              
76             $validator->add_check( not => sub {
77 5     5   62248 my ($validation, @tmp) = (shift, shift, shift);
78 5 100       53 return 0 if !@_;
79              
80 4         22 my $field = $validation->topic;
81 4         31 $validation->in( @_ );
82              
83 4 100       237 if ( $validation->has_error($field) ) {
84 2         22 delete $validation->{error}->{$field};
85 2         8 return 0;
86             }
87              
88 2         15 return 1;
89 28         1519 });
90              
91             $validator->add_check( color => sub {
92 83     83   832398 my ($validation, $field, $value, $type) = @_;
93              
94 83         321 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         173 state $rgb_percent = qr{
99             \s* (?: 100 | [1-9][0-] | [0-9] ) \%
100             }x;
101              
102 83         143 state $alpha = qr{
103             \s* (?: (?: 0 (?:\.[0-9]+)? )| (?: 1 (?:\.0)? ) )
104             }x;
105              
106 83         1256 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       330 return 1 if !$types->{$type};
144              
145 82         879 my $found = $value =~ $types->{$type};
146 82         299 return !$found;
147 28         1854 });
148              
149             $validator->add_check( uuid => sub {
150 47     47   507395 my ($validation, $field, $value, $type) = @_;
151              
152 47         566 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     228 $type ||= 'all';
160 47         107 my $regex = $regexes{$type};
161 47 100       134 $regex = $regexes{all} if !$regex;
162              
163 47         506 return $value !~ $regex;
164 28         1577 });
165              
166             $validator->add_check( hex => sub {
167 9     9   95074 my ($validation, $field, $value, $type) = @_;
168              
169 9         91 return $value !~ m{\A [0-9A-Fa-f]+ \z}xms;
170 28         1491 });
171              
172             $validator->add_check( float => sub {
173 27     27   148299 my ($validation, $field, $value) = @_;
174              
175 27         208 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         1692 });
193              
194             $validator->add_check( ip => sub {
195 61     61   601112 my ($validation, $field, $value, $type) = @_;
196              
197 61   100     411 $type //= 4;
198              
199 61         133 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         164 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         130 my $g = "[0-9a-fA-F]{1,4}";
205            
206 61         532 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         127 my $ipv6 = $g;
217 61         575 $ipv6 = "$g:($ipv6|$_)" for @tail;
218 61         248 $ipv6 = qq/:(:$g){0,5}((:$g){1,2}|:$ipv4)|$ipv6/;
219 61         1268 $ipv6 =~ s/\(/(?:/g;
220              
221 61         3311 my %regexes = (
222             4 => qr/\A (?: $octett \. ){3} $octett \z/xms,
223             6 => qr/\A$ipv6\z/,
224             );
225              
226 61         215 my $regex = $regexes{$type};
227 61 100       214 $regex = $regexes{4} if !$regex;
228              
229 61         850 return $value !~ $regex;
230 28         1628 });
231              
232             $validator->add_check( between => sub {
233 12 100 100 12   139442 my $op = (looks_like_number( $_[3] ) and looks_like_number( $_[4] ) ) ? 'n' : 's';
234              
235 12         68 my $result;
236 12 100       42 if ( $op eq 'n' ) {
237 3   100     19 $result = $_[2] < $_[3] || $_[2] > $_[4];
238             }
239             else {
240 9   100     55 $result = (
241             ( ( $_[2] cmp $_[3] ) == -1 ) ||
242             ( ( $_[4] cmp $_[2] ) == -1 )
243             );
244             }
245              
246 12         43 return $result;
247 28         1429 });
248              
249             $validator->add_check( number => sub {
250 19     19   189503 my ($validation, @tmp) = (shift, shift, shift);
251              
252 19 100       126 return 1 if !looks_like_number( $tmp[1] );
253              
254 16         48 my $field = $validation->topic;
255 16         123 $validation->check( 'int', $tmp[1] );
256              
257 16 100       276 return 0 if !$validation->has_error($field);
258              
259 12         78 $validation->output->{$field} = $tmp[1];
260 12         68 delete $validation->{error}->{$field};
261              
262 12         44 $validation->check( 'float', $tmp[1] );
263              
264 12 100       92 return 0 if !$validation->has_error($field);
265              
266 2         14 delete $validation->{error}->{$field};
267            
268 2         9 return 1;
269 28         1793 });
270              
271             $validator->add_check( date => sub {
272 22 100   22   205430 return 1 if !$_[2];
273 21 100       134 return 1 if $_[2] !~ m{\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z};
274              
275 13         29 my $date;
276 13 100       26 eval {
277 13         55 $date = Time::Piece->strptime( $_[2], '%Y-%m-%d' );
278 11         772 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       38 return 1 if $_[2] ne $date->ymd;
283              
284 6         166 return 0;
285 28         1914 });
286             }
287              
288             1;
289              
290             __END__