Merge pull request #30 from epoberezkin/1.0.0

1.0.0
master
Evgeny Poberezkin 2015-08-16 23:13:30 +01:00
commit cfd2e82d7e
43 changed files with 330 additions and 120 deletions

View File

@ -16,6 +16,7 @@ ajv implements full [JSON Schema draft 4](http://json-schema.org/) standard:
- all validation keywords
- full support of remote refs (remote schemas have to be added with `addSchema` or compiled to be available)
- support of circular dependencies between schemas
- correct string lengths for strings with unicode pairs (can be turned off)
- formats defined by JSON Schema draft 4 standard and custom formats (can be turned off)
@ -148,22 +149,18 @@ Validation errors will be available in the `errors` property of ajv instance (`n
##### .addSchema(Array<Object>|Object schema [, String key])
Add and compile schema(s). It does the same as `.compile` with three differences:
Add schema(s) to validator instance. From version 1.0.0 this method does not compile schemas (but it still validates them). Because of that change, dependencies can be added in any order and circular dependencies are supported. It also prevents unnecessary compilation of schemas that are containers for other schemas but not used as a whole.
- array of schemas can be passed (schemas should have ids), the second parameter will be ignored.
Array of schemas can be passed (schemas should have ids), the second parameter will be ignored.
- key can be passed that can be used to reference the schema and will be used as the schema id if there is no id inside the schema. If the key is not passed, the schema id will be used as the key.
- compiled schema is not returned.
Key can be passed that can be used to reference the schema and will be used as the schema id if there is no id inside the schema. If the key is not passed, the schema id will be used as the key.
Once the schema added it and all the references inside it can be referenced in other schemas and used to validate data.
Once the schema is added, it (and all the references inside it) can be referenced in other schemas and used to validate data.
In the current version all the referenced schemas should be added before the schema that uses them is compiled, so the circular references are not supported.
Although `addSchema` does not compile schemas, explicit compilation is not required - the schema will be compiled when it is used first time.
Version [1.0](https://github.com/epoberezkin/ajv/tree/1.0.0) will only compile schemas when they are used the first time. The order of addition in this version is not important and it supports circular references. Try it from the branch if you need them and please report any issues.
By default schema is validated against meta-schema before it is compiled and if the schema does not pass validation the exception is thrown. This behaviour is controlled by `validateSchema` option.
By default the schema is validated against meta-schema before it is added, and if the schema does not pass validation the exception is thrown. This behaviour is controlled by `validateSchema` option.
##### .addMetaSchema(Object schema [, String key])
@ -281,6 +278,15 @@ Improved/fixed data filtering with `removeAdditional` option.
`removeAdditional` option allowing to remove additional properties.
##### 1.0.0
`addSchema` no longer compiles schemas and its return value is `undefined`
Dependencies can be added in any order (all dependencies should be present when the schema is compiled though)
Circular dependencies support
##### 0.6.1
Errors for "required" keyword validation include missing properties

View File

@ -3,6 +3,7 @@
var compileSchema = require('./compile')
, resolve = require('./compile/resolve')
, Cache = require('./cache')
, SchemaObject = require('./compile/schema_obj')
, stableStringify = require('json-stable-stringify')
, formats = require('./compile/formats');
@ -42,6 +43,8 @@ function Ajv(opts) {
this.addFormat = addFormat;
this.errorsText = errorsText;
this._compile = _compile;
addInitialSchemas();
if (this.opts.formats) addInitialFormats();
@ -58,7 +61,10 @@ function Ajv(opts) {
if (typeof schemaKeyRef == 'string') {
v = getSchema(schemaKeyRef);
if (!v) throw new Error('no schema with key or ref "' + schemaKeyRef + '"');
} else v = _addSchema(schemaKeyRef);
} else {
var schemaObj = _addSchema(schemaKeyRef);
v = schemaObj.validate || _compile(schemaObj);
}
var valid = v(data);
self.errors = v.errors;
@ -72,7 +78,8 @@ function Ajv(opts) {
* @return {Object} validation result { valid: true/false, errors: [...] }
*/
function compile(schema) {
return _addSchema(schema);
var schemaObj = _addSchema(schema);
return schemaObj.validate || _compile(schemaObj);
}
@ -81,15 +88,16 @@ function Ajv(opts) {
* @param {Object|Array} schema schema or array of schemas. If array is passed, `key` will be ignored.
* @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`.
*/
function addSchema(schema, key, _skipValidation) {
if (Array.isArray(schema)) {
function addSchema(schema, key, _skipValidation, _meta) {
if (Array.isArray(schema)){
schema.forEach(function(sch) { addSchema(sch); });
return;
}
// can key/id have # inside?
key = resolve.normalizeId(key || schema.id);
checkUnique(key);
self._schemas[key] = _addSchema(schema, _skipValidation);
var schemaObj = self._schemas[key] = _addSchema(schema, _skipValidation);
schemaObj.meta = _meta;
}
@ -100,10 +108,7 @@ function Ajv(opts) {
* @param {String} key optional schema key
*/
function addMetaSchema(schema, key, _skipValidation) {
var currentRemoveAdditional = self.opts.removeAdditional;
self.opts.removeAdditional = false;
addSchema(schema, META_SCHEMA_ID, _skipValidation);
self.opts.removeAdditional = currentRemoveAdditional;
addSchema(schema, key, _skipValidation, true);
}
@ -130,6 +135,15 @@ function Ajv(opts) {
* @return {Function} schema validating function (with property `schema`).
*/
function getSchema(keyRef) {
var schemaObj = _getSchemaObj(keyRef);
switch (typeof schemaObj) {
case 'object': return schemaObj.validate || _compile(schemaObj);
case 'string': return getSchema(schemaObj);
}
}
function _getSchemaObj(keyRef) {
keyRef = resolve.normalizeId(keyRef);
return self._schemas[keyRef] || self._refs[keyRef];
}
@ -141,25 +155,29 @@ function Ajv(opts) {
* @param {String|Object} schemaKeyRef key, ref or schema object
*/
function removeSchema(schemaKeyRef) {
var str;
if (typeof schemaKeyRef == 'string') {
schemaKeyRef = resolve.normalizeId(schemaKeyRef);
var v = self._schemas[schemaKeyRef] || self._refs[schemaKeyRef];
delete self._schemas[schemaKeyRef];
delete self._refs[schemaKeyRef];
str = stableStringify(v.schema);
self._cache.put(str);
} else {
str = stableStringify(schemaKeyRef);
self._cache.put(str);
switch (typeof schemaKeyRef) {
case 'string':
var schemaObj = _getSchemaObj(schemaKeyRef);
self._cache.del(schemaObj.jsonStr);
delete self._schemas[schemaKeyRef];
delete self._refs[schemaKeyRef];
break;
case 'object':
var jsonStr = stableStringify(schemaKeyRef);
self._cache.del(jsonStr);
var id = schemaKeyRef.id;
if (id) {
id = resolve.normalizeId(id);
delete self._refs[id];
}
}
}
function _addSchema(schema, skipValidation) {
if (typeof schema != 'object') throw new Error('schema should be object');
var str = stableStringify(schema);
var cached = self._cache.get(str);
var jsonStr = stableStringify(schema);
var cached = self._cache.get(jsonStr);
if (cached) return cached;
var id = resolve.normalizeId(schema.id);
@ -175,11 +193,48 @@ function Ajv(opts) {
var localRefs = resolve.ids.call(self, schema);
var validate = compileSchema.call(self, schema, undefined, localRefs);
if (id[0] != '#') self._refs[id] = validate;
self._cache.put(str, validate);
var schemaObj = new SchemaObject({
id: id,
schema: schema,
localRefs: localRefs,
jsonStr: jsonStr,
});
return validate;
if (id[0] != '#') self._refs[id] = schemaObj;
self._cache.put(jsonStr, schemaObj);
return schemaObj;
}
function _compile(schemaObj, root) {
if (schemaObj.compiling) {
schemaObj.validate = callValidate;
callValidate.schema = schemaObj.schema;
callValidate.errors = null;
callValidate.root = root ? root : callValidate;
return callValidate;
}
schemaObj.compiling = true;
var currentRA = self.opts.removeAdditional;
if (currentRA && schemaObj.meta) self.opts.removeAdditional = false;
var v = compileSchema.call(self, schemaObj.schema, root, schemaObj.localRefs);
if (currentRA) self.opts.removeAdditional = currentRA;
schemaObj.validate = v;
schemaObj.refs = v.refs;
schemaObj.refVal = v.refVal;
schemaObj.root = v.root;
return v;
function callValidate() {
var v = schemaObj.validate;
var result = v.apply(null, arguments);
callValidate.errors = v.errors;
return result;
}
}

View File

@ -14,3 +14,8 @@ Cache.prototype.put = function Cache_put(key, value) {
Cache.prototype.get = function Cache_get(key) {
return this._cache[key];
};
Cache.prototype.del = function Cache_del(key) {
delete this._cache[key];
}

View File

@ -2,7 +2,8 @@
var url = require('url')
, equal = require('./equal')
, util = require('./util');
, util = require('./util')
, SchemaObject = require('./schema_obj');
module.exports = resolve;
@ -19,16 +20,23 @@ function resolve(compile, root, ref) {
if (this._refs[refVal]) refVal = this._refs[refVal];
else return resolve.call(this, compile, root, refVal);
}
refVal = refVal || this._schemas[ref];
if (typeof refVal == 'function') return refVal;
if (refVal instanceof SchemaObject)
return refVal.validate || this._compile(refVal, root);
var res = _resolve.call(this, root, ref);
var schema, v;
if (res) {
schema = res.schema;
root = res.root;
}
if (typeof schema == 'function') v = schema;
else if (schema) v = compile.call(this, schema, root);
if (schema instanceof SchemaObject)
var v = schema.validate || compile.call(this, schema.schema, root);
else if (schema)
var v = compile.call(this, schema, root);
if (v && ref[0] != '#') this._refs[ref] = v;
return v;
}
@ -43,10 +51,13 @@ function _resolve(root, ref) {
var id = normalizeId(refPath);
var refVal = this._refs[id];
if (typeof refVal == 'string') refVal = this._refs[refVal];
if (typeof refVal == 'function') root = refVal;
else {
if (refVal instanceof SchemaObject) {
if (!refVal.validate) this._compile(refVal);
root = refVal;
} else {
refVal = this._schemas[id];
if (typeof refVal == 'function') {
if (refVal instanceof SchemaObject) {
if (!refVal.validate) this._compile(refVal);
if (id == normalizeId(ref)) return { schema: refVal, root: root };
root = refVal;
}

View File

@ -0,0 +1,9 @@
'use strict';
var util = require('./util');
module.exports = SchemaObject;
function SchemaObject(obj) {
util.copy(obj, this);
}

View File

@ -141,16 +141,16 @@ function cleanUpCode(out) {
}
var ERRORS_REGEXP = /[^\.]errors/g
, VAR_ERRORS = 'var errors = 0;'
, INITIALIZE_ERRORS = 'validate.errors = null;'
, RETURN_ERRORS = 'return errors === 0;';
var ERRORS_REGEXP = /[^v\.]errors/g
, REMOVE_ERRORS = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g
, RETURN_VALID = 'return errors === 0;'
, RETURN_TRUE = 'validate.errors = null; return true;';
function cleanUpVarErrors(out) {
var matches = out.match(ERRORS_REGEXP);
if (matches && matches.length === 2)
return out.replace(VAR_ERRORS, '')
.replace(INITIALIZE_ERRORS, '')
.replace(RETURN_ERRORS, INITIALIZE_ERRORS + ' return true;');
return out.replace(REMOVE_ERRORS, '')
.replace(RETURN_VALID, RETURN_TRUE);
else
return out;
}

View File

@ -31,9 +31,9 @@
{{# def.addError:'anyOf' }}
} else {
errors = {{=$errs}};
if (validate.errors !== null) {
if ({{=$errs}}) validate.errors.length = {{=$errs}};
else validate.errors = null;
if (vErrors !== null) {
if ({{=$errs}}) vErrors.length = {{=$errs}};
else vErrors = null;
}
{{? it.opts.allErrors }} } {{?}}

View File

@ -99,8 +99,8 @@
{{## def.addError:_rule:
var err = {{# def._error:_rule }};
if (validate.errors === null) validate.errors = [err];
else validate.errors.push(err);
if (vErrors === null) vErrors = [err];
else vErrors.push(err);
errors++;
#}}

View File

@ -16,9 +16,9 @@
{{# def.error:'not' }}
} else {
errors = {{=$errs}};
if (validate.errors !== null) {
if ({{=$errs}}) validate.errors.length = {{=$errs}};
else validate.errors = null;
if (vErrors !== null) {
if ({{=$errs}}) vErrors.length = {{=$errs}};
else vErrors = null;
}
{{? it.opts.allErrors }} } {{?}}

View File

@ -35,9 +35,9 @@ if (!{{=$valid}}) {
{{# def.error:'oneOf' }}
} else {
errors = {{=$errs}};
if (validate.errors !== null) {
if ({{=$errs}}) validate.errors.length = {{=$errs}};
else validate.errors = null;
if (vErrors !== null) {
if ({{=$errs}}) vErrors.length = {{=$errs}};
else vErrors = null;
}
{{? it.opts.allErrors }} } {{?}}

View File

@ -3,9 +3,9 @@
{{## def.validateRef:_v:
if (!{{# def._validateRef:_v }}) {
if (validate.errors === null) validate.errors = {{=_v}}.errors;
else validate.errors = validate.errors.concat({{=_v}}.errors);
errors = validate.errors.length;
if (vErrors === null) vErrors = {{=_v}}.errors;
else vErrors = vErrors.concat({{=_v}}.errors);
errors = vErrors.length;
} {{? $breakOnError }} else { {{?}}
#}}
@ -18,12 +18,7 @@
return false;
else {
{{??}}
var errors{{=$lvl}} = validate.errors;
if (!{{# def._validateRef:'validate' }}) {
if (errors{{=$lvl}} !== null)
validate.errors = errors{{=$lvl}}.concat(validate.errors);
errors = validate.errors.length;
} {{? $breakOnError }} else { {{?}}
{{# def.validateRef:'validate' }}
{{?}}
{{??}}
{{# def.validateRef:'root.refVal[0]' }}

View File

@ -24,8 +24,8 @@
validate = function (data, dataPath) {
'use strict';
validate.errors = null;{{ /* don't edit, used in replace */ }}
var errors = 0; {{ /* don't edit, used in replace */ }}
var vErrors = null; {{ /* don't edit, used in replace */ }}
var errors = 0; {{ /* don't edit, used in replace */ }}
{{??}}
{{? it.opts._debug }} console.log('validate dataPath:', dataPath); {{?}}
@ -99,7 +99,8 @@
{{? $breakOnError }} {{= $closingBraces2 }} {{?}}
{{? $top }}
return errors === 0;{{ /* don't edit, used in replace */ }}
validate.errors = vErrors; {{ /* don't edit, used in replace */ }}
return errors === 0; {{ /* don't edit, used in replace */ }}
}
{{??}}
var {{=$valid}} = errors === errs_{{=$lvl}};

View File

@ -33,7 +33,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; } else { errors = ' + ($errs) + '; if (validate.errors !== null) { if (' + ($errs) + ') validate.errors.length = ' + ($errs) + '; else validate.errors = null; } ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } ';
if (it.opts.allErrors) {
out += ' } ';
}

View File

@ -60,7 +60,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' } ';
if ($breakOnError) {

View File

@ -22,7 +22,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' }';
if ($breakOnError) {

View File

@ -29,7 +29,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: \'' + (it.util.escapeQuotes($schema)) + '\', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' } ';
if ($breakOnError) {

View File

@ -30,7 +30,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: false, data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' } ';
if ($breakOnError) {

View File

@ -21,7 +21,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -27,7 +27,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -21,7 +21,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -24,7 +24,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -21,7 +21,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -27,7 +27,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -21,7 +21,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -24,7 +24,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -21,7 +21,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -27,9 +27,9 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' } else { errors = ' + ($errs) + '; if (validate.errors !== null) { if (' + ($errs) + ') validate.errors.length = ' + ($errs) + '; else validate.errors = null; } ';
out += ' } else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } ';
if (it.opts.allErrors) {
out += ' } ';
}
@ -38,7 +38,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
if ($breakOnError) {
out += ' if (false) { ';
}

View File

@ -45,9 +45,9 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} else { errors = ' + ($errs) + '; if (validate.errors !== null) { if (' + ($errs) + ') validate.errors.length = ' + ($errs) + '; else validate.errors = null; }';
out += '} else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; }';
if (it.opts.allErrors) {
out += ' } ';
}

View File

@ -22,7 +22,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: \'' + (it.util.escapeQuotes($schema)) + '\', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += '} ';
if ($breakOnError) {

View File

@ -62,7 +62,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: false, data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
if ($breakOnError) {
out += ' break; ';

View File

@ -14,13 +14,13 @@ module.exports = function anonymous(it) {
if ($breakOnError && it.wasTop) {
out += ' if (! ' + ('validate') + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) return false; else { ';
} else {
out += ' var errors' + ($lvl) + ' = validate.errors; if (! ' + ('validate') + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) { if (errors' + ($lvl) + ' !== null) validate.errors = errors' + ($lvl) + '.concat(validate.errors); errors = validate.errors.length; } ';
out += ' if (! ' + ('validate') + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) { if (vErrors === null) vErrors = ' + ('validate') + '.errors; else vErrors = vErrors.concat(' + ('validate') + '.errors); errors = vErrors.length; } ';
if ($breakOnError) {
out += ' else { ';
}
}
} else {
out += ' if (! ' + ('root.refVal[0]') + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) { if (validate.errors === null) validate.errors = ' + ('root.refVal[0]') + '.errors; else validate.errors = validate.errors.concat(' + ('root.refVal[0]') + '.errors); errors = validate.errors.length; } ';
out += ' if (! ' + ('root.refVal[0]') + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) { if (vErrors === null) vErrors = ' + ('root.refVal[0]') + '.errors; else vErrors = vErrors.concat(' + ('root.refVal[0]') + '.errors); errors = vErrors.length; } ';
if ($breakOnError) {
out += ' else { ';
}
@ -42,7 +42,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: \'' + (it.util.escapeQuotes($schema)) + '\', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
if ($breakOnError) {
out += ' if (false) { ';
@ -56,7 +56,7 @@ module.exports = function anonymous(it) {
throw new Error($message);
}
} else {
out += ' if (! ' + ($refVal) + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) { if (validate.errors === null) validate.errors = ' + ($refVal) + '.errors; else validate.errors = validate.errors.concat(' + ($refVal) + '.errors); errors = validate.errors.length; } ';
out += ' if (! ' + ($refVal) + '(' + ($data) + ', (dataPath || \'\') + ' + (it.errorPath) + ') ) { if (vErrors === null) vErrors = ' + ($refVal) + '.errors; else vErrors = vErrors.concat(' + ($refVal) + '.errors); errors = vErrors.length; } ';
if ($breakOnError) {
out += ' else { ';
}

View File

@ -42,7 +42,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' } else { ';
} else {
@ -63,7 +63,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' } else { ';
}
@ -82,7 +82,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; } ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } ';
}
}
} else {
@ -95,7 +95,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: validate.schema' + ($schemaPath) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; } } ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } } ';
}
}
it.errorPath = $currentErrorPath;

View File

@ -22,7 +22,7 @@ module.exports = function anonymous(it) {
if (it.opts.verbose) {
out += ', schema: ' + ($schema) + ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' } ';
if ($breakOnError) {

View File

@ -9,8 +9,8 @@ module.exports = function anonymous(it) {
it.rootId = it.baseId = it.resolve.fullPath(it.root.schema.id);
delete it.isTop;
it.wasTop = true;
out += ' validate = function (data, dataPath) { \'use strict\'; validate.errors = null;';
out += ' var errors = 0; ';
out += ' validate = function (data, dataPath) { \'use strict\'; var vErrors = null; ';
out += ' var errors = 0; ';
} else {
if (it.opts._debug) {
out += ' console.log(\'validate dataPath:\', dataPath); ';
@ -95,7 +95,7 @@ module.exports = function anonymous(it) {
}
out += ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' } ';
}
@ -153,7 +153,7 @@ module.exports = function anonymous(it) {
}
out += ', data: ' + ($data);
}
out += ' }; if (validate.errors === null) validate.errors = [err]; else validate.errors.push(err); errors++; ';
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
}
out += ' }';
}
@ -161,7 +161,8 @@ module.exports = function anonymous(it) {
out += ' ' + ($closingBraces2) + ' ';
}
if ($top) {
out += ' return errors === 0;';
out += ' validate.errors = vErrors; ';
out += ' return errors === 0; ';
out += ' }';
} else {
out += ' var ' + ($valid) + ' = errors === errs_' + ($lvl) + ';';

View File

@ -1,6 +1,6 @@
{
"name": "ajv",
"version": "0.7.2",
"version": "1.0.0",
"description": "Another JSON Schema Validator",
"main": "lib/ajv.js",
"scripts": {

View File

@ -76,9 +76,11 @@ describe('Ajv', function () {
describe('addSchema method', function() {
it('should add and compile schema with key', function() {
ajv.addSchema({ type: 'integer' }, 'int');
var res = ajv.addSchema({ type: 'integer' }, 'int');
should.not.exist(res);
var validate = ajv.getSchema('int');
validate .should.be.a('function');
validate(1) .should.equal(true);
validate(1.1) .should.equal(false);
validate('1') .should.equal(false);
@ -87,19 +89,19 @@ describe('Ajv', function () {
});
it('should add and compile schema without key', function() {
var validate = ajv.addSchema({ type: 'integer' });
ajv.addSchema({ type: 'integer' });
ajv.validate('', 1) .should.equal(true);
ajv.validate('', '1') .should.equal(false);
});
it('should add and compile schema with id', function() {
var validate = ajv.addSchema({ id: '//e.com/int.json', type: 'integer' });
ajv.addSchema({ id: '//e.com/int.json', type: 'integer' });
ajv.validate('//e.com/int.json', 1) .should.equal(true);
ajv.validate('//e.com/int.json', '1') .should.equal(false);
});
it('should normalize schema keys and ids', function() {
var validate = ajv.addSchema({ id: '//e.com/int.json#', type: 'integer' }, 'int#');
ajv.addSchema({ id: '//e.com/int.json#', type: 'integer' }, 'int#');
ajv.validate('int', 1) .should.equal(true);
ajv.validate('int', '1') .should.equal(false);
ajv.validate('//e.com/int.json', 1) .should.equal(true);
@ -177,7 +179,6 @@ describe('Ajv', function () {
it('should return compiled schema by id or ref', function() {
ajv.addSchema({ id: '//e.com/int.json', type: 'integer' });
var validate = ajv.getSchema('//e.com/int.json');
validate .should.equal(validate);
validate(1) .should.equal(true);
validate('1') .should.equal(false);
});
@ -189,7 +190,8 @@ describe('Ajv', function () {
validate('1') .should.equal(false);
var v = ajv.getSchema();
v .should.equal(validate);
v(1) .should.equal(true);
v('1') .should.equal(false);
});
});
@ -200,7 +202,9 @@ describe('Ajv', function () {
, str = stableStringify(schema);
ajv.addSchema(schema, 'int');
var v = ajv.getSchema('int')
ajv._cache.get(str) .should.equal(v);
v .should.be.a('function');
ajv._cache.get(str).validate .should.equal(v);
ajv.removeSchema('int');
should.not.exist(ajv.getSchema('int'));
@ -211,8 +215,10 @@ describe('Ajv', function () {
var schema = { id: '//e.com/int.json', type: 'integer' }
, str = stableStringify(schema);
ajv.addSchema(schema);
var v = ajv.getSchema('//e.com/int.json');
ajv._cache.get(str) .should.equal(v);
var v = ajv.getSchema('//e.com/int.json')
v .should.be.a('function');
ajv._cache.get(str).validate .should.equal(v);
ajv.removeSchema('//e.com/int.json');
should.not.exist(ajv.getSchema('//e.com/int.json'));
@ -223,8 +229,7 @@ describe('Ajv', function () {
var schema = { type: 'integer' }
, str = stableStringify(schema);
ajv.addSchema(schema);
ajv._cache.get(str) .should.be.a('function');
ajv._cache.get(str) .should.be.an('object');
ajv.removeSchema({ type: 'integer' });
// should.not.exist(ajv.getSchema('int'));
should.not.exist(ajv._cache.get(str));

View File

@ -22,6 +22,10 @@ var remoteRefsWithIds = [ // order is important
require('./remotes/bar.json'),
require('./remotes/foo.json'),
require('./remotes/buu.json'),
require('./remotes/tree.json'),
require('./remotes/node.json'),
require('./remotes/second.json'),
require('./remotes/first.json'),
];
var instances = [ ajv, fullAjv, verboseAjv ];

4
spec/remotes/first.json Normal file
View File

@ -0,0 +1,4 @@
{
"id": "http://localhost:1234/first.json",
"type": "string"
}

10
spec/remotes/node.json Normal file
View File

@ -0,0 +1,10 @@
{
"id": "http://localhost:1234/node.json",
"description": "node",
"type": "object",
"properties": {
"value": { "type": "number" },
"subtree": { "$ref": "tree.json" }
},
"required": ["value"]
}

7
spec/remotes/second.json Normal file
View File

@ -0,0 +1,7 @@
{
"id": "http://localhost:1234/second.json",
"type": "object",
"properties": {
"first": { "$ref": "first.json" }
}
}

13
spec/remotes/tree.json Normal file
View File

@ -0,0 +1,13 @@
{
"id": "http://localhost:1234/tree.json",
"description": "tree of nodes",
"type": "object",
"properties": {
"meta": { "type": "string" },
"nodes": {
"type": "array",
"items": { "$ref": "node.json"}
}
},
"required": ["meta", "nodes"]
}

View File

@ -0,0 +1,18 @@
[
{
"description": "Adding dependency after dependent schema (#5)",
"schema": "http://localhost:1234/second.json",
"tests": [
{
"description": "valid object",
"data": { "first": "foo" },
"valid": true
},
{
"description": "valid object",
"data": { "first": 1 },
"valid": false
}
]
}
]

View File

@ -0,0 +1,66 @@
[
{
"description": "Recursive references between schemas (#5)",
"schema": "http://localhost:1234/tree.json",
"tests": [
{
"description": "valid tree",
"data": {
"meta": "root",
"nodes": [
{
"value": 1,
"subtree": {
"meta": "child",
"nodes": [
{ "value": 1.1 },
{ "value": 1.2 }
]
}
},
{
"value": 2,
"subtree": {
"meta": "child",
"nodes": [
{ "value": 2.1 },
{ "value": 2.2 }
]
}
}
]
},
"valid": true
},
{
"description": "invalid tree",
"data": {
"meta": "root",
"nodes": [
{
"value": 1,
"subtree": {
"meta": "child",
"nodes": [
{ "value": "string is invalid" },
{ "value": 1.2 }
]
}
},
{
"value": 2,
"subtree": {
"meta": "child",
"nodes": [
{ "value": 2.1 },
{ "value": 2.2 }
]
}
}
]
},
"valid": false
}
]
}
]