pericmd 020: schemas

Now let’s talk about schemas:

$SPEC{hello} = {
    v => 1.1,
    summary => 'Say hello',
    args => {
        name => {
            summary => 'Name',
            schema  => 'str*',
            cmdline_aliases => {n=>{}},
            pos => 0,
        },
        salutation => {
            summary => 'Salutation',
            schema  => 'str*',
            cmdline_aliases => {s=>{}},
        },
    },
};

Both name and salutation are assigned a schema of str*. This means “string and required”. Schemas are written in Sah, and is another product of me trying to be lazy, minimizing coding and replacing it with declarative statements and code generation.

A schema is either a string to express a type, with optional asterisk * to signify required value, or an array of [type, clauses] where clauses is a hash/hashref containing more restrictions on possible values.

Sah schemas are geared towards code generation. From the schema, a data validator can be generated. So far, with the implementation Data::Sah, Perl and JavaScript code generators are available (PHP code generator was also once written, but currently out of date because I haven’t had the need to use it yet.) Perl code generator is useful to let us “automatically” validate function arguments (as well as return values), while JavaScript code generator is useful for web-based applications to generate browser-side data validator to accompany server-side validator for our backend Perl code.

Some more examples of schemas

Suppose we only want to allow certain values for salutation:

["str*", in=>["Mr", "Mrs", "Miss"]]

Note, during processing sometimes the schema will be normalized. Here’s the normalized form of the above schema:

["str", {req=>1, in=>["Mr", "Mrs", "Miss"]}, {}]

The third element of the array is called extras and is used for advanced stuffs.

And suppose we want to require certain lengths for the name:

["str*", min_len=>4, max_len=>50]

If you put these schemas in the hello program, here’s what it looks like:

$SPEC{hello} = {
    v => 1.1,
    summary => 'Say hello',
    args => {
        name => {
            summary => 'Name',
            schema  => 'str*',
            cmdline_aliases => {n=>{}},
            pos => 0,
        },
        salutation => {
            summary => 'Salutation',
            schema  => ["str*", in=>["Mr", "Mrs", "Miss"]],
            cmdline_aliases => {s=>{}},
        },
    },
};

One thing to note though. At the time of this writing, the Perinci::CmdLine::Lite backend does not yet support schema checking; you have to use the ::Classic backend. The reason is that schema generation with Data::Sah incurs some startup overhead (+- 0.25s or more, increasing as the schema becomes more complex). In the future I’m planning to speed things up so this overhead is back to acceptable level, but for now you can either:

  • Let users choose the backend itself by PERINCI_CMDLINE_ANY=classic.
  • Prefer the use of the classic backend by: use Perinci::CmdLine::Any -prefer_lite=>0.
  • Force the usage of the classic backend by replacing Perinci::CmdLine::Any with Perinci::CmdLine::Classic.
  • Do schema checking manually using Perl code in the function body.
% PERINCI_CMDLINE_ANY=classic ./hello -s Sir
ERROR 400: Argument 'salutation' fails validation: Must be one of ["Mr","Mrs","Miss"]

% PERINCI_CMDLINE_ANY=classic ./hello -n X
ERROR 400: Argument 'name' fails validation: Length must be between 4 and 50

% PERINCI_CMDLINE_ANY=classic ./hello -s Mrs -n Postman
Hello, Mrs Postman!

UPDATE 2015-04-09: Perinci::CmdLine::Lite as of version 1.06 now supports argument validation.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s