orm
Version:
NodeJS Object-relational mapping
123 lines (100 loc) • 3.1 kB
JavaScript
var enforce = require("enforce");
var util = require("util");
var validators = {
required : enforce.required,
notEmptyString : enforce.notEmptyString,
rangeNumber : enforce.ranges.number,
rangeLength : enforce.ranges.length,
insideList : enforce.lists.inside,
outsideList : enforce.lists.outside,
password : enforce.security.password,
patterns : enforce.patterns
};
/**
* Check if a value is the same as a value
* of another property (useful for password
* checking).
**/
validators.equalToProperty = function (name, msg) {
return function (v, next) {
if (v === this[name]) {
return next();
}
return next(msg || 'not-equal-to-property');
};
};
/**
* Check if a property is unique in the collection.
* This can take a while because a query has to be made against the Model.
*
* Due to the async nature of node, and concurrent web server environments,
* an index on the database column is the only way to gurantee uniqueness.
*
* For sensibility's sake, undefined and null values are ignored for uniqueness
* checks.
*
* Options:
* ignoreCase: for postgres; mysql ignores case by default.
* scope: (Array) scope uniqueness to listed properties
**/
validators.unique = function () {
var arg, k;
var msg = null, opts = {};
for (k in arguments) {
arg = arguments[k];
if (typeof arg === "string") {
msg = arg;
} else if (typeof arg === "object") {
opts = arg;
}
}
return function (v, next, ctx) {
var s, scopeProp;
if (typeof v === "undefined" || v === null) {
return next();
}
//Cannot process on database engines which don't support SQL syntax
if (!ctx.driver.isSql) {
return next('not-supported');
}
var chain = ctx.model.find();
var chainQuery = function (prop, value) {
var query = null;
if (opts.ignoreCase === true && ctx.model.properties[prop] && ctx.model.properties[prop].type === 'text') {
query = util.format('LOWER(%s.%s) LIKE LOWER(?)',
ctx.driver.query.escapeId(ctx.model.table), ctx.driver.query.escapeId(prop)
);
chain.where(query, [value]);
} else {
query = {};
query[prop] = value;
chain.where(query);
}
};
var handler = function (err, records) {
if (err) {
return next();
}
if (!records || records.length === 0) {
return next();
}
if (records.length === 1 && records[0][ctx.model.id] === this[ctx.model.id]) {
return next();
}
return next(msg || 'not-unique');
}.bind(this);
chainQuery(ctx.property, v);
if (opts.scope) {
for (s in opts.scope) {
scopeProp = opts.scope[s];
// In SQL unique index land, NULL values are not considered equal.
if (typeof ctx.instance[scopeProp] == 'undefined' || ctx.instance[scopeProp] === null) {
return next();
}
chainQuery(scopeProp, ctx.instance[scopeProp]);
}
}
chain.all(handler);
};
};
module.exports = validators;