The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

    Validator::Lazy

VERSION

Version 0.01

SYNOPSIS

    use Validator::Lazy;

    my $form = {
        first_name => 'John',
        last_name  => 'John',
        phone      => '+123456789',
        fax        => '',
        email      => 'john@site.com',
    };

    my $config = q~
        '/^(first|last)_name$/':
            - Trim: 'all'
            - Required
            - RegExp: '/^[A-Zaz]{3,64}$/'

        '[phone,fax]': Phone

        phone: Required

        email: [ 'Required', 'Email' ]

        myform:
            - Form: [ 'first_name', 'last_name', 'phone', 'fax', 'email' ]
    ~;

    my $validator = Validator::Lazy->new( $config );

    my $ok = $v->check(  myform => $form );  # true / false
    OR
    my ( $ok, $data ) = $validator->check( myform => $form );

    use Data::Dumper;
    say Dumper $v->errors;    # [ { code => any_error_code, field => field_with_error, data => { variable data for more accurate error definition } } ]
    say Dumper $v->warnings;  # [ { code => any_warn_code,  field => field_with_warn,  data => { variable data for more accurate warn  definition } } ]
    say Dumper $v->data;      # Fixed data. For example trimmed strings, corrected char case, etc...

For more details please see test files. For example t/14-int_roles-form.t can be the no.1 to see.

DESCRIPTION

Validator for different sets of data with easy and simple sets of rules.

Features:

  • Very simple and relatively short configuration

  • Allows to check almost everything: small web forms as well as deep multilevel structures with intersected params.

  • Some predefined check rules/roles ( Case, CountryCode, Email, Form, IP, IsIn, MinMax, Phone, RegExp, Required, Trim )

  • Easy way to create your own simple rules/roles and use them as native as predefined

  • No need to make and pass extra code to validator from caller during validation. Just a sets of rules.

  • No intersections and relations between rules (when you fix a one field validation, your other fields are be safe)

So, how it works...

Configuration/Init

    my $v = Validator::Lazy->new( $config );

$config can be a string, or text, or hashref

  • when config is a string, which looks like a filename,- validator will try to read configuration from this file

  • when config is a text, validator will think, that it's a pure YAML or JSON and will try to parse it

  • when config is a hashref, validator just will apply it without further ado

In all cases, finnaly we have a hashref:

    field_definition1:
        - rule1: param1
        - rule2: param2
        ...
    field_definition2:
        ...

in case, when a field have 1 rule without params, we can give a scalar instead of Arrayref[HashRef]:

    field_definition1: rule1

each rule can be an internal validator role, or external role, or a key from configuration

internal role example:

    user_phone: Phone

external/your role example:

    part_number: Where::Your::Role::IS::YourFieldRole

predefined role example:

    # Here, we predefine/declare the new field: "any_phone"
    any_phone: Phone

    # And now we can use it within other field rules:
    user_phone:
        - Required
        - any_phone

    # And here we have alias or clone for any_phone
    user_fax:  any_phone

each config key allows you to match more than one field of your data for checking:

for example we need to check a web form like this:

    firstname:   'John'
    lastname:    'John'
    phone:       '+123456789'
    fax:         ''
    email:       'john@site.com'
    secretkey:   'x1x2x3x4'

at first we make a config: firstname and lastname has equal requirements, so we put them in one key:

    '[firstname,lastname]':
        - Required
        - RegExp: '/^[A-Zaz]{3,64}$/'

    or like this

    '/^(first|last)_name$/':
        - Required
        - RegExp: '/^[A-Zaz]{3,64}$/'

phone and fax has similar requirements, but phone is required and fax is optional:

    [phone,fax]: Phone
    phone: Required

and email:

    email:
        - Required
        - Email

let's assume, that secretkey has some tricky checks, so it should be checked with external code. We should write a role (how to do this - see far below), and now just use it:

    secretkey:
        - Required
        - Path::To::Your::Roles::SecretKey : { secretkey: param }

Combining all together, and we have:

    # Config
    my $config = q~
        '/(first|last)_name/':
            - Trim: 'all'
            - Required
            - RegExp: '/^[A-Zaz]{3,64}$/'

        '[phone,fax]': Phone

        phone: Required

        email:
            - Required
            - Email

        secretkey:
            - Required
            - 'Path::To::Your::Roles::SecretKey': { secretkey: 'param' }

        myform:
            - Form:
                - first_name
                - last_name
                - phone
                - fax
                - email
                - secretkey
    ~;

    # Form to check:
    my $form = {
        first_name => 'John',
        last_name  => 'John',
        phone      => '+123456789',
        fax        => '',
        email      => 'john@site.com',
        secretkey  => 'x1x2x3x4',
    };

    my $validator = Validator::Lazy->new( $config );

    my $ok = $validator->check( $form ); # true
    or
    my ( $ok, $data ) = $validator->check( myform => $form );

Writing a roles

Let's write a role, that is required for example above:

    package Path::To::Your::Roles::SecretKey;

    use Modern::Perl;
    use Moose::Role;

    sub check {
        my ( $self, $value, $param ) = @_;

        $self;  # is a validator object
        $value; # is a value to check from form
        $param; # is a param = {secretkey:param}

        $self->add_error( ); # This will add to validator dafault error
        $self->add_error( 'CUSTOM_ERROR_CODE' );  # Custom error code will be plased instead of default
        $self->add_error( 'CUSTOM_ERROR_CODE', { some useful data } );  # Also, we can pass to error some data, and use it somwhere outside
        $self->add_error( { some useful data } );  # Default code, but some useful data

        $self->add_warning(); # All the same as with errors;

        return $value; # You should do it! Othervise you just lost your value.
    }

    # Also, you can use in check roles:
    sub before_check { similar to check }
    sub after_check { similar to check }
    # They are working exactly as check, but allow to do some code separation

    1;

Forms

Validator has a predefined "Form" role, let's, use it:

    # YAML sample:
    '/(first|last)_name/':
        - Trim: 'all'
        - Required
        - RegExp: '/^[A-Zaz]{3,64}$/'

    full_name:
        Form: [first_name, last_name ]

Corrected example from above:

    my $form = {
        full_name => {
            first_name => 'John',
            last_name  => 'Smith',
        },
        phone     => '+123456789',
        fax       => '',
        email     => 'john@site.com',
        secretkey => 'x1x2x3x4',
    };

    my $config = q~
        '/(first|last)_name/':
            - Trim: 'all'
            - Required
            - RegExp: '/^[A-Zaz]{3,64}$/'

        full_name:
            - Form: [first_name, last_name ]

        '[phone,fax]': Phone

        phone: Required

        email: Email

        secretkey:
            - Required
            - 'Path::To::Your::Roles::SecretKey': { secretkey: param }

        myform:
            - Form:
                - full_name
                - phone
                - fax
                - email
                - secretkey
    ~;

    my $validator = Validator::Lazy->new( $config );

    my $ok = $validator->check( $form ); # true

    or

    my ( $ok, $data ) = $validator->check( myform => $form );

warnings/errors/results

    $validator->check( $data ); can return 1 or 2 params;

the 1st - can be true or false - is a result of check. the 2nd - form data, but corrected. For example, if Trim used, some strings will be trimmed from spaces.

after $validator->check() validators has:

    $validator->errors - list = [ { code => 'SOME_ERROR_CODE', field => 'some_field', data => 'some data' }, ... ]
    $validator->error_codes - list of codes = [ 'SOME_ERROR_CODE', ... ]

And all the same with warnings...

when an external role adds an error, then, by default it has a error/warning code:

    role is "Path::To::Your::Roles::SecretKey"
    default error code is PATH_TO_YOUR_ROLES_SECRETKEY_ERROR

when a subform adds an error, then default field name is generated as form_name + '_' + field_name:

    myform:
        - Form:
            - full_name
                - Required

when full_name is absent, we have an error:

        { code => 'REQUIRED_ERROR', field => 'myform_full_name', data => {} }

Contestants

There are huge amount of other validators on CPAN.

All of them have their pros and cons, but all are very different.

I do not wish to write here detailed review of each, so let's just list them all:

https://metacpan.org/pod/Data::Validator

https://metacpan.org/pod/Validator::LIVR

https://metacpan.org/pod/QBit::Validator

https://metacpan.org/pod/Input::Validator

https://metacpan.org/pod/MojoX::Validator

https://metacpan.org/pod/Kossy::Validator

https://metacpan.org/pod/FormValidator::Tiny

https://metacpan.org/pod/FormValidator::LazyWay

METHODS

check $validator->check( $data );

SUPPORT AND DOCUMENTATION

    After installing, you can find documentation for this module with the perldoc command.

    perldoc Validator::Lazy

    You can also look for information at:

        RT, CPAN's request tracker (report bugs here)
            http://rt.cpan.org/NoAuth/Bugs.html?Dist=Validator-Lazy

        AnnoCPAN, Annotated CPAN documentation
            http://annocpan.org/dist/Validator-Lazy

        CPAN Ratings
            http://cpanratings.perl.org/d/Validator-Lazy

        Search CPAN
            http://search.cpan.org/dist/Validator-Lazy/

AUTHOR

ANTONC <antonc@cpan.org>

LICENSE

    This program is free software; you can redistribute it and/or modify it
    under the terms of the the Artistic License (2.0). You may obtain a
    copy of the full license at:

    L<http://www.perlfoundation.org/artistic_license_2_0>