pSchema validates the contents of tables.
local pSchema = require("p_schema")
local rect = {x=0, y=0, w=32, h=64}
local model = pSchema.newModel {
reject_unhandled = true,
keys = {
x = {pAssert.type, "number"},
y = {pAssert.type, "number"},
w = {pAssert.numberGe, 0},
h = {pAssert.numberGe, 0}
}
}
local function printErr(err)
if err then
print(table.concat(err, "\n"))
end
end
local ok, err
ok, err = pSchema.validate(model, rect, "Rectangle")
printErr(err) -- OK
local rect_outta_spec = {x="foo", y=nil, w=-100, h=true}
ok, err = pSchema.validate(rect_outta_spec, model, "Rectangle")
printErr(err)
--> Rectangle > x: expected number
--> Rectangle > y: unhandled key
--> Rectangle > w: number is out of range
--> Rectangle > h: expected number
API
pSchema.checkModel
Checks a model table, raising an error if a problem is found.
pSchema.checkModel(md)
-
md: The model to check.
pSchema.getMaxMessages
Gets the current maximum number of failure messages per validation call.
local n = pSchema.getMaxMessages()
Returns: The current max message count for validation failures.
pSchema.newKeysX
Creates a new model with a 'keys' filter and the model option reject_unhandled enabled. (This is a common configuration in the workloads that pSchema was designed for.)
local md = pSchema.newKeysX(keys)
-
keys: The 'keys' filter table. Should be a freshly created table with no metatable.
Returns: A new model table with keys attached.
pSchema.newModel
Creates a new model table.
local md = pSchema.newModel(t)
-
t: The table to use as the model. Should be a freshly created table with no metatable.
Returns: The table.
pSchema.setMaxMessages
Sets the maximum number of failure messages per validation call.
pSchema.setMaxMessages([n])
-
[n]: (500) The maximum number of messages. Must be at least 1.
Notes
When the maximum number of messages is reached, an additional message is appended to the final error output which states that this is the case.
pSchema.validate
Tests a table against a model.
local ok, err = pSchema.validate(model, tbl, [name], [fatal])
-
model: The model table. -
tbl: The table to validate. -
[name]: (nil) An optional name to display as the first label in error messages. -
[fatal]: (false) When true, raises an error upon the first validation failure. This may be desired when the validation action is time-sensitive.
Returns: true, or false plus an error string if there was a problem.
Module Notes
Structures
Model
Handler
A function that validates one value in a table. handlers are based on the function signature used in pAssert. On failure, handlers should raise an error; on success, they should do nothing.
local function myHandler(n, v, ...)
-
n: An argument number or generic label. (Unused here, but needed to maintain compatibility with pAssert functions.) -
v: The value to check. -
…: Additional function arguments.
local function myFoobar(n, v)
if v ~= "foobar" then
error("expected the string 'foobar'")
end
end
}
Filter
Selects keys during validation. All filters are optional, and they run in the same hard-coded sequence.
'metatable'
Selects the table’s metatable.
local model = pSchema.newModel {
metatable = <reference>
}
'keys'
Selects specific keys.
local model = pSchema.newModel {
keys = {
foo = <reference>,
bar = <reference>
}
}
'array'
Selects the table’s array keys, from 1 to #tbl. Any keys that were already selected by the 'keys' filter are skipped.
local model = pSchema.newModel {
array = <reference>
}
'remaining'
Selects any keys which have not been selected by the 'keys' and 'array' filters.
local model = pSchema.newModel {
remaining = <reference>
}
Model Options
-
reject_unhandled: (Boolean/nil) Whentrue, treat any keys that were not selected by a model’s filter as failures. This is effectively the same as assigning a handler to the 'remaining' filter that always fails.
These options apply when an 'array' filter is present:
-
array_len: (Number)#tblmust match this exactly. -
array_min: (Number)#tblmust be greater or equal to this. -
array_max: (Number)#tblmust be less or equal to this.
Reference Notation
Handler References
Handler references can be written in long form or short form. (The short form is applicable only to handlers which do not take additional arguments.)
Long Form
The handler reference is a table with its function stored in key [1]. Array elements starting at [2] are passed as arguments to the function.
foo = {someHandlerFunction, "foobar", 12345}
The above example would call someHandlerFunction(nil, v, "foobar", 12345).
|
Warning
|
Arrays of arguments do not support nil "gaps" because they will break the table length operator (#).
|
Short Form
The handler reference is just a function.
foo = someHandlerFunction
The above example would call someHandlerFunction(nil, v).
Sub-Models
Model references look like this:
foo = {"sub", model_table},
bar = model_table -- short version of the above
baz = {"sub-eval", model_table}, -- only runs if the value is neither false nor nil
Here is an example of validating a table within a table:
local pAssert = require("p_assert")
local pSchema = require("p_schema")
local md_bar = pSchema.newModel {
keys = {
zoot = {pAssert.type, "string"}
}
}
local md_foo = pSchema.newModel {
keys = {
doop = {pAssert.type, "number"},
foos = {"sub", md_bar}
}
}
local tbl = {
doop = 3,
foos = {
zoot = "woo"
}
}
pSchema.validate(md_foo, tbl, "TestSubModel") -- should return with no failures
Behavior in relation to Lua
The order in which pSchema checks values is not deterministic, because pairs is used to iterate hash keys. As a result, the order of failure messages may vary.
When you write duplicate keys in a Lua table constructor, the previous values are silently discarded:
local models = pSchema.models
local validator = pSchema.newValidator("Test", {
main = models.keys {
foo = {pAssert.type, "number"},
foo = {pAssert.type, "string"},
foo = {pAssert.type, "boolean"}
}
}
-- main.foo points to `{pAssert.type, "boolean"}`.
VERSION: 2.106