Oval - Options Validator
Table of Contents
Overview
Validate arguments and option hashes when passed to methods.
Module Description
This module implements simple to use data validators. It was initially thought to validate option hashes (so the name Oval stands for Options' Validator), but it appeared early that it's suitable to validate arbitrary parameters (variables).
The shape of acceptable data is described by a simple grammar. The validation is then carried out by a recursive-descent parser that matches the actual values provided by caller against declarators that comprise the hash declaration.
A declaration consists of terminal and non-terminal declarators. Non-terminal
declarators are created by methods of Oval module which have names starting
with ov_ prefix. All other values (such as :symbol, 'string', nil, or
Class) are terminals. Terminals use == operator to match the values
provided by caller. Non-terminal use its own logic introducing more elaborate
matching criteria (see for example ov_collection).
Oval raises Oval::DeclError if the declaration is not well-formed. This is raised from the point of declaration. Other, more common exception is the Oval::ValueError which is raised each time the validation fails. This one is raised from within a method which validates its arguments.
Usage
The usage is basically a two-step procedure. The first step is to declare data to be validated. This would create a validator object. The second step is to validate data using the previously constructed validator. For simple cases the entire construction may fit to a single line. Let's start with such a simple example.
Example 1: Declaring Simple Options
The method foo in the following code accepts only {} and {:foo => value}
as ops hash, and the value may be anything:
# Options validator
require 'oval'
class C
extend Oval
def self.foo(ops = {})
Oval.validate(ops, [ :foo => ov_anything ], 'ops')
end
end
What does it do? Just try it out:
C.foo # should pass
C.foo :foo => 10 # should pass
C.foo :foo => 10, :bar => 20 # Oval::ValueError "Invalid option :bar for ops. Allowed options are :foo"
Options are declared with ov_xxx declarators. The
ov_options, for example, declares a hash of options. In
ov_options all the allowed options should be listed inside of
[] square brackets. Keys may be any values convertible to strings (i.e. a key
given in declaration must respond_to? :to_s). Values are declared recursively
using ov_xxx declarators or terminal declarators (any other
ruby values).
In Example 1 we have declared options inside of a method for simplicity. This isn't an optimal technique. Usually options' declaration remains same for the entire lifetime of an application, so it is unnecessary to recreate the declaration each time function is called. In other words, we should move the declaration outside of the method, convert it to a singleton and only validate options inside of a function. For that purpose, the Example 1 could be modified to the following form
Example 2: Separating declaration from validation
In this example we separate options declaration from the validation to reduce costs related to options declaration:
# Options validator
require 'oval'
class C
extend Oval
# create a singleton declaration ov
def self.ov
@ov ||= [ :foo => ov_anything ]
end
# use ov to validate ops
def self.foo(ops = {})
Oval.validate(ops, ov, 'ops')
end
end
Reference
Declarators
A declaration of data being validated consists entirely of what we call
declarators. The grammar for defining acceptable data uses non-terminal and
terminal declarators. Non-terminal declarators are most of the
ov_xxx methods of Oval module, for example
ov_options. General syntax for non-terminal declarator is
ov_xxx[ args ], where args are declarator-specific arguments.
Terminal declarators include all the other ruby values, for example nil. They
are matched exactly against data being validated, so if the data doesn't equal
the given value an exception is raised.
In what follows, we'll document all the core declarators implemented in Oval.
Index of Declarators
ov_anything
- Declaration
ov_anything
or
ov_anything[]
- Validation - permits any value
- Example
ov = [ :bar => ov_anything ]
def foo(ops = {})
Oval.validate(ops, ov, 'ops')
end
[Table of Contents|Index of Declarators]
ov_collection
- Declaration
ov_collection[ class_decl, item_decl ]
- Validation - permits only collections of type class_decl with items matching item_decl declaration
- Allowed values for class_decl are:
HashorArrayor any subclass ofHashorArray,ov_subclass_of[klass]where klass isHashorArrayor a subclass of any of them.
- Allowed values for item_decl:
- if class_decl is
Array-like, then any value is allowed as item_decl, - if class_decl is
Hash-like, then item_decl should be a one-element Hash in form { key_decl => val_decl }.
- if class_decl is
- Example
ov = [
:bar => ov_collection[ Hash, { instance_of[Symbol] => anything } ],
:geez => ov_collection [ Array, instance_of[String] ]
]
def foo(ops = {})
Oval.validate(ops, ov, 'ops')
end
[Table of Contents|Index of Declarators]
ov_instance_of
- Declaration
ov_instance_of[klass]
- Validation - permits only instances of a given class klass
- Allowed values for klass - only class names, for example
String,Hash, etc. - Example
ov = [ :bar => ov_instance_of[String] ]
def foo(ops = {})
Oval.validate(ops, ov, 'ops')
end
[Table of Contents|Index of Declarators]
ov_kind_of
- Declaration
ov_kind_of[klass]
- Validation - permits only values that are a kind of given class klass
- Allowed values for klass - only class names, for example
String,Hash, etc. - Example
ov = [ :bar => ov_kind_of[Numeric] ]
def foo(ops = {})
Oval.validate(ops, ov, 'ops')
end
[Table of Contents|Index of Declarators]
ov_match
- Declaration
ov_match[re]
- Validation - permits only values matching regular expression re,
- Allowed values for re - must be a kind of
Regexp. - Example
# Only valid identifiers are allowed as :bar option
ov = [ :bar => ov_match[/^[a-z_]\w+$/] ]
def foo(ops = {})
Oval.validate(ops, ov, 'ops')
end
[Table of Contents|Index of Declarators]
ov_one_of
- Declaration
ov_one_of[decl1,decl2,...]
- Validation - permits only values matching one of declarations
decl,decl2, ... - Example
ov = [
:bar => ov_one_of[ ov_instance_of[String], ov_kind_of[Numeric], nil ]
]
def foo(ops = {})
Oval.validate(ops, ov, 'ops')
end
[Table of Contents|Index of Declarators]
ov_options
- Declaration
[ optkey_decl1 => optval_decl1, ... ]
- Validation - permits only declared options and their values.
- Allowed values for
optkey_declN- anything that is convertible to string (namely, anything that responds toto_smethod). - Example:
ov = [
:bar => ov_anything,
:geez => ov_instance_of[String],
# ...
]
def foo(ops = {})
Oval.validate(ops, ov, 'ops')
end
[Table of Contents|Index of Declarators]
ov_subclass_of
- Declaration
ov_subclass_of[klass]
- Validation - permits only subclasses of klass
- Allowed values for klass - only class names, for example
String,Hash, etc. - Example
ov = [ :bar => ov_subclass_of[Numeric] ]
def foo(ops = {})
Oval.validate(ops, ov, 'ops')
end
API Reference
API reference may be generated with
bundle exec rake yard
The generated documentation goes to doc/ directory. Note that this works only
under ruby >= 1.9.
The API documentation is also available online.
Limitations
- API documentation is currently very poor,


