NAME
Validation::Class - Centralized Input Validation for Any Application
VERSION
version 2.4.4
SYNOPSIS
use MyApp::Validation;
my $input = MyApp::Validation->new(params => $params);
unless ($input->validate()){
return $input->errors_to_string;
}
DESCRIPTION
Validation::Class is a different approach to data validation, it
attempts to simplify and centralize data validation rules to ensure DRY
(don't repeat yourself) code. The primary intent of this module is to
provide a simplistic validation framework. Your validation class is your
data input firewall and can be used anywhere and is flexible enough in
an MVC environment to be used in both the Controller and Model. A
validation class is defined as follows:
package MyApp::Validation;
use Validation::Class;
# a validation rule
field 'login' => {
label => 'User Login',
error => 'Login invalid.',
required => 1,
validation => sub {
my ($self, $this_field, $all_params) = @_;
return $this_field->{value} eq 'admin' ? 1 : 0;
}
};
# a validation rule
field 'password' => {
label => 'User Password',
error => 'Password invalid.',
required => 1,
validation => sub {
my ($self, $this_field, $all_params) = @_;
return $this_field->{value} eq 'pass' ? 1 : 0;
}
};
1;
The fields defined will be used to validate the specified input
parameters. You specify the input parameters at instantiaton, parameters
should take the form of a hashref of key/value pairs. Multi-level
(nested) hashrefs are allowed and are inflated/deflated in accordance
with the rules of Hash::Flatten or your hash inflator configuration. The
following is an example on using your validate class to validate input
in various scenarios:
# web app
package MyApp;
use MyApp::Validation;
use Misc::WebAppFramework;
get '/auth' => sub {
# get user input parameters
my $params = shift;
# initialize validation class and set input parameters
my $rules = MyApp::Validation->new(params => $params);
unless ($rules->validate('login', 'password')) {
# print errors to browser unless validation is successful
return $rules->errors_to_string;
}
return 'you have authenticated';
};
CHANGE NOTICE
Important Note! Validation::Class is subject to change, though not
dramatically, you've been warned. Users of this library pre-v2 should
not that the error accessors were changed. Validation::Class has been
re-written using Moose. Sorry if you feel this bloats your application
but using Moose was the better approach.
BUILDING A VALIDATION CLASS
package MyApp::Validation;
use Validation::Class;
# a validation rule template
mixin 'basic' => {
required => 1,
min_length => 1,
max_length => 255,
filters => ['lowercase', 'alphanumeric']
};
# a validation rule
field 'user.login' => {
mixin => 'basic',
label => 'user login',
error => 'login invalid',
validation => sub {
my ($self, $this, $fields) = @_;
return $this->{value} eq 'admin' ? 1 : 0;
}
};
# a validation rule
field 'user.password' => {
mixin => 'basic',
label => 'user login',
error => 'login invalid',
validation => sub {
my ($self, $this, $fields) = @_;
return $this->{value} eq 'pass' ? 1 : 0;
}
};
1;
THE MIXIN KEYWORD
The mixin keyword creates a validation rules template that can be
applied to any field using the mixin directive.
package MyApp::Validation;
use Validation::Class;
mixin 'constrain' => {
required => 1,
min_length => 1,
max_length => 255,
...
};
# e.g.
field 'login' => {
mixin => 'constrain',
...
};
THE FILTER KEYWORD
The filter keyword creates custom filters to be used in your field
definitions.
package MyApp::Validation;
use Validation::Class;
filter 'usa_telephone_number_converter' => sub {
$_[0] =~ s/\D//g;
my ($ac, $pre, $num) = $_[0] =~ /(\d{3})(\d{3})(\d{4})/;
$_[0] = "($ac) $pre-$num";
};
# e.g.
field 'my_telephone' => {
filter => ['trim', 'usa_telephone_number_converter'],
...
};
THE DIRECTIVE KEYWORD
The directive keyword creates custom validator directives to be used in
your field definitions. The routine is passed two parameters, the value
of directive and the value of the field the validator is being processed
against. The validator should return true or false.
package MyApp::Validation;
use Validation::Class;
directive 'between' => sub {
my ($directive, $value, $field, $class) = @_;
my ($min, $max) = split /\-/, $directive;
unless ($value > $min && $value < $max) {
my $handle = $field->{label} || $field->{name};
$class->error($field, "$handle must be between $directive");
return 0;
}
return 1;
};
# e.g.
field 'hours' => {
between => '00-24',
...
};
THE FIELD KEYWORD
The field keyword creates a validation block and defines validation
rules for reuse in code. The field keyword should correspond with the
parameter name expected to be passed to your validation class.
package MyApp::Validation;
use Validation::Class;
field 'login' => {
required => 1,
min_length => 1,
max_length => 255,
...
};
The field keword takes two arguments, the field name and a hashref of
key/values pairs.
AUTO-SERIALIZATION/DESERIALIZATION
Validation::Class supports hash automatic serialization/deserialization
which means that you can set the parameters using a hashref of nested
hashrefs and validate against them, or set the parameters using a
hashref of key/value pairs and validate against that. This function is
provided in Validation::Class via Hash::Flatten. The following is an
example of that:
my $params = {
user => {
login => 'admin',
password => 'pass'
}
};
my $rules = MyApp::Validation->new(params => $params);
# or
my $params = {
'user.login' => 'admin',
'user.password' => 'pass'
};
my $rules = MyApp::Validation->new(params => $params);
# field definition using field('user.login', ...)
# and field('user.password', ...) will match against the parameters above
# after filtering, validation, etc ... return your params as a hashref if
# needed
my $params = $rules->get_params_hash;
SEPERATION OF CONCERNS
For larger applications were a single validation class might become
cluttered and inefficient Validation::Class come equipped to help you
seperate your validation rules into seperate classes.
The idea is that you'll end up with a main validation class (most-likely
empty) that will simply serve as your point of entry into your relative
(child) classes. The following is an example of this:
package MyVal::User;
use Validation::Class;
field name => { ... };
field email => { ... };
field login => { ... };
field password => { ... };
package MyVal::Profile;
use Validation::Class;
field age => { ... };
field sex => { ... };
field birthday => { ... };
package MyVal;
use Validation::Class;
__PACKAGE__->load_classes;
package main;
my $rules = MyVal->new(params => $params);
my $user = $rules->class('user');
my $profile = $rules->class('profile');
...
1;
DEFAULT FIELD/MIXIN DIRECTIVES
package MyApp::Validation;
use Validation::Class;
# a validation template
mixin '...' => {
# mixin directives here
...
};
# a validation rule
field '...' => {
# field directives here
...
};
1;
When building a validation class, the first encountered and arguably two
most important keyword functions are field() and mixin() which are used
to declare their respective properties. A mixin() declares a validation
template where its properties are intended to be copied within field()
declarations which declares validation rules, filters and other
properties.
Both the field() and mixin() declarations/functions require two
parameters, the first being a name, used to identify the declaration and
to be matched against incoming input parameters, and the second being a
hashref of key/value pairs. The key(s) within a declaration are commonly
referred to as directives.
The following is a list of default directives which can be used in
field/mixin declarations:
alias
The alias directive is useful when many different parameters with
different names can be validated using a single rule. E.g. The paging
parameters in a webapp may take on different names but require the same
validation.
# the alias directive
field 'pager' => {
alias => ['page_user_list', 'page_other_list']
...
};
default
The default directive is used as a default value for a field to be used
when a matching parameter is not present.
# the default directive
field 'quantity' => {
default => 1,
...
};
error/errors
The error/errors directive is used to replace the system generated error
messages when a particular field doesn't validate. If a field fails
multiple directives, multiple errors will be generate for the same
field. This may not be desirable, the error directive overrides this
behavior and only the specified error is registered and displayed.
# the error(s) directive
field 'foobar' => {
errors => 'Foobar failed processing, Wtf?',
...
};
label
The label directive is used as a user-friendly reference when the field
name is a serialized hash key or just plain ugly.
# the label directive
field 'hashref.foo.bar' => {
label => 'Foo Bar',
...
};
mixin
The mixin directive is used to create a template of directives to be
applied to other fields.
mixin 'ID' => {
required => 1,
min_length => 1,
max_length => 11
};
# the mixin directive
field 'user.id' => {
mixin => 'ID',
...
};
mixin_field
The mixin directive is used to copy all directives from an existing
field except for the name, label, and validation directives.
# the mixin_field directive
field 'foobar' => {
label => 'Foo Bar',
required => 1
};
field 'barbaz' => {
mixin_field => 'foobar',
label => 'Bar Baz',
...
};
name
The name directive is used *internally* and cannot be changed.
# the name directive
field 'thename' => {
...
};
required
The required directive is an important directive but can be
misunderstood. The required directive used to ensure the *submitted*
parameter exists and has a value. If the parameter is never submitted,
the required directive has no effect and *in-fact* all filtering,
validation, etc is then skipped.
# the required directive
field 'foobar' => {
required => 1,
...
};
# fail
my $rules = MyApp::Validation->new(params => { });
$rules->validate('foobar');
# pass
my $rules = MyApp::Validation->new(params => { foobar => 'Nii=cce });
$rules->validate('foobar');
See the toggle functionality within the validate() method. This method
allows you to temporarily alter whether a field is required or not.
validation
The validation directive is a coderef used add additional custom
validation to the field.
# the validation directive
field 'login' => {
validation => sub {
my ($self, $this_field, $all_params) = @_;
return 0 unless $this_field->{value};
return $this_field->{value} eq 'admin' ? 1 : 0;
},
...
};
value
The value directive is used internally to store the field's matching
parameter's value. This value can be set in the definition but SHOULD
NOT be used as a default value unless you're sure no parameter will
overwrite it during runtime. If you need to set a default value, see the
default directive.
# the value directive
field 'quantity' => {
value => 1,
...
};
DEFAULT FIELD/MIXIN FILTER DIRECTIVES
filter/filters
The filter/filters directive is used to correct, altering and/or format
the values of the matching input parameter. Note: Filtering is applied
before validation. The filter directive can have multiple filters (even
a coderef) in the form of an arrayref of values.
# the filter(s) directive
field 'text' => {
filter => [qw/trim strip/ => sub {
$_[0] =~ s/\D//g;
}],
...
};
The following is a list of default filters that may be used with the
filter directive:
alpha
The alpha filter removes all non-Alphabetic characters from the field's
value.
field 'foobar' => {
filter => 'alpha',
};
alphanumeric
The alpha filter removes all non-Alphabetic and non-Numeric characters
from the field's value.
field 'foobar' => {
filter => 'alphanumeric',
};
capitalize
The capitalize filter attempts to capitalize the first word in each
sentence, where sentences are seperated by a period and space, within
the field's value.
field 'foobar' => {
filter => 'capitalize',
};
decimal
The decimal filter removes all non-decimal-based characters from the
field's value. Allows-only: decimal, comma, and numbers.
field 'foobar' => {
filter => 'decimal',
};
numeric
The numeric filter removes all non-Numeric characters from the field's
value.
field 'foobar' => {
filter => 'numeric',
};
strip
As with the trim filter the strip filter removes leading and trailing
whitespaces from the field's value and additionally removes multiple
whitespaces from between the values characters.
field 'foobar' => {
filter => 'strip',
};
titlecase
The titlecase filter converts the field's value to titlecase by
capitalizing the first letter of each word.
field 'foobar' => {
filter => 'titlecase',
};
trim
The trim filter removes leading and trailing whitespaces from the
field's value.
field 'foobar' => {
filter => 'trim',
};
uppercase
The uppercase filter converts the field's value to uppercase.
field 'foobar' => {
filter => 'uppercase',
};
DEFAULT FIELD/MIXIN VALIDATOR DIRECTIVES
package MyApp::Validation;
use Validation::Class;
# a validation rule with validator directives
field 'telephone_number' => {
length => 14,
pattern => '(###) ###-####',
...
};
1;
Validator directives are special directives with associated validation
code that is used to validate common use-cases such as "checking the
length of a parameter", etc.
The following is a list of the default validators which can be used in
field/mixin declarations:
between
# the between directive
field 'foobar' => {
between => '1-5',
...
};
depends_on
# the depends_on directive
field 'change_password' => {
depends_on => ['password', 'password_confirm'],
...
};
length
# the length directive
field 'foobar' => {
length => 20,
...
};
matches
# the matches directive
field 'this_field' => {
matches => 'another_field',
...
};
max_alpha
# the max_alpha directive
field 'password' => {
max_alpha => 30,
...
};
max_digits
# the max_digits directive
field 'password' => {
max_digits => 5,
...
};
max_length
# the max_length directive
field 'foobar' => {
max_length => '...',
...
};
max_sum
# the max_sum directive
field 'vacation_days' => {
max_sum => 5,
...
};
max_symbols
# the max_symbols directive
field 'password' => {
max_symbols => 1,
...
};
min_alpha
# the min_alpha directive
field 'password' => {
min_alpha => 2,
...
};
min_digits
# the min_digits directive
field 'password' => {
min_digits => 1,
...
};
min_length
# the min_length directive
field 'foobar' => {
min_length => '...',
...
};
min_sum
# the min_sum directive
field 'vacation_days' => {
min_sum => 0,
...
};
min_symbols
# the min_symbols directive
field 'password' => {
min_symbols => 0,
...
};
options
# the options directive
field 'status' => {
options => 'Active, Inactive',
...
};
pattern
# the pattern directive
field 'telephone' => {
pattern => '### ###-####',
...
};
field 'country_code' => {
pattern => 'XX',
filter => 'uppercase'
...
};
THE VALIDATION CLASS
The following is an example of how to use your constructed validation
class in other code, .e.g. Web App Controller, etc.
use MyApp::Validation;
my $input = MyApp::Validation->new(params => $params);
unless ($input->validate('field1','field2')){
return $input->errors_to_string;
}
Feeling lazy, have your validation class automatically find the
appropriate fields to validate against (params must match field names).
use MyApp::Validation;
my $input = MyApp::Validation->new(params => $params);
unless ($input->validate){
return $input->errors_to_string;
}
You can define an alias to automatically map a parameter to a validation
field whereby a field definition will have an alias attribute containing
an arrayref of alternate parameters that can be matched against
passed-in parameters.
package MyApp::Validation;
field 'foo.bar' => {
...,
alias => [
'foo',
'bar',
'baz',
'bax'
]
};
use MyApp::Validation;
my $input = MyApp::Validation->new(params => { foo => 1 });
unless ($input->validate(){
return $input->errors_to_string;
}
new
The new method instantiates and returns an instance of your validation
class.
use MyApp::Validation;
my $input = MyApp::Validation->new;
$input->params($params);
...
my $input = MyApp::Validation->new(params => $params);
...
VALIDATION CLASS ATTRIBUTES
ignore_unknown
The ignore_unknown boolean determines whether your application will live
or die upon encountering unregistered field directives during
validation.
my $self = MyApp::Validation->new(params => $params, ignore_unknown => 1);
$self->ignore_unknown(1);
...
fields
The fields attribute returns a hashref of defined fields, filtered and
merged with thier parameter counterparts.
my $self = MyApp::Validation->new(fields => $fields);
my $fields = $self->fields();
...
filters
The filters attribute returns a hashref of pre-defined filter
definitions.
my $filters = $self->filters();
...
hash_inflator
The hash_inflator value determines how the hash serializer
(inflation/deflation) behaves. The value must be a hashref of "OPTIONS"
in Hash::Flatten options. Purely for the sake of consistency, you can
use lowercase keys (with underscores) which will be converted to
camelcased keys before passed to the serializer.
my $self = MyApp::Validation->new(
hash_inflator => {
hash_delimiter => '/',
array_delimiter => '//'
}
);
...
mixins
The mixins attribute returns a hashref of defined validation templates.
my $mixins = $self->mixins();
...
params
The params attribute gets/sets the parameters to be validated.
my $input = {
...
};
my $self = MyApp::Validation->new(params => $input);
$self->params($input);
my $params = $self->params();
...
report_unknown
The report_unknown boolean determines whether your application will
report unregistered fields as class-level errors upon encountering
unregistered field directives during validation.
my $self = MyApp::Validation->new(params => $params,
ignore_unknown => 1, report_unknown => 1);
$self->report_unknown(1);
...
reset_fields
The reset_fields attribute effectively resets any altered field objects
at the class level. This method is called automatically everytime the
new() method is triggered.
$self->reset_fields();
stashed
The stashed attribute represents a list of field names stored to be used
in validation later. If the stashed attribute contains a list you can
omit arguments to the validate method.
$self->stashed([qw/this that .../]);
VALIDATION CLASS METHODS
class
The class method returns a new initialize child validation class under
the namespace of the calling class that issued the load_classes() method
call. Existing parameters and configuration options are passed to the
child class's constructor. All attributes can be easily overwritten
using the attribute's accessors on the child class.
package MyVal;
use Validation::Class; __PACKAGE__->load_classes;
package main;
my $rules = MyVal->new(params => $params);
my $kid1 = $rules->class('child'); # loads MyVal::Child;
my $kid2 = $rules->class('step_child'); # loads MyVal::StepChild;
1;
error
The error function is used to set and/or retrieve errors encountered
during validation. The error function with no parameters returns the
error message object which is an arrayref of error messages stored at
class-level.
# return all errors encountered/set as an arrayref
return $self->error();
# return all errors specific to the specified field (at the field-level)
# as an arrayref
return $self->error('some_param');
# set an error specific to the specified field (at the field-level)
# using the field object (hashref not field name)
$self->error($field_object, "i am your error message");
unless ($self->validate) {
my $fields = $self->error();
}
error_count
The error_count function returns the total number of error encountered
from the last validation call.
return $self->error_count();
unless ($self->validate) {
print "Found ". $self->error_count ." Errors";
}
error_fields
The error_fields method returns a hashref of fields whose value is an
arrayref of error messages.
unless ($self->validate) {
my $bad_fields = $self->error_fields();
}
errors_to_string
The errors_to_string function stringifies the error arrayref object
using the specified delimiter or ', ' by default.
return $self->errors_to_string();
return $self->errors_to_string("
\n");
unless ($self->validate) {
return $self->errors_to_string;
}
get_params
The get_params method returns the values (in list form) of the
parameters specified.
if ($self->validate) {
my $name_a = $self->get_params('name');
my ($name_b, $email, $login, $password) =
$self->get_params(qw/name email login password/);
# you should note that if the params dont exist they will return undef
# ... meaning you should check that it exists before checking its value
# e.g.
if (defined $name) {
if ($name eq '') {
print 'name parameter was passed but was empty';
}
}
else {
print 'name parameter was never submitted';
}
}
get_params_hash
If your fields and parameters are designed with complex hash structures,
The get_params_hash method returns the deserialized hashref of specified
parameters based on the the default or custom configuration of the hash
serializer Hash::Flatten.
my $params = {
'user.login' => 'member',
'user.password' => 'abc123456'
};
if ($self->validate(keys %$params)) {
my $params = $self->get_params_hash;
print $params->{user}->{login};
}
load_classes
The load_classes method is used Module::Find to load child classes for
convenient access through the class() method. Existing parameters and
configuration options are passed to the child class's constructor. All
attributes can be easily overwritten using the attribute's accessors on
the child class.
package MyVal;
use Validation::Class; __PACKAGE__->load_classes;
1;
load_plugins
The load_plugins method is used to load plugins that support
Validation::Class. A Validation::Class plugin is little more than a Role
(Moose::Role) that extends the Validation::Class core. As usual, an
official Validation::Class plugin can be referred to using shorthand
while custom plugins are called by prefixing a plus symbol to the
fully-qualified plugin name. Learn more about plugins at
Validation::Class::Plugins.
package MyVal;
use Validation::Class;
__PACKAGE__->load_plugins('SuperX');
# loads Validation::Class::Plugin::SuperX
__PACKAGE__->load_plugins('+MyApp::Validation::Plugin::SuperY');
1;
param
The param method returns a single parameter by name.
if ($self->param('chng_pass')) {
$self->validate('password_confirmation');
}
queue
The queue method is a convenience method used specifically to append the
stashed attribute allowing you to *queue* field to be validated. This
method also allows you to set fields that must always be validated.
# conditional validation flow WITHOUT the queue method
# imagine a user profile update action
my $rules = MyApp::Validation->new(params => $params);
my @fields = qw/name login/;
push @fields, 'email_confirm' if $rules->param('chg_email');
push @fields, 'password_confirm' if $rules->param('chg_pass');
... if $rules->validate(@fields);
# conditional validation WITH the queue method
my $rules = MyApp::Validation->new(params => $params);
$rules->queue(qw/name login/);
$rules->queue(qw/email_confirm/) if $rules->param('chg_email');
$rules->queue(qw/password_confirm/) if $rules->param('chg_pass');
... if $rules->validate();
# set fields that must ALWAYS be validated
# imagine a simple REST server
my $rules = MyApp::Validation->new(params => $params);
$rules->queue(qw/login password/);
if ($request eq '/resource/:id') {
if ($rules->validate('id')) {
# validated login, password and id
...
}
}
set_params_hash
Depending on how parameters are being input into your application, if
your input parameters are already complex hash structures, The
set_params_hash method will set and return the serialized version of
your hashref based on the the default or custom configuration of the
hash serializer Hash::Flatten.
my $params = {
user => {
login => 'member',
password => 'abc123456'
}
};
my $serialized_params = $self->set_params_hash($params);
reset
The reset method clears all errors, fields and stashed field names, both
at the class and individual field levels.
$self->reset();
reset_errors
The reset_errors method clears all errors, both at the class and
individual field levels. This method is called automatically everytime
the validate() method is triggered.
$self->reset_errors();
reset_fields
The reset_fields method clears all errors and field values, both at the
class and individual field levels. This method is called automatically
everytime the validate() method is triggered.
$self->reset_fields();
validate
The validate method returns true/false depending on whether all
specified fields passed validation checks.
use MyApp::Validation;
my $input = MyApp::Validation->new(params => $params);
# validate specific fields
unless ($input->validate('field1','field2')){
return $input->errors_to_string;
}
# validate existing parameters, if no parameters exist,
# validate all fields ... which will return true unless field(s) exist
# with a required directive
unless ($input->validate()){
return $input->errors_to_string;
}
# validate all fields period, obviously
unless ($input->validate(keys %{$input->fields})){
return $input->errors_to_string;
}
# validate specific parameters (by name) after mapping them to other fields
my $parameter_map = {
user => 'hey_im_not_named_login',
pass => 'password_is_that_really_you'
};
unless ($input->validate($parameter_map)){
return $input->errors_to_string;
}
Another cool trick the validate() method can perform is the ability to
temporarily alter whether a field is required or not during runtime.
This functionality is often referred to as the *toggle* function.
This function is important when you define a field (or two or three) as
required or non and want to change that per validation. This is done by
calling the validate() method with a list of fields to be validated and
prefixing the target fields with a plus or minus as follows:
use MyApp::Validation;
my $input = MyApp::Validation->new(params => $params);
# validate specific fields, force name, email and phone to be required
# regardless of the field definitions directives ... and force the age, sex
# and birthday to be optional
my @spec = qw(+name +email +phone -age -sex -birthday);
unless ($input->validate(@spec)){
return $input->errors_to_string;
}
AUTHOR
Al Newkirk
COPYRIGHT AND LICENSE
This software is copyright (c) 2011 by awncorp.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.