NAME

Validate::Simple - (Relatively) Simple way to validate input parameters

SYNOPSIS

use Validate::Simple;

my $specs = {
    username => {
        type     => 'string',
        required => 1,
    },
    first_name => {
        type     => 'string',
        required => 1,
    },
    last_name => {
        type => 'string',
    },
    age => {
        type => 'integer',
        gt   => 18,
    },
    gender => {
        type   => 'enum',
        values => [
            'male',
            'female',
            'id_rather_not_to_say',
        ],
    },
    tags => {
        type => 'array',
        of   => {
            type => 'string',
        },
    },
    hobbies => {
        type => 'array',
        of   => {
            type =>'enum',
            values => [ qw/hiking travelling surfing laziness/ ],
        },
    },
    score => {
        type => 'hash',
        of   => {
            type => 'non_negative_int',
        },
    },
    monthly_score => {
        type => 'hash',
        of   => {
            type => 'hash',
            of   => {
                type     => 'array',
                of => {
                    type => 'non_negative_int',
                },
                callback => sub {
                    @{ $_[0] } < 12;
                },
            },
        },
    },
    address => {
        type => 'spec',
        of   => {
            country => {
                type   => 'enum',
                values => [ qw/ af ax al ... zw / ],
            },
            zip_code => {
                type => 'string',
            },
            street => {
                type => 'string',
            },
            number => {
                type => 'spec',
                of   => {
                    house_nr => {
                       type => 'positive_int',
                    },
                    house_ext => {
                       type => 'string',
                    },
                    apt => {
                        type => 'positive_int',
                    },
                },
            },
        },
    },
};

my $vs1 = Validate::Simple->new( $specs );
my $is_valid1 = $vs1->validate( $params );
print join "\n", $vs1->errors()
    if !$is_valid1;

# Or

my $vs2 = Validate::Simple->new();
my $is_valid2 = $vs2->validate( $params, $specs );
print join "\n", $vs2->errors()
    if !$is_valid2;

DESCRIPTION

The module validates parameters against specifications.

LOGIC

The general use case is the like this.

You have a from handler or a handler of an API endpoint. It receives a hashref as a parameter. You want to make sure that the input hashref has all the required keys, you do not have unknown keys, and all the values satisfy certain criterias: type, size and other constraints...

Validate::Simple allows you to specify criterias for each input parameter in a (relatively) simple form and validate user input against your specification as many times as you need. A specification is just a hashref. The keys repeat parameters names, the values define criterias.

For example:

my $specs = {
    username => {
        type       => 'string',
        required   => 1,
        min_length => 1,
        max_length => 255,
    },
    password => {
        type       => 'string',
        required   => 1,
        min_length => 12,
    },
    subscriptions => {
        type  => 'array',
        empty => 0,
        of    => {
            type => 'positive_int',
        },
    }
};

This specification can be used to validate a sign up form. Validation passes only if user enters a non-empty username no longer than 255 character, a password no shorter than 12 characters and chooses 0 or more subscriptions (which are supposed to be provided as a list of IDs, i.e. positive integers. The list of subscriptions may absent (no required key in the rule), but if it exists, it cannot be empty (because of the value of the empty key is set to 0).

If input data contains a key 'remember_me', validation fails, because specification does not define rules for this key.

EXPORT

This module does not export anything. It is supposed to be used in OOP approach only.

SPECIFICATIONS (SPECS)

Specifications are the rules that describe which parameters are expected, their types and constraints (like lengths of strings or signs of numbers). Specification is just a hashref, the keys are parameters names, and the values describe criterias.

Every description must have a type key, and may have the keys required, undef and callback.

Common specs keys

type

The value of type defines an expected type of a parameter. If omitted, type 'any' is used. You can learn more about supported types in the "Types" section.

required

When it is set to a true value, the parameter is required, and, if it is not in the input data, the form is considered invalid. When it is set to a false value or does not exists, the parameter is optional.

undef

By default none parameters can be undefined. If you expect some values to be undefined, you need to explicitly set this key to true.

For example, if you allow the value of the param to be literally anything, you can do the following:

my $specs = {
    whatever => {
        type  => 'any',
        undef => 1,
    }
};

callback

If you have some special constraints to a value that does not fit to any of supported types, you can specify your own validation function and pass it to callback key as a coderef. The function should receive the value as a first parameter and returns true or false.

For example, if you need to check whether a value is an even positive integer, you can do the following:

my $specs = {
    even_positive => {
        type     => "positive_int",
        callback => sub { !( $_[0] % 2 ) },
    },
};

In case of the "enum" type, the second parameter is the hashref with the keys of allowed enum values.

The callback function is called after checking the type and constraints.

Types

any

The value can be of any type (including array or hash), except undef.

Number types

All number types support the following keys:

gt

Greater than.

ge

Greater than or equal to.

lt

Less than.

le

Less than or equal to.

For example, the following spec checks whether it's a valid month number:

my $specs = {
    month => {
        type => 'positive_int',
        le   => 12,
    },
};

number

The value is a valid number. On the backstage it simply performs looks_like_number from Scalar::Util.

positive

Same as "number", and checks whether it's greater than 0.

non_negative

Same as "number", and checks whether it's greater than or equal to 0.

negative

Same as "number", and checks whether it's greater than 0.

non_positive

Same as "number", and checks whether it's less than or equal to 0.

integer

The value is an integer. It performs is_int from Data::Types.

positive_int

The value is a positive integer. It performs is_count from Data::Types.

non_negative_int

The value is a non-negative integer. It performs is_whole from Data::Types.

negative_int

The value is a negative integer.

non_positive_int

The value is a non-positive integer.

string

The value is a string. It performs is_string from Data::Types.

NOTE: Any number is a valid string, so both $params1 = { string_param => 5 }; and $params1 = { string_param => "5" }; will pass.

You can add the following constraints to a string:

max_length

Positive integer number that defines the maximum string length allowed.

min_length

Positive integer number that defines the minimum string length allowed.

re

Regilar expression. The value will be checked aginst this regexp.

For example:

my $specs = {
    str_re => {
        type => 'string',
        re   => qr/Valid/,
    }
};

List types

The list types must have the key of, which contains a spec of all list values. For example:

my $specs = {
    ids => {
        type => 'array',
        of   => {
            type => 'positive_int',
        },
    },
    placeholders => {
        type => 'hash',
        of   => {
            type => 'string',
        },
    },
};

As long as the of key expects another specification, you can easily validate complex structures, like array of arrays of strings, hash of arrays of hashes of enums, arrays of hashes of arrays of arrays of any, and so on.

By default lists cannot be empty. If you expect an empty list, you should add a key empty and set it to a true value.

array

The value is an arrayref.

hash

The value is a hashref.

enum

The value is one of the predefined list.

Enum type always contains a string. The list of predefined strings is provided in the values key, which is required for enums:

my $specs = {
    data_types => {
        type   => 'enum',
        values => [
            qw/
                  any
                  number
                  positive
                  non_negative
                  negative
                  non_positive
                  integer
                  positive_int
                  non_negative_int
                  negative_int
                  non_positive_int
                  string
                  array
                  hash
                  enum
                  code
            /
        ],
    }
};

code

The value is a coderef. Validate::Simple only checks the type, it does not run the code and does not check its result.

spec

The value is another structure, of hashref with predefined keys and values of different types. The type spec requires the key of, which, in turn, expects another valid specification. Obviously, a specification may contain as many nested specifications as necessary.

my $resume_spec = {
    name => {
        type => 'string'
    },
    objective => {
        type => 'string'
    },
    experience => {
        type => 'array',
        of   => {
            type => 'spec',
            of   => {
                company => {
                    type => 'string',
                },
                position => {
                    type => 'string',
                },
                description => {
                    type => 'string',
                },
                start => {
                    type => 'spec',
                    of   => {
                        year => {
                            type => 'integer',
                            gt   => 1960,
                            le   => 1900 + (localtime)[5],
                        },
                        month => {
                            type => 'positive_int',
                            lt   => 12,
                        },
                    },
                    required => 1,
                },
                end => {
                    type => 'spec',
                    of   => {
                        year => {
                            type => 'integer',
                            gt   => 1960,
                            le   => 1900 + (localtime)[5],
                        },
                        month => {
                            type => 'positive_int',
                            lt   => 12,
                        },
                    },
                    required => 0,
                },
            },
        },
    },
};

METHODS

new( \%spec[, $all_errors] )

Creates an object. The parameter is optional, though it's recomended to pass it, because in this case it checks specs syntax only once, not on each call of validate method.

By default the module stops validation after finding the first invalid value. If the second parameter $all_errors is true, the call of the validate method will keep checking all \%params and will stack all found errors. The list of errors is returned by the errors method.

validate( \%params, [\%specs[, $all_errors]] )

Does validation of the \%params against \%specs. If \%specs is not provided, it performs validation against the spec, given in the new method. Returns 1 if \%params are valid, undef otherwise.

The parameter $all_errors works the same way as in the new() method.

errors

Returns the list of validation errors. The list is being emptied before each call of the validate method.

BUGS

Not reported... Yet...

SEE ALSO

AUTHOR

Andrei Pratasavitski <andreip@cpan.org>

LICENSE

This is free software; you can redistribute it and/or modify it under the same terms as Perl itself.