Skip to content

Commit

Permalink
Fleshed out validations.
Browse files Browse the repository at this point in the history
  • Loading branch information
mde committed Apr 11, 2010
1 parent 54393b4 commit e345c65
Showing 1 changed file with 134 additions and 93 deletions.
227 changes: 134 additions & 93 deletions lib/model/model.js
@@ -1,6 +1,7 @@
var sys = require('sys');

var meta = require('geddy/lib/util/meta');
var fleegix = require('geddy/lib/fleegix');

var model = new function () {
GLOBAL.modelRegistry = {};
Expand Down Expand Up @@ -29,7 +30,7 @@ var model = new function () {
var val;
for (var p in attrList) {
val = params[p];
validated = this.validateAttribute(attrList[p], val);
validated = this.validateAttribute(attrList[p], params);
if (validated.err) {
errs = errs || {};
errs[p] = validated.err;
Expand All @@ -44,81 +45,85 @@ var model = new function () {
callback(errs, obj);
};

this.validateAttribute = function (attr, val) {
var validatedVal = val;
var result = {
err: null,
val: null
};

// First check to see if value is required, before doing any other
// rule checking
if (attr.opts.required) {
delete attr.opts.required; // Don't do this with all the other opts
if (!validatedVal) {
return {
err: 'Field "' + attr.name + '" is required.',
val: null
};
}
}
this.validateAttribute = function (attr, origParams) {

var params = fleegix.mixin({}, origParams, true);
var name = attr.name;
var val = params[name];
// Validate for the base datatype only if there actually is a value --
// e.g., undefined will fail the validation for Number, even if the
// field is optional
if (validatedVal) {
result = model.validators[attr.dataType](attr.name, validatedVal);
if (val) {
var result = model.datatypes[attr.dataType](attr.name, val);
if (result.err) {
return result;
return {
err: result.err,
val: null
};
}
// Value may have been modified in the datatype check -- e.g.,
// 'false' changed to false, '8.0' changed to 8, '2112' changed to 2112, etc.
validatedVal = result.val;
params[name] = result.val;
}

// Check any other validators -- user could always add other rules like
// minLength that makes ostensibly non-required values actually required
var opts = attr.opts;
for (p in opts) {
result = model.validators[p](attr.name, validatedVal, opts[p]);
if (result.err) {
return result;
var validations = attr.validations;
var validator;
var err;
for (var p in validations) {
validator = model.validators[p]
if (typeof validator != 'function') {
throw new Error(p + ' is not a valid validator');
}
err = validator(name, params, validations[p]);
if (err) {
return {
err: err,
val: null
};
}
}

return result;
};

}();
return {
err: null,
val: params[name]
};

var ModelFactory = function (name) {
this.name = name;
this.property = function (name, dataType, o) {
modelRegistry[this.name].attributes[name] =
new model.Attribute(name, dataType, o);
};
modelRegistry[name] = new model.Model(name);
};

}();

model.Model = function (name) {
this.name = name;
this.attributes = {};
};

model.Attribute = function (name, dataType, opts) {
model.Attribute = function (name, dataType, o) {
var opts = o || {};
this.name = name;
this.dataType = dataType;
this.opts = opts || {};
var validations = {};
for (var p in opts) {
if (opts.required || opts.length) {
validations.present = true;
}
if (opts.length) {
validations.length = opts.length;
}
if (opts.format) {
validations.format = opts.format;
}
}
this.validations = validations;
};

model.validators = {
model.datatypes = {
'String': function (name, val) {
return {
err: null,
val: new String(val)
};
},

'Number': function (name, val) {
if (isNaN(val)) {
return {
Expand Down Expand Up @@ -171,7 +176,7 @@ model.validators = {
default:
// Nothing
}

if (typeof validated != 'boolean') {
return {
err: 'Field "' + name + '" must be a Boolean.',
Expand All @@ -182,63 +187,99 @@ model.validators = {
err: null,
val: validated
};
}

};

model.validators = {
present: function (name, params, rule) {
if (!params[name]) {
return rule.message || 'Field "' + name + '" is required.';
}
},

'minLength': function (name, val, qual) {
validated = val || '';
if (validated.length < qual) {
return {
err: 'Field "' + name + '" must be at least ' + qual + ' characters long.',
val: null
};
};
return {
err: null,
val: validated
};

absent: function (name, params, rule) {
if (params[name]) {
return rule.message || 'Field "' + name + '" must not be filled in.';
}
},

'maxLength': function (name, val, qual) {
validated = val || '';
if (validated.length > qual) {
return {
err: 'Field "' + name + '" may not be longer than ' + qual + ' characters.',
val: null
};
};
return {
err: null,
val: validated
};
confirmed: function (name, params, rule) {
if (params[name] != params[rule.qualifier]) {
return rule.message || 'Field "' + name + '" and field "' + qual +
'" must match.';
}
},

'minValue': function (name, val, qual) {
if (val < qual) {
return {
err: 'Field "' + name + '" must be at least ' + qual,
val: null
};
};
return {
err: null,
val: val
};
format: function (name, params, rule) {
if (!rule.qualifier.test(params[name])) {
return rule.message || 'Field "' + name + '" is not correctly formatted.';
}
},

'maxValue': function (name, val, qual) {
if (val > qual) {
return {
err: 'Field "' + name + '" must not be more than ' + qual,
val: null
};
};
return {
err: null,
val: val
};
length: function (name, params, rule) {
var qual = rule.qualifier;
var val = params[name];
var err;
if (typeof qual == 'number') {
if (val.length != qual) {
return rule.message || 'Field "' + name + '" must be ' + qual +
' characters long.';
}
}
else {
if (typeof qual.min == 'number' && val.length < qual.min) {
return rule.message || 'Field "' + name + '" must be at least ' +
qual.min + ' characters long.';
}
if (typeof qual.max == 'number' && val.length > qual.max) {
return rule.message || 'Field "' + name + '" may not be more than ' +
qual.max + ' characters long.';
}
}
},

withFunction: function (name, params, rule) {
var func = rule.qualifier;
if (typeof func != 'function') {
throw new Error('withFunction validator for field "' + name +
'" must be a function.');
}
if (!func(params[name], params)) {
return rule.message || 'Field "' + name + '" is not valid.';
}
}

};

var ModelFactory = function (name) {
var _this = this;
var _getValidator = function (p) {
return function () {
var args = Array.prototype.slice.call(arguments);
args.unshift(p);
return _this.validates.apply(_this, args);
};
};

this.name = name;

this.property = function (name, dataType, o) {
modelRegistry[this.name].attributes[name] =
new model.Attribute(name, dataType, o);
};

this.validates = function (condition, name, qual, origOpts) {
var opts = fleegix.mixin({}, origOpts, true);
opts.qualifier = qual;
modelRegistry[this.name].attributes[name].validations[condition] = opts;
};

for (var p in model.validators) {
this['validates' + fleegix.string.capitalize(p)] = _getValidator(p);
}

modelRegistry[name] = new model.Model(name);
};

for (var p in model) { this[p] = model[p]; }
Expand Down

0 comments on commit e345c65

Please sign in to comment.