Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added experimental validation support
- Loading branch information
Showing
5 changed files
with
431 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package Mojolicious::Validator; | ||
use Mojo::Base -base; | ||
|
||
use Mojolicious::Validator::Validation; | ||
|
||
has checks => sub { {range => \&_range} }; | ||
has errors => sub { | ||
{ | ||
range => sub {qq{Value needs to be $_[3]-$_[4] characters long.}}, | ||
required => sub {qq{Value is required.}} | ||
}; | ||
}; | ||
|
||
sub add_check { shift->_add(checks => @_) } | ||
sub add_error { shift->_add(errors => @_) } | ||
|
||
sub validation { | ||
Mojolicious::Validator::Validation->new(validator => shift); | ||
} | ||
|
||
sub _add { | ||
my ($self, $attr, $name, $cb) = @_; | ||
$self->$attr->{$name} = $cb; | ||
return $self; | ||
} | ||
|
||
sub _range { | ||
my ($validation, $name, $value, $min, $max) = @_; | ||
my $len = length $value; | ||
return $len >= $min && $len <= $max; | ||
} | ||
|
||
1; | ||
|
||
=encoding utf8 | ||
=head1 NAME | ||
Mojolicious::Validator - Validate form data | ||
=head1 SYNOPSIS | ||
use Mojolicious::Validator; | ||
my $validator = Mojolicious::Validator->new; | ||
my $validation = $validator->validation; | ||
=head1 DESCRIPTION | ||
L<Mojolicious::Validator> validates form data. | ||
=head1 ATTRIBUTES | ||
L<Mojolicious::Validator> implements the following attributes. | ||
=head2 checks | ||
my $checks = $validator->checks; | ||
$validator = $validator->checks({range => sub {...}}); | ||
Registered checks, by default only C<range> is already defined. | ||
=head2 errors | ||
my $errors = $validator->errors; | ||
$validator = $validator->errors({range => sub {...}}); | ||
Registered error generators, by default only C<range> and C<required> are | ||
already defined. | ||
=head1 METHODS | ||
L<Mojolicious::Validator> inherits all methods from L<Mojo::Base> and | ||
implements the following new ones. | ||
=head2 add_check | ||
$validator = $validator->add_check(range => sub {...}); | ||
Register a new check. | ||
=head2 add_error | ||
$validator = $validator->add_error(range => sub {...}); | ||
Register a new error generator. | ||
=head2 validation | ||
my $validation = $validator->validation; | ||
Get a new L<Mojolicious::Validator::Validation> object to perform validations. | ||
=head1 SEE ALSO | ||
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. | ||
=cut |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
package Mojolicious::Validator::Validation; | ||
use Mojo::Base -base; | ||
|
||
use Carp 'croak'; | ||
use Scalar::Util 'blessed'; | ||
|
||
has [qw(input output)] => sub { {} }; | ||
has [qw(topic validator)]; | ||
|
||
sub AUTOLOAD { | ||
my $self = shift; | ||
|
||
my ($package, $method) = our $AUTOLOAD =~ /^([\w:]+)::(\w+)$/; | ||
Carp::croak "Undefined subroutine &${package}::$method called" | ||
unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__); | ||
|
||
croak qq{Can't locate object method "$method" via package "$package"} | ||
unless $self->validator->checks->{$method}; | ||
return $self->check($method => @_); | ||
} | ||
|
||
sub DESTROY { } | ||
|
||
sub check { | ||
my ($self, $check) = (shift, shift); | ||
|
||
my $err = delete $self->{error}; | ||
return $self unless $self->is_valid; | ||
|
||
my $cb = $self->validator->checks->{$check}; | ||
my $name = $self->topic; | ||
return $self if $self->_check($name, $self->input->{$name}, $cb, @_); | ||
|
||
delete $self->output->{$name}; | ||
$self->_error($check, $err, $name, delete $self->input->{$name}, @_); | ||
return $self; | ||
} | ||
|
||
sub error { | ||
my $self = shift; | ||
$self->{error} = shift; | ||
return $self; | ||
} | ||
|
||
sub errors { @{shift->{errors}{shift()} // []} } | ||
|
||
sub has_errors { !!keys %{shift->{errors}} } | ||
|
||
sub is_valid { exists $_[0]->output->{$_[1] // $_[0]->topic} } | ||
|
||
sub optional { | ||
my ($self, $name) = @_; | ||
my $input = $self->input->{$name}; | ||
$self->output->{$name} = $input | ||
if $self->_check($name, $input, sub { defined $_[2] && length $_[2] }); | ||
return $self->topic($name); | ||
} | ||
|
||
sub param { | ||
my ($self, $name) = @_; | ||
|
||
# Multiple names | ||
return map { scalar $self->param($_) } @$name if ref $name eq 'ARRAY'; | ||
|
||
# List names | ||
return sort keys %{$self->output} unless $name; | ||
|
||
my $value = $self->output->{$name}; | ||
my @values = ref $value eq 'ARRAY' ? @$value : ($value); | ||
return wantarray ? @values : $values[0]; | ||
} | ||
|
||
sub required { | ||
my ($self, $name) = @_; | ||
$self->optional($name); | ||
my $err = delete $self->{error}; | ||
$self->_error('required', $err, $name, $self->input->{$name}) | ||
unless $self->is_valid; | ||
return $self; | ||
} | ||
|
||
sub _check { | ||
my ($self, $name, $input, $cb) = (shift, shift, shift, shift); | ||
for my $value (ref $input eq 'ARRAY' ? @$input : $input) { | ||
return undef unless $self->$cb($name, $value, @_); | ||
} | ||
return 1; | ||
} | ||
|
||
sub _error { | ||
my ($self, $check, $err, $name, $input) | ||
= (shift, shift, shift, shift, shift); | ||
my $cb = $self->validator->errors->{$check} // sub {'Value is invalid.'}; | ||
push @{$self->{errors}{$name}}, $err // $self->$cb($name, $_, @_) | ||
for ref $input eq 'ARRAY' ? @$input : $input; | ||
} | ||
|
||
1; | ||
|
||
=encoding utf8 | ||
=head1 NAME | ||
Mojolicious::Validator::Validation - Perform validations | ||
=head1 SYNOPSIS | ||
use Mojolicious::Validator; | ||
use Mojolicious::Validator::Validation; | ||
my $validator = Mojolicious::Validator->new; | ||
my $validation | ||
= Mojolicious::Validator::Validation->new(validator => $validator); | ||
=head1 DESCRIPTION | ||
L<Mojolicious::Validator::Validation> performs validations. | ||
=head1 ATTRIBUTES | ||
L<Mojolicious::Validator::Validation> implements the following attributes. | ||
=head2 input | ||
my $input = $validation->input; | ||
$validation = $validation->input({}); | ||
Data to be validated. | ||
=head2 output | ||
my $output = $validation->output; | ||
$validation = $validation->output({}); | ||
Validated data. | ||
=head2 topic | ||
my $topic = $validation->topic; | ||
$validation = $validation->topic('foo'); | ||
Current validation topic. | ||
=head2 validator | ||
my $validator = $validation->validator; | ||
$validation = $validation->validator(Mojolicious::Validator->new); | ||
L<Mojolicious::Validator> object this validation belongs to. | ||
=head1 METHODS | ||
L<Mojolicious::Validator::Validation> inherits all methods from L<Mojo::Base> | ||
and implements the following new ones. | ||
=head2 check | ||
$validation = $validation->check('range', 2, 7); | ||
Perform validation check. | ||
=head2 error | ||
$validation = $validation->error('This went wrong.'); | ||
Set custom error message for next validation check. | ||
=head2 errors | ||
my @messages = $validation->errors('foo'); | ||
Get error messages for failed validation checks. | ||
=head2 has_errors | ||
my $success = $validation->has_errors; | ||
Check if this validation has error messages. | ||
=head2 is_valid | ||
my $success = $validation->is_valid; | ||
my $success = $validation->is_valid('foo'); | ||
Check if validation was successful, defaults to checking the current C<topic>. | ||
=head2 optional | ||
$validation = $validation->optional('foo'); | ||
Change validation C<topic>. | ||
=head2 param | ||
my @names = $c->param; | ||
my $foo = $c->param('foo'); | ||
my @foo = $c->param('foo'); | ||
my ($foo, $bar) = $c->param(['foo', 'bar']); | ||
Access validated parameters. | ||
=head2 required | ||
$validation = $validation->required('foo'); | ||
Change validation C<topic> and make sure a value is present. | ||
=head1 SEE ALSO | ||
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. | ||
=cut |
Oops, something went wrong.