scrub

0.1.4 • Public • Published

scrub

The world's only javascript argument checker.

NPM version Build Status Coverage Status

Why Scrub?

Scrub solves this problem:

function fn(v) {
  // what the heck is v?
}

Install for nodejs

npm install scrub

Basic Use

var scrub = require('scrub')
 
function hello(arg) {
 
  var spec = {
    type: 'string',
    required: true,
    validate: function(v) { if (v.length < 3) return 'Expected string length > 3' }
  }
 
  var err = scrub(arg, spec)
  if (err) return err
 
  return 'hello ' + arg.toLowerCase() + ' your third char is ' + arg.slice(2,1)
}
 
hello()           // Error: 'Missing required'
hello(1)          // Error: 'Invalid type'
hello('fo')       // Error: 'Expected string length > 3'
hello('FOOBAR')   // '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 = scrub(val, spec)  // null
console.log(val)        // {s1: 'hello', s2: 'goodbye'}

Required

spec = {s1: {type: 'string', required: true}}
scrub({s1: 'foo', s2: 'bar'}, spec)   // null
scrub({s2: 'bar'}, spec)              // 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 = scrub({str1: 'foo', num1: 2}, spec)  // null
err = scrub({str1: 2}, spec)               // Error with code 'badType'

Value checking with delimted string enums

spec = {type: 'string', value: 'one|or|another'}
scrub('or', spec)      // null
scrub('notOne', spec)  // Error wtih code 'badValue'

Optionally fail on unknown object keys with option strict

spec = {foo: {type: 'string'}}
scrub({foo: 'hello', bar: 'goodbye'}, spec)  // null
scrub({foo: 'hello', bar: 'goodbye'}, spec, {strict: true})  // 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',
  default: function(v, options) {
    if (options.foo) return 2
    else return 1
  },
  value: function(v, options) {
    return ({n1: v[n1], n2: v[n1] * v[n1]})
  }
}}
val = {}
err = scrub({}, spec}                 // null
val                                   // {n1:1, n2:1}
err = scrub({}, spec, {foo: true})    // null
val                                   // {n1:2, n2:4}

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',
  validate: function(v, options) {
    if (< 0) return 'n1 must be greater than zero'
  }
}}
 
scrub({n1: 1}, spec)   // null
scrub({n1: -1}, spec)  // Error: 'n1 must be greater than zero', Error.code: 'badValue'

or

spec = {
  type: 'number',
  validate: function(v, options) {
    if (< 0) {
      err = new Error('n1 must be greater than zero')
      err.code = 'i_died'
      return err
    }
  }
}}
 
scrub({n1: 1}, spec)   // null
scrub({n1: -1}, spec)  // 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}
  }
  function n2Validate(v, options) {
    if (!== this.n1) return 'n2 must equal n1'
    if (options.foo) return 'options.foo must not be'
  }
  scrub({n1:1, n2:1}, spec)  // null
  scrub({n1:1, n2:2}, spec)  // Error: 'n2 must equal n1'
  scrub({n1:1, n2:1}, spec, {foo:true})  // 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 = scrub(val, spec)  // null
val                         // {n1: 12, b2: true}  types have been coerced
val = {n1: '12', b2: 'true'}
err = scrub({n1: '12', b2: 'true'}, spec, {doNotCoerce: true}) // 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 = scrub({a1: [1,2,3]})  // err is null
err = scrub({a1: [1, 2, '3']})  // 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 = scrub([{s1: 'foo', n1: 1}, {s1: 'bar', n1: 2}])  // err is null
err = scrub([{s1: 'foo', n1: 1}, {s1: 'bar', n1: 'baz'}])  // err is Error with code 'badType'

Options

The top level scurb call accepts

scrub(val, spec, options)

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 option
val.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

Package Sidebar

Install

npm i scrub

Weekly Downloads

9

Version

0.1.4

License

MIT

Last publish

Collaborators

  • georgesnelling