diff --git a/lib/dao.js b/lib/dao.js index 0edc2adc..9eae88d2 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -320,7 +320,7 @@ DataAccessObject.create = function(data, options, cb) { } else { cb(new ValidationError(obj), obj); } - }, data); + }, data, options); }); function create() { @@ -543,7 +543,7 @@ DataAccessObject.upsert = function(data, options, cb) { } } callConnector(); - }, update); + }, update, options); } function callConnector() { @@ -732,7 +732,7 @@ DataAccessObject.replaceOrCreate = function replaceOrCreate(data, options, cb) { inst.isValid(function(valid) { if (!valid) return cb(new ValidationError(inst), inst); callConnector(); - }, update); + }, update, options); function callConnector() { update = removeUndefined(update); @@ -1002,7 +1002,7 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) } else { cb(new ValidationError(obj), obj); } - }, data); + }, data, options); }); }); } else { @@ -2189,7 +2189,7 @@ DataAccessObject.prototype.save = function(options, cb) { } cb(err, inst); } - }); + }, data, options); // then save function save() { @@ -2701,7 +2701,7 @@ DataAccessObject.replaceById = function(id, data, options, cb) { if (!valid) return cb(new ValidationError(inst), inst); callConnector(); - }, data); + }, data, options); } else { callConnector(); } @@ -2889,7 +2889,7 @@ function(data, options, cb) { } triggerSave(); - }, data); + }, data, options); } else { triggerSave(); } diff --git a/lib/validations.js b/lib/validations.js index 6ccc701c..ebf5e5a3 100644 --- a/lib/validations.js +++ b/lib/validations.js @@ -238,7 +238,7 @@ Validatable.validatesUniquenessOf = getConfigurator('uniqueness', { async: true /*! * Presence validator */ -function validatePresence(attr, conf, err) { +function validatePresence(attr, conf, err, options) { if (blank(this[attr])) { err(); } @@ -247,7 +247,7 @@ function validatePresence(attr, conf, err) { /*! * Absence validator */ -function validateAbsence(attr, conf, err) { +function validateAbsence(attr, conf, err, options) { if (!blank(this[attr])) { err(); } @@ -256,7 +256,7 @@ function validateAbsence(attr, conf, err) { /*! * Length validator */ -function validateLength(attr, conf, err) { +function validateLength(attr, conf, err, options) { if (nullCheck.call(this, attr, conf, err)) return; var len = this[attr].length; @@ -274,7 +274,7 @@ function validateLength(attr, conf, err) { /*! * Numericality validator */ -function validateNumericality(attr, conf, err) { +function validateNumericality(attr, conf, err, options) { if (nullCheck.call(this, attr, conf, err)) return; if (typeof this[attr] !== 'number') { @@ -288,7 +288,7 @@ function validateNumericality(attr, conf, err) { /*! * Inclusion validator */ -function validateInclusion(attr, conf, err) { +function validateInclusion(attr, conf, err, options) { if (nullCheck.call(this, attr, conf, err)) return; if (!~conf.in.indexOf(this[attr])) { @@ -299,7 +299,7 @@ function validateInclusion(attr, conf, err) { /*! * Exclusion validator */ -function validateExclusion(attr, conf, err) { +function validateExclusion(attr, conf, err, options) { if (nullCheck.call(this, attr, conf, err)) return; if (~conf.in.indexOf(this[attr])) { @@ -310,7 +310,7 @@ function validateExclusion(attr, conf, err) { /*! * Format validator */ -function validateFormat(attr, conf, err) { +function validateFormat(attr, conf, err, options) { if (nullCheck.call(this, attr, conf, err)) return; if (typeof this[attr] === 'string') { @@ -325,14 +325,22 @@ function validateFormat(attr, conf, err) { /*! * Custom validator */ -function validateCustom(attr, conf, err, done) { +function validateCustom(attr, conf, err, options, done) { + if (typeof options === 'function') { + done = options; + options = {}; + } conf.customValidator.call(this, err, done); } /*! * Uniqueness validator */ -function validateUniqueness(attr, conf, err, done) { +function validateUniqueness(attr, conf, err, options, done) { + if (typeof options === 'function') { + done = options; + options = {}; + } if (blank(this[attr])) { return process.nextTick(done); } @@ -349,7 +357,7 @@ function validateUniqueness(attr, conf, err, done) { var idName = this.constructor.definition.idName(); var isNewRecord = this.isNewRecord(); - this.constructor.find(cond, function(error, found) { + this.constructor.find(cond, options, function(error, found) { if (error) { err(error); } else if (found.length > 1) { @@ -416,7 +424,8 @@ function getConfigurator(name, opts) { * @param {Function} callback called with (valid) * @returns {Boolean} True if no asynchronous validation is configured and all properties pass validation. */ -Validatable.prototype.isValid = function(callback, data) { +Validatable.prototype.isValid = function(callback, data, options) { + options = options || {}; var valid = true, inst = this, wait = 0, async = false; var validations = this.constructor.validations; @@ -455,7 +464,7 @@ Validatable.prototype.isValid = function(callback, data) { async = true; wait += 1; process.nextTick(function() { - validationFailed(inst, attr, v, done); + validationFailed(inst, attr, v, options, done); }); } else { if (validationFailed(inst, attr, v)) { @@ -514,9 +523,14 @@ function cleanErrors(inst) { }); } -function validationFailed(inst, attr, conf, cb) { +function validationFailed(inst, attr, conf, options, cb) { var opts = conf.options || {}; + if (typeof options === 'function') { + cb = options; + options = {}; + } + if (typeof attr !== 'string') return false; // here we should check skip validation conditions (if, unless) @@ -557,6 +571,7 @@ function validationFailed(inst, attr, conf, cb) { if (kind !== false) inst.errors.add(attr, message, code); fail = true; }); + validatorArguments.push(options); if (cb) { validatorArguments.push(function() { cb(fail); diff --git a/test/validations.test.js b/test/validations.test.js index 3d69ed2f..09d16fe3 100644 --- a/test/validations.test.js +++ b/test/validations.test.js @@ -282,6 +282,64 @@ describe('validations', function() { }); }); + describe('validation with or without options', function() { + it('should work on update with options', function(done) { + delete User.validations; + User.validatesPresenceOf('name'); + User.create({ name: 'Valid' }, function(e, d) { + d.updateAttribute('name', null, { options: 'options' }, function(e) { + should.exist(e); + e.should.be.instanceOf(Error); + e.should.be.instanceOf(ValidationError); + d.updateAttribute('name', 'Vasiliy', { options: 'options' }, function(e) { + should.not.exist(e); + done(); + }); + }); + }); + }); + + it('should work on update without options', function(done) { + delete User.validations; + User.validatesPresenceOf('name'); + User.create({ name: 'Valid' }, function(e, d) { + d.updateAttribute('name', null, function(e) { + should.exist(e); + e.should.be.instanceOf(Error); + e.should.be.instanceOf(ValidationError); + d.updateAttribute('name', 'Vasiliy', function(e) { + should.not.exist(e); + done(); + }); + }); + }); + }); + + it('should work on create with options', function(done) { + delete User.validations; + User.validatesPresenceOf('name'); + User.create(function(e, u) { + should.exist(e); + User.create({ name: 'Valid' }, { options: 'options' }, function(e, d) { + should.not.exist(e); + done(); + }); + }); + }); + + it('should work on create without options', function(done) { + delete User.validations; + User.validatesPresenceOf('name'); + User.create(function(e, u) { + should.exist(e); + User.create({ name: 'Valid' }, function(e, d) { + should.not.exist(e); + done(); + }); + }); + }); + }); + describe('presence', function() { it('should validate presence', function() { User.validatesPresenceOf('name', 'email');