From 8ce8ac0a480de84065ce6be60e3c42da8009c597 Mon Sep 17 00:00:00 2001 From: colprog Date: Thu, 30 Oct 2014 10:52:57 +0800 Subject: [PATCH 1/3] use codegen to generate doument validation function. this is significantly faster then old code. --- lib/document.js | 325 +---------------------------------- lib/model.js | 4 +- lib/validator.js | 434 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + test/document.js | 2 +- test/model.js | 2 +- test/schema.js | 166 +++++++++--------- test/settings.js | 2 +- 8 files changed, 526 insertions(+), 411 deletions(-) create mode 100644 lib/validator.js diff --git a/lib/document.js b/lib/document.js index 17949024..ac1cdf50 100644 --- a/lib/document.js +++ b/lib/document.js @@ -395,12 +395,6 @@ Document.prototype._validate = function(options, modelToValidate, validateAll, v }) } -var types = [ - ['string', String], - ['number', Number], - ['boolean', Boolean] -] - // The schema doesn't contain joined docs Document.prototype.__validate = function(doc, schema, prefix, options, originalDoc) { //TODO: Why do we pass doc here? @@ -427,324 +421,7 @@ Document.prototype.__validate = function(doc, schema, prefix, options, originalD localOptions.enforce_extra = localOptions.enforce_extra; } - // Check for primitives - bool, number, string - for(var i=0; i 10) { - message = message+"..." - } - else { - message = message+"." - } - - throw new Error(message); - } - } - } - } - if (fieldChecked === false) { // The field is not a primitive, let's keep looking - if ((schema === Date) || (util.isPlainObject(schema) && schema._type === Date)) { - if ((util.isPlainObject(schema)) && (typeof schema.validator === 'function')) { - if (schema.validator(doc) === false) { - throw new Error("Validator for the field "+prefix+"[key] returned `false`.") - } - } - - if (doc === undefined) { - if (localOptions.enforce_missing === true) { - util.undefinedField(prefix); - } - } - else if (doc === null) { - if (localOptions.enforce_type === "strict") { - util.strictType(prefix, "date"); - } - } - else if (localOptions.enforce_type !== "none") { - if (util.isPlainObject(doc) && (doc["$reql_type$"] === "TIME")) { - if (doc.epoch_time === undefined) { - util.pseudoTypeError("date", "epoch_time", prefix); - } - else if (doc.timezone === undefined) { - util.pseudoTypeError("date", "timezone", prefix); - } - } - else if ((typeof doc === 'function') && (Array.isArray(doc._query))) { - // TOIMPROvE -- we currently just check if it's a term from the driver - // We suppose for now that this is enough and we don't throw an error - } - else if (typeof doc === 'string') { - var date = new Date(doc); - if (date.getTime() !== date.getTime()) { - if (localOptions.enforce_type === "strict") { - util.strictType(prefix, "date or a valid string"); - } - else if (localOptions.enforce_type !== "none") { - util.looseType(prefix, "date or a valid string"); - } - } - } - else if ((doc instanceof Date) === false) { - if (localOptions.enforce_type === "strict") { - util.strictType(prefix, "date"); - } - else if (localOptions.enforce_type !== "none") { - util.looseType(prefix, "date"); - } - } - } - } - else if ((schema === 'Point') || (util.isPlainObject(schema) && schema._type === 'Point')) { - if ((util.isPlainObject(schema)) && (typeof schema.validator === 'function')) { - if (schema.validator(doc) === false) { - throw new Error("Validator for the field "+prefix+"[key] returned `false`.") - } - } - - if (doc === undefined) { - if (localOptions.enforce_missing === true) { - util.undefinedField(prefix); - } - } - else if (doc === null) { - if (localOptions.enforce_type === "strict") { - util.strictType(prefix, "date"); - } - } - else if (localOptions.enforce_type !== "none") { - if (util.isPlainObject(doc) && (doc["$reql_type$"] === "GEOMETRY")) { - if (doc.type === undefined) { - util.pseudoTypeError("Point", "type", prefix); - } - else if (doc.type !== "Point") { - throw new Error("The field `type` for "+prefix+" must be `'Point'`.") - } - else if (doc.coordinates === undefined) { - util.pseudoTypeError("date", "coordinates", prefix); - } - else if ((!Array.isArray(doc.coordinates)) || (doc.coordinates.length !== 2)) { - throw new Error("The field `coordinates` for "+prefix+" must be an Array of two numbers.") - } - } - else if (util.isPlainObject(doc) && (doc.type === "Point") && (Array.isArray(doc.coordinates)) && (doc.coordinates.length === 2)) { // Geojson - // Geojson format - } - else if ((typeof doc === 'function') && (Array.isArray(doc._query))) { - // TOIMPROvE -- we currently just check if it's a term from the driver - // We suppose for now that this is enough and we don't throw an error - } - else if (util.isPlainObject(doc)) { - var keys = Object.keys(doc).sort(); - if (((keys.length !== 2) || keys[0] !== 'latitude') || (keys[1] !== 'longitude') || (typeof doc.latitude !== "number") || (typeof doc.longitude !== "number")) { - throw new Error("The value for "+prefix+" must be a ReQL Point (`r.point(, )`), an object `{longitude: , latitude: }`, or an array [, ].") - } - else if ((typeof doc.latitude !== 'number') || (typeof doc.latitude !== 'number')) { - throw new Error("The value for "+prefix+" must be a ReQL Point (`r.point(, )`), an object `{longitude: , latitude: }`, or an array [, ].") - } - } - else if (Array.isArray(doc)) { - if ((doc.length !== 2) || (typeof doc[0] !== "number") || (typeof doc[1] !== "number")) { - throw new Error("The value for "+prefix+" must be a ReQL Point (`r.point(, )`), an object `{longitude: , latitude: }`, or an array [, ].") - } - } - } - } - - else if ((schema === Buffer) || (util.isPlainObject(schema) && schema._type === Buffer)) { - if ((util.isPlainObject(schema)) && (typeof schema.validator === 'function')) { - if (schema.validator(doc) === false) { - throw new Error("Validator for the field "+prefix+"[key] returned `false`.") - } - } - - if (doc === undefined) { - if (localOptions.enforce_missing === true) { - util.undefinedField(prefix); - } - } - else if (doc === null) { - if (localOptions.enforce_type === "strict") { - util.strictType(prefix, "date"); - } - } - else if (util.isPlainObject(doc) && (doc["$reql_type$"] === "BINARY")) { - if (doc.data === undefined) { - util.pseudoTypeError("binary", "data", prefix); - } - } - else if ((typeof doc === 'function') && (Array.isArray(doc._query))) { - // TOIMPROvE -- we currently just check if it's a term from the driver - // We suppose for now that this is enough and we don't throw an error - } - else if ((doc instanceof Buffer) === false) { - if (localOptions.enforce_type === "strict") { - util.strictType(prefix, "buffer"); - } - else if (localOptions.enforce_type !== "none") { - util.looseType(prefix, "buffer"); - } - } - } - else if (Array.isArray(schema) || (util.isPlainObject(schema) && schema._type === Array)) { - if (doc === undefined) { - if (localOptions.enforce_missing === true) { - util.undefinedField(prefix); - } - } - else if (doc === null) { - if (localOptions.enforce_type === "strict") { - util.strictType(prefix, "array"); - } - } - else if (Array.isArray(doc)) { - for(var i=0; i {} -> Document.prototype <-cp- model -> Model.prototype @@ -191,8 +192,9 @@ Model.new = function(name, schema, options, thinky) { })(key) } + proto.gen_validator = validator(name, schema); - return model + return model; } Model.prototype.isReady = function() { diff --git a/lib/validator.js b/lib/validator.js new file mode 100644 index 00000000..ba5f9c1e --- /dev/null +++ b/lib/validator.js @@ -0,0 +1,434 @@ +var genFunc = require('generate-function'); +var genObj = require('generate-object-property'); +var util = require(__dirname+'/util.js'); + + +function validatePrimitive(doc, localOptions, typeOf, prefix, Enum, _Enum) { + if (doc === undefined) { + if (localOptions.enforce_missing === true) { + util.undefinedField(prefix); + } + } + else if (doc === null) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, typeOf); + } + } + else if (typeof doc !== typeOf) { // doc is not null/undefined + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, typeOf); + } + else if (localOptions.enforce_type === "loose") { + util.looseType(prefix, typeOf); + } + } + else if (Enum && _Enum) { + if (_Enum[doc] !== true) { + var validValues = Object.keys(_Enum); + var message = "The field "+prefix+" must be one of these values: "; + + for(var i=0; i 10) { + message = message+"..."; + } + else { + message = message+"."; + } + + throw new Error(message); + } + } +} + +function validateDate(doc, localOptions, prefix) { + if (doc === undefined) { + if (localOptions.enforce_missing === true) { + util.undefinedField(prefix); + } + } + else if (doc === null) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "date"); + } + } + else if (localOptions.enforce_type !== "none") { + if (util.isPlainObject(doc) && (doc["$reql_type$"] === "TIME")) { + if (doc.epoch_time === undefined) { + util.pseudoTypeError("date", "epoch_time", prefix); + } + else if (doc.timezone === undefined) { + util.pseudoTypeError("date", "timezone", prefix); + } + } + else if ((typeof doc === 'function') && (Array.isArray(doc._query))) { + // TOIMPROvE -- we currently just check if it's a term from the driver + // We suppose for now that this is enough and we don't throw an error + } + else if (typeof doc === 'string') { + var date = new Date(doc); + if (date.getTime() !== date.getTime()) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "date or a valid string"); + } + else if (localOptions.enforce_type !== "none") { + util.looseType(prefix, "date or a valid string"); + } + } + } + else if ((doc instanceof Date) === false) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "date"); + } + else if (localOptions.enforce_type !== "none") { + util.looseType(prefix, "date"); + } + } + } +} + +function validateBuffer(doc, localOptions, prefix) { + if (doc === undefined) { + if (localOptions.enforce_missing === true) { + util.undefinedField(prefix); + } + } + else if (doc === null) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "date"); + } + } + else if (util.isPlainObject(doc) && (doc["$reql_type$"] === "BINARY")) { + if (doc.data === undefined) { + util.pseudoTypeError("binary", "data", prefix); + } + } + else if ((typeof doc === 'function') && (Array.isArray(doc._query))) { + // TOIMPROvE -- we currently just check if it's a term from the driver + // We suppose for now that this is enough and we don't throw an error + } + else if ((doc instanceof Buffer) === false) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "buffer"); + } + else if (localOptions.enforce_type !== "none") { + util.looseType(prefix, "buffer"); + } + } +} + +function validatePoint(doc, localOptions, prefix) { + if (doc === undefined) { + if (localOptions.enforce_missing === true) { + util.undefinedField(prefix); + } + } + else if (doc === null) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "date"); + } + } + else if (localOptions.enforce_type !== "none") { + if (util.isPlainObject(doc) && (doc["$reql_type$"] === "GEOMETRY")) { + if (doc.type === undefined) { + util.pseudoTypeError("Point", "type", prefix); + } + else if (doc.type !== "Point") { + throw new Error("The field `type` for "+prefix+" must be `'Point'`."); + } + else if (doc.coordinates === undefined) { + util.pseudoTypeError("date", "coordinates", prefix); + } + else if ((!Array.isArray(doc.coordinates)) || (doc.coordinates.length !== 2)) { + throw new Error("The field `coordinates` for "+prefix+" must be an Array of two numbers."); + } + } + else if (util.isPlainObject(doc) && (doc.type === "Point") && (Array.isArray(doc.coordinates)) && (doc.coordinates.length === 2)) { // Geojson + // Geojson format + } + else if ((typeof doc === 'function') && (Array.isArray(doc._query))) { + // TOIMPROvE -- we currently just check if it's a term from the driver + // We suppose for now that this is enough and we don't throw an error + } + else if (util.isPlainObject(doc)) { + var keys = Object.keys(doc).sort(); + if (((keys.length !== 2) || keys[0] !== 'latitude') || (keys[1] !== 'longitude') || (typeof doc.latitude !== "number") || (typeof doc.longitude !== "number")) { + throw new Error("The value for "+prefix+" must be a ReQL Point (`r.point(, )`), an object `{longitude: , latitude: }`, or an array [, ].") + } + else if ((typeof doc.latitude !== 'number') || (typeof doc.latitude !== 'number')) { + throw new Error("The value for "+prefix+" must be a ReQL Point (`r.point(, )`), an object `{longitude: , latitude: }`, or an array [, ].") + } + } + else if (Array.isArray(doc)) { + if ((doc.length !== 2) || (typeof doc[0] !== "number") || (typeof doc[1] !== "number")) { + throw new Error("The value for "+prefix+" must be a ReQL Point (`r.point(, )`), an object `{longitude: , latitude: }`, or an array [, ].") + } + } + } +} + +function validateDocNotNull(doc, localOptions, prefix) { + if (doc === undefined) { + if (localOptions.enforce_missing === true) { + util.undefinedField(prefix); + } + } + else if (doc === null) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "object"); + } + } + else if (!util.isPlainObject(doc)) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "object"); + } + else if (localOptions.enforce_type === "loose") { + util.looseType(prefix, "object"); + } + } +} + +function validateDocIsArray(doc, localOptions, prefix) { + if (doc === undefined) { + if (localOptions.enforce_missing === true) { + util.undefinedField(prefix); + } + } + else if (doc === null) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "array"); + } + } + else if (!Array.isArray(doc)) { + if (localOptions.enforce_type === "strict") { + util.strictType(prefix, "array"); + } + else if (localOptions.enforce_type === "loose") { + util.looseType(prefix, "array"); + } + } +} + +function validateExtraFields(doc, schema, localOptions, prefix) { + if ((localOptions.enforce_extra !== "none")) { + for(var key in doc) { + if (((doc._getModel == null) || (doc._getModel()._joins.hasOwnProperty(key) === false)) && (doc.hasOwnProperty(key)) && + (util.isPlainObject(schema) && (schema._type === undefined) && (schema.hasOwnProperty(key) === false)) + || (util.isPlainObject(schema) && (schema._type === Object) && (util.isPlainObject(schema.schema)) && (schema.schema.hasOwnProperty(key) === false))) { + + if (localOptions.enforce_extra === 'remove') { + delete doc[key]; + } + else if (localOptions.enforce_extra === 'strict') { + util.extraField(prefix, key); + } + } + } + } +} + +function getType(schema) { + if (util.isPlainObject(schema) && schema._type){ + return schema._type; + } + else { + return schema; + } +} + +function isSchema(object) { + return util.isPlainObject(object) && object._type === undefined; +} + +function getSchema(node) { + if (getType(node) === Object || getType(node) === Array) { + return node.schema; + } + else if (Array.isArray(node)) { + return getSchema(node[0]); + } + else { + return node; + } +} + +function mergeSchemaOption(base, local) { + if (local) { + base = util.deepCopy(base); + base.enforce_missing = local.enforce_missing == null ? base.enforce_missing : local.enforce_missing; + base.enforce_extra = local.enforce_extra == null ? base.enforce_extra : local.enforce_extra; + base.enforce_type = local.enforce_type == null ? base.enforce_type : local.enforce_type; + } + return base; +} + +function visit(validate, key, node, path, validateContext) { + var nodeType = getType(node); + path = key === "[i]" ? path + key : genObj(path, key); + var field = "doc" + path; + var subSchema; + + if (node && (typeof node.validator === 'function')) { + validateContext.schemas[path] = node; + validate("if (%s.validator(%s) === false) throw new Error(\"Validator for the field %s returned `false`.\")", + genObj("schemas", path), field, path); + } + + if (nodeType === Number) { + var Enum, _Enum; + if (Array.isArray(node.enum) && (util.isPlainObject(node._enum))) { + Enum = node.enum; + _Enum = node._enum; + } + validate("validatePrimitive(%s, %s, 'number', prefix+'%s', %s, %s);", + field, + node.options ? "mergeSchemaOption(options, " + JSON.stringify(node.options) + ")" : 'options', + path, + Enum ? JSON.stringify(Enum) : 'undefined', + _Enum ? JSON.stringify(_Enum) : 'undefined' + ); + } + else if (nodeType === String) { + var Enum, _Enum; + if (Array.isArray(node.enum) && (util.isPlainObject(node._enum))) { + Enum = node.enum; + _Enum = node._enum; + } + validate("validatePrimitive(%s, %s, 'string', prefix+'%s', %s, %s);", + field, + node.options ? "mergeSchemaOption(options, " + JSON.stringify(node.options) + ")" : 'options', + path, + Enum ? JSON.stringify(Enum) : 'undefined', + _Enum ? JSON.stringify(_Enum) : 'undefined' + ); + } + else if (nodeType === Boolean) { + var Enum, _Enum; + if (Array.isArray(node.enum) && (util.isPlainObject(node._enum))) { + Enum = node.enum; + _Enum = node._enum; + } + validate("validatePrimitive(%s, %s, 'boolean', prefix+'%s', %s, %s);", + field, + node.options ? "mergeSchemaOption(options, " + JSON.stringify(node.options) + ")" : 'options', + path, + Enum ? JSON.stringify(Enum) : 'undefined', + _Enum ? JSON.stringify(_Enum) : 'undefined' + ); + } + else if (nodeType === Date) { + validate("validateDate(%s, %s, prefix+'%s');", + field, + node.options ? "mergeSchemaOption(options, " + JSON.stringify(node.options) + ")" : 'options', + path); + } + else if (nodeType === Buffer) { + validate("validateBuffer(%s, %s, prefix+'%s');", + field, + node.options ? "mergeSchemaOption(options, " + JSON.stringify(node.options) + ")" : 'options', + path); + } + else if (nodeType === "Point") { + validate("validatePoint(%s, %s, prefix+'%s');", + field, + node.options ? "mergeSchemaOption(options, " + JSON.stringify(node.options) + ")" : 'options', + path); + } + else if (nodeType === Array || Array.isArray(node)) { + validate("validateDocIsArray(%s, options, prefix+'%s');", field, path); + subSchema = getSchema(node); + if (subSchema) { + if (node.options) { + validate("optionStack.push(mergeSchemaOption(options, " + JSON.stringify(node.options) + ");"); + validate("options = optionStack[optionStack.length - 1];"); + } + validate("if (Array.isArray(%s)) {", field); + validate("for (var i=0;i<%s.length;i++) {", field); + validate('if (%s === undefined) throw new Error("The element in the array %s (position "+i+") cannot be `undefined`.");', field + "[i]", path); + visit(validate, "[i]", subSchema, path, validateContext); + validate("}"); + validate("}"); + if (node.options) { + validate("optionStack.pop();"); + validate("options = optionStack[optionStack.length - 1];"); + } + } + } + else if (nodeType === Object || isSchema(node)) { + validate("validateDocNotNull(%s, options, prefix+'%s');", field, path); + subSchema = getSchema(node); + if (subSchema) { + if (node.options) { + validate("optionStack.push(mergeSchemaOption(options, " + JSON.stringify(node.options) + "));"); + validate("options = optionStack[optionStack.length - 1];"); + } + validate("if (util.isPlainObject(%s)) {", field); + var subKeys = Object.keys(subSchema); + for (var i=0;i Date: Thu, 30 Oct 2014 11:16:45 +0800 Subject: [PATCH 2/3] cleanup --- lib/document.js | 31 +------------------------------ lib/validator.js | 11 ++--------- 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/lib/document.js b/lib/document.js index ac1cdf50..a27a93ee 100644 --- a/lib/document.js +++ b/lib/document.js @@ -323,7 +323,7 @@ Document.prototype._validate = function(options, modelToValidate, validateAll, v // Validate this document util.tryCatch(function() { - self.__validate(self, schema, prefix, docOptions, self); + self._getModel().gen_validator(self, prefix, docOptions); }, function(err) { error = err; }); @@ -395,35 +395,6 @@ Document.prototype._validate = function(options, modelToValidate, validateAll, v }) } -// The schema doesn't contain joined docs -Document.prototype.__validate = function(doc, schema, prefix, options, originalDoc) { - //TODO: Why do we pass doc here? - - var typeOf, className, key - - // We need a deepcopy because we are going to pass the options around and overwrite them - var localOptions = util.deepCopy(options) - - /* - // An element in an array can never be undefined because RethinkDB doesn't support such type - */ - var fieldChecked = false; - - // Set the local settings - if (util.isPlainObject(schema) && (schema._type !== undefined) && (util.isPlainObject(schema.options))) { - localOptions.enforce_missing = (schema.options.enforce_missing != null) ? schema.options.enforce_missing : localOptions.enforce_missing; - localOptions.enforce_type = (schema.options.enforce_type != null) ? schema.options.enforce_type : localOptions.enforce_type; - localOptions.enforce_extra = (schema.options.enforce_extra != null) ? schema.options.enforce_extra : localOptions.enforce_extra; - } - else { - localOptions.enforce_missing = localOptions.enforce_missing; - localOptions.enforce_type = localOptions.enforce_type; - localOptions.enforce_extra = localOptions.enforce_extra; - } - - this._getModel().gen_validator(doc, prefix, localOptions); -} - Document.prototype.save = function(callback) { var self = this; diff --git a/lib/validator.js b/lib/validator.js index ba5f9c1e..dfc7007f 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -394,9 +394,9 @@ function visit(validate, key, node, path, validateContext) { } } -function makeSchemaValidator(name, schema) { +var compile = module.exports = function (name, schema) { var validate = genFunc()("function _validate_%s(doc, prefix, options) {", name) - ("var optionStack = [options];"); + ("var optionStack = [options];"); var schemaKeys = Object.keys(schema); var validateContext = { validatePrimitive: validatePrimitive, @@ -424,11 +424,4 @@ function makeSchemaValidator(name, schema) { validate("}"); return validate.toFunction(validateContext); -} - -module.exports = function (name, schema) { - var f = makeSchemaValidator(name, schema, ""); - //if (name === "user") - //console.log(f.toString()); - return f; }; From 30843f6e4d7eee4c39a01a4f7f3b68252aec70ed Mon Sep 17 00:00:00 2001 From: colprog Date: Fri, 31 Oct 2014 22:01:35 +0800 Subject: [PATCH 3/3] apply the same technique to generate default and virtuals --- lib/defaultValue.js | 152 ++++++++++++++++++++++++++++++++++++++++++++ lib/document.js | 152 +------------------------------------------- lib/model.js | 5 +- lib/util.js | 26 ++++++++ lib/validator.js | 33 ++-------- 5 files changed, 189 insertions(+), 179 deletions(-) create mode 100644 lib/defaultValue.js diff --git a/lib/defaultValue.js b/lib/defaultValue.js new file mode 100644 index 00000000..a521aa85 --- /dev/null +++ b/lib/defaultValue.js @@ -0,0 +1,152 @@ +var genFunc = require('generate-function'); +var genObj = require('generate-object-property'); +var util = require(__dirname+'/util.js'); + +function schemaTreeHasDefault(schema, isVirtual) { + var nodeType, subSchema; + var schemaKeys = Object.keys(schema); + + for (var i=0;i {} -> Document.prototype <-cp- model -> Model.prototype @@ -192,7 +193,9 @@ Model.new = function(name, schema, options, thinky) { })(key) } - proto.gen_validator = validator(name, schema); + proto.__doc_validator = validator(name, schema); + proto.__fillDefault = fillDefault(name, schema, false); + proto.__fillVirtual = fillDefault(name, schema, true); return model; } diff --git a/lib/util.js b/lib/util.js index 5120d43c..db0cad3a 100644 --- a/lib/util.js +++ b/lib/util.js @@ -265,4 +265,30 @@ function _hook(options) { } util.hook = hook; +util.getType = function (schema) { + if (util.isPlainObject(schema) && schema._type){ + return schema._type; + } + else { + return schema; + } +} + +util.isSchema = function (object) { + return util.isPlainObject(object) && object._type === undefined; +} + +util.getSchema = function (node) { + if (util.getType(node) === Object || util.getType(node) === Array) { + return node.schema; + } + else if (Array.isArray(node)) { + return util.getSchema(node[0]); + } + else { + return node; + } +} + + module.exports = util; diff --git a/lib/validator.js b/lib/validator.js index dfc7007f..1fd8bce4 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -234,31 +234,6 @@ function validateExtraFields(doc, schema, localOptions, prefix) { } } -function getType(schema) { - if (util.isPlainObject(schema) && schema._type){ - return schema._type; - } - else { - return schema; - } -} - -function isSchema(object) { - return util.isPlainObject(object) && object._type === undefined; -} - -function getSchema(node) { - if (getType(node) === Object || getType(node) === Array) { - return node.schema; - } - else if (Array.isArray(node)) { - return getSchema(node[0]); - } - else { - return node; - } -} - function mergeSchemaOption(base, local) { if (local) { base = util.deepCopy(base); @@ -270,7 +245,7 @@ function mergeSchemaOption(base, local) { } function visit(validate, key, node, path, validateContext) { - var nodeType = getType(node); + var nodeType = util.getType(node); path = key === "[i]" ? path + key : genObj(path, key); var field = "doc" + path; var subSchema; @@ -343,7 +318,7 @@ function visit(validate, key, node, path, validateContext) { } else if (nodeType === Array || Array.isArray(node)) { validate("validateDocIsArray(%s, options, prefix+'%s');", field, path); - subSchema = getSchema(node); + subSchema = util.getSchema(node); if (subSchema) { if (node.options) { validate("optionStack.push(mergeSchemaOption(options, " + JSON.stringify(node.options) + ");"); @@ -361,9 +336,9 @@ function visit(validate, key, node, path, validateContext) { } } } - else if (nodeType === Object || isSchema(node)) { + else if (nodeType === Object || util.isSchema(node)) { validate("validateDocNotNull(%s, options, prefix+'%s');", field, path); - subSchema = getSchema(node); + subSchema = util.getSchema(node); if (subSchema) { if (node.options) { validate("optionStack.push(mergeSchemaOption(options, " + JSON.stringify(node.options) + "));");