(function () {
var mode
, messages = [];
var _ = this._ || require('underscore');
dbc = {
(function () {
var mode
, messages = [];
var _ = this._ || require('underscore');
dbc = {
Check asserts that an object o
meets a specification spec
.
If o
does not satisfy spec
then an exception is thrown.
eg.
dbc.check({
name: 'John',
age: 22
}, {
name: [{validator:'type',args:['string']}],
age: [{validator:'type',args:['number']}],
});
check: function(o, spec, message) {
message = message || ''
mode = 'check';
try {
applyValidators.call(this, o, spec);
} catch (e) {
throw new Error(message ? message + ': ' + e.message : e.message);
}
},
Validate is the same as check, except that errors are returned as an array of messages instead of throwing an exception.
validate: function (o, spec) {
mode = 'validate';
applyValidators.call(this, o, spec);
return messages;
},
Wrap returns a wrapped function that applies 'specs' validators to the functions arguments and 'returnSpec' validators to the return value. eg
var add = dbc.wrap(function (f,s) {
return f + s;
}, {
// validators for the first arg
0: [{validator:'type', args:['number']}],
// validators for the second arg
1:[{validator:'type', args:['number']}]
},
// validators for the return value
[{validator: 'type', args:['number']}]);
wrap: function(original, specs, returnSpec) {
return function () {
var r;
checkArgs(arguments);
r = original.apply(this,arguments);
checkReturn();
return r;
function checkArgs(args) {
_.each(_.keys(specs || {}), function (k,index) {
dbc.check(
{k: args[index] },
{ k: specs[k] });
});
}
function checkReturn() {
if (returnSpec) {
dbc.check({ret: r}, {ret: returnSpec});
}
}
};
},
MakeConstructor generates a constructor from a spec. The constructor validates that the created objects meet the spec. ie
var Person = dbc.MakeConstructor({
name: [{validator:'type',args:'string'}],
age: [{validator:'type',args:'number'}]
});
Person.prototype.canDrive = dbc.wrap(function () {
return this.age > 16;
}, null, [{validator:'type',args:'number'}]);
var p = new Person('John', 22);
p.canDrive(); // true
makeConstructor: function(spec) {
var f = function (prps) {
var c = this;
_.each(_.keys(spec), function (key) {
c[key] = prps[key];
});
dbc.check(c, c.__spec);
};
f.prototype.__spec = spec;
return f;
},
The custom
validator applies a custom predicate.
eg
dbc.check({
number: 7
},{
number: [{validator:'custom',args:[function isEven(n) {
return n % 2 === 0;
}]}]
});
custom: function(v, test, message) {
if (!isExisting(v)) return;
if (!test(v)) {
storeMessage(message || 'failed custom function condition for value ' + v);
}
},
assert: function(condition, message) {
if (!condition) {
storeMessage(message);
}
},
Type asserts the type of a value using JavaScript's typeof
operator.
In addition to the JavaScript types (undefined, object, boolean, number, string, function) you can also use array
. eg
dbc.check({
name:'John'
}, {
name: [{validator:'type',args: ['string']}]
});
type: function (v, type, message) {
if (type.charAt(type.length-1) == '?') {
if (!isExisting(v)) return;
type = type.substring(0, type.length-1);
}
message = message || 'Expected type of ' + type + ' but was ' + typeof v;
if (type == 'array') {
if (!_.isArray(v)) {
storeMessage(message)
}
return;
}
if (typeof v == 'undefined' || v == null) {
storeMessage('Expected type of ' + type + ' but was null or undefined');
}
if ((typeof v) != type) {
storeMessage(message);
}
},
Required asserts that a value is not null or undefined. This validator does not require any args. eg
dbc.check({
name:'John'
}, {
name: [{validator:'required'}]
});
required: function(v, message) {
if (!isExisting(v)) {
storeMessage(message || 'expected a defined value');
}
},
IsArray asserts that a value is an array. This validator does not require any args. eg
dbc.check({
colours: ['red','green','blue']
}, {
colours: [{validator:'isArray'}]
});
isArray: function (v, message) {
if (isExisting(v) && !_.isArray(v)) {
storeMessage(message || 'expected an array')
}
},
IsEnumerable asserts that a value has a forEach function. This validator does not require any args. eg
dbc.check({
colours: ['red','green','blue']
}, {
colours: [{validator:'isEnumerable'}]
});
isEnumerable: function (v, message) {
if (isExisting(v) && typeof v.forEach !== 'function') {
storeMessage(message || 'expected an object with a forEach function');
}
},
isNonEmptyCollection asserts that a value has a length property greater than zero. This validator does not require any args. eg
dbc.check({
divs: $('div') // assuming jQuery
}, {
divs: [{validator:'isNonEmptyCollection'}]
});
isNonEmptyCollection: function (v, message) {
if (!isExisting(v)) return;
if (!(typeof v.length === 'number' && v.length > 0)) {
throw new Error(message || 'expected collection with length > 0');
}
},
isFunction asserts that a value is a function. This validator does not require any args. eg
dbc.check({
square: function (n) { return n * n; }
}, {
square: [{validator:'isFunction'}]
});
isFunction: function(f, message) {
this.type(f, 'function', message || 'expected a function');
},
isObject asserts that a value is an object. This validator does not require any args. eg
dbc.check({
thing: {}
}, {
thing: [{validator:'isObject'}]
});
isObject: function(o, message) {
this.type(o, 'object', message || 'expected an object');
},
isInstance asserts the constructor of an object. eg
dbc.check({
john: new Person('John')
}, {
john: [{validator:'isInstance',args:[Person]}]
});
isInstance: function(o, type, message) {
if (isExisting(o) && !(o instanceof type)) {
storeMessage(message || "expected " + o + " to be an instance of " + type.name);
}
},
functionArity asserts the arity of a function. eg
dbc.check({
add: function (f,s) { return f + s; }
}, {
add: [{validator:'functionArity',args:[2]}]
});
functionArity: function (f, arity, message) {
if (!isExisting(f)) return;
this.isFunction(f, 'cannot check arity of an object that is not a function');
if (f.length !== arity) {
storeMessage(message || 'Function arity is ' + f.length + '. Expected ' + arity);
}
}
};
function isExisting(v) {
return typeof v !== "undefined" && v !== null;
}
function applyValidators(o, spec) {
var specKeys = _.keys(spec),
dbc = this;
specKeys.forEach(function (key) {
var validators = spec[key];
validators.forEach(function(validator) {
dbc[validator.validator].apply(dbc, [o[key]].concat(validator.args || []))
});
});
}
function storeMessage(message) {
if (mode === 'validate') {
messages.push(message);
return;
}
throw new Error(message);
}
if (typeof define !== "undefined" && define !== null) {
define('dbc', [], function () {
return dbc;
});
} else if (typeof window !== "undefined" && window !== null) {
window.dbc = dbc;
}
if (typeof module !== "undefined" && module !== null) {
module.exports = dbc;
}
})();