NAME

validate.args - validate arguments


SYNOPSIS

  va = require('validate.args')
  -- foo( a, b )
  func foo( ... )
    local spec = { <specifications> }
    local ok, a, b = va.validate( spec, ... )
  end
  --- goo( c, d )
  func goo( ... )
    local spec = { <specifications> }
    local ok, c, d = va.validate( options, spec, ... )
  end


DESCRIPTION

validate.args validates that a function's arguments meet certain specifications. Both scalar and table arguments are validated, and validation of nested tables is supported. validate.args provides two main validation functions, validate() and validate_opts(). They differ in that validate() uses global settings for options which control the validation process while validat_opts() allows setting those options for a specific validation.

Positional, named, and mixed positional and named arguments are supported. Positional arguments may be converted to named arguments for uniformity of access (see Validation Options). Each argument has a validation specification which provides the validation constraints.

Positional arguments
  foo( 3, 'n' )

Positional arguments are not explicitly named when passed to the function. Their validation specifications are passed as a list, one element per argument:

  { { pos1 specification },
    { pos2 specification }
  }
Named arguments
  goo{ a = 3, b = 'n' }

Named arguments are passed as a single table to the function (notice the use of the {} syntactic sugar in the function invocation). Their validation specifications are passed as a table:

 { arg1_name = { arg1 specification },
   arg2_name = { arg2 specification }
 }
"mixed" mode
  bar( 3, 'n', { c = 22 } )

Here a nested table is used to hold the named arguments. The table is simply another positional argument, so the validation specifications are passed as a list, one per argument:

  { { pos1 specification },
    { pos2 specification },
    { table specification }
  }

The validation specification for the table specifies the constraints on the named arguments, typically using the vtable constraint.

Validation Specifications

A validation specification is a set of constraints which an argument must meet. In most cases the specification is encoded in a table, where each key-value pair represents a type of constraint and its parameters. The specification may also be specified by a function; see Mutating Validation Specifications.

Multiple constraints may be specified for each argument. There are no guarantees as to the order in which the constraints are applied.

The caller may provide constraints which modify the passed arguments; these must not expect a particular sequence of operation.

The following constraint types are recognized:

optional

This is a boolean attribute which, if true, indicates that the argument need not be present. Positional as well as named arguments may be optional; if they are not at the end of the list they may be specified as nil in the function call, e.g.

  foo( nil, 3 )

It defaults to false. All arguments are required by default.

default

This provides a value for the argument if it is not specified, as well as indicating that the argument is optional. This may be a function, which will be called if a default value is required. The function should return two values:

  1. a boolean indicating success or failure;

  2. the default value upon success, an error message upon failure

type

This specifies the expected type of argument. It may be either a single type or a list of types:

  type = 'number'
  type = { 'number', 'boolean' }

Types are specified as strings, with the following types available:

'nil'
'number'
'string'
'boolean'
'table'
'function'
'thread'
'userdata'

These are the built-in types as returned by the Lua type function.

'posnum'

The argument must be a number greater than zero.

'zposnum'

The argument must be a number greater than or equal to zero.

'posint'

The argument must be an integer greater than zero.

'zposint'

The argument must be an integer greater than or equal to zero.

To add additional types see the add_type function.

enum

This specifies one or more explicit values which the argument may take. It may be either a single value or a list of values:

  enum = 33
  enum = { 'a', 33, 'b' }
not_nil

This is a boolean and indicates that the value must not be nil. This only pertains to positional arguments.

requires

This lists the names of one or more arguments which must be specified in addition to the current argument. The value is either a single name or a list of names:

  requires = 'arg3'
  requires = { 'arg3', 'arg4' }

See also Argument Groups

excludes

This lists the names of one or more arguments which may not be specified in addition to the current argument. The value is either a single name or a list of names:

  excludes = 'arg3'
  excludes = { 'arg3', 'arg4' }

See also Argument Groups

one_of

This provides a list of names of other arguments of which exactly one must be specified in addition to the current argument:

  one_of = { 'arg3', 'arg4' }

See also Argument Groups

vfunc

This specifies a function which is called to validate the argument. It is called with a single argument, the passed argument value. It must return two values:

  1. a boolean indicating success or failure;

  2. the (possibly modified) argument value upon success, an error message upon failure

For example,

  vfunc = function( orig )
            if type(orig) == 'number' and orig >= 3 then
              return true, orig / 22
            end
              return false, 'not a number or less then 3'
          end
vtable

This is used to validate the contents of an argument which is a table. It's value may be either:

a table of specifications

There should be one element in the specification table for each element in the argument table. For example, to validate a call such as

  foo( 'hello', { nv1 = 3, nv2 = 2 } )

Use

  spec = { { type = 'string' },
           { vtable = { nv1 = { type = 'posint' },
                        nv2 = { type = 'int' },
                      }
           }
         }
  ok, pos, tbl = validate( spec, ... )

which will return

   pos = 'hello'
   tbl = { nv1 = 3, nv2 = 2 }
 in the above invocation.
a function

The function should take a single parameter - the passed argument value - and must return two values:

  1. a boolean indicating success or failure;

  2. Upon success, a table of validation specifcations. Upon failure, an error message. See Examples for an example of this in use.

name

A name for a positional argument. If specified and the named validation option is true, then the argument will be assigned this name in the returned argument table. See Validation Options for more information.

Mutating Validation Specifications

A validation specification is usually (as documented above) a table of constraints. In the case where the entire validation table must be created on the fly the validation specification may be a function. The function should take a single parameter - the passed argument value - and must return two values:

  1. a boolean indicating success or failure;

  2. Upon success, a table of validation specifcations. Upon failure, an error message.

Groups of Arguments

Some operations on groups of arguments are possible for named arguments. These are specified as special "arguments" in the validation specification. In order to accomodate multiple groups, these "arguments" take as values a list of lists,

  ['%one_of'] = { { 'a', 'b', 'c' } }

not a simple list:

  ['%one_of'] = { 'a', 'b', 'c' }

in ordeThis allows specifying multiple groups:

  ['%one_of'] = { { 'a', 'b', 'c' } , { 'd', 'e', 'f' } }
%one_of

This ensures that exactly one argument in a group is specified. For example, say that the caller must provide exactly one of the arguments arg1, arg2, or arg3. Exclusivity is obtained via

  arg1 = { optional = true, excludes = { 'arg2', 'arg3' } },
  arg2 = { optional = true, excludes = { 'arg1', 'arg3' } },
  arg3 = { optional = true, excludes = { 'arg1', 'arg2' } }

But that doesn't force the user to specify any. This addition will:

  ['%one_of'] = {{ 'arg1', 'arg2', 'arg3' }}

Note that specifying the excludes attribute is redundant with %one_of, so the above could be rewritten as

  arg1 = { optional = true },
  arg2 = { optional = true },
  arg3 = { optional = true }
  ['%one_of'] = {{ 'arg1', 'arg2', 'arg3' }}
%oneplus_of

This ensures that at least one argument in a group is specified. More may be specified. As a complicated example:

  sigma   = { optional = true, excludes = { 'sigma_x', 'sigma_y' } },
  sigma_x = { optional = true, requires = { 'sigma_y' } },
  sigma_y = { optional = true, requires = { 'sigma_x' } },
  ['%oneplus_of'] = { { 'sigma_x', 'sigma_y', 'sigma' } },

ensures that only one of the two following situations occurs:

  sigma
  sigma_x sigma_y

Validation Options

There are a few options which affect the validation process. These may be set for individual validations using validate_opts(), or may be set globally for validations done via validate() using opts().

check_spec

By default the passed validation specification is not itself checked for consistency, as this may be too much of a performance hit. Setting this to true will cause the specifications to be checked.

This defaults to false.

error_on_invalid

If true, the Lua error() function will be called the case of invalid arguments instead of returning a status code and message.

This defaults to false.

error_on_bad_spec

If this is true, an invalid validation specification will result in a call to the Lua error() function.

This defaults to false.

named

If this is true, positional arguments are returned as a table, with their names given either by the name attribute in the validation specification or by their cardinal index in the argument list. For example:

   ok, opts = validate_opts( { named = true },
                             { { name = a }, { }, },
                             22, 3
                              )

will result in

   opts.a = 22
   opts[2] = 3

This defaults to false.

allow_extra

If this is true, then any extra arguments (either named or positional) which are not mentioned in the validation specification are quietly ignored. For example:

  local ok, a, b, c = validate_opts( { allow_extra = true,
                                       pass_through = true,
                                      },
                                      { {}, {} },
                                     1, 2, 3)

would result in

  a = 1
  b = 2
  c = nil

This defaults to false.

pass_through

If this is true and allow_extra is also true, then any extra arguments (either named or positional) which are not mentioned in the validation specification are passed through. For example:

  local ok, a, b, c = validate_opts( { allow_extra = true,
                                       pass_through = true,
                                      },
                                      { {}, {} },
                                     1, 2, 3)

would result in

  a = 1
  b = 2
  c = 3

This defaults to false.

baseOptions

This option is useful only for the validation functions which take an options table (such as validate_opts()). If true, any options which are not specified in the options table will be set from the global options table as modified by the opts() function. Normally they are set from the default options values.

This defaults to false.

Functions

validate( specs, ... )

Validate the passed argument list against the specifications. It returns a list of values. The first value is a boolean indicating whether or not the validation succeeded.

If validation succeeded, the remainder of the list contains the values of the arguments (possibly modified during the validation).

If validation failed, the second value is a string indicating what caused the failure.

validate_opts( opts, specs, ... )

Validate the passed argument list against the specifications. The validation workflow may be altered via options passed via the opts argument. The return arguments are the same as validate.

Those options which are not set in opts are set to the default values. If instead they should be set to the values which were specified by the opts() function, set the special option baseOptions to true.

validate_tbl( opts, specs, tble )

Validate the contents of the passed table against the specifications. The validation workflow may be altered via options passed via the opts argument. The return arguments are the same as validate.

Those options which are not set in opts are set to the default values. If instead they should be set to the values which were specified by the opts() function, set the special option baseOptions to true.

add_type( type_name, func )

Register a validation function for the named type which will be accepted by the type validation attribute.

The function will be passed the argument to validate. It should return a list of values. The first value is a boolean indicating whether or not the validation succeeded.

Upon success it should return the (possibly modified) argument as the second value.

For example, the following

  add_type( 'mytype', function( arg )
                          return 'number' == type(arg) and  arg > 2 and arg < 3,
                                 3 * arg
                           end
          )

adds a new type called mytype which accepts only numbers between 2 and 3 (exclusive) and modifies the argument by multiplying it by 3.

opts( table of options )

Set the default values for the passed options. See Validation Options for the available options.


EXAMPLES


AUTHOR

Diab Jerius, <djerius@cfa.harvard.edu>


COPYRIGHT AND LICENSE

Copyright (C) 2010 by the Smithsonian Astrophysical Observatory

This software is released under the GNU General Public License. You may find a copy at http://www.fsf.org/copyleft/gpl.html.