File Coverage

blib/lib/Dancer/Plugin/KossyValidator.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Dancer::Plugin::KossyValidator;
2              
3 1     1   22124 use strict;
  1         2  
  1         43  
4 1     1   5 use warnings;
  1         2  
  1         35  
5              
6 1     1   1183 use Dancer ':syntax';
  1         296583  
  1         6  
7 1     1   1908 use Dancer::Plugin;
  1         2562  
  1         87  
8 1     1   586 use Hash::MultiValue;
  0            
  0            
9              
10              
11             our $VERSION = '0.04';
12              
13             our %VALIDATOR = (
14             NOT_NULL => sub {
15             my ($req,$val) = @_;
16             return if not defined($val);
17             return if $val eq "";
18             return 1;
19             },
20             CHOICE => sub {
21             my ($req, $val, @args) = @_;
22             for my $c (@args) {
23             if ($c eq $val) {
24             return 1;
25             }
26             }
27             return;
28             },
29             INT => sub {
30             my ($req,$val) = @_;
31             return if not defined($val);
32             $val =~ /^\-?[\d]+$/;
33             },
34             UINT => sub {
35             my ($req,$val) = @_;
36             return if not defined($val);
37             $val =~ /^\d+$/;
38             },
39             NATURAL => sub {
40             my ($req,$val) = @_;
41             return if not defined($val);
42             $val =~ /^\d+$/ && $val > 0;
43             },
44             '@SELECTED_NUM' => sub {
45             my ($req,$vals,@args) = @_;
46             my ($min,$max) = @args;
47             scalar(@$vals) >= $min && scalar(@$vals) <= $max
48             },
49             '@SELECTED_UNIQ' => sub {
50             my ($req,$vals) = @_;
51             my %vals;
52             $vals{$_} = 1 for @$vals;
53             scalar(@$vals) == scalar keys %vals;
54             },
55             );
56              
57             register validator => sub {
58             my $rule = shift || [];
59              
60             my @errors;
61             my $valid = Hash::MultiValue->new;
62             my $req = request;
63              
64             for ( my $i=0; $i < @$rule; $i = $i+2 ) {
65             my $param = $rule->[$i];
66             my $constraints;
67             my $param_name = $param;
68             $param_name =~ s!^@!!;
69             my @vals = param($param_name);
70             my $vals = ( $param =~ m!^@! ) ? \@vals : [$vals[-1]];
71              
72             if ( ref($rule->[$i+1]) && ref($rule->[$i+1]) eq 'HASH' ) {
73             if ( $param !~ m!^@! && !$VALIDATOR{NOT_NULL}->($req,$vals->[0]) && exists $rule->[$i+1]->{default} ) {
74             my $default = $rule->[$i+1]->{default};
75             $vals = [$default];
76             }
77             $constraints = $rule->[$i+1]->{rule};
78             }
79             else {
80             $constraints = $rule->[$i+1];
81             }
82              
83             my $error;
84             PARAM_CONSTRAINT: for my $constraint ( @$constraints ) {
85             if ( ref($constraint->[0]) eq 'ARRAY' ) {
86             my @constraint = @{$constraint->[0]};
87             my $constraint_name = shift @constraint;
88             if ( ref($constraint_name) && ref($constraint_name) eq 'CODE' ) {
89             for my $val ( @$vals ) {
90             if ( !$constraint_name->($req, $val, @constraint) ) {
91             push @errors, { param => $param_name, message => $constraint->[1] };
92             $error=1;
93             last PARAM_CONSTRAINT;
94             }
95             }
96             next PARAM_CONSTRAINT;
97             }
98             die "constraint:$constraint_name not found" if ! exists $VALIDATOR{$constraint_name};
99             if ( $constraint_name =~ m!^@! ) {
100             if ( !$VALIDATOR{$constraint_name}->($req,$vals,@constraint) ) {
101             push @errors, { param => $param_name, message => $constraint->[1] };
102             $error=1;
103             last PARAM_CONSTRAINT;
104             }
105             }
106             else {
107             for my $val ( @$vals ) {
108             if ( !$VALIDATOR{$constraint_name}->($req,$val,@constraint) ) {
109             push @errors, { param => $param_name, message => $constraint->[1] };
110             $error=1;
111             last PARAM_CONSTRAINT;
112             }
113             }
114             }
115             }
116             elsif ( ref($constraint->[0]) eq 'CODE' ) {
117             for my $val ( @$vals ) {
118             if ( !$constraint->[0]->($req, $val) ) {
119             push @errors, { param => $param_name, message => $constraint->[1] };
120             $error=1;
121             last PARAM_CONSTRAINT;
122             }
123             }
124             }
125             else {
126             die "constraint:".$constraint->[0]." not found" if ! exists $VALIDATOR{$constraint->[0]};
127             if ( $constraint->[0] =~ m!^@! ) {
128             if ( !$VALIDATOR{$constraint->[0]}->($req,$vals) ) {
129             push @errors, { param => $param_name, message => $constraint->[1] };
130             $error=1;
131             last PARAM_CONSTRAINT;
132             }
133             }
134             else {
135             for my $val ( @$vals ) {
136             if ( !$VALIDATOR{$constraint->[0]}->($req, $val) ) {
137             push @errors, { param => $param_name, message => $constraint->[1] };
138             $error=1;
139             last PARAM_CONSTRAINT;
140             }
141             }
142             }
143             }
144             }
145             $valid->add($param_name,@$vals) unless $error;
146             }
147            
148             Kossy::Validator::Result->new(\@errors,$valid);
149             };
150              
151             register_plugin;
152              
153             package Kossy::Validator::Result;
154              
155             use strict;
156             use warnings;
157              
158             sub new {
159             my $class = shift;
160             my $errors = shift;
161             my $valid = shift;
162             bless {errors=>$errors,valid=>$valid}, $class;
163             }
164              
165             sub has_error {
166             my $self = shift;
167             return 1 if @{$self->{errors}};
168             return;
169             }
170              
171             sub messages {
172             my $self = shift;
173             my @errors = map { $_->{message} } @{$self->{errors}};
174             \@errors;
175             }
176              
177             sub errors {
178             my $self = shift;
179             my %errors = map { $_->{param} => $_->{message} } @{$self->{errors}};
180             \%errors;
181             }
182              
183             sub valid {
184             my $self = shift;
185             if ( @_ == 2 ) {
186             $self->{valid}->add(@_);
187             return $_[1];
188             }
189             elsif ( @_ == 1 ) {
190             return $self->{valid}->get($_[0]) if ! wantarray;
191             return $self->{valid}->get_all($_[0]);
192             }
193             $self->{valid};
194             }
195              
196             1;
197              
198             =head1 NAME
199              
200             Dancer::Plugin::KossyValidator - 根据 Kossy 中的 Validator 移植过来的模块
201              
202             =head1 SYNOPSIS
203              
204             use Dancer ':syntax';
205             use Dancer::Plugin::KossyValidator;
206              
207             any ['post', 'put'] => '/isp' => sub {
208             my $result = validator([
209             'name' => {
210             rule => [
211             ['NOT_NULL', '运营商名不能为空'],
212             ],
213             },
214             'description' => {
215             default => '无',
216             rule => [],
217             },
218             ]);
219            
220             return {
221             result => 'false',
222             messages => $result->errors
223             } if $result->has_error;
224              
225             $result->has_error:Flag
226             $result->messages:ArrayRef[`Str]
227            
228             my $val = $result->valid('name'); # 注意取请求过来的参数时原函数 param 替换为 valid 了
229             my @val = $result->valid('description');
230            
231             my $hash = $result->valid:Hash::MultiValue;
232             # ...
233             };
234              
235             dancer;
236              
237             =head1 DESCRIPTION
238              
239             Kossy 是 Perl 中另一个迷你框架,这个模块根据 Kossy 中的 Validator 移植过来支持 Dancer 的模块。
240              
241             =head1 VALIDATORS
242              
243             =over 4
244              
245             =item NOT_NULL
246              
247             =item CHOICE
248              
249             ['CHOICE',qw/dog cat/]
250              
251             =item INT
252              
253             int
254              
255             =item UINT
256              
257             unsigned int
258              
259             =item NATURAL
260              
261             natural number
262              
263             =item @SELECTED_NUM
264              
265             ['@SELECTED_NUM',min,max]
266              
267             =item @SELECTED_UNIQ
268              
269             all selected values are unique
270              
271             =back
272              
273             =head1 CODEref VALIDATOR
274              
275             my $result = validator([
276             'q' => [
277             [sub{
278             my ($req,$val) = @_;
279             },'invalid']
280             ],
281             ]);
282            
283             my $result = validator([
284             'q' => [
285             [[sub{
286             my ($req,$val,@args) = @_;
287             },0,1],'invalid']
288             ],
289             ]);
290              
291              
292             =head1 AUTHOR
293              
294             原模块作者 Masahiro Nagano Ekazeburo {at} gmail.comE 移植人: 扶凯 iakuf {at} 163.com
295              
296             =head1 SEE ALSO
297              
298             L
299              
300             =head1 LICENSE
301              
302             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
303              
304             =cut
305