js-schema
Advanced tools
Comparing version 0.4.1 to 0.5.0
var schema = require('../schema') | ||
, EqualitySchema = require('../patterns/equality') | ||
, anything = require('../patterns/anything') | ||
, _ = require('underscore') | ||
, RandExp = require('randexp') | ||
@@ -56,2 +58,19 @@ var ArraySchema = module.exports = function(itemSchema, max, min) { | ||
leafs : function() { | ||
var item_leafs = this.itemSchema.leafs ? this.itemSchema.leafs() | ||
: { certain : [ this.itemSchema ], uncertain : [] } | ||
if (this.min > 0) return item_leafs | ||
return { certain : [] | ||
, uncertain : _.union(item_leafs.certain, item_leafs.uncertain) | ||
} | ||
}, | ||
getId : function() { | ||
if (!this.id) this.id = new RandExp(/^[a-zA-Z]{4,6}$/).gen() | ||
return this.id | ||
}, | ||
toJSON : function() { | ||
@@ -64,2 +83,4 @@ var schema = { type : 'array' } | ||
if (this.id) schema.id = this.id | ||
return schema | ||
@@ -84,3 +105,4 @@ } | ||
if (args.length === 2) args[2] = args[1] | ||
return new ArraySchema(schema(args[0]), args[1], args[2]) | ||
return new ArraySchema(schema.fromJS(args[0]), args[1], args[2]) | ||
} | ||
@@ -87,0 +109,0 @@ |
var schema = require('../schema') | ||
var BooleanSchema = function() { | ||
return new schema(this) | ||
} | ||
BooleanSchema.prototype = { | ||
var booleanSchema = module.exports = new schema({ | ||
compile : function() { | ||
@@ -19,6 +15,4 @@ return { expression : 'Object(instance) instanceof Boolean' } | ||
} | ||
} | ||
}) | ||
var booleanSchema = new BooleanSchema() | ||
schema.fromJSON.def(function(sch) { | ||
@@ -25,0 +19,0 @@ if (!sch || sch.type !== 'boolean') return |
var schema = require('../schema') | ||
var NumberSchema = module.exports = function(minimum, exclusiveMinimum, maximum, exclusiveMaximum, divisibleBy) { | ||
this.minimum = minimum | ||
this.minimum = minimum != null ? minimum : -Infinity | ||
this.exclusiveMinimum = exclusiveMinimum | ||
this.maximum = maximum | ||
this.maximum = minimum != null ? maximum : Infinity | ||
this.exclusiveMaximum = exclusiveMaximum | ||
this.divisibleBy = divisibleBy | ||
this.divisibleBy = divisibleBy || 0 | ||
@@ -14,21 +14,2 @@ return new schema(this) | ||
NumberSchema.prototype = { | ||
compile : function() { | ||
var references = [this.minimum, this.maximum, this.divisibleBy] | ||
, checks = ['Object(instance) instanceof Number'] | ||
if (this.minimum !== undefined && this.minimum !== -Infinity) { | ||
checks.push('instance ' + (this.exclusiveMinimum ? '>' : '>=') + ' {0}') | ||
} | ||
if (this.maximum !== undefined && this.maximum !== Infinity) { | ||
checks.push('instance ' + (this.exclusiveMaximum ? '<' : '<=') + ' {1}') | ||
} | ||
if (this.divisibleBy !== undefined && this.divisibleBy !== 0) { | ||
checks.push('instance % {2} === 0') | ||
} | ||
return { references : references, expression : checks.join(' && ') } | ||
}, | ||
min : function(minimum) { | ||
@@ -49,7 +30,26 @@ return new NumberSchema(minimum, false, this.maximum, this.exclusiveMaximum, this.divisibleBy) | ||
}, | ||
step : function(divisibleBy) { | ||
return new NumberSchema(this.minimum, this.exclusiveMinimum, this.maximum, this.exclusiveMaximum, divisibleBy) | ||
}, | ||
compile : function() { | ||
var references = [this.minimum, this.maximum, this.divisibleBy] | ||
, checks = ['Object(instance) instanceof Number'] | ||
if (this.minimum !== -Infinity) { | ||
checks.push('instance ' + (this.exclusiveMinimum ? '>' : '>=') + ' {0}') | ||
} | ||
if (this.maximum !== Infinity) { | ||
checks.push('instance ' + (this.exclusiveMaximum ? '<' : '<=') + ' {1}') | ||
} | ||
if (this.divisibleBy !== 0) { | ||
checks.push('instance % {2} === 0') | ||
} | ||
return { references : references, expression : checks.join(' && ') } | ||
}, | ||
generate : function() { | ||
@@ -61,16 +61,19 @@ var length, random | ||
// If there's no decalred maximum or minimum then assigning a reasonable value | ||
if (min == null || min === -Infinity) { | ||
if (max == null || max === Infinity) { | ||
min = 0 | ||
max = 10 | ||
} else { | ||
min = max > 0 ? 0 : max*2 | ||
// If there's no declared maximum or minimum then assigning a reasonable value | ||
if (min === -Infinity || max === Infinity) { | ||
length = 10 | ||
while (Math.random() < 0.5) length = length * 10 | ||
if (min === -Infinity && max === Infinity) { | ||
min = length / -2 | ||
max = length / +2 | ||
} else if (min === -Infinity) { | ||
min = max - length | ||
} else if (max === Infinity) { | ||
max = min + length | ||
} | ||
} else if (max == null || max === Infinity) { | ||
max = this.min < 0 ? 0 : this.min*2 | ||
} | ||
// Choosing random number from a continuous set | ||
if (step == null || step === 0) { | ||
if (step === 0) { | ||
length = max - min | ||
@@ -97,8 +100,11 @@ do { | ||
}, | ||
toJSON : function() { | ||
var integer = this.divisibleBy !== 0 && this.divisibleBy === Math.floor(this.divisibleBy) | ||
var schema = { type : integer ? 'integer' : 'number' } | ||
var step = this.divisibleBy | ||
, integer = ( step !== 0 && step === Math.floor(step) ) | ||
, schema = { type : integer ? 'integer' : 'number' } | ||
if (this.minimum !== undefined) { | ||
if (step !== 0 && step !== 1) schema.divisibleBy = step | ||
if (this.minimum !== -Infinity) { | ||
schema.minimum = this.minimum | ||
@@ -108,3 +114,3 @@ if (this.exclusiveMinimum === true) schema.exclusiveMinimum = true | ||
if (this.maximum !== undefined) { | ||
if (this.maximum !== Infinity) { | ||
schema.maximum = this.maximum | ||
@@ -114,5 +120,2 @@ if (this.exclusiveMaximum === true) schema.exclusiveMaximum = true | ||
var step = this.divisibleBy | ||
if (step !== undefined && step !== 0 && step !== 1) schema.divisibleBy = step | ||
return schema | ||
@@ -119,0 +122,0 @@ } |
var native_functions = [Boolean, Number, String, Object, Array, Function, Date] | ||
var instance_regexp = RegExp('(\\W|^)instance(\\W|$)', 'g') | ||
@@ -6,2 +7,8 @@ var c = 0 | ||
var replace = function(assembly, regexp, target) { | ||
var replace = function(str) { return str.replace(regexp, target) } | ||
assembly.expression = replace(assembly.expression) | ||
assembly.subroutines = assembly.subroutines.map(replace) | ||
} | ||
var linker = { | ||
@@ -27,3 +34,2 @@ resolve_references : function(assembly) { | ||
resolved = function(match, validate, call, param) { | ||
var instance_regexp = RegExp('(\\W|^)instance(\\W|$)', 'g') | ||
var expr = referenced_assembly.expression.replace(instance_regexp, '$1' + param + '$2') | ||
@@ -60,11 +66,17 @@ return '(' + expr + ')' | ||
// replace strings similar to "{1}.validate(parameter)", where the validate call is optional | ||
var format = function(str) { | ||
return str.replace(RegExp('\\{' + id + '\\}((\\.validate)?\\(([^)]*)\\))?', 'g'), resolved) | ||
} | ||
assembly.expression = format(assembly.expression) | ||
assembly.subroutines = assembly.subroutines.map(format) | ||
replace(assembly, RegExp('\\{' + id + '\\}((\\.validate)?\\(([^)]*)\\))?', 'g'), resolved) | ||
} | ||
}, | ||
link : function(assembly) { | ||
// An assembly is a compiled version of a schema. | ||
// Assembly.schema = schema({ | ||
// references : Array.of(Function), | ||
// subroutines : Array.of(String), | ||
// expression : String, | ||
// '?fn' : String | ||
// }) | ||
// | ||
// If self is given, it means that references to that schema can be replaced with inline | ||
// self references, because the schema is sealed, so its code will not be inlined in other schemas | ||
link : function(assembly, self) { | ||
assembly.subroutines = assembly.subroutines || [] | ||
@@ -78,2 +90,3 @@ assembly.references = assembly.references || [] | ||
assembly.expression = name + '(instance)' | ||
delete assembly.fn | ||
} | ||
@@ -83,6 +96,17 @@ | ||
// If there's self given, finding and inlining references. | ||
if (self) { | ||
for (var key in assembly.references) { | ||
if (assembly.references[key] !== self) continue | ||
delete assembly.references[key] | ||
replace(assembly, RegExp(key + '\\(', 'g'), 'self(') | ||
} | ||
} | ||
var closure_arg_names = Object.keys(assembly.references) | ||
var closure_args = closure_arg_names.map(function(key){ return assembly.references[key] }) | ||
var closure_body = assembly.subroutines.join('\n') + '\n' | ||
+ 'return function(instance){ return ' + assembly.expression + '; }' | ||
closure_body += assembly.fn ? 'var self = ' + name + ';\n' | ||
: 'function self(instance){ return ' + assembly.expression + '; }\n' | ||
closure_body += 'return self;' | ||
var closure = Function.apply(null, closure_arg_names.concat(closure_body)) | ||
@@ -89,0 +113,0 @@ var validate = closure.apply(null, closure_args) |
var schema = require('../schema') | ||
, nothing = require('./nothing') | ||
var AnythingSchema = function() { | ||
return new schema(this) | ||
} | ||
AnythingSchema.prototype = { | ||
var anything = module.exports = new schema({ | ||
compile : function() { | ||
@@ -13,3 +10,3 @@ return { expression : 'instance != null' } | ||
generate : function() { | ||
var type = [Boolean, Number, String, Array, Object][Math.floor(Math.random()*5)] | ||
var type = [nothing, Boolean, Number, String, Array, Object][Math.floor(Math.random()*6)] | ||
return type.schema.generate() | ||
@@ -21,12 +18,10 @@ }, | ||
} | ||
} | ||
}) | ||
var anythingSchema = module.exports = new AnythingSchema() | ||
schema.fromJS.def(function(sch) { | ||
if (sch === undefined) return anythingSchema | ||
if (sch === undefined) return anything | ||
}) | ||
schema.fromJSON.def(function(sch) { | ||
if (sch.type === 'any') return anythingSchema | ||
if (sch.type === 'any') return anything | ||
}) |
var schema = require('../schema') | ||
var NothingSchema = function() { | ||
return new schema(this) | ||
} | ||
NothingSchema.prototype = { | ||
var nothing = module.exports = new schema({ | ||
compile : function() { | ||
@@ -13,18 +9,16 @@ return { expression : 'instance == null' } | ||
generate : function() { | ||
return null | ||
return Math.random() < 0.5 ? null : undefined | ||
}, | ||
toJSON : function() { | ||
return { disallow : 'any' } | ||
return { type : 'null' } | ||
} | ||
} | ||
}) | ||
var nothingSchema = module.exports = new NothingSchema() | ||
schema.fromJS.def(function(sch) { | ||
if (sch === null) return nothingSchema | ||
if (sch === null) return nothing | ||
}) | ||
schema.fromJSON.def(function(sch) { | ||
if (sch.disallow === 'any') return nothingSchema | ||
if (sch.type === 'null') return nothing | ||
}) |
@@ -6,2 +6,3 @@ var schema = require('../schema') | ||
, nothing = require('./nothing') | ||
, _ = require('underscore') | ||
@@ -46,3 +47,3 @@ var ObjectSchema = module.exports = function(properties, other) { | ||
if (!this.regexpProps.length && this.other !== anything) { | ||
if (!this.regexpProps.length && this.other === anything) { | ||
return { references : references, expression : checks.join(' && ') } | ||
@@ -123,2 +124,29 @@ } | ||
leafs : function() { | ||
var certain = [], uncertain = [] | ||
this.properties.forEach(function(property) { | ||
var property_leafs = property.value.leafs ? property.value.leafs() | ||
: { certain : [ property.value ], uncertain : [] } | ||
if (property.min > 0) { | ||
certain.push(property_leafs.certain) | ||
uncertain.push(property_leafs.uncertain) | ||
} else { | ||
uncertain.push(property_leafs.uncertain) | ||
uncertain.push(property_leafs.certain) | ||
} | ||
}) | ||
return { certain : _.union.apply(null, certain) | ||
, uncertain : _.union.apply(null, uncertain.concat([this.other])) | ||
} | ||
}, | ||
getId : function() { | ||
if (!this.id) this.id = new RandExp(/^[a-zA-Z]{4,6}$/).gen() | ||
return this.id | ||
}, | ||
toJSON : function() { | ||
@@ -146,2 +174,4 @@ var i, property, regexp, json = { type : 'object' } | ||
if (this.id) json.id = this.id | ||
return json | ||
@@ -199,3 +229,3 @@ } | ||
for (var key in object) { | ||
value = schema(object[key]) | ||
value = schema.fromJS(object[key]) | ||
@@ -241,3 +271,3 @@ if (key === '*') { | ||
if (sch.additionalProperties !== undefined) { | ||
other = sch.additionalProperties === false ? schema(null) : schema.fromJSON(sch.additionalProperties) | ||
other = sch.additionalProperties === false ? nothing : schema.fromJSON(sch.additionalProperties) | ||
} | ||
@@ -244,0 +274,0 @@ |
var schema = require('../schema') | ||
, EqualitySchema = require('../patterns/equality') | ||
, _ = require('underscore') | ||
, RandExp = require('randexp') | ||
@@ -24,2 +26,25 @@ var OrSchema = module.exports = function(schemas) { | ||
leafs : function() { | ||
// Certain and uncertain leafs of subschemas | ||
var subschema_leafs = this.schemas.map(function(sub) { | ||
return sub.leafs ? sub.leafs() | ||
: { certain : [ sub ], uncertain : [] } | ||
}) | ||
subschema_leafs.certain = subschema_leafs.map(function(leafs){ return leafs.certain }) | ||
subschema_leafs.uncertain = subschema_leafs.map(function(leafs){ return leafs.uncertain }) | ||
// If some leaf appears in all subschemas as certain then it is certain. Otherwise uncertain. | ||
var all = _.union.apply(null, subschema_leafs.certain.concat(subschema_leafs.uncertain)) | ||
var certain = _.intersection.apply(null, subschema_leafs.certain) | ||
var uncertain = _.difference(all, certain) | ||
return { certain : certain, uncertain : uncertain } | ||
}, | ||
getId : function() { | ||
if (!this.id) this.id = new RandExp(/^[a-zA-Z]{4,6}$/).gen() | ||
return this.id | ||
}, | ||
toJSON : function() { | ||
@@ -37,6 +62,10 @@ var jsons = this.schemas.map(schema.toJSON) | ||
return { 'type' : jsons.map(function(json) { | ||
var json = { 'type' : jsons.map(function(json) { | ||
var simpleType = typeof json.type === 'string' && Object.keys(json).length === 1 | ||
return simpleType ? json.type : json | ||
})} | ||
if (this.id) schema.id = this.id | ||
return json | ||
} | ||
@@ -47,3 +76,5 @@ } | ||
schema.fromJS.def(function(schemas) { | ||
if (schemas instanceof Array) return new OrSchema(schemas.map(schema.fromJS)) | ||
if (schemas instanceof Array) return new OrSchema(schemas.map(function(sch) { | ||
return sch === undefined ? schema.self : schema.fromJS(sch) | ||
})) | ||
}) | ||
@@ -50,0 +81,0 @@ |
var schema = require('../schema') | ||
, RandExp = require('randexp') | ||
var defaultRandExp = new RandExp(/[a-zA-Z0-9]*/) | ||
var defaultRandExp = new RandExp(/^[a-zA-Z0-9]{1,10}$/) | ||
@@ -6,0 +6,0 @@ var RegexpSchema = module.exports = function(regexp) { |
var def = require('def.js') | ||
, linker = require('./linker') | ||
, global = (function(){ return this }()) | ||
var schema = module.exports = function(schemaObject) { | ||
// When called as simple function, forward everything to fromJS | ||
if (this === global) return schema.fromJS.apply(global, arguments) | ||
// When called as simple function, forward everything to fromJS, and then seal the resulting schema | ||
if (!(this instanceof schema)) return schema.fromJS.apply(null, arguments).seal() | ||
// When called with new, transform the parameter schema object to a compiled schema function | ||
if (!schemaObject.compile && !schemaObject.validate) { | ||
throw new Error('Schemas must have either compile or validate function.') | ||
throw new Error('Schema definition objects must have either compile or validate function.') | ||
} | ||
@@ -17,4 +16,29 @@ | ||
for (var property in schemaObject) { | ||
if (schemaObject[property] instanceof Function) validate[property] = schemaObject[property].bind(schemaObject) | ||
validate.seal = function() { | ||
if (schemaObject.sealed) return validate | ||
schemaObject.sealed = true | ||
if (schemaObject.leafs) { | ||
var leafs = schemaObject.leafs() | ||
if (leafs.certain.indexOf(schema.self) !== -1) { | ||
throw new Error('There\'s no object that satisfies this schema because of necessary recursion.') | ||
} | ||
if (leafs.uncertain.indexOf(schema.self) !== -1) { | ||
// If the validate function is compiled then recompiling it with self inlining | ||
if (validate.assembly) { | ||
var newValidate = linker.link(schemaObject.compile(), schema.self) | ||
for (var key in validate) newValidate[key] = validate[key] | ||
newValidate.schema = newValidate | ||
validate = newValidate | ||
} | ||
// schema.self needs to be pointed to this schema, and then it must be reset | ||
schema.self.set(validate) | ||
schema.self = new SelfSchema() | ||
} | ||
} | ||
delete validate.assembly | ||
return validate | ||
} | ||
@@ -24,5 +48,37 @@ | ||
for (var key in schemaObject) { | ||
if (schemaObject[key] instanceof Function && !schemaObject[key].schema) { | ||
validate[key] = schemaObject[key].bind(schemaObject) | ||
} | ||
} | ||
return validate | ||
} | ||
var SelfSchema = function() { | ||
return new schema(this) | ||
} | ||
SelfSchema.prototype = { | ||
set : function(target) { | ||
if (!this.target) this.target = target | ||
}, | ||
validate : function(instance) { | ||
return this.target(instance) | ||
}, | ||
generate : function() { | ||
return this.target.generate() | ||
}, | ||
toJSON : function() { | ||
return { '$ref' : this.target.getId() } | ||
} | ||
} | ||
schema.self = new SelfSchema() | ||
schema.fromJS = def() | ||
@@ -29,0 +85,0 @@ |
@@ -12,3 +12,3 @@ { | ||
], | ||
"version": "0.4.1", | ||
"version": "0.5.0", | ||
"homepage": "https://github.com/molnarg/js-schema", | ||
@@ -21,3 +21,4 @@ "repository": { | ||
"def.js" : "*", | ||
"randexp" : "*" | ||
"randexp" : "*", | ||
"underscore" : "*" | ||
}, | ||
@@ -24,0 +25,0 @@ "devDependencies": {}, |
141
README.md
@@ -8,4 +8,4 @@ js-schema | ||
A simple example | ||
================ | ||
Features | ||
======== | ||
@@ -18,16 +18,16 @@ Defining a schema: | ||
var Duck = schema({ // A duck | ||
quack : Function, // - can quack | ||
feed : Function, // - can be fed | ||
age : Number.min(0).max(5), // - is 0 to 5 years old | ||
color : ['yellow', 'brown'] // - has either yellow or brown color | ||
var Duck = schema({ // A duck | ||
swim : Function, // - can swim | ||
quack : Function, // - can quack | ||
age : Number.min(0).max(5), // - is 0 to 5 years old | ||
color : ['yellow', 'brown'] // - has either yellow or brown color | ||
}); | ||
``` | ||
The resulting function can be used for checking or validating objects: | ||
The resulting function (`Duck`) can be used for checking or validating objects: | ||
```javascript | ||
var myDuck = { quack : function() {}, feed : function() {}, age : 2, color : 'yellow' }; | ||
var myCat = { purr : function() {}, feed : function() {}, age : 3, color : 'black' }; | ||
var animals = [myDuck, myCat, {}, /*...*/ ]; | ||
var myDuck = { quack : function() {}, swim : function() {}, age : 2, color : 'yellow' }, | ||
myCat = { purr : function() {}, walk : function() {}, age : 3, color : 'black' }, | ||
animals = [ myDuck, myCat, {}, /*...*/ ]; | ||
@@ -37,15 +37,31 @@ console.log( Duck(myDuck) ); // true | ||
console.log( animals.filter(Duck) ); // every Duck-like object | ||
console.log( animals.filter(schema({ feed : Function })) ); // every animal that can be fed | ||
var ducks = animals.filter( Duck ); // every Duck-like animal | ||
var walking = animals.filter( schema({ walk : Function }) ); // every animal that can walk | ||
``` | ||
It is also possible to generate random objects for testing purposes: | ||
js-schema can generate random objects for a given schema for testing purposes: | ||
```javascript | ||
console.log( Duck.generate() ); | ||
var duck = schema.generate( Duck ); | ||
var testcases = schema.generate( Array.of(5, Duck) ); | ||
``` | ||
var testcases = Array.of(5, Duck).generate(); | ||
console.log(testcases); | ||
It is also possible to define self-referencing data structures: | ||
```javascript | ||
var Tree = schema({ left : [ Number, Tree ], right : [ Number, Tree ] }); | ||
var tree = schema.generate( Tree ); | ||
``` | ||
The schema description is _compiled_ into validation function for achieving maximal performance. | ||
```javascript | ||
console.log( Tree.toString() ); | ||
// function self(instance) { | ||
// return instance != null && | ||
// ((Object(instance["left" ]) instanceof Number) || self(instance["left" ])) && | ||
// ((Object(instance["right"]) instanceof Number) || self(instance["right"])); | ||
// } | ||
``` | ||
Usage | ||
@@ -79,3 +95,3 @@ ===== | ||
There are 9 basic rules used for describing schemas: | ||
There are 10 basic rules used for describing schemas: | ||
@@ -90,5 +106,7 @@ 1. `Class` (where `Class` is a function, and has a function type property called `schema`) | ||
`pattern2` matches `x.b`, etc. For details see the object pattern subsection. | ||
7. `undefined` matches `x` if `x` _is not_ `null` or `undefined`. | ||
7. `primitive` (where `primitive` is boolean, number, or string) matches `x` if `primitive === x`. | ||
8. `null` matches `x` if `x` _is_ `null` or `undefined`. | ||
9. `primitive` (where `primitive` is boolean, number, or string) matches `x` if `primitive === x`. | ||
9. `undefined` matches anything. | ||
10. `schema.self` references the schema returned by the last use of the `schema` function. | ||
For details see the self-referencing subsection. | ||
@@ -110,8 +128,10 @@ The order is important. When calling `schema(pattern)`, the rules are examined one by one, | ||
// (2) 'instanceof' pattern | ||
// (9) 'primitive' pattern | ||
// (7) 'primitive' pattern | ||
// (4) 'deep equality' pattern | ||
b : Number, // (1) 'class schema' pattern | ||
c : /The meaning of life is \d+/, // (3) regexp pattern | ||
d : undefined, // (7) 'anything' pattern | ||
e : null // (8) 'nothing' pattern | ||
d : undefined, // (9) 'anything' pattern | ||
e : [null, schema.self] // (5) 'or' pattern | ||
// (8) 'nothing' pattern | ||
// (10) 'self' pattern | ||
}); | ||
@@ -127,4 +147,4 @@ | ||
* `x.c` is a string that matches the /The meaning of life is \d+/ regexp | ||
* `x` does have a property called `d` | ||
* `x` doesn't have a property called `e`, or it does but it is `null` or `undefined` | ||
* `x` doesn't have a property called `e`, or it does but it is `null` or `undefined`, | ||
or it is an object that matches this schema | ||
@@ -163,24 +183,25 @@ ### The object pattern ### | ||
Extensions | ||
========== | ||
### Self-referencing ### | ||
### Objects ### | ||
The easiest way to do self-referencing is using `schema.self`. However, to support a more | ||
intuitive notation (as seen in the Tree example above) there is an other way to reference | ||
the schema that is being described. When executing this: | ||
`Object.reference(object)` matches `x` if `x === object`. | ||
```javascript | ||
var Tree = schema({ left : [ Number, Tree ], right : [ Number, Tree ] }); | ||
``` | ||
`Object.like(object)` matches `x` if `x` deep equals `object`. | ||
js-schema sees in fact `{ left : [ Number, undefined ], right : [ Number, undefined ] }` as first | ||
parameter, since the value of the `Tree` variable is undefined when the schema function is | ||
called. Consider the meaning of `[ Number, undefined ]` according to the rules described above: | ||
'this property must be either Number, or anything else'. It doesn't make much sense to include | ||
'anything else' in an 'or' relation. If js-schema sees `undefined` in an or relation, it assumes | ||
that this is in fact a self-reference. | ||
### Functions ### | ||
Use this feature carefully, because it may easily lead to bugs. Only use it when the return value | ||
of the schema function is assigned to a newly defined variable. | ||
`Function.reference(func)` matches `x` if `x === func`. | ||
Extensions | ||
========== | ||
### Arrays ### | ||
The `Array.like(array)` matches `x` if `x instanceof Array` and it deep equals `array`. | ||
The `Array.of` method has three signatures: | ||
- `Array.of(pattern)` matches `x` if `x instanceof Array` and `pattern` matches every element of `x`. | ||
- `Array.of(length, pattern)` additionally checks the length of the instance and returns true only if it equals to `length`. | ||
- `Array.of(minLength, maxLength, pattern)` is similar, but checks if the length is in the given interval. | ||
### Numbers ### | ||
@@ -205,2 +226,21 @@ | ||
### Arrays ### | ||
The `Array.like(array)` matches `x` if `x instanceof Array` and it deep equals `array`. | ||
The `Array.of` method has three signatures: | ||
- `Array.of(pattern)` matches `x` if `x instanceof Array` and `pattern` matches every element of `x`. | ||
- `Array.of(length, pattern)` additionally checks the length of the instance and returns true only if it equals to `length`. | ||
- `Array.of(minLength, maxLength, pattern)` is similar, but checks if the length is in the given interval. | ||
### Objects ### | ||
`Object.reference(object)` matches `x` if `x === object`. | ||
`Object.like(object)` matches `x` if `x` deep equals `object`. | ||
### Functions ### | ||
`Function.reference(func)` matches `x` if `x === func`. | ||
Future plans | ||
@@ -213,20 +253,9 @@ ============ | ||
Error reporting. js-schema should be able to report validation errors in a meaningful way instead | ||
of just stopping and returning false. Error handling shouldn't be the default mode of operation | ||
because it comes at a significant performance cost and it is not needed in all usecases. | ||
Using the random object generation, it should be possible to build a QucikCheck-like testing | ||
framework, which could be used to generate testcases for js-schema (yes, I like resursion). | ||
Defining and validating resursive data structures: | ||
```javascript | ||
// Defining the data structure: | ||
var Tree = schema({ left : [Tree, Number], right : [Tree, Number] }); | ||
// The schema function gets this as argument: | ||
// { left : [undefined, Number], right : [undefined, Number] } | ||
// Since providing 'undefined' as part of an 'or' patterns doesn't make sense, | ||
// it must be a self-reference. Self-reference usually occur as part of 'or' patterns. | ||
// validation | ||
console.log( Tree({left : {left : 1, right : 2 }, right : 9}) ); // true | ||
console.log( Tree({left : {left : 1, right : null}, right : 9}) ); // false | ||
``` | ||
Contributing | ||
@@ -233,0 +262,0 @@ ============ |
Sorry, the diff of this file is not supported yet
Floating dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
46991
23
871
269
3
4
+ Addedunderscore@*
+ Addedunderscore@1.13.6(transitive)