Async validations hooks
This commit is contained in:
parent
fbf118695e
commit
3b2b57eb7b
|
@ -107,26 +107,33 @@ AbstractClass.create = function (data) {
|
|||
if (data instanceof AbstractClass && !data.id) {
|
||||
obj = data;
|
||||
data = obj.toObject(true);
|
||||
create();
|
||||
} else {
|
||||
obj = new this(data);
|
||||
|
||||
// validation required
|
||||
if (!obj.isValid()) {
|
||||
return callback(new Error('Validation error'), obj);
|
||||
}
|
||||
obj.isValid(function (valid) {
|
||||
if (!valid) {
|
||||
callback(new Error('Validation error'), obj);
|
||||
} else {
|
||||
create();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
obj.trigger("create", function () {
|
||||
this._adapter().create(modelName, data, function (err, id) {
|
||||
if (id) {
|
||||
defineReadonlyProp(obj, 'id', id);
|
||||
this.constructor.cache[id] = obj;
|
||||
}
|
||||
if (callback) {
|
||||
callback(err, obj);
|
||||
}
|
||||
}.bind(this));
|
||||
});
|
||||
function create() {
|
||||
obj.trigger("create", function () {
|
||||
this._adapter().create(modelName, data, function (err, id) {
|
||||
if (id) {
|
||||
defineReadonlyProp(obj, 'id', id);
|
||||
this.constructor.cache[id] = obj;
|
||||
}
|
||||
if (callback) {
|
||||
callback(err, obj);
|
||||
}
|
||||
}.bind(this));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
AbstractClass.exists = function exists(id, cb) {
|
||||
|
@ -221,34 +228,47 @@ AbstractClass.prototype.save = function (options, callback) {
|
|||
options.throws = false;
|
||||
}
|
||||
|
||||
if (options.validate && !this.isValid()) {
|
||||
var err = new Error('Validation error');
|
||||
if (options.throws) {
|
||||
throw err;
|
||||
}
|
||||
return callback && callback(err, this);
|
||||
if (options.validate) {
|
||||
this.isValid(function (valid) {
|
||||
if (valid) {
|
||||
save.call(this);
|
||||
} else {
|
||||
var err = new Error('Validation error');
|
||||
// throws option is dangerous for async usage
|
||||
if (options.throws) {
|
||||
throw err;
|
||||
}
|
||||
if (callback) {
|
||||
callback(err, this);
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
} else {
|
||||
save.call(this);
|
||||
}
|
||||
|
||||
function save() {
|
||||
this.trigger("save", function(){
|
||||
var modelName = this.constructor.modelName;
|
||||
var data = this.toObject(true);
|
||||
if (this.id) {
|
||||
this.trigger("update", function(){
|
||||
this._adapter().save(modelName, data, function (err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
this.constructor.call(this, data);
|
||||
}
|
||||
if (callback) {
|
||||
callback(err, this);
|
||||
}
|
||||
}.bind(this));
|
||||
});
|
||||
} else {
|
||||
this.constructor.create(this, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.trigger("save", function(){
|
||||
var modelName = this.constructor.modelName;
|
||||
var data = this.toObject(true);
|
||||
if (this.id) {
|
||||
this.trigger("update", function(){
|
||||
this._adapter().save(modelName, data, function (err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
this.constructor.call(this, data);
|
||||
}
|
||||
if (callback) {
|
||||
callback(err, this);
|
||||
}
|
||||
}.bind(this));
|
||||
});
|
||||
} else {
|
||||
this.constructor.create(this, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
AbstractClass.prototype.isNewRecord = function () {
|
||||
|
@ -295,27 +315,34 @@ AbstractClass.prototype.updateAttributes = function updateAttributes(data, cb) {
|
|||
this[key] = data[key];
|
||||
}.bind(this));
|
||||
|
||||
if (!this.isValid()) {
|
||||
var err = new Error('Validation error');
|
||||
return cb && cb(err);
|
||||
}
|
||||
|
||||
this.trigger("update", function(){
|
||||
this._adapter().updateAttributes(model, this.id, data, function (err) {
|
||||
if (!err) {
|
||||
Object.keys(data).forEach(function (key) {
|
||||
this[key] = data[key];
|
||||
Object.defineProperty(this, key + '_was', {
|
||||
writable: false,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: data[key]
|
||||
});
|
||||
}.bind(this));
|
||||
this.isValid(function (valid) {
|
||||
if (!valid) {
|
||||
if (cb) {
|
||||
cb(new Error('Validation error'));
|
||||
}
|
||||
cb(err);
|
||||
}.bind(this));
|
||||
});
|
||||
} else {
|
||||
update.call(this);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
function update() {
|
||||
this.trigger("update", function(){
|
||||
this._adapter().updateAttributes(model, this.id, data, function (err) {
|
||||
if (!err) {
|
||||
Object.keys(data).forEach(function (key) {
|
||||
this[key] = data[key];
|
||||
Object.defineProperty(this, key + '_was', {
|
||||
writable: false,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: data[key]
|
||||
});
|
||||
}.bind(this));
|
||||
}
|
||||
cb(err);
|
||||
}.bind(this));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@ Validatable.validatesExclusionOf = getConfigurator('exclusion');
|
|||
Validatable.validatesFormatOf = getConfigurator('format');
|
||||
Validatable.validate = getConfigurator('custom');
|
||||
Validatable.validateAsync = getConfigurator('custom', {async: true});
|
||||
Validatable.validateUniquenessOf = getConfigurator('uniqueness');
|
||||
Validatable.validatesUniquenessOf = getConfigurator('uniqueness', {async: true});
|
||||
|
||||
// implementation of validators
|
||||
var validators = {
|
||||
|
@ -70,8 +70,20 @@ var validators = {
|
|||
err();
|
||||
}
|
||||
},
|
||||
custom: function (attr, conf, err, cb) {
|
||||
conf.customValidator.call(this, err, cb);
|
||||
custom: function (attr, conf, err, done) {
|
||||
conf.customValidator.call(this, err, done);
|
||||
},
|
||||
uniqueness: function (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));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -88,6 +100,9 @@ Validatable.prototype.isValid = function (callback) {
|
|||
// exit with success when no errors
|
||||
if (!this.constructor._validations) {
|
||||
cleanErrors(this);
|
||||
if (callback) {
|
||||
callback(valid);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
@ -121,6 +136,7 @@ Validatable.prototype.isValid = function (callback) {
|
|||
}
|
||||
|
||||
if (valid) cleanErrors(this);
|
||||
if (!async && callback) callback(valid);
|
||||
|
||||
return valid;
|
||||
};
|
||||
|
@ -216,7 +232,8 @@ var defaultMessages = {
|
|||
'number': 'is not a number'
|
||||
},
|
||||
inclusion: 'is not included in the list',
|
||||
exclusion: 'is reserved'
|
||||
exclusion: 'is reserved',
|
||||
uniqueness: 'is not unique'
|
||||
};
|
||||
|
||||
function nullCheck(attr, conf, err) {
|
||||
|
|
|
@ -57,6 +57,10 @@ function testOrm(schema) {
|
|||
published: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
Post.validateAsync('title', function (err, done) {
|
||||
process.nextTick(done);
|
||||
});
|
||||
|
||||
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'});
|
||||
// creates instance methods:
|
||||
// user.posts(conds)
|
||||
|
|
|
@ -27,6 +27,16 @@ validAttributes =
|
|||
createdByAdmin: false
|
||||
createdByScript: true
|
||||
|
||||
getValidAttributes = ->
|
||||
name: 'Anatoliy'
|
||||
email: 'email@example.com'
|
||||
state: ''
|
||||
age: 26
|
||||
gender: 'male'
|
||||
domain: '1602'
|
||||
createdByAdmin: false
|
||||
createdByScript: true
|
||||
|
||||
it 'should validate presence', (test) ->
|
||||
User.validatesPresenceOf 'email', 'name'
|
||||
|
||||
|
@ -251,3 +261,16 @@ it 'should validate asynchronously', (test) ->
|
|||
test.ok not valid, 'not valid name'
|
||||
test.done()
|
||||
|
||||
it 'should validate uniqueness', (test) ->
|
||||
User.validatesUniquenessOf 'email'
|
||||
User.create getValidAttributes(), ->
|
||||
user = new User getValidAttributes()
|
||||
|
||||
# test.ok not user.isValid(), 'not valid because async validation'
|
||||
user.isValid (valid) ->
|
||||
test.ok not valid, 'email is not unique'
|
||||
user.email = 'unique@email.tld'
|
||||
user.isValid (valid) ->
|
||||
test.ok valid, 'valid with unique email'
|
||||
test.done()
|
||||
|
||||
|
|
Loading…
Reference in New Issue