NAME

Data::Sah::Manual::ParamsValidating - Validating function/method parameters using Data::Sah

VERSION

This document describes version 0.916 of Data::Sah::Manual::ParamsValidating (from Perl distribution Data-Sah), released on 2024-02-16.

OVERVIEW

There are several choices when it comes to validating function/method parameters using Sah schemas and Data::Sah. The job of Data::Sah is just to generate Perl code from a Sah schema. The Perl code can be integrated into your code in several ways. The final result might or might not need Data::Sah itself.

The document is divided into two parts depending on where the Sah schemas are put.

IF YOU PUT SCHEMAS IN RINCI METADATA

One recommended way to put the schemas are in the Rinci function metadata. With Rinci metadata you specify more than just types/constraints for your function parameters/arguments, including: summary & description, return value, command-line short options, tab completion, and so on.

Once you have your Rinci metadata, e.g.:

$SPEC{foo} = {
    v => 1.1,
    summary => 'Does foo',
    args => {
        a1 => {
            summary => 'First argument',
            schema => 'int*',
            req => 1,
        },
        a2 => {
            summary => 'Second argument',
            schema => ['array', of=>'int*', min_len=>1],
            default => [1],
        },
    },
};
sub foo {
    my %args = @_;
    ...
}

You can decide whether you want to depend on Data::Sah or not during runtime.

Dist::Zilla::Plugin::Rinci::Validate

If you do not want to depend on Data::Sah, what you can do is insert the generated validator code:

my %args = @_; # VALIDATE_ARGS

This requires that your code be organized as a Perl distribution, and you use Dist::Zilla to build your distribution, and you use the Dist::Zilla::Plugin::Rinci::Validate plugin to scan for the # VALIDATE_ARGS labels and insert the validator code there (all in one long line, as to not mess with line numbers). Also, the validator code will only be present in the built version of your code. But this way, you can avoid an extra sub call to the validator code and the validator code no longer needs need Data::Sah. The the overhead of compilation from Sah schema to Perl code is moved into distribution build time.

If you use this method, it is advisable that you also put this attribute in your Rinci metadata (the dzil plugin will remind you):

'x.func.validate_args' => 1

This is to express that your function already performs argument validation and other tools/frameworks (e.g. Perinci::CmdLine::Lite or Perinci::Sub::Wrapper, see below) can skip doing argument validation again. Forgetting to add this attribute is not dangerous, it just means you're validating twice (where once is already enough).

Perinci::Sub::ValidateArgs

If you find the above method too cumbersome, you can also generate the validator dynamically using Perinci::Sub::ValidateArgs.

use Perinci::Sub::ValidateArgs;

$SPEC{foo} = {
    ...
};
sub foo {
    state $validator = gen_args_validator();
    my %args = @_;
    if (my $err = $validator->(\%args)) { return $err }

    ...
}

or, if you want to die on validation failure:

$SPEC{foo} = {
    ...
};
sub foo {
    state $validator = gen_args_validator(die => 1);
    my %args = @_;
    $validator->(\%args);

    ...
}

Perinci::Sub::ValidateArgs will retrieve the Rinci metadata from the caller's %SPEC package variable, then generate Perl validator code for all the arguments specified in the Rinci metadata, and compose them into a single arguments validator subroutine which is then returned for you to use. This generation process is only done once, the first time your function is called. The subsequent call will be faster since the arguments validator routine is cached by the state variable. Note that state variable is available from perl 5.010. If you are stuck with older perl, you can use the alternative solution like:

my $validator_foo;
sub foo {
    $validator_foo ||= gen_args_validator();
    if (my $err = $validator_foo->(\%args)) { return $err }
    ...
}

or perhaps:

{
    my $validator;
    sub foo {
        $validator ||= gen_args_validator();
        if (my $err = $validator->(\%args)) { return $err }
        ...
    }
}

Using Perinci::Sub::ValidateArgs is more convenient than using the dzil plugin because you do not need to build a Perl distribution first. But this method is a tiny bit slower during runtime and will require Data::Sah during runtime.

Two other alternatives are available under some situation.

Perinci::CmdLine::Lite and Perinci::CmdLine::Inline

If you only use your function through a CLI using Perinci::CmdLine::Lite or Perinci::CmdLine::Inline framework, you can skip validating in the function body and let the framework validate (or generate a validator) for you.

During runtime, Perinci::CmdLine::Lite will retrieve your function's Rinci metadata and generate Data::Sah validators for all the specified arguments and check them. This means you will require Data::Sah during runtime.

Perinci::CmdLine::Inline on the other hand will generate a CLI script with the arguments validator embedded in it. Data::Sah will no longer be required during runtime.

Note that if your Rinci metadata already contains this attribute x.perl.validate_args set to true, argument validation will not be done.

Perinci::Sub::Wrapper (used by e.g. Perinci::CmdLine::Classic)

Perinci::Sub::Wrapper will create a wrapper for your function and add functionalities (including argument validation) according to information from Rinci metadata. This can be done dynamically, or the wrapper code can be included in the source code during distribution build time using Dist::Zilla::Plugin::Rinci::Wrap.

Perinci::CmdLine::Classic is a CLI framework which uses Perinci::Sub::Wrapper to perform argument validation.

IF YOU DO NOT PUT SCHEMAS IN RINCI METADATA

TBD. Data::Sah::Params.

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/Data-Sah.

SOURCE

Source repository is at https://github.com/perlancar/perl-Data-Sah.

SEE ALSO

Alternatives to Sah/Data::Sah to validate your function/method parameters: Params::Validate, Type::Tiny/Type::Params (see Type::Tiny::Manual::Params).

AUTHOR

perlancar <perlancar@cpan.org>

CONTRIBUTING

To contribute, you can send patches by email/via RT, or send pull requests on GitHub.

Most of the time, you don't need to build the distribution yourself. You can simply modify the code, then test via:

% prove -l

If you want to build the distribution (e.g. to try to install it locally on your system), you can install Dist::Zilla, Dist::Zilla::PluginBundle::Author::PERLANCAR, Pod::Weaver::PluginBundle::Author::PERLANCAR, and sometimes one or two other Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond that are considered a bug and can be reported to me.

COPYRIGHT AND LICENSE

This software is copyright (c) 2024, 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012 by perlancar <perlancar@cpan.org>.

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

BUGS

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Sah

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.