node package manager

doormen

Doormen

Validate, sanitize and assert: the silver bullet of data!

Beta version.

/!\ This documentation is still a Work In Progress /!\

Basic validation

  • sanitize Array of string the sanitizer's name to apply before any type checking
  • optional boolean the data can be null or undefined, if so the data validate immediately
  • default (anything) the data can be null or undefined, if so it is overwritten by the default value and it validates immediately
  • type string the name of the type checker
  • instanceOf
  • min
  • max
  • length
  • minLength
  • maxLength
  • match
  • in
  • notIn
  • when
  • properties object of schema, it iterates through each properties and checks that they all match their own schema
  • elements Array same than properties but for arrays
  • only boolean used in conjunction with properties or elements, it checks that no properties other than those listed are present
  • of object contains one schema that will check each elements of an array or each properties

Type Checkers

Javascript primitive types:

  • undefined: the data should be undefined
  • null: the data should be null
  • boolean: the data should be a boolean, i.e. true or false
  • number: the data should be a number. NOTE that Infinity and NaN are ok, so you may consider using real instead of number in almost all cases
  • string: the data should be a string
  • object: the data should be an Object
  • function: the data should be a Function

Javascript/Node.js built-in types:

  • array: the data should be an Array
  • error: the data should be an instance of Error
  • date: the data should be an instance of Date
  • buffer: the data should be a Node.js Buffer

Common meta types:

  • real: a number that is not NaN nor +/- Infinity
  • integer: a number that is not NaN nor +/- Infinity, and that do not have decimal part

Sanitizers

  • toNumber: try to convert a string to a number
  • trim: trim a string, removing whitespace at the beginning and the end

TOC

# Assertion utilities doormen.shouldThrow() should throw if the callback has not throw, and catch if it has throw.

var thrown ;
 
 
thrown = false ;
 
try {
    doormen.shouldThrow( function() {} ) ;
}
catch ( error ) {
    thrown = true ;
}
 
if ( ! thrown ) { throw new Error( 'It should throw!' ) ; }
 
 
thrown = false ;
 
try {
    doormen.shouldThrow( function() { throw new Error( 'Fatal error' ) ; } ) ;
}
catch ( error ) {
    thrown = true ;
}
 
if ( thrown ) { throw new Error( 'It should *NOT* throw' ) ; }

doormen.not() should throw if the data validate, and catch if it has throw.

var thrown ;
 
 
thrown = false ;
 
try {
    doormen.not( { type: 'string' } , 'text' ) ;
}
catch ( error ) {
    thrown = true ;
}
 
if ( ! thrown ) { throw new Error( 'It should throw' ) ; }
 
 
thrown = false ;
 
try {
    doormen.not( { type: 'string' } , 1 ) ;
}
catch ( error ) {
    thrown = true ;
}
 
if ( thrown ) { throw new Error( 'It should *NOT* throw' ) ; }

# Equality checker Equality of simple type.

doormen.equals( undefined , undefined ) ;
doormen.equals( null , null ) ;
doormen.equals( true , true ) ;
doormen.equals( false , false ) ;
doormen.not.equals( undefined , null ) ;
doormen.not.equals( true , false ) ;
doormen.not.equals( null , false ) ;
doormen.not.equals( undefined , false ) ;
 
doormen.equals( NaN , NaN ) ;
doormen.not.equals( NaN , null ) ;
doormen.not.equals( NaN , undefined ) ;
 
doormen.equals( Infinity , Infinity ) ;
doormen.equals( -Infinity , -Infinity ) ;
doormen.not.equals( Infinity , -Infinity ) ;
 
doormen.equals( 0 , 0 ) ;
doormen.equals( 123 , 123 ) ;
doormen.equals( 0.123 , 0.123 ) ;
 
doormen.equals( "" , "" ) ;
doormen.equals( "abc" , "abc" ) ;
doormen.equals( "   abc" , "   abc" ) ;
doormen.equals( "abc  " , "abc  " ) ;
doormen.equals( "     abc  " , "     abc  " ) ;
 
doormen.not.equals( 0 , "" ) ;
doormen.not.equals( false , "" ) ;

Equality of objects.

var o = {} ;
 
doormen.equals( {} , {} ) ;
doormen.equals( o , o ) ;
doormen.equals( { a: 2 , b: 5 } , { a: 2 , b: 5 } ) ;
doormen.not.equals( { a: 2 , b: 6 } , { a: 2 , b: 5 } ) ;
doormen.equals( { b: 5 , a: 2 } , { a: 2 , b: 5 } ) ;
doormen.not.equals( { a: 2 , b: 5 , c: null } , { a: 2 , b: 5 } ) ;
doormen.not.equals( { a: 2 , b: 5 } , { a: 2 , b: 5 , c: null } ) ;
 
doormen.not.equals( { a: 2 , b: 5 , c: {} } , { a: 2 , b: 5 } ) ;
doormen.equals( { a: 2 , b: 5 , c: {} } , { a: 2 , b: 5 , c: {} } ) ;
doormen.equals( { a: 2 , b: 5 , c: { d: 'titi' } } , { a: 2 , b: 5 , c: { d: 'titi' } } ) ;
doormen.not.equals( { a: 2 , b: 5 , c: { d: 'titi' } } , { a: 2 , b: 5 , c: { d: 'toto' } } ) ;
doormen.equals(
    { a: 2 , b: 5 , c: { d: 'titi' , e: { f: 'f' , g: 7 } } } ,
    { a: 2 , b: 5 , c: { d: 'titi' , e: { f: 'f' , g: 7 } } }
) ;

when a property is undefined in the left-side and non-existant in the right-side, they should be equals.

doormen.equals( { a: 2 , b: 5 , c: undefined } , { a: 2 , b: 5 } ) ;
doormen.equals( { a: 2 , b: 5 } , { a: 2 , b: 5 , c: undefined } ) ;
doormen.equals( { a: 2 , b: 5 , c: undefined } , { a: 2 , b: 5 , c: undefined } ) ;
doormen.equals( { a: 2 , b: 5 , c: undefined } , { a: 2 , b: 5 , d: undefined } ) ;
doormen.equals( { a: 2 , b: 5 , c: { d: 'titi' } } , { a: 2 , b: 5 , c: { d: 'titi' , e: undefined } } ) ;

Equality of arrays.

var o = [] ;
 
doormen.equals( [] , [] ) ;
doormen.equals( o , o ) ;
doormen.equals( [ 1 ] , [ 1 ] ) ;
doormen.not.equals( [ 1 , undefined ] , [ 1 ] ) ;
doormen.not.equals( [ 1 ] , [ 1 , undefined ] ) ;
doormen.not.equals( [ 1 ] , [ 2 ] ) ;
doormen.equals( [ 1 , 2 , 3 ] , [ 1 , 2 , 3 ] ) ;
doormen.equals( [ 1 , [] , 3 ] , [ 1 , [] , 3 ] ) ;
doormen.equals( [ 1 , [ 2 ] , 3 ] , [ 1 , [ 2 ] , 3 ] ) ;
doormen.equals( [ 1 , [ 2 , 'a' ] , 3 ] , [ 1 , [ 2 , 'a' ] , 3 ] ) ;
doormen.not.equals( [ 1 , [ 2 , 'a' ] , 3 ] , [ 1 , [ 2 , 'b' ] , 3 ] ) ;
doormen.equals( [ 1 , [ 2 , [ null ] , 'a' ] , 3 ] , [ 1 , [ 2 , [ null ] , 'a' ] , 3 ] ) ;
doormen.not.equals( [ 1 , [ 2 , [ undefined ] , 'a' ] , 3 ] , [ 1 , [ 2 , [ null ] , 'a' ] , 3 ] ) ;

Equality of nested and mixed objects and arrays.

doormen.not.equals( {} , [] ) ;
doormen.equals(
    { a: 2 , b: 5 , c: [ 'titi' , { f: 'f' , g: 7 } ] } ,
    { a: 2 , b: 5 , c: [ 'titi' , { f: 'f' , g: 7 } ] }
) ;
doormen.equals(
    [ 'a' , 'b' , { c: 'titi' , d: [ 'f' , 7 ] } ] ,
    [ 'a' , 'b' , { c: 'titi' , d: [ 'f' , 7 ] } ]
) ;

Circular references: stop searching when both part have reached circular references.

var a , b ;
 
= { a: 1, b: 2 } ;
a.c = a ;
 
= { a: 1, b: 2 } ;
b.c = b ;
 
doormen.equals( a , b ) ;
 
= { a: 1, b: 2 , c: { a: 1, b: 2 } } ;
a.c.c = a ;
 
= { a: 1, b: 2 } ;
b.c = b ;
 
doormen.equals( a , b ) ;

# Basic types should validate undefined accordingly.

doormen( { type: 'undefined' } , undefined ) ;
doormen.not( { type: 'undefined' } , null ) ;
doormen.not( { type: 'undefined' } , false ) ;
doormen.not( { type: 'undefined' } , true ) ;
doormen.not( { type: 'undefined' } , 0 ) ;
doormen.not( { type: 'undefined' } , 1 ) ;
doormen.not( { type: 'undefined' } , '' ) ;
doormen.not( { type: 'undefined' } , 'text' ) ;
doormen.not( { type: 'undefined' } , {} ) ;
doormen.not( { type: 'undefined' } , [] ) ;

should validate null accordingly.

doormen.not( { type: 'null' } , undefined ) ;
doormen( { type: 'null' } , null ) ;
doormen.not( { type: 'null' } , false ) ;
doormen.not( { type: 'null' } , true ) ;
doormen.not( { type: 'null' } , 0 ) ;
doormen.not( { type: 'null' } , 1 ) ;
doormen.not( { type: 'null' } , '' ) ;
doormen.not( { type: 'null' } , 'text' ) ;
doormen.not( { type: 'null' } , {} ) ;
doormen.not( { type: 'null' } , [] ) ;

should validate boolean accordingly.

doormen.not( { type: 'boolean' } , undefined ) ;
doormen.not( { type: 'boolean' } , null ) ;
doormen( { type: 'boolean' } , false ) ;
doormen( { type: 'boolean' } , true ) ;
doormen.not( { type: 'boolean' } , 0 ) ;
doormen.not( { type: 'boolean' } , 1 ) ;
doormen.not( { type: 'boolean' } , '' ) ;
doormen.not( { type: 'boolean' } , 'text' ) ;
doormen.not( { type: 'boolean' } , {} ) ;
doormen.not( { type: 'boolean' } , [] ) ;

should validate number accordingly.

doormen.not( { type: 'number' } , undefined ) ;
doormen.not( { type: 'number' } , null ) ;
doormen.not( { type: 'number' } , false ) ;
doormen.not( { type: 'number' } , true ) ;
doormen( { type: 'number' } , 0 ) ;
doormen( { type: 'number' } , 1 ) ;
doormen( { type: 'number' } , Infinity ) ;
doormen( { type: 'number' } , NaN ) ;
doormen.not( { type: 'number' } , '' ) ;
doormen.not( { type: 'number' } , 'text' ) ;
doormen.not( { type: 'number' } , {} ) ;
doormen.not( { type: 'number' } , [] ) ;

should validate string accordingly.

doormen.not( { type: 'string' } , undefined ) ;
doormen.not( { type: 'string' } , null ) ;
doormen.not( { type: 'string' } , false ) ;
doormen.not( { type: 'string' } , true ) ;
doormen.not( { type: 'string' } , 0 ) ;
doormen.not( { type: 'string' } , 1 ) ;
doormen( { type: 'string' } , '' ) ;
doormen( { type: 'string' } , 'text' ) ;
doormen.not( { type: 'string' } , {} ) ;
doormen.not( { type: 'string' } , [] ) ;

should validate object accordingly.

doormen.not( { type: 'object' } , undefined ) ;
doormen.not( { type: 'object' } , null ) ;
doormen.not( { type: 'object' } , false ) ;
doormen.not( { type: 'object' } , true ) ;
doormen.not( { type: 'object' } , 0 ) ;
doormen.not( { type: 'object' } , 1 ) ;
doormen.not( { type: 'object' } , '' ) ;
doormen.not( { type: 'object' } , 'text' ) ;
doormen( { type: 'object' } , {} ) ;
doormen( { type: 'object' } , { a:1 , b:2 } ) ;
doormen( { type: 'object' } , [] ) ;
doormen( { type: 'object' } , [ 1,2,3 ] ) ;
doormen( { type: 'object' } , new Date() ) ;
doormen.not( { type: 'object' } , function(){} ) ;

should validate function accordingly.

doormen.not( { type: 'function' } , undefined ) ;
doormen.not( { type: 'function' } , null ) ;
doormen.not( { type: 'function' } , false ) ;
doormen.not( { type: 'function' } , true ) ;
doormen.not( { type: 'function' } , 0 ) ;
doormen.not( { type: 'function' } , 1 ) ;
doormen.not( { type: 'function' } , '' ) ;
doormen.not( { type: 'function' } , 'text' ) ;
doormen.not( { type: 'function' } , {} ) ;
doormen.not( { type: 'function' } , [] ) ;
doormen( { type: 'function' } , function(){} ) ;

# Optional and default data when a data is null, undefined or unexistant, and the optional flag is set the schema, it should validate.

doormen.not( { type: 'string' } , null ) ;
doormen( { optional: true, type: 'string' } , null ) ;
doormen.not( { type: 'string' } , undefined ) ;
doormen( { optional: true, type: 'string' } , undefined ) ;
 
doormen( { type: 'string' } , 'text' ) ;
doormen( { optional: true, type: 'string' } , 'text' ) ;
doormen.not( { type: 'string' } , 1 ) ;
doormen.not( { optional: true, type: 'string' } , 1 ) ;
 
doormen.not( { properties: { a: { type: 'string' } } } , {} ) ;
doormen( { properties: { a: { optional: true, type: 'string' } } } , {} ) ;

missing optional properties should not be created (i.e. with undefined)..

var result ;
 
result = doormen( { properties: { a: { optional: true, type: 'string' } } } , {} ) ;
 
// {a:undefined} is equals to {} for doormen.equals() (this is the correct behaviour), but here we want to know for sure 
// that a key is not defined, so we have to check it explicitly 
 
doormen.equals( 'a' in result , false ) ;
 
result = doormen( {
        properties: {
            a: { optional: true, type: 'string' },
            b: { optional: true, type: 'string' },
            c: {
                optional: true,
                properties: {
                    d: { optional: true, type: 'string' }
                }
            }
        }
    } ,
    {}
) ;
 
doormen.equals( 'a' in result , false ) ;
doormen.equals( 'b' in result , false ) ;
doormen.equals( 'c' in result , false ) ;
 
result = doormen( {
        properties: {
            a: { optional: true, type: 'string' },
            b: { optional: true, type: 'string' },
            c: {
                optional: true,
                properties: {
                    d: { optional: true, type: 'string' }
                }
            }
        }
    } ,
    { c: undefined }
) ;
 
doormen.equals( 'a' in result , false ) ;
doormen.equals( 'b' in result , false ) ;
doormen.equals( 'c' in result , true ) ;
doormen.equals( result.c , undefined ) ;
 
result = doormen( {
        properties: {
            a: { optional: true, type: 'string' },
            b: { optional: true, type: 'string' },
            c: {
                optional: true,
                properties: {
                    d: { optional: true, type: 'string' }
                }
            }
        }
    } ,
    { c: null }
) ;
 
doormen.equals( 'a' in result , false ) ;
doormen.equals( 'b' in result , false ) ;
doormen.equals( 'c' in result , true ) ;
doormen.equals( result.c , null ) ;
 
result = doormen( {
        properties: {
            a: { optional: true, type: 'string' },
            b: { optional: true, type: 'string' },
            c: {
                optional: true,
                properties: {
                    d: { optional: true, type: 'string' }
                }
            }
        }
    } ,
    { c: {} }
) ;
 
doormen.equals( 'a' in result , false ) ;
doormen.equals( 'b' in result , false ) ;
doormen.equals( 'c' in result , true ) ;
doormen.equals( 'd' in result.c , false ) ;

when a data is null, undefined or unexistant, and a default value is specified in the schema, that default value should overwrite the original one.

doormen.equals( doormen( { type: 'string' , "default": 'default!' } , null ) , 'default!' ) ;
doormen.equals(
    doormen(
        { properties: { a: { type: 'string' , "default": 'default!' } } } ,
        { a: null } ) ,
    { a: 'default!' }
) ;
doormen.equals(
    doormen(
        { properties: { a: { type: 'string' , "default": 'default!' } , b: { type: 'object' , "default": { c: 5 } } } } ,
        { a: null, b: undefined } ) ,
    { a: 'default!' , b: { c: 5 } }
) ;
doormen.equals(
    doormen(
        { properties: { a: { type: 'string' , "default": 'default!' } , b: { type: 'object' , "default": { c: 5 } } } } ,
        {} ) ,
    { a: 'default!' , b: { c: 5 } }
) ;

# Built-in types should validate 'unset' accordingly (undefined or null).

doormen( { type: 'unset' } , undefined ) ;
doormen( { type: 'unset' } , null ) ;
doormen.not( { type: 'unset' } , false ) ;
doormen.not( { type: 'unset' } , true ) ;
doormen.not( { type: 'unset' } , 0 ) ;
doormen.not( { type: 'unset' } , 1 ) ;
doormen.not( { type: 'unset' } , '' ) ;
doormen.not( { type: 'unset' } , 'text' ) ;
doormen.not( { type: 'unset' } , {} ) ;
doormen.not( { type: 'unset' } , [] ) ;

should validate array accordingly.

doormen.not( { type: 'array' } , undefined ) ;
doormen.not( { type: 'array' } , null ) ;
doormen.not( { type: 'array' } , false ) ;
doormen.not( { type: 'array' } , true ) ;
doormen.not( { type: 'array' } , 0 ) ;
doormen.not( { type: 'array' } , 1 ) ;
doormen.not( { type: 'array' } , '' ) ;
doormen.not( { type: 'array' } , 'text' ) ;
doormen.not( { type: 'array' } , {} ) ;
doormen.not( { type: 'array' } , { a:1 , b:2 } ) ;
doormen( { type: 'array' } , [] ) ;
doormen( { type: 'array' } , [ 1,2,3 ] ) ;
doormen.not( { type: 'array' } , function(){} ) ;

should validate date accordingly.

doormen( { type: 'date' } , new Date() ) ;
 
doormen.not( { type: 'date' } , undefined ) ;
doormen.not( { type: 'date' } , null ) ;
doormen.not( { type: 'date' } , false ) ;
doormen.not( { type: 'date' } , true ) ;
doormen.not( { type: 'date' } , 0 ) ;
doormen.not( { type: 'date' } , 1 ) ;
doormen.not( { type: 'date' } , '' ) ;
doormen.not( { type: 'date' } , 'text' ) ;
doormen.not( { type: 'date' } , {} ) ;
doormen.not( { type: 'date' } , { a:1 , b:2 } ) ;
doormen.not( { type: 'date' } , [] ) ;
doormen.not( { type: 'date' } , [ 1,2,3 ] ) ;
doormen.not( { type: 'date' } , function(){} ) ;

should validate error accordingly.

doormen( { type: 'error' } , new Error() ) ;
 
doormen.not( { type: 'error' } , undefined ) ;
doormen.not( { type: 'error' } , null ) ;
doormen.not( { type: 'error' } , false ) ;
doormen.not( { type: 'error' } , true ) ;
doormen.not( { type: 'error' } , 0 ) ;
doormen.not( { type: 'error' } , 1 ) ;
doormen.not( { type: 'error' } , '' ) ;
doormen.not( { type: 'error' } , 'text' ) ;
doormen.not( { type: 'error' } , {} ) ;
doormen.not( { type: 'error' } , { a:1 , b:2 } ) ;
doormen.not( { type: 'error' } , [] ) ;
doormen.not( { type: 'error' } , [ 1,2,3 ] ) ;
doormen.not( { type: 'error' } , function(){} ) ;

should validate arguments accordingly.

var fn = function() { doormen( { type: 'arguments' } , arguments ) ; } ;
 
fn() ;
fn( 1 ) ;
fn( 1 , 2 , 3 ) ;
 
doormen.not( { type: 'arguments' } , undefined ) ;
doormen.not( { type: 'arguments' } , null ) ;
doormen.not( { type: 'arguments' } , false ) ;
doormen.not( { type: 'arguments' } , true ) ;
doormen.not( { type: 'arguments' } , 0 ) ;
doormen.not( { type: 'arguments' } , 1 ) ;
doormen.not( { type: 'arguments' } , '' ) ;
doormen.not( { type: 'arguments' } , 'text' ) ;
doormen.not( { type: 'arguments' } , {} ) ;
doormen.not( { type: 'arguments' } , { a:1 , b:2 } ) ;
doormen.not( { type: 'arguments' } , [] ) ;
doormen.not( { type: 'arguments' } , [ 1,2,3 ] ) ;
doormen.not( { type: 'arguments' } , function(){} ) ;

# Mixed types should validate 'strictObject' accordingly, i.e. objects that are NOT arrays.

doormen.not( { type: 'strictObject' } , undefined ) ;
doormen.not( { type: 'strictObject' } , null ) ;
doormen.not( { type: 'strictObject' } , false ) ;
doormen.not( { type: 'strictObject' } , true ) ;
doormen.not( { type: 'strictObject' } , 0 ) ;
doormen.not( { type: 'strictObject' } , 1 ) ;
doormen.not( { type: 'strictObject' } , '' ) ;
doormen.not( { type: 'strictObject' } , 'text' ) ;
doormen( { type: 'strictObject' } , {} ) ;
doormen( { type: 'strictObject' } , { a:1 , b:2 } ) ;
doormen.not( { type: 'strictObject' } , [] ) ;
doormen.not( { type: 'strictObject' } , [ 1,2,3 ] ) ;
doormen.not( { type: 'strictObject' } , function(){} ) ;

should validate 'regexp' accordingly, i.e. RegExp instance or string convertible to RegExp.

doormen( { type: 'regexp' } , /Random/ ) ;
doormen( { type: 'regexp' } , new RegExp( "Random" ) ) ;
doormen( { type: 'regexp' } , "Random" ) ;
doormen.not( { type: 'regexp' } , "(Random" ) ;
 
doormen.not( { type: 'regexp' } , undefined ) ;
doormen.not( { type: 'regexp' } , null ) ;
doormen.not( { type: 'regexp' } , false ) ;
doormen.not( { type: 'regexp' } , true ) ;
doormen.not( { type: 'regexp' } , 0 ) ;
doormen.not( { type: 'regexp' } , 1 ) ;
doormen( { type: 'regexp' } , '' ) ;
doormen( { type: 'regexp' } , 'text' ) ;
doormen.not( { type: 'regexp' } , {} ) ;
doormen.not( { type: 'regexp' } , { a:1 , b:2 } ) ;
doormen.not( { type: 'regexp' } , [] ) ;
doormen.not( { type: 'regexp' } , [ 1,2,3 ] ) ;
doormen.not( { type: 'regexp' } , function(){} ) ;

should validate 'classId' accordingly, i.e. function (constructor) or non-empty string.

doormen.not( { type: 'classId' } , undefined ) ;
doormen.not( { type: 'classId' } , null ) ;
doormen.not( { type: 'classId' } , false ) ;
doormen.not( { type: 'classId' } , true ) ;
doormen.not( { type: 'classId' } , 0 ) ;
doormen.not( { type: 'classId' } , 1 ) ;
doormen.not( { type: 'classId' } , '' ) ;
doormen( { type: 'classId' } , 'text' ) ;
doormen.not( { type: 'classId' } , {} ) ;
doormen.not( { type: 'classId' } , { a:1 , b:2 } ) ;
doormen.not( { type: 'classId' } , [] ) ;
doormen.not( { type: 'classId' } , [ 1,2,3 ] ) ;
doormen( { type: 'classId' } , function(){} ) ;

# Top-level filters 'instanceOf' should validate object accordingly.

if ( doormen.isBrowser ) { window[ 'MyClass' ] = function MyClass(){} ; }
else { global[ 'MyClass' ] = function MyClass(){} ; }
 
doormen( { instanceOf: Date } , new Date ) ;
doormen( { instanceOf: Array } , new Array ) ; // jshint ignore:line 
doormen( { instanceOf: MyClass } , new MyClass() ) ;
doormen( { instanceOf: Object } , new MyClass() ) ;
 
doormen( { instanceOf: 'MyClass' } , new MyClass() ) ;
doormen( { instanceOf: 'Object' } , new MyClass() ) ;
 
doormen.not( { instanceOf: Date } , new Array ) ;
doormen.not( { instanceOf: 'Date' } , new Array ) ;

min filter should validate accordingly, non-number should throw.

doormen( { min: 3 } , 10 ) ;
doormen( { min: 3 } , 3 ) ;
doormen.not( { min: 3 } , 1 ) ;
doormen.not( { min: 3 } , 0 ) ;
doormen.not( { min: 3 } , -10 ) ;
doormen( { min: 3 } , Infinity ) ;
doormen( { min: Infinity } , Infinity ) ;
doormen.not( { min: 3 } , -Infinity ) ;
doormen.not( { min: 3 } , NaN ) ;
doormen.not( { min: 3 } , true ) ;
doormen.not( { min: 3 } , false ) ;
doormen.not( { min: 3 } , undefined ) ;
doormen.not( { min: 0 } , undefined ) ;
doormen.not( { min: -3 } , undefined ) ;
doormen.not( { min: 3 } , '10' ) ;

max filter should validate accordingly, non-number should throw.

doormen.not( { max: 3 } , 10 ) ;
doormen( { max: 3 } , 3 ) ;
doormen( { max: 3 } , 1 ) ;
doormen( { max: 3 } , 0 ) ;
doormen( { max: 3 } , -10 ) ;
doormen.not( { max: 3 } , Infinity ) ;
doormen( { max: 3 } , -Infinity ) ;
doormen( { max: -Infinity } , -Infinity ) ;
doormen.not( { max: 3 } , NaN ) ;
doormen.not( { max: 3 } , true ) ;
doormen.not( { max: 3 } , false ) ;
doormen.not( { max: 3 } , '1' ) ;

min + max filter should validate accordingly, non-number should throw.

doormen.not( { min: 3, max: 10 } , 15 ) ;
doormen( { min: 3, max: 10 } , 10 ) ;
doormen( { min: 3, max: 10 } , 5 ) ;
doormen( { min: 3, max: 10 } , 3 ) ;
doormen.not( { min: 3, max: 10 } , 1 ) ;
doormen.not( { min: 3, max: 10 } , 0 ) ;
doormen.not( { min: 3, max: 10 } , -10 ) ;
doormen.not( { min: 3, max: 10 } , Infinity ) ;
doormen.not( { min: 3, max: 10 } , -Infinity ) ;
doormen.not( { min: 3, max: 10 } , NaN ) ;
doormen.not( { min: 3, max: 10 } , true ) ;
doormen.not( { min: 3, max: 10 } , false ) ;
doormen.not( { min: 3, max: 10 } , '6' ) ;

'length' filter should validate accordingly, data that do not have a length should throw.

doormen( { length: 3 } , "abc" ) ;
doormen.not( { length: 3 } , "abcde" ) ;
doormen.not( { length: 3 } , "ab" ) ;
doormen.not( { length: 3 } , "" ) ;
 
doormen.not( { length: 3 } , 1 ) ;
doormen.not( { length: 0 } , 1 ) ;
doormen.not( { length: 3 } , NaN ) ;
doormen.not( { length: 3 } , true ) ;
doormen.not( { length: 3 } , false ) ;

minLength filter should validate accordingly, data that do not have a length should throw.

doormen( { minLength: 3 } , "abc" ) ;
doormen( { minLength: 3 } , "abcde" ) ;
doormen.not( { minLength: 3 } , "ab" ) ;
doormen.not( { minLength: 3 } , "" ) ;
 
doormen.not( { minLength: 3 } , 1 ) ;
doormen.not( { minLength: 0 } , 1 ) ;
doormen.not( { minLength: 3 } , NaN ) ;
doormen.not( { minLength: 3 } , true ) ;
doormen.not( { minLength: 3 } , false ) ;

maxLength filter should validate accordingly, data that do not have a length should throw.

doormen( { maxLength: 3 } , "abc" ) ;
doormen.not( { maxLength: 3 } , "abcde" ) ;
doormen( { maxLength: 3 } , "ab" ) ;
doormen( { maxLength: 3 } , "" ) ;
 
doormen.not( { maxLength: 3 } , 1 ) ;
doormen.not( { maxLength: 0 } , 1 ) ;
doormen.not( { maxLength: 3 } , NaN ) ;
doormen.not( { maxLength: 3 } , true ) ;
doormen.not( { maxLength: 3 } , false ) ;

minLength + maxLength filter should validate accordingly, data that do not have a length should throw.

doormen( { minLength: 3 , maxLength: 5 } , "abc" ) ;
doormen( { minLength: 3 , maxLength: 5 } , "abcd" ) ;
doormen( { minLength: 3 , maxLength: 5 } , "abcde" ) ;
doormen.not( { minLength: 3 , maxLength: 5 } , "abcdef" ) ;
doormen.not( { minLength: 3 , maxLength: 5 } , "ab" ) ;
doormen.not( { minLength: 3 , maxLength: 5 } , "" ) ;
 
doormen.not( { minLength: 3 , maxLength: 5 } , 1 ) ;
doormen.not( { maxLength: 0 } , 1 ) ;
doormen.not( { minLength: 3 , maxLength: 5 } , NaN ) ;
doormen.not( { minLength: 3 , maxLength: 5 } , true ) ;
doormen.not( { minLength: 3 , maxLength: 5 } , false ) ;

'match' filter should validate accordingly using a RegExp.

doormen( { match: "^[a-f]*$" } , "" ) ;
doormen.not( { match: "^[a-f]+$" } , "" ) ;
doormen( { match: "^[a-f]*$" } , "abc" ) ;
doormen( { match: "^[a-f]*$" } , "abcdef" ) ;
doormen.not( { match: "^[a-f]*$" } , "ghi" ) ;
doormen.not( { match: /^[a-f]*$/ } , "ghi" ) ;
 
doormen.not( { match: "^[a-f]*$" } , 1 ) ;
doormen.not( { match: "^[a-f]*$" } , NaN ) ;
doormen.not( { match: "^[a-f]*$" } , true ) ;
doormen.not( { match: "^[a-f]*$" } , false ) ;

'in' filter should validate if the value is listed.

doormen.not( { in: [ 1,5,7 ] } , 10 ) ;
doormen( { in: [ 1,5,7 ] } , 5 ) ;
doormen( { in: [ 1,5,7 ] } , 1 ) ;
doormen.not( { in: [ 1,5,7 ] } , 0 ) ;
doormen.not( { in: [ 1,5,7 ] } , -10 ) ;
doormen.not( { in: [ 1,5,7 ] } , Infinity ) ;
doormen( { in: [ 1,5,Infinity,7 ] } , Infinity ) ;
doormen.not( { in: [ 1,5,7 ] } , -Infinity ) ;
doormen.not( { in: [ 1,5,7 ] } , NaN ) ;
doormen( { in: [ 1,5,NaN,7 ] } , NaN ) ;
 
doormen( { in: [ 1,true,5,7 ] } , true ) ;
doormen.not( { in: [ 1,5,7 ] } , true ) ;
doormen( { in: [ 1,false,5,7 ] } , false ) ;
doormen.not( { in: [ 1,5,7 ] } , false ) ;
 
doormen.not( { in: [ 1,5,7 ] } , "text" ) ;
doormen( { in: [ 1,"text",5,7 ] } , "text" ) ;
doormen( { in: [ "string", "text", "bob" ] } , "text" ) ;
doormen.not( { in: [ "string", "text", "bob" ] } , "bobby" ) ;
doormen( { in: [ "string", "text", "" ] } , "" ) ;
doormen.not( { in: [ "string", "text", "bob" ] } , "" ) ;

'notIn' filter should validate if the value is listed.

doormen( { notIn: [ 1,5,7 ] } , 10 ) ;
doormen.not( { notIn: [ 1,5,7 ] } , 5 ) ;
doormen.not( { notIn: [ 1,5,7 ] } , 1 ) ;
doormen( { notIn: [ 1,5,7 ] } , 0 ) ;
doormen( { notIn: [ 1,5,7 ] } , -10 ) ;
doormen( { notIn: [ 1,5,7 ] } , Infinity ) ;
doormen.not( { notIn: [ 1,5,Infinity,7 ] } , Infinity ) ;
doormen( { notIn: [ 1,5,7 ] } , -Infinity ) ;
doormen( { notIn: [ 1,5,7 ] } , NaN ) ;
doormen.not( { notIn: [ 1,5,NaN,7 ] } , NaN ) ;
 
doormen.not( { notIn: [ 1,true,5,7 ] } , true ) ;
doormen( { notIn: [ 1,5,7 ] } , true ) ;
doormen.not( { notIn: [ 1,false,5,7 ] } , false ) ;
doormen( { notIn: [ 1,5,7 ] } , false ) ;
 
doormen( { notIn: [ 1,5,7 ] } , "text" ) ;
doormen.not( { notIn: [ 1,"text",5,7 ] } , "text" ) ;
doormen.not( { notIn: [ "string", "text", "bob" ] } , "text" ) ;
doormen( { notIn: [ "string", "text", "bob" ] } , "bobby" ) ;
doormen.not( { notIn: [ "string", "text", "" ] } , "" ) ;
doormen( { notIn: [ "string", "text", "bob" ] } , "" ) ;

'in' filter containing object and arrays.

doormen( { in: [ 1 , { a: 2 } , 5 , 7 ] } , { a: 2 } ) ;
doormen.not( { in: [ 1 , { a: 2 } , 5 , 7 ] } , { a: 2 , b: 5 } ) ;
doormen.not( { in: [ 1 , { a: 2 } , { b: 5 } , 7 ] } , { a: 2 , b: 5 } ) ;
doormen( { in: [ 1 , { a: 2 } , { a: 2 , b: 5 } , { b: 5 } , 7 ] } , { a: 2 , b: 5 } ) ;
doormen( { in: [ 1 , [ 'a', 2 ] , 5 , 7 ] } , [ 'a' , 2 ] ) ;
doormen.not( { in: [ 1 , [ 'a', 2 , 3 ] , 5 , 7 ] } , [ 'a' , 2 ] ) ;

# Filters 'greaterThan' and aliases ('gt' and '>') filter should validate accordingly, non-number should throw.

doormen( { filter: { greaterThan: 3 } } , 10 ) ;
doormen( { filter: { greaterThan: 3 } } , 3.00001 ) ;
doormen.not( { filter: { greaterThan: 3 } } , 3 ) ;
doormen.not( { filter: { greaterThan: 3 } } , 1 ) ;
doormen.not( { filter: { greaterThan: 3 } } , 0 ) ;
doormen.not( { filter: { greaterThan: 3 } } , -10 ) ;
doormen( { filter: { greaterThan: 3 } } , Infinity ) ;
doormen.not( { filter: { greaterThan: Infinity } } , Infinity ) ;
doormen.not( { filter: { greaterThan: 3 } } , -Infinity ) ;
doormen.not( { filter: { greaterThan: 3 } } , NaN ) ;
doormen.not( { filter: { greaterThan: 3 } } , true ) ;
doormen.not( { filter: { greaterThan: 3 } } , false ) ;
doormen.not( { filter: { greaterThan: 3 } } , undefined ) ;
doormen.not( { filter: { greaterThan: 0 } } , undefined ) ;
doormen.not( { filter: { greaterThan: -3 } } , undefined ) ;
doormen.not( { filter: { greaterThan: 3 } } , '10' ) ;
 
doormen( { filter: { gt: 3 } } , 3.00001) ;
doormen.not( { filter: { gt: 3 } } , 3 ) ;
doormen( { filter: { '>': 3 } } , 3.00001 ) ;
doormen.not( { filter: { '>': 3 } } , 3 ) ;

'lesserThan' and aliases ('lt' and '<') filter should validate accordingly, non-number should throw.

doormen.not( { filter: { lesserThan: 3 } } , 10 ) ;
doormen( { filter: { lesserThan: 3 } } , 2.999 ) ;
doormen.not( { filter: { lesserThan: 3 } } , 3 ) ;
doormen( { filter: { lesserThan: 3 } } , 1 ) ;
doormen( { filter: { lesserThan: 3 } } , 0 ) ;
doormen( { filter: { lesserThan: 3 } } , -10 ) ;
doormen.not( { filter: { lesserThan: 3 } } , Infinity ) ;
doormen( { filter: { lesserThan: 3 } } , -Infinity ) ;
doormen.not( { filter: { lesserThan: -Infinity } } , -Infinity ) ;
doormen.not( { filter: { lesserThan: 3 } } , NaN ) ;
doormen.not( { filter: { lesserThan: 3 } } , true ) ;
doormen.not( { filter: { lesserThan: 3 } } , false ) ;
doormen.not( { filter: { lesserThan: 3 } } , '1' ) ;
 
doormen( { filter: { lt: 3 } } , 2.999 ) ;
doormen.not( { filter: { lt: 3 } } , 3 ) ;
doormen( { filter: { '<': 3 } } , 2.999 ) ;
doormen.not( { filter: { '<': 3 } } , 3 ) ;

# Children and recursivity 'of' should perform the check recursively for each children, using the same given schema for all of them..

var schema ;
 
schema = {
    of: { type: 'string' }
} ;
 
// Object 
doormen( schema , { b: 'text' } ) ;
doormen.not( schema , { a: 1 } ) ;
doormen.not( schema , { a: 1, b: 'text' } ) ;
doormen.not( schema , { a: 'text', b: 3 } ) ;
doormen( schema , { a: 'text', b: 'string' } ) ;
doormen.not( schema , { A: 'TEXT', b: 'text' , c: undefined } ) ;
 
// Array 
doormen( schema , [ 'text' ] ) ;
doormen( schema , [] ) ;
doormen( schema , [ 'text' , 'string' ] ) ;
doormen.not( schema , [ 'text' , 'string' , null ] ) ;
doormen.not( schema , [ 1 , 'text' , 'string' ] ) ;
doormen.not( schema , [ 'text' , 'string' , null ] ) ;
doormen.not( schema , [ true ] ) ;

when 'properties' is an array, it should check if the value has all listed properties, no extra properties are allowed.

var schema = {
    properties: [ 'a' , 'b' ]
} ;
 
doormen( schema , { a: 1, b: 'text' } ) ;
doormen( schema , { a: 'text', b: 3 } ) ;
doormen.not( schema , { A: 'TEXT', a: 1, b: 'text' , c: 5 } ) ;
doormen.not( schema , { b: 'text' } ) ;
doormen.not( schema , { a: 1 } ) ;

when 'properties' is an array and 'extraProperties' is set, it should allow non-listed extra-properties.

var schema = {
    properties: [ 'a' , 'b' ],
    extraProperties: true
} ;
 
doormen( schema , { a: 1, b: 'text' } ) ;
doormen( schema , { a: 'text', b: 3 } ) ;
doormen( schema , { A: 'TEXT', a: 1, b: 'text' , c: 5 } ) ;
doormen.not( schema , { b: 'text' } ) ;
doormen.not( schema , { a: 1 } ) ;

when 'properties' is an object, it should perform the check recursively for each listed child, no extra properties are allowed.

var schema = {
    properties: {
        a: { type: 'number' },
        b: { type: 'string' }
    }
} ;
 
doormen( schema , { a: 1, b: 'text' } ) ;
doormen.not( schema , { a: 'text', b: 3 } ) ;
doormen.not( schema , { A: 'TEXT', a: 1, b: 'text' , c: 5 } ) ;
doormen.not( schema , { b: 'text' } ) ;
doormen.not( schema , { a: 1 } ) ;

when 'properties' is an object and 'extraProperties' is set, it should allow extra-properties.

var schema = {
    properties: {
        a: { type: 'number' },
        b: { type: 'string' }
    },
    extraProperties: true
} ;
 
doormen( schema , { a: 1, b: 'text' } ) ;
doormen.not( schema , { a: 'text', b: 3 } ) ;
doormen( schema , { A: 'TEXT', a: 1, b: 'text' , c: 5 } ) ;
doormen.not( schema , { b: 'text' } ) ;
doormen.not( schema , { a: 1 } ) ;

'elements' should perform the check recursively for each children elements, using a specific schema for each one, extra-element are not allowed.

var schema = {
    elements: [
        { type: 'string' },
        { type: 'number' },
        { type: 'boolean' }
    ]
} ;
 
doormen( schema , [ 'text' , 3 , false ] ) ;
doormen.not( schema , [ 'text' , 3 , false , 'extra' , true ] ) ;
doormen.not( schema , [] ) ;
doormen.not( schema , [ 'text' , 3 ] ) ;
doormen.not( schema , [ true ] ) ;

when 'elements' is used in conjunction with 'extraElements', extra-elements are allowed.

var schema = {
    elements: [
        { type: 'string' },
        { type: 'number' },
        { type: 'boolean' }
    ],
    extraElements: true
} ;
 
doormen( schema , [ 'text' , 3 , false ] ) ;
doormen( schema , [ 'text' , 3 , false , 'extra' , true ] ) ;
doormen.not( schema , [] ) ;
doormen.not( schema , [ 'text' , 3 ] ) ;
doormen.not( schema , [ true ] ) ;

# Properties having 'when' when 'properties' is an object and one child's schema contains a 'when' properties, it should be deleted if the 'siblingVerify' condition is met for the 'sibling'.

var schema = {
    properties: {
        a: {
            type: 'number'
        },
        b: {
            type: 'string' ,
            when: {
                sibling: 'a',
                siblingVerify: { in: [ 1 ] },
                set: undefined
            }
        }
    }
} ;
 
doormen.equals(
    doormen( schema , { a: 0, b: 'text' } ) ,
    { a: 0, b: 'text' }
) ;
 
doormen.equals(
    doormen( schema , { a: 1, b: 'text' } ) ,
    { a: 1 }
) ;
 
doormen.not( schema , { a: 0 } ) ;
 
doormen.equals(
    doormen( schema , { a: 1 } ) ,
    { a: 1 }
) ;
 
 
var schema = {
    properties: {
        b: {
            type: 'string' ,
            when: {
                sibling: 'a',
                siblingVerify: { in: [ 1 ] },
                set: undefined
            }
        },
        a: {
            type: 'number'
        }
    }
} ;
 
doormen.equals(
    doormen( schema , { a: 0, b: 'text' } ) ,
    { a: 0, b: 'text' }
) ;
 
doormen.equals(
    doormen( schema , { a: 1, b: 'text' } ) ,
    { a: 1 }
) ;
 
doormen.not( schema , { a: 0 } ) ;
 
doormen.equals(
    doormen( schema , { a: 1 } ) ,
    { a: 1 }
) ;

'when' and 'clone'.

var schema = {
    properties: {
        a: {
            type: 'number'
        },
        b: {
            type: 'string' ,
            when: {
                sibling: 'a',
                verify: { type: 'unset' },
                clone: true
            }
        }
    }
} ;
 
doormen.equals(
    doormen( schema , { a: 2, b: '1' } ) ,
    { a: 2, b: '1' }
) ;
 
doormen.equals(
    doormen( schema , { a: 2, b: null } ) ,
    { a: 2, b: 2 }
) ;
 
doormen.equals(
    doormen( schema , { a: 2 } ) ,
    { a: 2, b: 2 }
) ;

complex dependencies tests.

var schema = {
    properties: {
        c: {
            type: 'string' ,
            when: {
                sibling: 'b',
                siblingVerify: { in: [ undefined , 'text' ] },
                set: undefined
            }
        },
        b: {
            type: 'string' ,
            when: {
                sibling: 'a',
                siblingVerify: { in: [ 1 ] },
                set: undefined
            }
        },
        a: {
            type: 'number'
        }
    }
} ;
 
doormen.equals(
    doormen( schema , { a: 0, b: 'text', c: 'toto' } ) ,
    { a: 0, b: 'text' }
) ;
 
doormen.equals(
    doormen( schema , { a: 0, b: 'text' } ) ,
    { a: 0, b: 'text' }
) ;
 
doormen.equals(
    doormen( schema , { a: 1, b: 'text' } ) ,
    { a: 1 }
) ;
 
doormen.equals(
    doormen( schema , { a: 1, b: undefined } ) ,
    { a: 1 }
) ;
 
doormen.not( schema , { a: 0, b: undefined } ) ;
doormen.not( schema , { a: 0 } ) ;
 
doormen.equals(
    doormen( schema , { a: 1 } ) ,
    { a: 1 }
) ;

when circular 'when' properties exists, it should throw.

var schema = {
    properties: {
        a: {
            type: 'number',
            when: {
                sibling: 'b',
                siblingVerify: { in: [ 'text' ] },
                set: undefined
            }
        },
        b: {
            type: 'string' ,
            when: {
                sibling: 'a',
                siblingVerify: { in: [ 1 ] },
                set: undefined
            }
        }
    }
} ;
 
// Circular 'when' throw 
doormen.not( schema , { a: 0, b: 'text' } ) ;
 
var schema = {
    properties: {
        a: {
            type: 'number',
            when: {
                sibling: 'a',
                siblingVerify: { in: [ 1 ] },
                set: undefined
            }
        }
    }
} ;
 
// Circular 'when' throw 
doormen.not( schema , { a: 0, b: 'text' } ) ;

# Mask Should mask data using a tier-level.

var schema = {
    properties: {
        a: {
            type: 'number' ,
            tier: 1
        } ,
        b: {
            type: 'boolean' ,
            tier: 3
        } ,
        c: {
            type: 'string' ,
            tier: 2
        }
    }
} ;
 
var data = {
    a: 1 ,
    b: true ,
    c: 'blah!'
} ;
 
doormen.equals(
    doormen.mask( schema , data , { tier: 0 } ) ,
    {} 
) ;
doormen.equals(
    doormen.mask( schema , data , { tier: 1 } ) ,
    { a: 1 } 
) ;
doormen.equals(
    doormen.mask( schema , data , { tier: 2 } ) ,
    { a: 1 , c: 'blah!' } 
) ;
doormen.equals(
    doormen.mask( schema , data , { tier: 3 } ) ,
    { a: 1 , b: true , c: 'blah!' } 
) ;
doormen.equals(
    doormen.mask( schema , data , { tier: 4 } ) ,
    { a: 1 , b: true , c: 'blah!' } 
) ;

Should mask nested data using a tier-level.

var schema = {
    properties: {
        a: {
            type: 'number' ,
            tier: 1
        } ,
        b: {
            type: 'boolean' ,
            tier: 3
        } ,
        c: {
            type: 'string' ,
            tier: 2
        } ,
        d: {
            type: 'strictObject' ,
            properties: {
                e: {
                    type: 'number' ,
                    tier: 1
                } ,
                f: {
                    type: 'boolean' ,
                    tier: 3
                } ,
                g: {
                    type: 'string' ,
                    tier: 2
                }
            }
        } ,
        d2: {
            type: 'strictObject' ,
            tier: 2 ,
            properties: {
                e: {
                    type: 'number' ,
                    tier: 1
                } ,
                f: {
                    type: 'boolean' ,
                    tier: 3
                } ,
                g: {
                    type: 'string' ,
                    tier: 2
                }
            }
        }
    }
} ;
 
var data = {
    a: 1 ,
    b: true ,
    c: 'blah!' ,
    d: {
        e: 7 ,
        f: false ,
        g: 'bob'
    } ,
    d2: {
        e: 7 ,
        f: false ,
        g: 'bob'
    }
} ;
 
doormen.equals(
    doormen.mask( schema , data , { tier: 1 } ) ,
    { a: 1 , d: { e: 7 } } 
) ;
doormen.equals(
    doormen.mask( schema , data , { tier: 2 } ) ,
    { a: 1 , c: 'blah!' , d: { e: 7 , g: 'bob' } , d2: { e: 7 , f: false , g: 'bob' } } 
) ;
doormen.equals(
    doormen.mask( schema , data , { tier: 3 } ) ,
    { a: 1 , b: true , c: 'blah!' , d: { e: 7 , f: false , g: 'bob' } , d2: { e: 7 , f: false , g: 'bob' } } 
) ;

Should mask data using tags.

var schema = {
    properties: {
        _id: { tags: [] } ,
        slug: { tags: [ 'internal' , 'meta' ] } ,
        access: { tags: [ 'internal' ] } ,
        title: { tags: [ 'meta' ] } ,
        post: { tags: [ 'content' ] }
    }
} ;
 
var data = {
    _id: '1978f09ac3e' ,
    slug: 'ten-things-about-nothing' ,
    access: 'public' ,
    title: '10 things you should know about nothing' ,
    post: 'blah blah blah blah'
} ;
 
doormen.equals(
    doormen.mask( schema , data , { tags: [ 'meta' ] } ) ,
    {
        _id: '1978f09ac3e' ,
        slug: 'ten-things-about-nothing' ,
        title: '10 things you should know about nothing'
    }
) ;
 
doormen.equals(
    doormen.mask( schema , data , { tags: [ 'internal' ] } ) ,
    {
        _id: '1978f09ac3e' ,
        slug: 'ten-things-about-nothing' ,
        access: 'public'
    }
) ;
 
doormen.equals(
    doormen.mask( schema , data , { tags: [ 'internal' , 'content' ] } ) ,
    {
        _id: '1978f09ac3e' ,
        slug: 'ten-things-about-nothing' ,
        access: 'public' ,
        post: 'blah blah blah blah'
    }
) ;
 
doormen.equals(
    doormen.mask( schema , data , { tags: [ 'internal' , 'meta' , 'content' ] } ) ,
    {
        _id: '1978f09ac3e' ,
        slug: 'ten-things-about-nothing' ,
        access: 'public' ,
        title: '10 things you should know about nothing' ,
        post: 'blah blah blah blah'
    }
) ;

Should mask nested data using tags.

var schema = {
    properties: {
        _id: {} ,
        slug: { tags: [ 'internal' , 'meta' ] } ,
        accesses: {
            of: {
                properties: {
                    userId: {} ,
                    accessLevel: { tags: [ 'internal' ] }
                }
            }
        } ,
        title: { tags: [ 'meta' ] } ,
        post: { tags: [ 'content' ] }
    }
} ;
 
var data = {
    _id: '1978f09ac3e' ,
    slug: 'ten-things-about-nothing' ,
    accesses: [
        {
            userId: 'bob' ,
            accessLevel: 2
        } ,
        {
            userId: 'bill' ,
            accessLevel: 3
        }
    ] ,
    title: '10 things you should know about nothing' ,
    post: 'blah blah blah blah'
} ;
 
doormen.equals(
    doormen.mask( schema , data , { tags: [ 'meta' ] } ) ,
    {
        _id: '1978f09ac3e' ,
        slug: 'ten-things-about-nothing' ,
        accesses: [ { userId: 'bob' }, { userId: 'bill' } ],
        title: '10 things you should know about nothing'
    }
) ;
 
doormen.equals(
    doormen.mask( schema , data , { tags: [ 'internal' ] } ) ,
    {
        _id: '1978f09ac3e' ,
        slug: 'ten-things-about-nothing' ,
        accesses: [
            { userId: 'bob', accessLevel: 2 },
            { userId: 'bill', accessLevel: 3 }
        ]
    }
) ;
 
doormen.equals(
    doormen.mask( schema , data , { tags: [ 'internal' , 'content' ] } ) ,
    {
        _id: '1978f09ac3e' ,
        slug: 'ten-things-about-nothing' ,
        accesses: [
            { userId: 'bob', accessLevel: 2 },
            { userId: 'bill', accessLevel: 3 }
        ] ,
        post: 'blah blah blah blah'
    }
) ;
 
doormen.equals(
    doormen.mask( schema , data , { tags: [ 'internal' , 'meta' , 'content' ] } ) ,
    {
        _id: '1978f09ac3e' ,
        slug: 'ten-things-about-nothing' ,
        accesses: [
            { userId: 'bob', accessLevel: 2 },
            { userId: 'bill', accessLevel: 3 }
        ] ,
        title: '10 things you should know about nothing' ,
        post: 'blah blah blah blah'
    }
) ;

# Numbers meta types should validate real accordingly.

doormen( { type: 'real' } , 0 ) ;
doormen( { type: 'real' } , 1 ) ;
doormen( { type: 'real' } , -1 ) ;
doormen( { type: 'real' } , 0.3 ) ;
doormen( { type: 'real' } , 18.36 ) ;
doormen.not( { type: 'real' } , 1/0 ) ;
doormen.not( { type: 'real' } , -1/0 ) ;
doormen.not( { type: 'real' } , Infinity ) ;
doormen.not( { type: 'real' } , -Infinity ) ;
doormen.not( { type: 'real' } , NaN ) ;
 
doormen.not( { type: 'real' } , undefined ) ;
doormen.not( { type: 'real' } , null ) ;
doormen.not( { type: 'real' } , false ) ;
doormen.not( { type: 'real' } , true ) ;
doormen.not( { type: 'real' } , '' ) ;
doormen.not( { type: 'real' } , 'text' ) ;
doormen.not( { type: 'real' } , {} ) ;
doormen.not( { type: 'real' } , [] ) ;

should validate integer accordingly.

doormen( { type: 'integer' } , 0 ) ;
doormen( { type: 'integer' } , 1 ) ;
doormen( { type: 'integer' } , 123456789 ) ;
doormen( { type: 'integer' } , -1 ) ;
doormen.not( { type: 'integer' } , 0.00001 ) ;
doormen.not( { type: 'integer' } , -0.00001 ) ;
doormen.not( { type: 'integer' } , 123456.00001 ) ;
doormen.not( { type: 'integer' } , 123456.99999 ) ;
doormen.not( { type: 'integer' } , 0.3 ) ;
doormen.not( { type: 'integer' } , 18.36 ) ;
doormen.not( { type: 'integer' } , 1/0 ) ;
doormen.not( { type: 'integer' } , Infinity ) ;
doormen.not( { type: 'integer' } , -Infinity ) ;
doormen.not( { type: 'integer' } , NaN ) ;
 
doormen.not( { type: 'integer' } , undefined ) ;
doormen.not( { type: 'integer' } , null ) ;
doormen.not( { type: 'integer' } , false ) ;
doormen.not( { type: 'integer' } , true ) ;
doormen.not( { type: 'integer' } , '' ) ;
doormen.not( { type: 'integer' } , 'text' ) ;
doormen.not( { type: 'integer' } , {} ) ;
doormen.not( { type: 'integer' } , [] ) ;

# Strings meta types should validate hex accordingly.

doormen( { type: 'hex' } , '1234' ) ;
doormen( { type: 'hex' } , '12af34' ) ;
doormen( { type: 'hex' } , '12AF34' ) ;
doormen.not( { type: 'hex' } , '12g34' ) ;

should validate ipv4 accordingly.

doormen( { type: 'ipv4' } , '127.0.0.1' ) ;
doormen( { type: 'ipv4' } , '127.000.00.001' ) ;
doormen.not( { type: 'ipv4' } , '127.0000.00.001' ) ;
doormen.not( { type: 'ipv4' } , '0127.000.00.001' ) ;
doormen.not( { type: 'ipv4' } , '127.0.0.0001' ) ;
doormen.not( { type: 'ipv4' } , '127.0.0.' ) ;
doormen.not( { type: 'ipv4' } , '127.0.0.256' ) ;
doormen.not( { type: 'ipv4' } , '127.0.0.1.' ) ;
doormen.not( { type: 'ipv4' } , '.127.0.0.1' ) ;
doormen.not( { type: 'ipv4' } , '.127.0.0.' ) ;

should validate ipv6 accordingly.

doormen( { type: 'ipv6' } , '2001:0db8:0000:0000:0000:ff00:0042:8329' ) ;
doormen.not( { type: 'ipv6' } , ':2001:0db8:0000:0000:0000:ff00:0042:8329' ) ;
doormen.not( { type: 'ipv6' } , 'abcd:2001:0db8:0000:0000:0000:ff00:0042:8329' ) ;
doormen.not( { type: 'ipv6' } , '2001:0db8:0000:0000:0000:ff00:0042:8329:' ) ;
doormen.not( { type: 'ipv6' } , '2001:0000:0000:0000:ff00:0042:8329:' ) ;
doormen.not( { type: 'ipv6' } , ':2001:0000:0000:0000:ff00:0042:8329' ) ;
doormen( { type: 'ipv6' } , '2001:db8:0:0:0:ff00:0042:8329' ) ;
doormen( { type: 'ipv6' } , '2001:db8::ff00:0042:8329' ) ;
doormen.not( { type: 'ipv6' } , '2001:db8:::0042:8329' ) ;
doormen.not( { type: 'ipv6' } , '2001:db8::ff00::0042:8329' ) ;
doormen.not( { type: 'ipv6' } , '2001::ff00::0042:8329' ) ;
doormen( { type: 'ipv6' } , '::1' ) ;
doormen( { type: 'ipv6' } , '1::' ) ;

should validate ip accordingly.

doormen( { type: 'ip' } , '127.0.0.1' ) ;
doormen( { type: 'ip' } , '127.000.00.001' ) ;
doormen.not( { type: 'ip' } , '127.0000.00.001' ) ;
doormen.not( { type: 'ip' } , '0127.000.00.001' ) ;
doormen.not( { type: 'ip' } , '127.0.0.0001' ) ;
doormen.not( { type: 'ip' } , '127.0.0.' ) ;
doormen.not( { type: 'ip' } , '127.0.0.256' ) ;
doormen.not( { type: 'ip' } , '127.0.0.1.' ) ;
doormen.not( { type: 'ip' } , '.127.0.0.1' ) ;
doormen.not( { type: 'ip' } , '.127.0.0.' ) ;
 
doormen( { type: 'ip' } , '2001:0db8:0000:0000:0000:ff00:0042:8329' ) ;
doormen.not( { type: 'ip' } , ':2001:0db8:0000:0000:0000:ff00:0042:8329' ) ;
doormen.not( { type: 'ip' } , 'abcd:2001:0db8:0000:0000:0000:ff00:0042:8329' ) ;
doormen.not( { type: 'ip' } , '2001:0db8:0000:0000:0000:ff00:0042:8329:' ) ;
doormen.not( { type: 'ip' } , '2001:0000:0000:0000:ff00:0042:8329:' ) ;
doormen.not( { type: 'ip' } , ':2001:0000:0000:0000:ff00:0042:8329' ) ;
doormen( { type: 'ip' } , '2001:db8:0:0:0:ff00:0042:8329' ) ;
doormen( { type: 'ip' } , '2001:db8::ff00:0042:8329' ) ;
doormen.not( { type: 'ip' } , '2001:db8:::0042:8329' ) ;
doormen.not( { type: 'ip' } , '2001:db8::ff00::0042:8329' ) ;
doormen.not( { type: 'ip' } , '2001::ff00::0042:8329' ) ;
doormen( { type: 'ip' } , '::1' ) ;
doormen( { type: 'ip' } , '1::' ) ;

should validate url accordingly.

doormen( { type: 'url' } , 'http://google.com' ) ;
doormen( { type: 'url' } , 'http://google.com/' ) ;
doormen( { type: 'url' } , 'https://stackoverflow.com/questions/1303872/url-validation-using-javascript' ) ;
doormen( { type: 'url' } , 'http://regexlib.com/DisplayPatterns.aspx?cattabindex=1&categoryId=2' ) ;
doormen( { type: 'url' } , 'https://uk.reuters.com/article/2013/02/25/rosneft-tender-idUKL6N0BPJZC20130225' ) ;
doormen( { type: 'url