Rewrite save and create for correct hooks order

Validation should be called first, and then all other actions
to allow modify data after validation (for example hash
password after validating length.

Issue #242
This commit is contained in:
Anatoliy Chakkaev 2013-03-28 15:26:51 +04:00
parent 48d78b71f6
commit 1f29064b3d
1 changed files with 81 additions and 93 deletions

View File

@ -171,38 +171,30 @@ AbstractClass.create = function (data, callback) {
callback = function () {};
}
var obj = null;
var obj;
// if we come from save
if (data instanceof this && !data.id) {
obj = data;
data = obj.toObject(true);
obj._initProperties(data, false);
create();
} else {
obj = new this(data);
}
data = obj.toObject(true);
obj.trigger('save', function(saveDone) {
// validation required
obj.isValid(function(valid) {
if (!valid) {
callback(new Error('Validation error'), obj);
if (valid) {
create();
} else {
create(saveDone);
callback(new Error('Validation error'), obj);
}
});
}, data);
}, obj);
}
function create(saveDone) {
obj.trigger('create', function (done) {
var data = this.toObject(true); // Added this to fix the beforeCreate trigger not fire.
// The fix is per issue #72 and the fix was found by by5739.
function create() {
obj.trigger('create', function(createDone) {
obj.trigger('save', function(saveDone) {
this._adapter().create(modelName, this.constructor._forDB(data), function (err, id, rev) {
obj._initProperties(data, false);
if (id) {
obj.__data.id = id;
obj.__dataWas.id = id;
@ -211,19 +203,17 @@ AbstractClass.create = function (data, callback) {
if (rev) {
obj._rev = rev
}
done.call(this, function () {
if (saveDone) {
if (err) {
return callback(err, obj);
}
saveDone.call(obj, function () {
if (callback) {
createDone.call(obj, function () {
callback(err, obj);
}
});
} else if (callback) {
callback(err, obj);
}
});
}.bind(this));
}, obj);
});
}, data);
}, data);
}
};
@ -618,36 +608,42 @@ AbstractClass.prototype.save = function (options, callback) {
options.throws = false;
}
if (options.validate) {
this.isValid(function (valid) {
var inst = this;
var data = inst.toObject(true);
var Model = this.constructor;
var modelName = Model.modelName;
if (!this.id) {
return Model.create(this, callback);
}
// validate first
if (!options.validate) {
return save();
}
inst.isValid(function (valid) {
if (valid) {
save.call(this);
save();
} else {
var err = new Error('Validation error');
// throws option is dangerous for async usage
if (options.throws) {
throw err;
}
callback(err, this);
}
}.bind(this));
} else {
save.call(this);
callback(err, inst);
}
});
// then save
function save() {
this.trigger('save', function (saveDone) {
var modelName = this.constructor.modelName;
var data = this.toObject(true);
var inst = this;
if (inst.id) {
inst.trigger('save', function (saveDone) {
inst.trigger('update', function (updateDone) {
inst._adapter().save(modelName, inst.constructor._forDB(data), function (err) {
if (err) {
console.log(err);
} else {
inst._initProperties(data, false);
return callback(err, inst);
}
inst._initProperties(data, false);
updateDone.call(inst, function () {
saveDone.call(inst, function () {
callback(err, inst);
@ -655,15 +651,7 @@ AbstractClass.prototype.save = function (options, callback) {
});
});
}, data);
} else {
inst.constructor.create(inst, function (err) {
saveDone.call(inst, function () {
callback(err, inst);
});
});
}
}, this);
}, data);
}
};
@ -784,15 +772,15 @@ AbstractClass.prototype.updateAttributes = function updateAttributes(data, cb) {
inst[key] = data[key];
});
inst.trigger('save', function (saveDone) {
inst.trigger('update', function (done) {
inst.isValid(function (valid) {
if (!valid) {
if (cb) {
cb(new Error('Validation error'), inst);
}
} else {
inst.trigger('save', function (saveDone) {
inst.trigger('update', function (done) {
Object.keys(data).forEach(function (key) {
inst[key] = data[key];
});
@ -810,10 +798,10 @@ AbstractClass.prototype.updateAttributes = function updateAttributes(data, cb) {
});
});
});
}
});
}, data);
}, data);
}
}, data);
};
AbstractClass.prototype.fromObject = function (obj) {