Validatable
Validation encapsulated in this abstract class.
Basically validation configurators is just class methods, which adds validations
configs to AbstractClass._validations. Each of this validations run when
obj.isValid()
method called.
Each configurator can accept n params (n-1 field names and one config). Config
is Object depends on specific validation, but all of them has one common part:
message
member. It can be just string, when only one situation possible,
e.g. Post.validatesPresenceOf('title', { message: 'can not be blank' });
In more complicated cases it can be Hash of messages (for each case):
User.validatesLengthOf('password', { min: 6, max: 20, message: {min: 'too short', max: 'too long'}});
Source codefunction Validatable() {
// validatable class
};
Class methods Instance methods Helper methods Validatable.validatesPresenceOf
Declared as getConfigurator('presence');
Validate presence. This validation fails when validated field is blank.
Default error message "can't be blank"
example Post.validatesPresenceOf('title')
example Post.validatesPresenceOf('title', {message: 'Can not be blank'})
sync
see helper/validatePresence
Source codegetConfigurator('presence');
Validatable.validatesLengthOf
Declared as getConfigurator('length');
Validate length. Three kinds of validations: min, max, is.
Default error messages:
- min: too short
- max: too long
- is: length is wrong
example User.validatesLengthOf('password', {min: 7});
example User.validatesLengthOf('email', {max: 100});
example User.validatesLengthOf('state', {is: 2});
example `User.validatesLengthOf('nick', {min: 3, max: 15});
sync
see helper/validateLength
Source codegetConfigurator('length');
Validatable.validatesNumericalityOf
Declared as getConfigurator('numericality');
Validate numericality.
example User.validatesNumericalityOf('age', { message: { number: '...' }});
example User.validatesNumericalityOf('age', {int: true, message: { int: '...' }});
Default error messages:
- number: is not a number
- int: is not an integer
sync
see helper/validateNumericality
Source codegetConfigurator('numericality');
Validatable.validatesInclusionOf
Declared as getConfigurator('inclusion');
Validate inclusion in set
example User.validatesInclusionOf('gender', {in: ['male', 'female']});
Default error message: is not included in the list
see helper/validateInclusion
Source codegetConfigurator('inclusion');
Validatable.validatesExclusionOf
Declared as getConfigurator('exclusion');
Validate exclusion
example Company.validatesExclusionOf('domain', {in: ['www', 'admin']});
Default error message: is reserved
see helper/validateExclusion
Source codegetConfigurator('exclusion');
Validatable.validatesFormatOf
Declared as getConfigurator('format');
Source codegetConfigurator('format');
Validatable.validate
Declared as getConfigurator('custom');
Source codegetConfigurator('custom');
Validatable.validateAsync
Declared as getConfigurator('custom',
Source codegetConfigurator('custom',
Validatable.validatesUniquenessOf
Declared as getConfigurator('uniqueness',
Source codegetConfigurator('uniqueness',
Validatable.prototype.isValid
Declared as function (callback)
This method performs validation, triggers validation hooks.
Before validation obj.errors
collection cleaned.
Each validation can add errors to obj.errors
collection.
If collection is not blank, validation failed.
Warning! This method can be called as sync only when no async validation configured. It's strongly recommended to run all validations as asyncronous.
param Function callback called with (valid)
return Boolean true if no async validation configured and all passed
example ExpressJS controller: render user if valid, show flash otherwise
user.isValid(function (valid) {
if (valid) res.render({user: user});
else res.flash('error', 'User is not valid'), console.log(user.errors), res.redirect('/users');
});
Source codefunction (callback) {
var valid = true, inst = this, wait = 0, async = false;
// exit with success when no errors
if (!this.constructor._validations) {
cleanErrors(this);
if (callback) {
callback(valid);
}
return valid;
}
Object.defineProperty(this, 'errors', {
enumerable: false,
configurable: true,
value: new Errors
});
this.trigger('validation', function (validationsDone) {
var inst = this;
this.constructor._validations.forEach(function (v) {
if (v[2] && v[2].async) {
async = true;
wait += 1;
validationFailed(inst, v, done);
} else {
if (validationFailed(inst, v)) {
valid = false;
}
}
});
if (!async) {
validationsDone();
}
var asyncFail = false;
function done(fail) {
asyncFail = asyncFail || fail;
if (--wait === 0 && callback) {
validationsDone.call(inst, function () {
if( valid && !asyncFail ) cleanErrors(inst);
callback(valid && !asyncFail);
});
}
}
});
if (!async) {
if (valid) cleanErrors(this);
if (callback) callback(valid);
return valid;
} else {
// in case of async validation we should return undefined here,
// because not all validations are finished yet
return;
}
};
validatePresence
Declared as function validatePresence(attr, conf, err)
Source codefunction validatePresence(attr, conf, err) {
if (blank(this[attr])) {
err();
}
}
validateLength
Declared as function validateLength(attr, conf, err)
Source codefunction validateLength(attr, conf, err) {
if (nullCheck.call(this, attr, conf, err)) return;
var len = this[attr].length;
if (conf.min && len < conf.min) {
err('min');
}
if (conf.max && len > conf.max) {
err('max');
}
if (conf.is && len !== conf.is) {
err('is');
}
}
validateNumericality
Declared as function validateNumericality(attr, conf, err)
Source codefunction validateNumericality(attr, conf, err) {
if (nullCheck.call(this, attr, conf, err)) return;
if (typeof this[attr] !== 'number') {
return err('number');
}
if (conf.int && this[attr] !== Math.round(this[attr])) {
return err('int');
}
}
validateInclusion
Declared as function validateInclusion(attr, conf, err)
Source codefunction validateInclusion(attr, conf, err) {
if (nullCheck.call(this, attr, conf, err)) return;
if (!~conf.in.indexOf(this[attr])) {
err()
}
}
validateExclusion
Declared as function validateExclusion(attr, conf, err)
Source codefunction validateExclusion(attr, conf, err) {
if (nullCheck.call(this, attr, conf, err)) return;
if (~conf.in.indexOf(this[attr])) {
err()
}
}
validateFormat
Declared as function validateFormat(attr, conf, err)
Source codefunction validateFormat(attr, conf, err) {
if (nullCheck.call(this, attr, conf, err)) return;
if (typeof this[attr] === 'string') {
if (!this[attr].match(conf['with'])) {
err();
}
} else {
err();
}
}
validateCustom
Declared as function validateCustom(attr, conf, err, done)
Source codefunction validateCustom(attr, conf, err, done) {
conf.customValidator.call(this, err, done);
}
validateUniqueness
Declared as function validateUniqueness(attr, conf, err, done)
Source codefunction validateUniqueness(attr, conf, err, done) {
var cond = {where: {}};
cond.where[attr] = this[attr];
this.constructor.all(cond, function (error, found) {
if (found.length > 1) {
err();
} else if (found.length === 1 && found[0].id !== this.id) {
err();
}
done();
}.bind(this));
}
blank
Declared as function blank(v)
Return true when v is undefined, blank array, null or empty string
otherwise returns false
param Mix v
returns Boolean whether v
blank or not
Source codefunction blank(v) {
if (typeof v === 'undefined') return true;
if (v instanceof Array && v.length === 0) return true;
if (v === null) return true;
if (typeof v == 'string' && v === '') return true;
return false;
}