scrub
The world's only javascript argument checker.
Why Scrub?
Scrub solves this problem:
{ // what the heck is v?}
Install for nodejs
npm install scrub
Basic Use
var scrub = { var spec = type: 'string' required: true { if vlength < 3 return 'Expected string length > 3' } var err = if err return err return 'hello ' + arg + ' your third char is ' + arg} // Error: 'Missing required' // Error: 'Invalid type' // Error: 'Expected string length > 3' // 'hello foobar your third char is O'
Why is this useful?
You now know everything you need to know about the variable arg. This is a simple example, but scrub aims to handle virtually any state that can be described syncronously. The descripton syntax aims to be as concise as possible. The error reporting aims to be as precise as possible.
With scrub you define all your assumptions about a value with a succint spec. If the value fails your spec, scub crafts a detailed error explaining where things went wrong. Within the spec, if you care, you have full control over the error that is thrown. Scrub is particularly well-suited for checking data between a public api, like a web service, and a schemaless store, like mongodb. It lets you remove virtually all the type and value checking from the body of your functions, and move them to a single, easy-to-read spec at the top of the file.
Scrub specs
Scrub specs are ordinary objects that you craft. Here is the bootstrap spec for a spec. The most important properties are type, value, required, and default. Scrub recurses on the value property for nested specs, and iterates the scrub over arrays.
var _spec = type: 'object' value: init: type: 'function' default: {} required: type: 'boolean' type: type: 'string' value: {} validate: type: 'function' finish: type: 'function'
Default
spec = s1: type: 'string' s2: type: 'string' default: 'goodbye'val = s1: 'hello'err = // nullconsole // {s1: 'hello', s2: 'goodbye'}
Required
spec = s1: type: 'string' required: true // null // Error with code 'missingParam'
Type
The type property is string with the following supported values:
'undefined'
'null'
'string'
'boolean'
'number'
'object'
'array'
'error'
'function'
'arguments'
Specify multiple valid types for a value like so:
type: 'string|number|boolean'
The type of any value is determied by the tipe module: https://github.com/3meters/tipe. Tipe supports custom types, enabling you to extend the type list.
spec = str1: type: 'string' num1: type: 'number'err = // nullerr = // Error with code 'badType'
Value checking with delimted string enums
spec = type: 'string' value: 'one|or|another' // null // Error wtih code 'badValue'
Optionally fail on unknown object keys with option strict
spec = foo: type: 'string' // null // Error with code 'badParam'
Custom Setters
The init, default, value, and finish properties of a spec accept a setter function. Setters are passed the curent value being scrubed and the options object. Setters should return the new value or an error.
spec = n1: type: 'number' { if optionsfoo return 2 else return 1 } { return n1: vn1 n2: vn1 * vn1 }val = {}err =
Custom Validators
Validators are similar to setters, except they return a positive value on failure, in most cases a simple string. Scrub will convert a string returned by validator into an error with the code property set to 'badValue'. To override this code, return an Error with its code property set.
spec = type: 'number' { if v < 0 return 'n1 must be greater than zero' }} // null // Error: 'n1 must be greater than zero', Error.code: 'badValue'
or
spec = type: 'number' { if v < 0 err = 'n1 must be greater than zero' errcode = 'i_died' return err }} // null // Error: 'n1 must be greater than zero', Error.code: 'i_died'
Passing Data to Setters and Validators
Passing data to validators or setters is easy. Within your validator or setter, the this object refers to the top-level passed-in value, and any data you pass in on the options object is passed through.
spec = n1: type: 'number' default: 0 n2: type: 'number' validate: n2Validate { if v !== thisn1 return 'n2 must equal n1' if optionsfoo return 'options.foo must not be' } // null // Error: 'n2 must equal n1' // Error: 'options.foo must not be'
Type coersion
For values with exected types of number or boolean, when fed a value of type string, by default scub will try to coerce the string to the spec'ed type before evalutating, converting '1' to 1 for a number, and 'true' to true for a boolean. This is handy for accepting web query strings. To turn this behavior off at any level, set options.doNotCoerce to true.
spec = n1: type: 'number' b1: type: 'boolean'val = n1: '12' b2: 'true'err = // nullval // {n1: 12, b2: true} types have been coercedval = n1: '12' b2: 'true'err = // Error with code 'badType'
Arrays
For arrays Scrub tests each element in the array against the spec
spec = a1: type: 'array' value: type: 'number'err = // err is nullerr = // err is Error with code 'badType'
or with an array of objects
spec = type: 'array' value: type: 'object' value: s1: type: 'string' n1: type: 'number'err = // err is nullerr = // err is Error with code 'badType'
Options
The top level scurb call accepts
Where options is a non-strict object with following supported properties and their defaults:
strict: false // do not allow unspecified properties of objects ignoreDefaults: false // do not set default values, handy for db updates ignoreRequired: false // do not enforce required, handy for db updates doNotCoerce: false // do not coerce types returnValue: false // return the scrubed value, rather than null, on success // handy for scrubing scalars or for recasting scalars to objects log: false // log the arguments to each recursive scrub call, // handy for debugging deeply nested spec
Options can be set as an optional third argument to the top level call, or as properties of any spec or sub-spec. They remain set for all children unless they are overridden. For example, a top-level spec can be strict, meaning no unrecognized properties are allowed, except for one property, which can be unstrict, allowing un-specified sub-properties, except for one of its sub-properties, which must be strict, etc. For example:
spec = o1: type: 'object' value: s1: type: 'string' s2: type: 'string' o2: type: 'object' strict: false value: s1: type: 'string' s2: type: 'string' o1: type: 'object' strict: true: value: n1: type: 'number' val = o1: s1: 'foo' s2: 'bar' o2: s1: 'foo' s2: 'bar' s3: 'baz}}err = scrub(val, spec, {strict: true}) // err is null because o2 strict attribute overrode optionval.o2.o1 = {n2: 100}err = scrub(val, spec, {strict: true}) // err is Error because spec.o2.o1 does not allow properties other than n1
Production Use
3meters dogfoods the master branch in production.
Contributing
Contributions welcome.
Copyright
Copyright (c) 2013 3meters. All rights reserverd.
License
MIT