不要怂,就是干,撸起袖子干!

Commit 001b1567 by Sascha Depold

updated the validator to be async

2 parents 671718a5 41080488
......@@ -2,33 +2,53 @@ var Validator = require("validator")
, Utils = require("./utils")
var DaoValidator = module.exports = function(model) {
this.model = model
this.model = model
this.chainer = new Utils.QueryChainer()
}
DaoValidator.prototype.validate = function() {
var errors = {}
errors = Utils._.extend(errors, validateAttributes.call(this))
errors = Utils._.extend(errors, validateModel.call(this))
return errors
return new Utils.CustomEventEmitter(function(emitter) {
validateAttributes.call(this)
validateModel.call(this)
this
.chainer
.run()
.success(function () {
emitter.emit('success')
})
.error(function(err) {
var errors = {}
Utils._.each(err, function (value) {
Utils._.extend(errors, value)
})
emitter.emit('success', errors)
})
}.bind(this)).run()
}
// private
var validateModel = function() {
var errors = {}
// for each model validator for this DAO
Utils._.each(this.model.__options.validate, function(validator, validatorType) {
try {
validator.apply(this.model)
} catch (err) {
errors[validatorType] = [err.message] // TODO: data structure needs to change for 2.0
}
Utils._.each(this.model.__options.validate, function(_validator, validatorType) {
var validator = prepareValidationOfAttribute.call(this, undefined, _validator, validatorType, { omitValue: true })
this.chainer.add(new Utils.CustomEventEmitter(function(emitter) {
var next = function(err) {
if (err) {
var error = {}
error[validatorType] = [err]
emitter.emit('error', error)
} else {
emitter.emit('success')
}
}
validator.args.unshift(next);
validator.fn.apply(null, validator.args)
}.bind(this)).run())
}.bind(this))
return errors
}
var validateAttributes = function() {
......@@ -48,32 +68,27 @@ var validateAttributes = function() {
}
var validateAttribute = function(value, field) {
var errors = {}
// for each validator
Utils._.each(this.model.validators[field], function(details, validatorType) {
var validator = prepareValidationOfAttribute.call(this, value, details, validatorType)
try {
validator.fn.apply(null, validator.args)
} catch (err) {
var msg = err.message
// if we didn't provide a custom error message then augment the default one returned by the validator
if (!validator.msg && !validator.isCustom) {
msg += ": " + field
this.chainer.add(new Utils.CustomEventEmitter(function(emitter) {
var next = function(err) {
if (err) {
var error = {}
error[field] = [err]
emitter.emit('error', error)
} else {
emitter.emit('success')
}
}
// each field can have multiple validation errors stored against it
errors[field] = errors[field] || []
errors[field].push(msg)
}
validator.args.unshift(next);
validator.fn.apply(null, validator.args)
}.bind(this)).run())
}.bind(this)) // for each validator for this field
return errors
}
var prepareValidationOfAttribute = function(value, details, validatorType) {
var prepareValidationOfAttribute = function(value, details, validatorType, options) {
var isCustomValidator = false // if true then it's a custom validation method
, validatorFunction = null // the validation function to call
, validatorArgs = [] // extra arguments to pass to validation function
......@@ -82,7 +97,9 @@ var prepareValidationOfAttribute = function(value, details, validatorType) {
if (typeof details === 'function') {
// it is a custom validator function?
isCustomValidator = true
validatorFunction = Utils._.bind(details, this.model, value)
validatorFunction = function(next) {
details.apply(this.model, ((options || {}).omitValue) ? [next] : [value, next])
}.bind(this)
} else {
// it is a validator module function?
......@@ -105,7 +122,16 @@ var prepareValidationOfAttribute = function(value, details, validatorType) {
}
// bind to validator obj
validatorFunction = Utils._.bind(validator[validatorType], validator)
validatorFunction = function(next) {
var args = Array.prototype.slice.call(arguments, 1)
try {
validator[validatorType].apply(validator, args)
next()
} catch (err) {
next(err.message)
}
}
}
return {
......
......@@ -148,27 +148,29 @@ module.exports = (function() {
this.dataValues[updatedAtAttr] = values[updatedAtAttr] = Utils.now()
}
var errors = this.validate()
if (!!errors) {
return new Utils.CustomEventEmitter(function(emitter) {
emitter.emit('error', errors)
}).run()
}
else if (this.isNewRecord) {
return this.QueryInterface.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values)
} else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id;
if (identifier === null && this.__options.whereCollection !== null) {
identifier = this.__options.whereCollection;
}
return new Utils.CustomEventEmitter(function(emitter) {
this.validate().success(function(errors) {
if (!!errors) {
emitter.emit('error', errors)
} else if (this.isNewRecord) {
this
.QueryInterface
.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values)
.proxy(emitter)
} else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id;
if (identifier === null && this.__options.whereCollection !== null) {
identifier = this.__options.whereCollection;
}
var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory)
, query = this.QueryInterface.update(this, tableName, values, identifier)
var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory)
, query = this.QueryInterface.update(this, tableName, values, identifier)
return query
}
query.proxy(emitter)
}
}.bind(this))
}.bind(this)).run()
}
/*
......@@ -209,13 +211,9 @@ module.exports = (function() {
* @return null if and only if validation successful; otherwise an object containing { field name : [error msgs] } entries.
*/
DAO.prototype.validate = function() {
var validator = new DaoValidator(this)
, errors = validator.validate()
return (Utils._.isEmpty(errors) ? null : errors)
return new DaoValidator(this).validate()
}
DAO.prototype.updateAttributes = function(updates, fields) {
this.setAttributes(updates)
return this.save(fields)
......
......@@ -6,13 +6,13 @@ var util = require("util")
var bindToProcess = function(fct) {
if (fct) {
if(process.domain) {
return process.domain.bind(fct);
}
if (process.domain) {
return process.domain.bind(fct)
}
}
return fct;
};
return fct
}
module.exports = (function() {
var CustomEventEmitter = function(fct) {
......@@ -54,6 +54,7 @@ module.exports = (function() {
return this
}
// emit the events on the foreign emitter once events got triggered for me
CustomEventEmitter.prototype.proxy = function(emitter) {
proxyEventKeys.forEach(function (eventKey) {
this.on(eventKey, function (result) {
......@@ -73,7 +74,5 @@ module.exports = (function() {
}).then(onFulfilled, onRejected)
}
return CustomEventEmitter;
return CustomEventEmitter
})()
......@@ -201,7 +201,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
for (var i = 0; i < validatorDetails.fail.length; i++) {
var failingValue = validatorDetails.fail[i]
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function() {
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {}
, message = validator + "(" + failingValue + ")"
......@@ -221,11 +221,15 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
})
var failingUser = UserFail.build({ name : failingValue })
, errors = failingUser.validate()
, errors = undefined;
expect(errors).not.toBeNull()
expect(errors).toEqual({ name : [message] })
})
failingUser.validate().done( function(err,_errors) {
expect(_errors).not.toBeNull();
expect(_errors).toEqual({ name : [message] });
done();
});
});
}
////////////////////////////
......@@ -234,7 +238,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
for (var j = 0; j < validatorDetails.pass.length; j++) {
var succeedingValue = validatorDetails.pass[j]
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function() {
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {}
if (validatorDetails.hasOwnProperty('spec')) {
......@@ -252,21 +256,30 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
}
})
var successfulUser = UserSuccess.build({ name: succeedingValue })
expect(successfulUser.validate()).toBeNull()
var successfulUser = UserSuccess.build({ name: succeedingValue });
successfulUser.validate().success( function() {
expect(arguments.length).toBe(0);
done();
}).error(function(err) {
expect(err).toBe({})
done();
});
})
}
}
}
it('correctly validates using custom validation methods', function() {
it('correctly validates using custom validation methods', function(done) {
var User = this.sequelize.define('User' + Math.random(), {
name: {
type: Sequelize.STRING,
validate: {
customFn: function(val) {
customFn: function(val,next) {
if (val !== "2") {
throw new Error("name should equal '2'")
next("name should equal '2'")
} else {
next()
}
}
}
......@@ -274,16 +287,23 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
})
var failingUser = User.build({ name : "3" })
, errors = failingUser.validate()
expect(errors).not.toBeNull(null)
expect(errors).toEqual({ name: ["name should equal '2'"] })
failingUser.validate().error(function(errors) {
expect(errors).toEqual({ name: ["name should equal '2'"] })
var successfulUser = User.build({ name : "2" })
successfulUser.validate().success(function() {
expect(arguments.length).toBe(0);
done();
}).error(function(err) {
expect(err).toBe({});
done();
})
});
var successfulUser = User.build({ name : "2" })
expect(successfulUser.validate()).toBeNull()
})
it('skips other validations if allowNull is true and the value is null', function() {
it('skips other validations if allowNull is true and the value is null', function(done) {
var User = this.sequelize.define('User' + Math.random(), {
age: {
type: Sequelize.INTEGER,
......@@ -294,20 +314,22 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
}
})
var failingUser = User.build({ age: -1 })
, errors = failingUser.validate()
expect(errors).not.toBeNull(null)
expect(errors).toEqual({ age: ['must be positive'] })
var successfulUser1 = User.build({ age: null })
expect(successfulUser1.validate()).toBeNull()
User
.build({ age: -1 })
.validate()
.success(function(errors) {
expect(errors).not.toBeNull(null)
expect(errors).toEqual({ age: ['must be positive'] })
var successfulUser2 = User.build({ age: 1 })
expect(successfulUser2.validate()).toBeNull()
User.build({ age: null }).validate().success(function() {
User.build({ age: 1 }).validate().success(function() {
done()
})
})
})
})
it('validates a model with custom model-wide validation methods', function() {
it('validates a model with custom model-wide validation methods', function(done) {
var Foo = this.sequelize.define('Foo' + Math.random(), {
field1: {
type: Sequelize.INTEGER,
......@@ -319,22 +341,32 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
}
}, {
validate: {
xnor: function() {
xnor: function(done) {
if ((this.field1 === null) === (this.field2 === null)) {
throw new Error('xnor failed');
done('xnor failed')
} else {
done()
}
}
}
})
var failingFoo = Foo.build({ field1: null, field2: null })
, errors = failingFoo.validate()
Foo
.build({ field1: null, field2: null })
.validate()
.success(function(errors) {
expect(errors).not.toBeNull()
expect(errors).toEqual({ 'xnor': ['xnor failed'] })
expect(errors).not.toBeNull()
expect(errors).toEqual({ 'xnor': ['xnor failed'] })
var successfulFoo = Foo.build({ field1: 33, field2: null })
expect(successfulFoo.validate()).toBeNull()
Foo
.build({ field1: 33, field2: null })
.validate()
.success(function(errors) {
console.log(errors)
expect(errors).not.toBeDefined()
done()
})
})
})
})
})
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!