diff --git a/specs/validators/format-spec.js b/specs/validators/format-spec.js index 0551e2d..2496ccf 100644 --- a/specs/validators/format-spec.js +++ b/specs/validators/format-spec.js @@ -1,7 +1,10 @@ describe("validators.format", function() { var format = validate.validators.format.bind(validate.validators.format) , options1 = {pattern: /^foobar$/i} - , options2 = {pattern: "^foobar$", flags: "i"}; + , options2 = {pattern: "^foobar$", flags: "i"} + , options3 = {pattern: /^foobar$/i, excludes: [function (v) { + return v === ''; + }]}; afterEach(function() { delete validate.validators.format.message; @@ -11,13 +14,17 @@ describe("validators.format", function() { it("allows empty values", function() { expect(format(null, options1)).not.toBeDefined(); expect(format(null, options2)).not.toBeDefined(); + expect(format(null, options3)).not.toBeDefined(); expect(format(undefined, options1)).not.toBeDefined(); expect(format(undefined, options2)).not.toBeDefined(); + expect(format(undefined, options3)).not.toBeDefined(); + expect(format("", options3)).not.toBeDefined(); }); it("allows values that matches the pattern", function() { expect(format("fooBAR", options1)).not.toBeDefined(); expect(format("fooBAR", options2)).not.toBeDefined(); + expect(format("fooBAR", options3)).not.toBeDefined(); }); it("doesn't allow values that doesn't matches the pattern", function() { @@ -25,21 +32,26 @@ describe("validators.format", function() { expect(format("", options2)).toBeDefined("is invalid"); expect(format(" ", options1)).toBeDefined("is invalid"); expect(format(" ", options2)).toBeDefined("is invalid"); + expect(format(" ", options3)).toBeDefined("is invalid"); expect(format("barfoo", options1)).toEqual("is invalid"); expect(format("barfoo", options2)).toEqual("is invalid"); + expect(format("barfoo", options3)).toBeDefined("is invalid"); }); it("non strings are not allowed", function() { var obj = {toString: function() { return "foobar"; }}; expect(format(obj, options1)).toBeDefined(); expect(format(obj, options2)).toBeDefined(); + expect(format(obj, options3)).toBeDefined(); expect(format(3, options1)).toBeDefined(); expect(format(3, options2)).toBeDefined(); + expect(format(3, options3)).toBeDefined(); }); it("non strings are not allowed", function() { expect(format(3, options1)).toBeDefined(); expect(format(3, options2)).toBeDefined(); + expect(format(3, options3)).toBeDefined(); }); it("doesn't allow partial matches", function() { diff --git a/specs/validators/inclusion-spec.js b/specs/validators/inclusion-spec.js index 950280d..0abef6d 100644 --- a/specs/validators/inclusion-spec.js +++ b/specs/validators/inclusion-spec.js @@ -10,6 +10,10 @@ describe("validators.inclusion", function() { it("allows empty values", function() { expect(inclusion(null, {})).not.toBeDefined(); expect(inclusion(undefined, {})).not.toBeDefined(); + expect(inclusion('', { + within: within, + excludes: [function (v) { return v === ''; }], + })).not.toBeDefined(); }); it("returns nothing if the value is allowed", function() { diff --git a/specs/validators/numericality-spec.js b/specs/validators/numericality-spec.js index 4a127a2..bdeaf1e 100644 --- a/specs/validators/numericality-spec.js +++ b/specs/validators/numericality-spec.js @@ -21,6 +21,11 @@ describe("validators.numericality", function() { it("allows empty values", function() { expect(numericality(null, {})).not.toBeDefined(); expect(numericality(undefined, {})).not.toBeDefined(); + expect(numericality('', {excludes: [{ + presence: { + allowEmpty: true, + }, + }]})).not.toBeDefined(); }); it("allows numbers", function() { diff --git a/validate.js b/validate.js index e1c9816..7446d9c 100644 --- a/validate.js +++ b/validate.js @@ -350,6 +350,29 @@ return false; }, + shouldValueBeExcluded: function (value, hooks) { + var ret = false; + + for (var i = 0; i < hooks.length; i++) { + var hook = hooks[i]; + + if (v.isFunction(hook)) { + // Hook is a custom function + ret = !!hook(value); // Coerce to boolean + } else if (v.isObject(hook)) { + // Hook is a constraint + // Negate results from isDefined as we are talking about exclude + ret = !v.isDefined(v.single(value, hook)); + } + + if (ret) { + break; + } + } + + return ret; + }, + // Formats the specified strings with the given values like so: // ``` // format("Foo: %{foo}", {foo: "bar"}) // "Foo bar" @@ -818,6 +841,13 @@ return; } + // Allows users to exclude value before further processing + var hooks = options.excludes || []; + if (v.shouldValueBeExcluded(value, hooks)) { + // Return if given value should be exclude + return; + } + options = v.extend({}, this.options, options); var errors = [] @@ -992,6 +1022,13 @@ return message; } + // Allows users to exclude value before further processing + var hooks = options.excludes || []; + if (v.shouldValueBeExcluded(value, hooks)) { + // Return if given value should be exclude + return; + } + if (v.isString(pattern)) { pattern = new RegExp(options.pattern, options.flags); } @@ -1008,6 +1045,14 @@ if (v.isArray(options)) { options = {within: options}; } + + // Allows users to exclude value before further processing + var hooks = options.excludes || []; + if (v.shouldValueBeExcluded(value, hooks)) { + // Return if given value should be exclude + return; + } + options = v.extend({}, this.options, options); if (v.contains(options.within, value)) { return;