use strict;
use warnings;
package Mojolicious::Plugin::KossyValidator;
use Smart::Comments;
our $VERSION = '0.02';
use Mojo::Base 'Mojolicious::Plugin';
use Hash::MultiValue;
my $NUM_RE = qr/^[-+]?[0-9]+(:?\.[0-9]+)?$/;
our %VALIDATOR = (
NOT_NULL => sub {
my ($req,$val) = @_;
return if not defined($val);
return if $val eq "";
return 1;
},
CHOICE => sub {
my ($req, $val, @args) = @_;
for my $c (@args) {
if ($c eq $val) {
return 1;
}
}
return;
},
INT => sub {
my ($req, $val) = @_;
return if not defined($val);
return $val =~ /^\-?[\d]+$/;
},
UINT => sub {
my ($req, $val) = @_;
return if not defined($val);
$val =~ /^\d+$/;
},
NATURAL => sub {
my ($req,$val) = @_;
return if not defined($val);
$val =~ /^\d+$/ && $val > 0;
},
ALPHA=> sub {
my ($req,$val) = @_;
return if not defined($val);
$val =~ /^[A-Za-z]+$/ && $val > 0;
},
BETWEEN => sub {
my ($req, $value, @args) = @_;
my ($start, $end) = @args;
return unless defined($start) && $start =~ /$NUM_RE/ && defined($end) && $end =~ /$NUM_RE/;
return unless defined $value && $value =~ /$NUM_RE/;
return $value >= $start && $value <= $end ? 1 : 0;
},
LENGTH => sub {
my ($req, $value, $args) = @_;
return unless defined $value;
my $min;
my $max;
if(ref $args eq 'ARRAY') { ($min, $max) = @$args }
elsif (ref $args eq 'HASH') {
$min = $args->{min};
$max = $args->{max};
}
else { $min = $max = $args }
return 0 unless defined $min || defined $max;
my $length = length $value;
my $is_valid;
if (defined $min && defined $max) {
$is_valid = $length >= $min && $length <= $max;
}
elsif (defined $min) {
$is_valid = $length >= $min;
}
elsif (defined $max) {
$is_valid = $length <= $max;
}
return $is_valid;
},
'@SELECTED_NUM' => sub {
my ($req,$vals,@args) = @_;
my ($min,$max) = @args;
scalar(@$vals) >= $min && scalar(@$vals) <= $max
},
'@SELECTED_UNIQ' => sub {
my ($req,$vals) = @_;
my %vals;
$vals{$_} = 1 for @$vals;
scalar(@$vals) == scalar keys %vals;
},
);
sub register {
my ($self, $app, $conf) = @_;
$app->helper(
validator => sub {
my ($self, $rule) = @_;
my @errors;
my $valid = Hash::MultiValue->new;
my $req = $self->req;
for ( my $i=0; $i < @$rule; $i = $i+2 ) {
my $param = $rule->[$i];
my $constraints;
my $param_name = $param;
$param_name =~ s!^@!!;
my @vals = $self->param($param_name);
my $vals = ( $param =~ m!^@! ) ? \@vals : [$vals[-1]];
if ( ref($rule->[$i+1]) && ref($rule->[$i+1]) eq 'HASH' ) {
if ( $param !~ m!^@! && !$VALIDATOR{NOT_NULL}->($req, $vals->[0]) && exists $rule->[$i+1]->{default} ) {
my $default = $rule->[$i+1]->{default};
$vals = [$default];
}
$constraints = $rule->[$i+1]->{rule};
}
else {
$constraints = $rule->[$i+1];
}
my $error;
PARAM_CONSTRAINT: for my $constraint ( @$constraints ) {
if ( ref($constraint->[0]) eq 'ARRAY' ) {
my @constraint = @{$constraint->[0]};
my $constraint_name = shift @constraint;
if ( ref($constraint_name) && ref($constraint_name) eq 'CODE' ) {
for my $val ( @$vals ) {
if ( !$constraint_name->($req, $val, @constraint) ) {
push @errors, { param => $param_name, message => $constraint->[1] };
$error=1;
last PARAM_CONSTRAINT;
}
}
next PARAM_CONSTRAINT;
}
die "constraint:$constraint_name not found" if ! exists $VALIDATOR{$constraint_name};
if ( $constraint_name =~ m!^@! ) {
if ( !$VALIDATOR{$constraint_name}->($req,$vals,@constraint) ) {
push @errors, { param => $param_name, message => $constraint->[1] };
$error=1;
last PARAM_CONSTRAINT;
}
}
else {
for my $val ( @$vals ) {
if ( !$VALIDATOR{$constraint_name}->($req,$val,@constraint) ) {
push @errors, { param => $param_name, message => $constraint->[1] };
$error=1;
last PARAM_CONSTRAINT;
}
}
}
}
elsif ( ref($constraint->[0]) eq 'CODE' ) {
for my $val ( @$vals ) {
if ( !$constraint->[0]->($req, $val) ) {
push @errors, { param => $param_name, message => $constraint->[1] };
$error=1;
last PARAM_CONSTRAINT;
}
}
}
else {
die "constraint:".$constraint->[0]." not found" if ! exists $VALIDATOR{$constraint->[0]};
if ( $constraint->[0] =~ m!^@! ) {
if ( !$VALIDATOR{$constraint->[0]}->($req,$vals) ) {
push @errors, { param => $param_name, message => $constraint->[1] };
$error=1;
last PARAM_CONSTRAINT;
}
}
else {
for my $val ( @$vals ) {
if ( !$VALIDATOR{$constraint->[0]}->($req, $val) ) {
push @errors, { param => $param_name, message => $constraint->[1] };
$error=1;
last PARAM_CONSTRAINT;
}
}
}
}
}
$valid->add($param_name,@$vals) unless $error;
}
KossyValidator::Result->new(\@errors,$valid);
}
);
}
1;
package KossyValidator::Result;
use strict;
use warnings;
sub new {
my $class = shift;
my $errors = shift;
my $valid = shift;
bless {errors=>$errors,valid=>$valid}, $class;
}
sub has_error {
my $self = shift;
return 1 if @{$self->{errors}};
return;
}
sub messages {
my $self = shift;
my @errors = map { $_->{message} } @{$self->{errors}};
\@errors;
}
sub errors {
my $self = shift;
my %errors = map { $_->{param} => $_->{message} } @{$self->{errors}};
\%errors;
}
sub valid {
my $self = shift;
if ( @_ == 2 ) {
$self->{valid}->add(@_);
return $_[1];
}
elsif ( @_ == 1 ) {
return $self->{valid}->get($_[0]) if ! wantarray;
return $self->{valid}->get_all($_[0]);
}
$self->{valid};
}
sub to_hash {
my $self = shift;
return %{ $self->valid() };
}
1;
=head1 NAME
Mojolicious::Plugin::KossyValidator - 根据 Kossy 中的 Validator 移植过来的模块
=head1 SYNOPSIS
use Mojolicious::Plugin::KossyValidator;
sub show {
my $c = shift
my $result = $c->validator([
'name' => {
rule => [
['NOT_NULL', '不能为空'],
#['ALPHA', '需要是数字'],
#[['LENGTH',(10, 20)], '长度为 10-20 之间'],
#[['BETWEEN',(1, 2)], '参数只能为数字'],
],
},
'description' => {
default => '无',
rule => [],
},
]);
$c->render( json => {
result => 'false',
messages => $result->errors
}) if $result->has_error;
$result->has_error:Flag
$result->messages:ArrayRef[`Str]
my $val = $result->valid('name'); # 注意取请求过来的参数时原函数 param 替换为 valid 了
my @val = $result->valid('description');
my %hash = $result->to_hash;
# ...
};
dancer;
=head1 DESCRIPTION
Kossy 是 Perl 中另一个迷你框架,这个模块根据 Kossy 中的 Validator 移植过来支持 Mojolicious 的模块。
=head1 VALIDATORS
=over 4
=item NOT_NULL
=item CHOICE
['CHOICE',qw/dog cat/]
=item INT
int
=item UINT
unsigned int
=item NATURAL
natural number
=item ALPHA
English alphabet
=item BETWEEN
between two number
=item LENGTH
param length
=item @SELECTED_NUM
['@SELECTED_NUM',min,max]
=item @SELECTED_UNIQ
all selected values are unique
=back
=head1 CODEref VALIDATOR
my $result = $c->validator([
'q' => [
[sub{
my ($req,$val) = @_;
},'invalid']
],
]);
my $result = $c->validator([
'q' => [
[[sub{
my ($req,$val,@args) = @_;
},0,1],'invalid']
],
]);
=head1 AUTHOR
原模块作者 Masahiro Nagano E<lt>kazeburo {at} gmail.comE<gt> 移植人: 扶凯 iakuf {at} 163.com
=head1 SEE ALSO
L<Kossy::Validator>
=head1 LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
=cut
1;