File Coverage

blib/lib/Mojolicious/Plugin/KossyValidator.pm
Criterion Covered Total %
statement 18 107 16.8
branch 0 42 0.0
condition 0 12 0.0
subroutine 6 14 42.8
pod 1 1 100.0
total 25 176 14.2


line stmt bran cond sub pod time code
1 1     1   25862 use strict;
  1         2  
  1         48  
2 1     1   6 use warnings;
  1         3  
  1         77  
3             package Mojolicious::Plugin::KossyValidator;
4              
5             our $VERSION = '0.04';
6              
7 1     1   1058 use Mojo::Base 'Mojolicious::Plugin';
  1         13990  
  1         8  
8 1     1   2477 use Hash::MultiValue;
  1         2942  
  1         2443  
9              
10             my $NUM_RE = qr/^[-+]?[0-9]+(:?\.[0-9]+)?$/;
11              
12             our %VALIDATOR = (
13             NOT_NULL => sub {
14             my ($req,$val) = @_;
15             return if not defined($val);
16             return if $val eq "";
17             return 1;
18             },
19             CHOICE => sub {
20             my ($req, $val, @args) = @_;
21             for my $c (@args) {
22             if ($c eq $val) {
23             return 1;
24             }
25             }
26             return;
27             },
28             INT => sub {
29             my ($req, $val) = @_;
30             return if not defined($val);
31             return $val =~ /^\-?[\d]+$/;
32             },
33             UINT => sub {
34             my ($req, $val) = @_;
35             return if not defined($val);
36             $val =~ /^\d+$/;
37             },
38             NATURAL => sub {
39             my ($req,$val) = @_;
40             return if not defined($val);
41             $val =~ /^\d+$/ && $val > 0;
42             },
43             ALPHA=> sub {
44             my ($req,$val) = @_;
45             return if not defined($val);
46             $val =~ /^[A-Za-z]+$/;
47             },
48             BETWEEN => sub {
49             my ($req, $value, @args) = @_;
50             my ($start, $end) = @args;
51             return unless defined($start) && $start =~ /$NUM_RE/ && defined($end) && $end =~ /$NUM_RE/;
52             return unless defined $value && $value =~ /$NUM_RE/;
53             return $value >= $start && $value <= $end ? 1 : 0;
54             },
55             LENGTH => sub {
56             my ($req, $value, $args) = @_;
57              
58             return unless defined $value;
59              
60             my $min;
61             my $max;
62             if(ref $args eq 'ARRAY') { ($min, $max) = @$args }
63             elsif (ref $args eq 'HASH') {
64             $min = $args->{min};
65             $max = $args->{max};
66             }
67             else { $min = $max = $args }
68              
69             return 0 unless defined $min || defined $max;
70              
71             my $length = length $value;
72             my $is_valid;
73             if (defined $min && defined $max) {
74             $is_valid = $length >= $min && $length <= $max;
75             }
76             elsif (defined $min) {
77             $is_valid = $length >= $min;
78             }
79             elsif (defined $max) {
80             $is_valid = $length <= $max;
81             }
82             return $is_valid;
83             },
84             '@SELECTED_NUM' => sub {
85             my ($req,$vals,@args) = @_;
86             my ($min,$max) = @args;
87             scalar(@$vals) >= $min && scalar(@$vals) <= $max
88             },
89             '@SELECTED_UNIQ' => sub {
90             my ($req,$vals) = @_;
91             my %vals;
92             $vals{$_} = 1 for @$vals;
93             scalar(@$vals) == scalar keys %vals;
94             },
95             );
96              
97              
98              
99             sub register {
100 0     0 1   my ($self, $app, $conf) = @_;
101             $app->helper(
102             validator => sub {
103 0     0     my ($self, $rule) = @_;
104              
105 0           my @errors;
106 0           my $valid = Hash::MultiValue->new;
107 0           my $req = $self->req;
108              
109 0           for ( my $i=0; $i < @$rule; $i = $i+2 ) {
110 0           my $param = $rule->[$i];
111 0           my $constraints;
112 0           my $param_name = $param;
113 0           $param_name =~ s!^@!!;
114 0           my @vals = $self->param($param_name);
115 0 0         my $vals = ( $param =~ m!^@! ) ? \@vals : [$vals[-1]];
116              
117 0 0 0       if ( ref($rule->[$i+1]) && ref($rule->[$i+1]) eq 'HASH' ) {
118 0 0 0       if ( $param !~ m!^@! && !$VALIDATOR{NOT_NULL}->($req, $vals->[0]) && exists $rule->[$i+1]->{default} ) {
      0        
119 0           my $default = $rule->[$i+1]->{default};
120 0           $vals = [$default];
121             }
122 0           $constraints = $rule->[$i+1]->{rule};
123             }
124             else {
125 0           $constraints = $rule->[$i+1];
126             }
127              
128 0           my $error;
129 0           PARAM_CONSTRAINT: for my $constraint ( @$constraints ) {
130 0 0         if ( ref($constraint->[0]) eq 'ARRAY' ) {
    0          
131 0           my @constraint = @{$constraint->[0]};
  0            
132 0           my $constraint_name = shift @constraint;
133 0 0 0       if ( ref($constraint_name) && ref($constraint_name) eq 'CODE' ) {
134 0           for my $val ( @$vals ) {
135 0 0         if ( !$constraint_name->($req, $val, @constraint) ) {
136 0           push @errors, { param => $param_name, message => $constraint->[1] };
137 0           $error=1;
138 0           last PARAM_CONSTRAINT;
139             }
140             }
141 0           next PARAM_CONSTRAINT;
142             }
143 0 0         die "constraint:$constraint_name not found" if ! exists $VALIDATOR{$constraint_name};
144 0 0         if ( $constraint_name =~ m!^@! ) {
145 0 0         if ( !$VALIDATOR{$constraint_name}->($req,$vals,@constraint) ) {
146 0           push @errors, { param => $param_name, message => $constraint->[1] };
147 0           $error=1;
148 0           last PARAM_CONSTRAINT;
149             }
150             }
151             else {
152 0           for my $val ( @$vals ) {
153 0 0         if ( !$VALIDATOR{$constraint_name}->($req,$val,@constraint) ) {
154 0           push @errors, { param => $param_name, message => $constraint->[1] };
155 0           $error=1;
156 0           last PARAM_CONSTRAINT;
157             }
158             }
159             }
160             }
161             elsif ( ref($constraint->[0]) eq 'CODE' ) {
162 0           for my $val ( @$vals ) {
163 0 0         if ( !$constraint->[0]->($req, $val) ) {
164 0           push @errors, { param => $param_name, message => $constraint->[1] };
165 0           $error=1;
166 0           last PARAM_CONSTRAINT;
167             }
168             }
169             }
170             else {
171 0 0         die "constraint:".$constraint->[0]." not found" if ! exists $VALIDATOR{$constraint->[0]};
172 0 0         if ( $constraint->[0] =~ m!^@! ) {
173 0 0         if ( !$VALIDATOR{$constraint->[0]}->($req,$vals) ) {
174 0           push @errors, { param => $param_name, message => $constraint->[1] };
175 0           $error=1;
176 0           last PARAM_CONSTRAINT;
177             }
178             }
179             else {
180 0           for my $val ( @$vals ) {
181 0 0         if ( !$VALIDATOR{$constraint->[0]}->($req, $val) ) {
182 0           push @errors, { param => $param_name, message => $constraint->[1] };
183 0           $error=1;
184 0           last PARAM_CONSTRAINT;
185             }
186             }
187             }
188             }
189             }
190 0 0         $valid->add($param_name,@$vals) unless $error;
191             }
192            
193 0           KossyValidator::Result->new(\@errors,$valid);
194             }
195 0           );
196             }
197              
198             1;
199              
200             package KossyValidator::Result;
201              
202 1     1   13 use strict;
  1         3  
  1         37  
203 1     1   6 use warnings;
  1         3  
  1         544  
204              
205             sub new {
206 0     0     my $class = shift;
207 0           my $errors = shift;
208 0           my $valid = shift;
209 0           bless {errors=>$errors,valid=>$valid}, $class;
210             }
211              
212             sub has_error {
213 0     0     my $self = shift;
214 0 0         return 1 if @{$self->{errors}};
  0            
215 0           return;
216             }
217              
218             sub messages {
219 0     0     my $self = shift;
220 0           my @errors = map { $_->{message} } @{$self->{errors}};
  0            
  0            
221 0           \@errors;
222             }
223              
224             sub errors {
225 0     0     my $self = shift;
226 0           my %errors = map { $_->{param} => $_->{message} } @{$self->{errors}};
  0            
  0            
227 0           \%errors;
228             }
229              
230             sub valid {
231 0     0     my $self = shift;
232 0 0         if ( @_ == 2 ) {
    0          
233 0           $self->{valid}->add(@_);
234 0           return $_[1];
235             }
236             elsif ( @_ == 1 ) {
237 0 0         return $self->{valid}->get($_[0]) if ! wantarray;
238 0           return $self->{valid}->get_all($_[0]);
239             }
240 0           $self->{valid};
241             }
242              
243             sub to_hash {
244 0     0     my $self = shift;
245 0           return %{ $self->valid() };
  0            
246             }
247              
248             1;
249              
250             =pod
251              
252             =encoding utf-8
253              
254             =head1 NAME
255              
256             Mojolicious::Plugin::KossyValidator - 根据 Kossy 中的 Validator 移植过来的模块
257              
258             =head1 SYNOPSIS
259              
260             use Mojolicious::Plugin::KossyValidator;
261              
262             sub show {
263             my $c = shift
264             my $result = $c->validator([
265             'name' => {
266             rule => [
267             ['NOT_NULL', '不能为空'],
268             #['ALPHA', '需要是数字'],
269             #[['LENGTH',(10, 20)], '长度为 10-20 之间'],
270             #[['BETWEEN',(1, 2)], '参数只能为数字'],
271             ],
272             },
273             'description' => {
274             default => '无',
275             rule => [],
276             },
277             ]);
278            
279             $c->render( json => {
280             result => 'false',
281             messages => $result->errors
282             }) if $result->has_error;
283              
284             $result->has_error:Flag
285             $result->messages:ArrayRef[`Str]
286            
287             my $val = $result->valid('name'); # 注意取请求过来的参数时原函数 param 替换为 valid 了
288             my @val = $result->valid('description');
289            
290             my %hash = $result->to_hash;
291             # ...
292             };
293              
294             dancer;
295              
296              
297             =head1 DESCRIPTION
298              
299             Kossy 是 Perl 中另一个迷你框架,这个模块根据 Kossy 中的 Validator 移植过来支持 Mojolicious 的模块。
300              
301             =head1 VALIDATORS
302              
303             =over 4
304              
305             =item NOT_NULL
306              
307             =item CHOICE
308              
309             ['CHOICE',qw/dog cat/]
310              
311             =item INT
312              
313             int
314              
315             =item UINT
316              
317             unsigned int
318              
319             =item NATURAL
320              
321             natural number
322              
323             =item ALPHA
324              
325             English alphabet
326              
327             =item BETWEEN
328              
329             between two number
330              
331             =item LENGTH
332              
333             param length
334              
335             =item @SELECTED_NUM
336              
337             ['@SELECTED_NUM',min,max]
338              
339             =item @SELECTED_UNIQ
340              
341             all selected values are unique
342              
343             =back
344              
345             =head1 CODEref VALIDATOR
346              
347             my $result = $c->validator([
348             'q' => [
349             [sub{
350             my ($req,$val) = @_;
351             },'invalid']
352             ],
353             ]);
354            
355             my $result = $c->validator([
356             'q' => [
357             [[sub{
358             my ($req,$val,@args) = @_;
359             },0,1],'invalid']
360             ],
361             ]);
362              
363              
364             =head1 AUTHOR
365              
366             原模块作者 Masahiro Nagano Ekazeburo {at} gmail.comE 移植人: 扶凯 iakuf {at} 163.com
367              
368             =head1 SEE ALSO
369              
370             L
371              
372             =head1 LICENSE
373              
374             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
375              
376             =cut
377              
378              
379             1;