Skip to content

Commit

Permalink
doc fixups, get rid of concept of tests -- they're validations
Browse files Browse the repository at this point in the history
  • Loading branch information
bronson committed Oct 4, 2011
1 parent 6af5a21 commit 4694fca
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 61 deletions.
47 changes: 26 additions & 21 deletions README.md
Expand Up @@ -20,16 +20,20 @@ A lightweight, chaining validation library.

var Schema = {
Name: Valid.notBlank(),
Numbers: Valid.array(Valid.integer()).len(2,5),
State: /^[A-Z][A-Z]$/,
Country: "US"
Numbers: Valid.array(Valid.integer()).len(2,5), // an array of 2, 3, 4, or 5 integers
Address: {
State: /^[A-Z][A-Z]$/, // shortcut for Valid.match(/^[A-Z][A-Z]$/)
Country: "US"
}
};

var data = {
Name: "Jed",
Numbers: [1, 9, 25],
State: "CA",
Country: "US"
Address: {
State: "CA",
Country: "US"
}
}

Valid.json(Schema).verify(data);
Expand Down Expand Up @@ -72,20 +76,21 @@ for JSON validations (see _Errors_ below).
This is probably incomplete.
See [valid.js](https://github.com/bronson/valid/blob/master/lib/valid.js).

- equal(a[,b...]), notEqual(...), oneOf(arrayOrObject)
- defined(), undef(), undefined()
- nil(), null(), notNull()
- exists(), noexisty() (!)
- array([test]), len(min,max), empty()
- boolean(), true(), false()
- number(), integer(), mod(x[,rem]), even(), odd(), max(n), min(n)
- string([test]), len(min,max), blank(), notBlank()
- match(regex[,modifiers]), nomatch(regex[,modifiers])
- eq(n), lt(n), le(n), ge(n), gt(n), ne(n)
- nop(), fail([message]), messageFor(test,message), todo([test])
- and(test[,test...]), or(test[,test...]), not(test,message)
- json(schema)

- Presence: defined(), undef(), undefined\*(), nil(), null\*(), notNull()
- Equality: equal(a[,b...]), notEqual(...), oneOf(arrayOrObject), in\*(arrayOrObject)
- Comparison: eq(n), lt(n), le(n), ge(n), gt(n), ne(n)
- Numbers: number(), integer(), mod(x[,rem]), even(), odd()
- Booleans: boolean(), isTrue(), true\*(), isFalse(), false\*()
- Arrays: array([validationForEachItem]), len(min,max), empty()
- Strings: string(), len(min,max), blank(), notBlank()
- Regexps: match(regex[,modifiers]), nomatch(regex[,modifiers])
- Logic: and(test[,test...]), or(test[,test...]), not(test,message)
- Utilities: nop(), fail([message]), messageFor(test,message), todo([test])
- JSON: json(schema)

\*: These are JavaScript keywords. While `Valid.undefined()` will work
with a lot of interpreters, it won't work everywhere.
Each keyword validation has a more compatible alternative: Valid.undef(), Valid.nil(), etc.

# Errors

Expand All @@ -108,7 +113,7 @@ the value that failed to validate.

# Extending Valid

To define your own tests, just end the chain with "define()"
To define your own validations, just end the chain with "define()"
and add it to the root object:

```javascript
Expand All @@ -117,7 +122,7 @@ and add it to the root object:
Valid.integer().latitude().verify(20); // success!
```

You can also add tests that take parameters:
You can also add validations that take parameters:

```javascript
Valid.mod10 = function(rem) { return this.mod(10,rem) }
Expand Down
78 changes: 39 additions & 39 deletions lib/valid.js
Expand Up @@ -25,31 +25,31 @@ Valid.GetChain = function GetChain() {
return this;
};

// Adds the given test to the current Chain.
// If data is supplied, it's added to the passed-in test to help introspect when debugging.
Valid.AddTest = function AddTest(test, data) {
// Adds the given validation to the current Chain.
// Data is optional but can help identify the validation when debugging.
Valid.AddValidation = function AddValidation(test, data) {
var self = this.GetChain();
if(self._queue === undefined) self._queue = [];
if(data) test.data = data;
self._queue.push(test);
return self;
};

// Supply a function that that returns undefined on success or an error message on failure, produces a full, chainable test.
// The first arg passed to your your function is the value to test, the rest are the args passed when adding the test.
// i.e. Valid.t = SimpleTest(fn(){...}); Valid.t(4,2).check(9) would call your function with arguments 9, 4, 2.
Valid.SimpleTest = function SimpleTest(fn) {
// Supply a function that that returns undefined on success or an error message on failure, produces a full, chainable validation.
// The first arg passed to your your function is the value to test, the rest are the args passed when declaring the validation.
// i.e. Valid.t = SimpleValidation(fn(){...}); Valid.t(4,2).check(9) would call your function with arguments 9, 4, 2.
Valid.SimpleValidation = function SimpleValidation(fn) {
return function() {
var args = Array.prototype.slice.call(arguments, 0);
return this.AddTest( function SimpleTest(value) {
return this.AddValidation( function SimpleValidation(value) {
return fn.apply(this, [value].concat(args));
}, args);
};
};

// Run all the tests in the given queue
// Run all the validations in the given queue
Valid.ValidateQueue = function ValidateQueue(queue, value) {
if(!queue || queue.length < 1) return "no tests!";
if(!queue || queue.length < 1) return "no validations!";
for(var i=0; i<queue.length; i++) {
var error = queue[i].call(this, value);
if(error === Valid) return; // indicates early success, used by optional()
Expand All @@ -64,8 +64,8 @@ Valid.Escape = function Escape(value) {
};


// Allows you to reuse a chain as as a chainable test:
// Valid.isFour = Valid.equal(4).define(); // define the isFour test
// Turns a Chain object into a callable Validation.
// Valid.isFour = Valid.equal(4).define(); // define the isFour validation
// Valid.integer().isFour().test(4); // success!
// If you get this error then you forgot to call define() on your chain:
// Property 'myfunc' of object function Valid() { } is not a function
Expand All @@ -81,7 +81,7 @@ Valid.define = function define() {
return function() {
var self = this.GetChain();
for(var i=0; i<queue.length; i++) {
self.AddTest(queue[i]);
self.AddValidation(queue[i]);
}
return self;
};
Expand Down Expand Up @@ -114,16 +114,16 @@ Valid.verify = function assert(value) {
};


// core tests
// core validations

Valid.nop = Valid.SimpleTest(function Nop(val) { });
Valid.fail = Valid.SimpleTest(function Fail(val,msg) { return msg || "failed"; });
Valid.mod = Valid.SimpleTest(function mod(val,by,rem) { if(val%by !== (rem||0)) return "mod "+by+" is "+(val%by)+" not "+rem; });
Valid.optional = Valid.SimpleTest(function Optional(value) { if(value === null || value === undefined) return Valid; });
Valid.nop = Valid.SimpleValidation(function Nop(val) { });
Valid.fail = Valid.SimpleValidation(function Fail(val,msg) { return msg || "failed"; });
Valid.mod = Valid.SimpleValidation(function mod(val,by,rem) { if(val%by !== (rem||0)) return "mod "+by+" is "+(val%by)+" not "+rem; });
Valid.optional = Valid.SimpleValidation(function Optional(value) { if(value === null || value === undefined) return Valid; });

Valid.equal = Valid.SimpleTest(function Equal(value) {
Valid.equal = Valid.SimpleValidation(function Equal(value) {
// Here is the old equal, not sure supporting multiple values is worth the additional complexity...
// Valid.equal = Valid.SimpleTest(function Equal(val,want) { if(val !== want) return "is not equal to "+Valid.Escape(want); });
// Valid.equal = Valid.SimpleValidation(function Equal(val,want) { if(val !== want) return "is not equal to "+Valid.Escape(want); });
if(arguments.length === 1) return "equal needs at least one argument";
var opts = [];
for(var i=1; i<arguments.length; i++) {
Expand All @@ -135,18 +135,18 @@ Valid.equal = Valid.SimpleTest(function Equal(value) {
return "is not " + opts.join(", ") + " or " + lastopt;
});

Valid.oneOf = Valid.SimpleTest(function OneOf(value,collection) {
Valid.oneOf = Valid.SimpleValidation(function OneOf(value,collection) {
if(collection === null || collection === undefined) return "oneOf needs a collection";
if(value in collection) return;
return "is not one of the options";
});

Valid.type = Valid.SimpleTest(function Type(value,type) {
Valid.type = Valid.SimpleValidation(function Type(value,type) {
if(typeof type !== 'string') return "type requires a string argument, not "+(typeof type);
if(typeof value !== type) return "is of type " + (typeof value) + " not " + type;
});

Valid.array = Valid.SimpleTest(function Arry(value, test) {
Valid.array = Valid.SimpleValidation(function Arry(value, test) {
if(!Array.isArray(value)) return "is not an array";
if(test !== undefined) {
for(var i=0; i<value.length; i++) {
Expand All @@ -156,7 +156,7 @@ Valid.array = Valid.SimpleTest(function Arry(value, test) {
}
});

Valid.len = Valid.SimpleTest(function Len(value,min,max) {
Valid.len = Valid.SimpleValidation(function Len(value,min,max) {
if(typeof value === 'null' || typeof value === 'undefined' || typeof value.length === 'undefined') return "doesn't have a length field";
if(typeof value.length !== 'number') return "length field is of type " + (typeof value.length) + ", not number";
// now we can read the property without risking throwing an exception
Expand All @@ -166,20 +166,20 @@ Valid.len = Valid.SimpleTest(function Len(value,min,max) {
}
});

Valid.messageFor = Valid.SimpleTest(function Msg(value, test, message) {
Valid.messageFor = Valid.SimpleValidation(function Msg(value, test, message) {
var error = this.ValidateQueue(test._queue, value);
if(error) return message;
});

Valid.not = Valid.SimpleTest(function Not(value, test, message) {
Valid.not = Valid.SimpleValidation(function Not(value, test, message) {
var error = this.ValidateQueue(test._queue, value);
if(!error) return message || "test should have failed";
});

// seems somewhat useless since V.a().b() is the same as V.and(V.a(),V.b())
Valid.and = function and() {
var chains = arguments;
return this.AddTest( function And(value) {
return this.AddValidation( function And(value) {
for(var i=0; i<chains.length; i++) {
var error = this.ValidateQueue(chains[i]._queue, value);
if(error) return error;
Expand All @@ -189,7 +189,7 @@ Valid.and = function and() {

Valid.or = function or() {
var chains = arguments;
return this.AddTest(function Or(value) {
return this.AddValidation(function Or(value) {
var errors = [];
for(var i=0; i<chains.length; i++) {
var error = this.ValidateQueue(chains[i]._queue, value);
Expand All @@ -202,14 +202,14 @@ Valid.or = function or() {

Valid.match = function match(pattern, modifiers) {
if(typeof pattern !== 'function') pattern = new RegExp(pattern, modifiers);
return this.string().AddTest( function Match(value) {
return this.string().AddValidation( function Match(value) {
if(!value.match(pattern)) return "does not match " + pattern;
}, pattern);
};



// composite tests
// composite validations

Valid.undef = Valid.equal(undefined).define();
Valid.defined = Valid.not(Valid.undef(), "is undefined").define();
Expand All @@ -219,6 +219,8 @@ Valid.noexisty = Valid.equal(undefined, null).define();
Valid.exists = Valid.not(Valid.noexisty(), "does not exist").define();
Valid.empty = Valid.messageFor(Valid.optional().len(0,0), "is not empty").define();
Valid.boolean = Valid.type('boolean').define();
Valid.isTrue = Valid.equal(true).define();
Valid.isFalse = Valid.equal(false).define();
Valid.number = Valid.type('number').define();
Valid.integer = Valid.number().messageFor(Valid.mod(1), "is not an integer").define();
Valid.even = Valid.number().messageFor(Valid.mod(2), "is not even").define();
Expand All @@ -230,11 +232,9 @@ Valid.object = Valid.type('object').define();
// reserved words, calling them with dot notation may cause problems with crappy JS implementations
Valid['undefined'] = Valid.undef;
Valid['null'] = Valid.nil;
Valid['true'] = Valid.equal(true).define();
Valid['false'] = Valid.equal(false).define();
Valid['function'] = Valid.type('function').define();
Valid['true'] = Valid.isTrue;
Valid['false'] = Valid.isFalse;
Valid['in'] = Valid.oneOf;
Valid.length = Valid.len;

// composites that take arguments
Valid.todo = function(name) { return this.fail((name ? name : "this") + " is still todo"); };
Expand All @@ -244,10 +244,10 @@ Valid.nomatch = function(pat,mods) { var match = this.match(pat,mods); return
// comparisons
Valid.eq = Valid.equal;
Valid.ne = Valid.notEqual;
Valid.lt = Valid.SimpleTest(function lt(val,than) { if(val >= than) return "is not less than " + Valid.Escape(than); });
Valid.le = Valid.SimpleTest(function le(val,than) { if(val > than) return "is not less than or equal to " + Valid.Escape(than); });
Valid.gt = Valid.SimpleTest(function gt(val,than) { if(val <= than) return "is not greater than " + Valid.Escape(than); });
Valid.ge = Valid.SimpleTest(function ge(val,than) { if(val < than) return "is not greater than or equal to " + Valid.Escape(than); });
Valid.lt = Valid.SimpleValidation(function lt(val,than) { if(val >= than) return "is not less than " + Valid.Escape(than); });
Valid.le = Valid.SimpleValidation(function le(val,than) { if(val > than) return "is not less than or equal to " + Valid.Escape(than); });
Valid.gt = Valid.SimpleValidation(function gt(val,than) { if(val <= than) return "is not greater than " + Valid.Escape(than); });
Valid.ge = Valid.SimpleValidation(function ge(val,than) { if(val < than) return "is not greater than or equal to " + Valid.Escape(than); });
Valid.min = Valid.ge;
Valid.max = Valid.le;

Expand Down Expand Up @@ -337,7 +337,7 @@ Valid.JsonField = function(path, value, schema) {


Valid.json = function json(schema) {
return this.AddTest(function Json(value, maxErrors) {
return this.AddValidation(function Json(value, maxErrors) {
this._errors = {};
this._errorCount = 0;
this._maxErrors = maxErrors || 20;
Expand Down
6 changes: 5 additions & 1 deletion test/valid.test.js
Expand Up @@ -34,7 +34,7 @@ if(error !== "5 is not equal to 4") throw "verify() failure threw: " + error;


// pathological cases
Valid.assert("any value", "no tests!");
Valid.assert("any value", "no validations!");
Valid.nop().assert("any value"); // no-op always succeeds
Valid.fail("die!").assert("anything", "die!"); // fail always fails

Expand Down Expand Up @@ -104,6 +104,10 @@ Valid.type(123).assert(123, "type requires a string argument, not number");
Valid.boolean().assert(true);
Valid.boolean().assert(false);
Valid.boolean().assert(undefined, "is of type undefined not boolean");
Valid.isTrue().assert(true);
Valid.isTrue().assert(false, "is not equal to true");
Valid.isFalse().assert(false);
Valid.isFalse().assert(true, "is not equal to false");
Valid['true']().assert(true);
Valid['true']().assert(false, "is not equal to true");
Valid['false']().assert(false);
Expand Down

0 comments on commit 4694fca

Please sign in to comment.