From 9dcd976546c390b3fe4e8fc81a83403e1e8dec9c Mon Sep 17 00:00:00 2001 From: Anatoliy Chakkaev Date: Sat, 19 Nov 2011 01:59:46 +0700 Subject: [PATCH] Rewrite custom validation --- lib/validatable.js | 38 ++++++++++++++++++++---------------- test/hookable_test.coffee | 19 +++++++++--------- test/validations_test.coffee | 12 ++++++++---- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/lib/validatable.js b/lib/validatable.js index 858f5f11..53dac713 100644 --- a/lib/validatable.js +++ b/lib/validatable.js @@ -10,17 +10,7 @@ Validatable.validatesNumericalityOf = getConfigurator('numericality'); Validatable.validatesInclusionOf = getConfigurator('inclusion'); Validatable.validatesExclusionOf = getConfigurator('exclusion'); Validatable.validatesFormatOf = getConfigurator('format'); -Validatable.validate = function () { - args = [].slice.call(arguments); - var valFn = function () {}; // noop - if (typeof args[args.length - 1] === 'function'){ - valFn = args.pop(); - } - wrapperFn = function customValidator(attr, conf, err) { - return valFn.call(this, err); - }; - configure(this, wrapperFn, args) -}; +Validatable.validate = getConfigurator('custom'); // implementation of validators var validators = { @@ -77,13 +67,16 @@ var validators = { } else { err(); } + }, + custom: function (attr, conf, err) { + conf.customValidator.call(this, err); } }; -function getConfigurator(name) { +function getConfigurator(name, opts) { return function () { - configure(this, name, arguments); + configure(this, name, arguments, opts); }; } @@ -124,6 +117,9 @@ function cleanErrors(inst) { function validationFailed(inst, v) { var attr = v[0]; var conf = v[1]; + var opts = v[2] || {}; + + if (typeof attr !== 'string') return false; // here we should check skip validation conditions (if, unless) // that can be specified in conf @@ -131,8 +127,11 @@ function validationFailed(inst, v) { if (skipValidation(inst, conf, 'unless')) return false; var fail = false; - var validator = typeof conf.validation === "function" ? conf.validation : validators[conf.validation]; - validator.call(inst, attr, conf, function onerror(kind) { + var validator = validators[conf.validation]; + var validatorArguments = []; + validatorArguments.push(attr); + validatorArguments.push(conf); + validatorArguments.push(function onerror(kind) { var message; if (conf.message) { message = conf.message; @@ -154,6 +153,7 @@ function validationFailed(inst, v) { inst.errors.add(attr, message); fail = true; }); + validator.apply(inst, validatorArguments); return fail; } @@ -221,7 +221,7 @@ function blank(v) { return false; } -function configure(cls, validation, args) { +function configure(cls, validation, args, opts) { if (!cls._validations) { Object.defineProperty(cls, '_validations', { writable: true, @@ -237,9 +237,12 @@ function configure(cls, validation, args) { } else { conf = {}; } + if (validation === 'custom' && typeof args[args.length - 1] === 'function') { + conf.customValidator = args.pop(); + } conf.validation = validation; args.forEach(function (attr) { - cls._validations.push([attr, conf]); + cls._validations.push([attr, conf, opts]); }); } @@ -253,3 +256,4 @@ Errors.prototype.add = function (field, message) { this[field].push(message); } }; + diff --git a/test/hookable_test.coffee b/test/hookable_test.coffee index df04a902..95566498 100644 --- a/test/hookable_test.coffee +++ b/test/hookable_test.coffee @@ -18,62 +18,63 @@ User = schema.define 'User', createdByAdmin: Boolean it "should trigger after initialize", (test)-> - User.afterInitialize = ()-> + User.afterInitialize = ()-> User.afterInitialize = null test.done() user = new User it "should trigger before create", (test)-> - User.beforeCreate = ()-> + User.beforeCreate = ()-> User.beforeCreate = null test.done() User.create {}, ()-> test.ok "saved" it "should trigger after create", (test)-> - User.afterCreate = ()-> + User.afterCreate = ()-> User.afterCreate = null test.done() User.create {}, ()-> test.ok "saved" it "should trigger before save", (test)-> - User.beforeSave = ()-> + User.beforeSave = ()-> User.beforeSave = null test.done() user = new User user.save ()-> test.ok "saved" it "should trigger after save", (test)-> - User.afterSave = ()-> + User.afterSave = ()-> User.afterSave = null test.done() user = new User user.save ()-> test.ok "saved" it "should trigger before update", (test)-> - User.beforeUpdate = ()-> + User.beforeUpdate = ()-> User.beforeUpdate = null test.done() User.create {}, (err, user)-> user.updateAttributes {email:"1@1.com"}, ()-> test.ok "updated" it "should trigger after update", (test)-> - User.afterUpdate = ()-> + User.afterUpdate = ()-> User.afterUpdate = null test.done() User.create {}, (err, user)-> user.updateAttributes {email:"1@1.com"}, ()-> test.ok "updated" it "should trigger before destroy", (test)-> - User.beforeDestroy = ()-> + User.beforeDestroy = ()-> User.beforeDestroy = null test.done() User.create {}, (err, user)-> user.destroy() it "should trigger after destroy", (test)-> - User.afterDestroy = ()-> + User.afterDestroy = ()-> User.afterDestroy = null test.done() User.create {}, (err, user)-> user.destroy() + diff --git a/test/validations_test.coffee b/test/validations_test.coffee index a8c27835..1cec4f2b 100644 --- a/test/validations_test.coffee +++ b/test/validations_test.coffee @@ -214,16 +214,20 @@ it 'should validate format', (test) -> test.done() -it 'should validate a field using a custom validator', (test)-> +it 'should validate a field using a custom validator', (test) -> - User.validate 'email', (err)-> err("crash") if @email.length is 0 + validator = (err) -> + err('crash') if @name == 'bad name' + + User.validate 'name', validator, message: crash: 'custom message' user = new User validAttributes test.ok user.isValid() user = new User validAttributes - user.email = "" - test.ok not user.isValid() + user.name = 'bad name' + test.ok not user.isValid(), 'invalid due custom name validator' + test.equal user.errors.name[0], 'custom message' test.done()