Skip to content

Commit

Permalink
added some documentation for form validation
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Sep 27, 2013
1 parent bcad6ff commit dced100
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 12 deletions.
66 changes: 66 additions & 0 deletions lib/Mojolicious/Guides/Rendering.pod
Expand Up @@ -614,6 +614,72 @@ template with named blocks that child templates can override.

This chain could go on and on to allow a very high level of template reuse.

=head2 Form validation

You can use L<Mojolicious::Controller/"validation"> to validate GET/POST
parameters submitted to your application. All unknown fields will be ignored
by default, so you have to decide which should be required or optional before
you can perform checks on their values.

use Mojolicious::Lite;

get '/' => sub {
my $self = shift;

# Check if parameters have been submitted
my $validation = $self->validation;
return $self->render unless $validation->has_data;

# Validate parameters
$validation->required('user')->regex(qr/^[e-t]$/);
$validation->optional('pass')->size(7, 500);
$validation->optional('pass_again')->equal_to('pass');

# Render confirmation if validation was successful
$self->render('thanks') unless $validation->has_error;
} => 'index';

app->start;
__DATA__

@@ index.html.ep
<!DOCTYPE html>
<html>
<head>
%= stylesheet begin
.fields_with_errors { display: inline }
.fields_with_errors input { background-color: #ffbbbb }
% end
</head>
<body>
%= form_for index => begin
<p>Username (required, can only contain the characters e-t)</p>
%= text_field 'user'
%= submit_button
<p>Password (optional, needs to be 7-500 characters long)</p>
%= password_field 'pass'
<p>Password again (needs to be equal to the value above)</p>
%= password_field 'pass_again'
% end
</html>
</html>

@@ thanks.html.ep
<!DOCTYPE html>
<html><body>Thank you <%= validation->param('user') %>.</body></html>

Form elements generated with tag helpers from
L<Mojolicious::Plugin::TagHelpers> will automatically remember their previous
values and wrap fields that failed validation with C<div> blocks, to make
styling with CSS easier.

<div class="fields_with_errors">
<input type="text" name="number" value="hello" />
</div>

For a full list of available checks see also
L<Mojolicious::Validator/"CHECKS">.

=head2 Adding helpers

Adding and redefining helpers is very easy, you can use them to do pretty much
Expand Down
9 changes: 7 additions & 2 deletions lib/Mojolicious/Plugin/TagHelpers.pm
Expand Up @@ -25,8 +25,7 @@ sub register {
$app->helper(javascript => \&_javascript);
$app->helper(link_to => \&_link_to);

$app->helper(password_field =>
sub { shift; _tag('input', name => shift, @_, type => 'password') });
$app->helper(password_field => \&_password_field);
$app->helper(radio_button =>
sub { _input(shift, shift, value => shift, @_, type => 'radio') });

Expand Down Expand Up @@ -119,6 +118,12 @@ sub _link_to {
return _tag('a', href => $self->url_for(@url), @_);
}

sub _password_field {
my ($self, $name) = (shift, shift);
return _wrap($self, $name,
_tag('input', name => $name, @_, type => 'password'));
}

sub _select_field {
my ($self, $name, $options, %attrs) = (shift, shift, shift, @_);

Expand Down
31 changes: 30 additions & 1 deletion lib/Mojolicious/Validator.pm
Expand Up @@ -65,7 +65,36 @@ Mojolicious::Validator - Validate form data
=head1 DESCRIPTION
L<Mojolicious::Validator> validates form data.
L<Mojolicious::Validator> validates form data. Note that this module is
EXPERIMENTAL and might change without warning!
=head1 CHECKS
These checks are available for validation by default.
=head2 equal_to
$validation->equal_to('foo');
Value needs to be equal to the value of another field.
=head2 in
$validation->in('foo', 'bar', 'baz');
Value needs to match one of the values in the list.
=head2 regex
$validation->regex(qr/^[A-Z]/);
Value needs to match the regular expression.
=head2 size
$validation->size(2, 5);
Value length in characters needs to be between these two values.
=head1 ATTRIBUTES
Expand Down
13 changes: 12 additions & 1 deletion lib/Mojolicious/Validator/Validation.pm
Expand Up @@ -114,7 +114,8 @@ Mojolicious::Validator::Validation - Perform validations
=head1 DESCRIPTION
L<Mojolicious::Validator::Validation> performs validations.
L<Mojolicious::Validator::Validation> performs validations. Note that this
module is EXPERIMENTAL and might change without warning!
=head1 ATTRIBUTES
Expand Down Expand Up @@ -214,6 +215,16 @@ Access validated parameters.
Change validation C<topic> and make sure a value is present.
=head1 CHECKS
In addition to the methods above, you can also call checks provided by
L<Mojolicious::Validator> on L<Mojolicious::Validator::Validation> objects,
similar to C<check>.
$validation->required('foo')->size(2, 5)->regex(qr/^[A-Z]/);
$validation->optional('bar')->equal_to('foo');
$validation->optional('baz')->in(qw(test 123));
=head1 SEE ALSO
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
Expand Down
23 changes: 15 additions & 8 deletions t/mojolicious/validation_lite_app.t
Expand Up @@ -18,6 +18,7 @@ get '/' => sub {
$validation->required('foo')->size(2, 5);
$validation->optional('bar')->size(2, 5);
$validation->optional('baz')->size(2, 5);
$validation->optional('yada')->size(2, 5);
} => 'index';

my $t = Test::Mojo->new;
Expand Down Expand Up @@ -133,21 +134,26 @@ is $validation->topic, 'yada', 'right topic';
ok $validation->has_error('bar'), 'has error';

# No validation
$t->get_ok('/')->status_is(200)->element_exists('form > input')
->element_exists('form > textarea')->element_exists('form > select');
$t->get_ok('/')->status_is(200)->element_exists('form > input[type="text"]')
->element_exists('form > textarea')->element_exists('form > select')
->element_exists('form > input[type="password"]');

# Successful validation
$t->get_ok('/?foo=ok')->status_is(200)->element_exists('form > input')
->element_exists('form > textarea')->element_exists('form > select');
$t->get_ok('/?foo=ok')->status_is(200)
->element_exists('form > input[type="password"]')
->element_exists('form > textarea')->element_exists('form > select')
->element_exists('form > input[type="password"]');

# Failed validation
$t->get_ok('/?foo=too_long&bar=too_long_too&baz=way_too_long')->status_is(200)
->element_exists_not('form > input')
->element_exists('form > div.fields_with_errors > input')
$t->get_ok('/?foo=too_long&bar=too_long_too&baz=way_too_long&yada=whatever')
->status_is(200)->element_exists_not('form > input[type="text"]')
->element_exists('form > div.fields_with_errors > input[type="text"]')
->element_exists_not('form > textarea')
->element_exists('form > div.fields_with_errors > textarea')
->element_exists_not('form > select')
->element_exists('form > div.fields_with_errors > select');
->element_exists('form > div.fields_with_errors > select')
->element_exists_not('form > input[type="password"]')
->element_exists('form > div.fields_with_errors > input[type="password"]');

done_testing();

Expand All @@ -158,4 +164,5 @@ __DATA__
%= text_field 'foo'
%= text_area 'bar'
%= select_field baz => [qw(yada yada)]
%= password_field 'yada'
% end

0 comments on commit dced100

Please sign in to comment.