Not Preposterously Macho
Share your code. npm Orgs help your team discover, share, and reuse code. Create a free org »

doormen

0.7.21 • Public • Published

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

WARNING: MongoDB module not found, the end of the test is skipped. WARNING: MongoDB module not found, the end of the test is skipped.

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 ) ;

Buffers.

var buf , buf2 , i ;
buf = Buffer.allocUnsafe( 80 ) ;
buf2 = Buffer.allocUnsafe( 80 ) ;
for ( i = 0 ; i < 80 ; i ++ ) { buf[ i ] = i ; }
buf.copy( buf2 ) ;
doormen.equals( buf , buf2 ) ;
buf2[ 4 ] = 117 ;
doormen.not.equals( buf , buf2 ) ;

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(