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

Commit 56237112 by Ram

Added ability to supply custom validation methods. Each custom validation method…

… recieves the field value to validate as its sole argument. It should throw an error to indicate a validation failure. For example:

<pre>
      User = sequelize.define('User', {
        name: {
          type: Sequelize.STRING,
          validate: {
              len: {
                args: 9,
		msg: "Need atleast 9 chars"
              },
              customFn1: function(val) {
                # if name doesn't start with 'joe' then fail
                if (val.substr(0,3) !== "joe")
                    throw new Error("name should start with 'joe'")
              },
              customFn2: function(val) {
                # if name doesn't end in 'bloggs' then fail
                if (val.substr(-6) !== "bloggs")
                    throw new Error("name should end with 'bloggs'")
              }
          }
        }
      });
</pre>

You still invoke validation as you normally would, i.e.:

<pre>
# build user
var user = User.build({
    name : "test"
});

# validate
errors = user.validate();
# At this point errors will contain:
#
# { name : [
#	"Need atleast 9 chars"
#	"name should start with 'joe'"
#	"name should end with 'bloggs'"
# ] }
#
</pre>
1 parent 6ae3b957
Showing with 258 additions and 212 deletions
......@@ -65,24 +65,38 @@ module.exports = (function() {
// for each validator
Utils._.each(self.validators[field], function(details, validatorType) {
// validator arguments
var fn_args = details.hasOwnProperty("args") ? details.args : []
if (!Utils._.isArray(fn_args))
fn_args = [fn_args]
// error msg
var fn_msg = details.hasOwnProperty("msg") ? details.msg : false
var validator = Validator.check(value, fn_msg)
if (!Utils._.isFunction(validator[validatorType]))
throw new Error("Invalid validator function: " + validatorType)
var is_custom_fn = false // if true then it's a custom validation method
var fn_method = null // the validation function to call
var fn_args = [] // extra arguments to pass to validation function
var fn_msg = "" // the error message to return if validation fails
// is it a custom validator function?
if (Utils._.isFunction(details)) {
is_custom_fn = true
fn_method = Utils._.bind(details, self, value)
}
// is it a validator module function?
else {
// extra args
fn_args = details.hasOwnProperty("args") ? details.args : []
if (!Utils._.isArray(fn_args))
fn_args = [fn_args]
// error msg
fn_msg = details.hasOwnProperty("msg") ? details.msg : false
// check method exists
var v = Validator.check(value, fn_msg)
if (!Utils._.isFunction(v[validatorType]))
throw new Error("Invalid validator function: " + validatorType)
// bind to validator obj
fn_method = Utils._.bind(v[validatorType], v)
}
try {
validator[validatorType].apply(validator, fn_args)
fn_method.apply(null, fn_args)
} catch (err) {
err = err.message
// if we didn't provide a custom error message then augment the default one returned by the validator
if (!fn_msg)
if (!fn_msg && !is_custom_fn)
err += ": " + field
// each field can have multiple validation failures stored against it
if (failures.hasOwnProperty(field)) {
......
......@@ -72,217 +72,249 @@ describe('Model', function() {
})
})
describe('Model', function() {
describe('Validations', function() {
var checks = {
is : {
spec : { args: [/[a-z]/,'i'] },
fail: "0",
pass: "a"
}
, not : {
spec: { args: [/[a-z]/,'i'] },
fail: "a",
pass: "0"
}
, isEmail : {
fail: "a",
pass: "abc@abc.com"
}
, isUrl : {
fail: "abc",
pass: "http://abc.com"
}
, isIP : {
fail: "abc",
pass: "129.89.23.1"
}
, isAlpha : {
fail: "012",
pass: "abc"
}
, isAlphanumeric : {
fail: "_abc019",
pass: "abc019"
}
, isNumeric : {
fail: "abc",
pass: "019"
}
, isInt : {
fail: "9.2",
pass: "-9"
}
, isLowercase : {
fail: "AB",
pass: "ab"
}
, isUppercase : {
fail: "ab",
pass: "AB"
}
, isDecimal : {
fail: "a",
pass: "0.2"
}
, isFloat : {
fail: "a",
pass: "9.2"
}
, notNull : {
fail: null,
pass: 0
}
, isNull : {
fail: 0,
pass: null
}
, notEmpty : {
fail: " ",
pass: "a"
}
, equals : {
spec : { args : "bla bla bla" },
fail: "bla",
pass: "bla bla bla"
}
, contains : {
spec : { args : "bla" },
fail: "la",
pass: "0bla23"
}
, notContains : {
spec : { args : "bla" },
fail: "0bla23",
pass: "la"
}
, regex : {
spec : { args: [/[a-z]/,'i'] },
fail: "0",
pass: "a"
}
, notRegex : {
spec: { args: [/[a-z]/,'i'] },
fail: "a",
pass: "0"
}
, len : {
spec: { args: [2,4] },
fail: ["1", "12345"],
pass: ["12", "123", "1234"],
raw: true
}
, isUUID : {
spec: { args: 4 },
fail: "f47ac10b-58cc-3372-a567-0e02b2c3d479",
pass: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
, isDate : {
fail: "not a date",
pass: "2011-02-04"
}
, isAfter : {
spec: { args: "2011-11-05" },
fail: "2011-11-04",
pass: "2011-11-05"
}
, isBefore : {
spec: { args: "2011-11-05" },
fail: "2011-11-06",
pass: "2011-11-05"
}
, isIn : {
spec: { args: "abcdefghijk" },
fail: "ghik",
pass: "ghij"
}
, notIn : {
spec: { args: "abcdefghijk" },
fail: "ghij",
pass: "ghik"
}
, max : {
spec: { args: 23 },
fail: "24",
pass: "23"
}
, min : {
spec: { args: 23 },
fail: "22",
pass: "23"
}
, isArray : {
fail: 22,
pass: [22]
}
, isCreditCard : {
fail: "401288888888188f",
pass: "4012888888881881"
}
};
describe('Validations', function() {
var checks = {
is : {
spec : { args: [/[a-z]/,'i'] },
fail: "0",
pass: "a"
}
, not : {
spec: { args: [/[a-z]/,'i'] },
fail: "a",
pass: "0"
}
, isEmail : {
fail: "a",
pass: "abc@abc.com"
}
, isUrl : {
fail: "abc",
pass: "http://abc.com"
}
, isIP : {
fail: "abc",
pass: "129.89.23.1"
}
, isAlpha : {
fail: "012",
pass: "abc"
}
, isAlphanumeric : {
fail: "_abc019",
pass: "abc019"
}
, isNumeric : {
fail: "abc",
pass: "019"
}
, isInt : {
fail: "9.2",
pass: "-9"
}
, isLowercase : {
fail: "AB",
pass: "ab"
}
, isUppercase : {
fail: "ab",
pass: "AB"
}
, isDecimal : {
fail: "a",
pass: "0.2"
}
, isFloat : {
fail: "a",
pass: "9.2"
}
, notNull : {
fail: null,
pass: 0
}
, isNull : {
fail: 0,
pass: null
}
, notEmpty : {
fail: " ",
pass: "a"
}
, equals : {
spec : { args : "bla bla bla" },
fail: "bla",
pass: "bla bla bla"
}
, contains : {
spec : { args : "bla" },
fail: "la",
pass: "0bla23"
}
, notContains : {
spec : { args : "bla" },
fail: "0bla23",
pass: "la"
}
, regex : {
spec : { args: [/[a-z]/,'i'] },
fail: "0",
pass: "a"
}
, notRegex : {
spec: { args: [/[a-z]/,'i'] },
fail: "a",
pass: "0"
}
, len : {
spec: { args: [2,4] },
fail: ["1", "12345"],
pass: ["12", "123", "1234"],
raw: true
}
, isUUID : {
spec: { args: 4 },
fail: "f47ac10b-58cc-3372-a567-0e02b2c3d479",
pass: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
, isDate : {
fail: "not a date",
pass: "2011-02-04"
}
, isAfter : {
spec: { args: "2011-11-05" },
fail: "2011-11-04",
pass: "2011-11-05"
}
, isBefore : {
spec: { args: "2011-11-05" },
fail: "2011-11-06",
pass: "2011-11-05"
}
, isIn : {
spec: { args: "abcdefghijk" },
fail: "ghik",
pass: "ghij"
}
, notIn : {
spec: { args: "abcdefghijk" },
fail: "ghij",
pass: "ghik"
}
, max : {
spec: { args: 23 },
fail: "24",
pass: "23"
}
, min : {
spec: { args: 23 },
fail: "22",
pass: "23"
}
, isArray : {
fail: 22,
pass: [22]
}
, isCreditCard : {
fail: "401288888888188f",
pass: "4012888888881881"
}
};
var User, i;
var User, i;
it('should correctly validate according to supplied rules', function() {
Helpers.async(function(done) {
for (var validator in checks) {
if (checks.hasOwnProperty(validator)) {
// build spec
var v = {};
v[validator] = checks[validator].hasOwnProperty("spec") ? checks[validator].spec : {};
it('should correctly validate using node-validator methods', function() {
Helpers.async(function(done) {
for (var validator in checks) {
if (checks.hasOwnProperty(validator)) {
// build spec
var v = {};
v[validator] = checks[validator].hasOwnProperty("spec") ? checks[validator].spec : {};
var check = checks[validator];
var check = checks[validator];
// test for failure
if (!check.hasOwnProperty("raw"))
check.fail = new Array(check.fail);
// test for failure
if (!check.hasOwnProperty("raw"))
check.fail = new Array(check.fail);
for (i=0; i<check.fail.length; ++i) {
v[validator].msg = validator + "(" + check.fail[i] + ")";
for (i=0; i<check.fail.length; ++i) {
v[validator].msg = validator + "(" + check.fail[i] + ")";
// define user
User = sequelize.define('User' + Math.random(), {
name: {
type: Sequelize.STRING,
validate: v
}
});
// define user
User = sequelize.define('User' + Math.random(), {
name: {
type: Sequelize.STRING,
validate: v
}
});
var u_fail = User.build({
name : check.fail[i]
});
var errors = u_fail.validate();
expect(errors).toNotBe(null);
expect(errors).toEqual({
name : [v[validator].msg]
});
}
// test for success
if (!check.hasOwnProperty("raw"))
check.pass = new Array(check.pass);
var u_fail = User.build({
name : check.fail[i]
});
var errors = u_fail.validate();
expect(errors).toNotBe(null);
expect(errors).toEqual({
name : [v[validator].msg]
});
}
// test for success
if (!check.hasOwnProperty("raw"))
check.pass = new Array(check.pass);
for (i=0; i<check.pass.length; ++i) {
v[validator].msg = validator + "(" + check.pass[i] + ")";
for (i=0; i<check.pass.length; ++i) {
v[validator].msg = validator + "(" + check.pass[i] + ")";
// define user
User = sequelize.define('User' + Math.random(), {
name: {
type: Sequelize.STRING,
validate: v
}
});
// define user
User = sequelize.define('User' + Math.random(), {
name: {
type: Sequelize.STRING,
validate: v
}
});
var u_success = User.build({
name : check.pass[i]
});
expect(u_success.validate()).toBe(null);
}
var u_success = User.build({
name : check.pass[i]
});
expect(u_success.validate()).toBe(null);
}
} // for each check
}
} // for each check
done();
});
});
done();
it('should correctly validate using custom validation methods', function() {
Helpers.async(function(done) {
User = sequelize.define('User' + Math.random(), {
name: {
type: Sequelize.STRING,
validate: {
customFn: function(val) {
if (val !== "2")
throw new Error("name should equal '2'")
}
}
}
});
var u_fail = User.build({
name : "3"
});
var errors = u_fail.validate();
expect(errors).toNotBe(null);
expect(errors).toEqual({
name : ["name should equal '2'"]
});
var u_success = User.build({
name : "2"
});
expect(u_success.validate()).toBe(null);
done();
});
})
});
});
})
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!