Pass options in operation hooks context.

This commit is contained in:
Fabien Franzen 2015-03-30 15:03:45 +02:00
parent b5188a5af4
commit 1ab3d74ab5
2 changed files with 146 additions and 42 deletions

View File

@ -178,6 +178,8 @@ DataAccessObject.create = function (data, options, cb) {
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
var hookState = {};
if (Array.isArray(data)) {
// Undefined item will be skipped by async.map() which internally uses
// Array.prototype.map(). The following loop makes sure all items are
@ -216,7 +218,6 @@ DataAccessObject.create = function (data, options, cb) {
var enforced = {};
var obj;
var idValue = getIdValue(this, data);
var hookState = {};
// if we come from save
if (data instanceof Model && !idValue) {
@ -235,7 +236,8 @@ DataAccessObject.create = function (data, options, cb) {
Model: Model,
instance: obj,
isNewInstance: true,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('before save', context, function(err) {
if (err) return cb(err);
@ -279,7 +281,8 @@ DataAccessObject.create = function (data, options, cb) {
Model: Model,
instance: obj,
isNewInstance: true,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('after save', context, function(err) {
cb(err, obj);
@ -356,16 +359,22 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
var hookState = {};
var self = this;
var Model = this;
var hookState = {};
var id = getIdValue(this, data);
if (!id) {
return this.create(data, options, cb);
}
var context = { Model: Model, query: byIdQuery(Model, id), hookState: hookState };
var context = {
Model: Model,
query: byIdQuery(Model, id),
hookState: hookState,
options: options
};
Model.notifyObserversOf('access', context, doUpdateOrCreate);
function doUpdateOrCreate(err, ctx) {
@ -377,7 +386,8 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
Model: Model,
where: ctx.query.where,
data: data,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('before save', context, function(err, ctx) {
if (err) return cb(err);
@ -418,7 +428,8 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
Model: Model,
instance: obj,
isNewInstance: result ? result.isNewInstance : undefined,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('after save', context, function(err) {
cb(err, obj);
@ -502,9 +513,10 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
var hookState = {};
var Model = this;
var self = this;
var hookState = {};
function _findOrCreate(query, data) {
var modelName = self.modelName;
@ -524,7 +536,8 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
Model: Model,
instance: obj,
isNewInstance: true,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('after save', context, function(err) {
if (cb.promise) {
@ -558,7 +571,12 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
this.applyScope(query);
var context = { Model: Model, query: query, hookState: hookState };
var context = {
Model: Model,
query: query,
hookState: hookState,
options: options
};
Model.notifyObserversOf('access', context, function (err, ctx) {
if (err) return cb(err);
@ -575,7 +593,8 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
Model: Model,
instance: obj,
isNewInstance: true,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('before save', context, function(err, ctx) {
if (err) return cb(err);
@ -1117,8 +1136,8 @@ DataAccessObject.find = function find(query, options, cb) {
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
var self = this;
var hookState = {};
var self = this;
try {
this._normalize(query);
@ -1143,7 +1162,12 @@ DataAccessObject.find = function find(query, options, cb) {
// using all documents
// TODO [fabien] use default scope here?
var context = { Model: self, query: query, hookState: hookState };
var context = {
Model: self,
query: query,
hookState: hookState,
options: options
};
self.notifyObserversOf('access', context, function(err, ctx) {
if (err) return cb(err);
@ -1238,7 +1262,12 @@ DataAccessObject.find = function find(query, options, cb) {
if (options.notify === false) {
self.getDataSource().connector.all(self.modelName, query, allCb);
} else {
var context = { Model: this, query: query, hookState: hookState };
var context = {
Model: this,
query: query,
hookState: hookState,
options: options
};
this.notifyObserversOf('access', context, function(err, ctx) {
if (err) return cb(err);
var query = ctx.query;
@ -1327,29 +1356,42 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
cb = cb || utils.createPromiseCallback();
where = where || {};
options = options || {};
assert(typeof where === 'object', 'The where argument must be an object');
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
var hookState = {};
var query = { where: where };
this.applyScope(query);
where = query.where;
var hookState = {};
var context = {
Model: Model, where: whereIsEmpty(where) ? {} : where, hookState: hookState
Model: Model,
where: whereIsEmpty(where) ? {} : where,
hookState: hookState,
options: options
};
if (options.notify === false) {
doDelete(where);
} else {
query = { where: whereIsEmpty(where) ? {} : where };
var context = { Model: Model, query: query, hookState: hookState };
var context = {
Model: Model,
query: query,
hookState: hookState,
options: options
};
Model.notifyObserversOf('access', context, function(err, ctx) {
if (err) return cb(err);
var context = { Model: Model, where: ctx.query.where, hookState: hookState };
var context = {
Model: Model,
where: ctx.query.where,
hookState: hookState,
options: options
};
Model.notifyObserversOf('before delete', context, function(err, ctx) {
if (err) return cb(err);
doDelete(ctx.where);
@ -1382,7 +1424,12 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
return cb(err, data);
}
var context = { Model: Model, where: where, hookState: hookState };
var context = {
Model: Model,
where: where,
hookState: hookState,
options: options
};
Model.notifyObserversOf('after delete', context, function(err) {
cb(err, data);
if (!err)
@ -1489,6 +1536,8 @@ DataAccessObject.count = function (where, options, cb) {
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
var hookState = {};
var query = { where: where };
this.applyScope(query);
where = query.where;
@ -1504,9 +1553,13 @@ DataAccessObject.count = function (where, options, cb) {
}
var Model = this;
var hookState = {};
var context = { Model: Model, query: { where: where }, hookState: hookState };
var context = {
Model: Model,
query: { where: where },
hookState: hookState,
options: options
};
this.notifyObserversOf('access', context, function(err, ctx) {
if (err) return cb(err);
where = ctx.query.where;
@ -1540,6 +1593,8 @@ DataAccessObject.prototype.save = function (options, cb) {
assert(typeof options === 'object', 'The options argument should be an object');
assert(typeof cb === 'function', 'The cb argument should be a function');
var hookState = {};
if (options.validate === undefined) {
options.validate = true;
@ -1554,9 +1609,13 @@ DataAccessObject.prototype.save = function (options, cb) {
var inst = this;
var modelName = Model.modelName;
var hookState = {};
var context = { Model: Model, instance: inst, hookState: hookState };
var context = {
Model: Model,
instance: inst,
hookState: hookState,
options: options
};
Model.notifyObserversOf('before save', context, function(err) {
if (err) return cb(err);
@ -1596,7 +1655,8 @@ DataAccessObject.prototype.save = function (options, cb) {
Model: Model,
instance: inst,
isNewInstance: result && result.isNewInstance,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('after save', context, function(err) {
if (err) return cb(err, inst);
@ -1674,6 +1734,8 @@ DataAccessObject.updateAll = function (where, data, options, cb) {
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
var hookState = {};
var query = { where: where };
this.applyScope(query);
this.applyProperties(data);
@ -1681,13 +1743,21 @@ DataAccessObject.updateAll = function (where, data, options, cb) {
where = query.where;
var Model = this;
var hookState = {};
var context = { Model: Model, query: { where: where }, hookState: hookState };
var context = {
Model: Model,
query: { where: where },
hookState: hookState,
options: options
};
Model.notifyObserversOf('access', context, function(err, ctx) {
if (err) return cb(err);
var context = {
Model: Model, where: ctx.query.where, data: data, hookState: hookState
Model: Model,
where: ctx.query.where,
data: data,
hookState: hookState,
options: options
};
Model.notifyObserversOf('before save', context,
function(err, ctx) {
@ -1713,7 +1783,11 @@ DataAccessObject.updateAll = function (where, data, options, cb) {
connector.update(Model.modelName, where, data, function(err, count) {
if (err) return cb (err);
var context = {
Model: Model, where: where, data: data, hookState: hookState
Model: Model,
where: where,
data: data,
hookState: hookState,
options: options
};
Model.notifyObserversOf('after save', context, function(err, ctx) {
return cb(err, count);
@ -1762,13 +1836,17 @@ DataAccessObject.prototype.remove =
assert(typeof options === 'object', 'The options argument should be an object');
assert(typeof cb === 'function', 'The cb argument should be a function');
var hookState = {};
var inst = this;
var Model = this.constructor;
var id = getIdValue(this.constructor, this);
var hookState = {};
var context = {
Model: Model, query: byIdQuery(Model, id), hookState: hookState
Model: Model,
query: byIdQuery(Model, id),
hookState: hookState,
options: options
};
Model.notifyObserversOf('access', context, function(err, ctx) {
@ -1777,7 +1855,8 @@ DataAccessObject.prototype.remove =
Model: Model,
where: ctx.query.where,
instance: inst,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('before delete', context, function(err, ctx) {
if (err) return cb(err);
@ -1796,7 +1875,8 @@ DataAccessObject.prototype.remove =
Model: Model,
where: where,
instance: inst,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('after delete', context, function(err) {
cb(err);
@ -1817,7 +1897,8 @@ DataAccessObject.prototype.remove =
Model: Model,
where: where,
instance: inst,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('after delete', context, function(err) {
cb(err);
@ -1944,10 +2025,11 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
var hookState = {};
var inst = this;
var Model = this.constructor;
var model = Model.modelName;
var hookState = {};
// Convert the data to be plain object so that update won't be confused
if (data instanceof Model) {
@ -1974,7 +2056,8 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
where: byIdQuery(Model, getIdValue(Model, inst)).where,
data: data,
currentInstance: inst,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('before save', context, function(err, ctx) {
@ -2015,7 +2098,8 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
Model: Model,
instance: inst,
isNewInstance: false,
hookState: hookState
hookState: hookState,
options: options
};
Model.notifyObserversOf('after save', context, function(err) {
if(!err) Model.emit('changed', inst);

View File

@ -473,8 +473,8 @@ module.exports = function(dataSource, should) {
observedContexts.should.eql(aTestModelCtx({ instance: {
id: existingInstance.id,
name: 'changed',
extra: undefined
}}));
extra: undefined,
}, options: { throws: false, validate: true } }));
done();
});
});
@ -524,8 +524,8 @@ module.exports = function(dataSource, should) {
name: 'changed',
extra: undefined
},
isNewInstance: false
}));
isNewInstance: false,
options: { throws: false, validate: true } }));
done();
});
});
@ -1297,6 +1297,23 @@ module.exports = function(dataSource, should) {
done();
});
});
it('accepts hookState from options', function(done) {
TestModel.observe('after save', pushContextAndNext());
TestModel.updateAll(
{ id: existingInstance.id },
{ name: 'updated name' },
{ foo: 'bar' },
function(err) {
if (err) return done(err);
observedContexts.options.should.eql({
foo: 'bar'
});
done();
});
});
});
function pushContextAndNext(fn) {
@ -1351,6 +1368,9 @@ module.exports = function(dataSource, should) {
if (!ctx.hookState) {
ctx.hookState = { test: true };
}
if (!ctx.options) {
ctx.options = {};
}
return deepCloneToObject(ctx);
}