Implement operation hooks' context
This commit is contained in:
parent
b7abb08da8
commit
f42859f2e5
124
lib/dao.js
124
lib/dao.js
|
@ -216,6 +216,7 @@ DataAccessObject.create = function (data, options, cb) {
|
||||||
var enforced = {};
|
var enforced = {};
|
||||||
var obj;
|
var obj;
|
||||||
var idValue = getIdValue(this, data);
|
var idValue = getIdValue(this, data);
|
||||||
|
var hookState = {};
|
||||||
|
|
||||||
// if we come from save
|
// if we come from save
|
||||||
if (data instanceof Model && !idValue) {
|
if (data instanceof Model && !idValue) {
|
||||||
|
@ -230,7 +231,9 @@ DataAccessObject.create = function (data, options, cb) {
|
||||||
Model = this.lookupModel(data); // data-specific
|
Model = this.lookupModel(data); // data-specific
|
||||||
if (Model !== obj.constructor) obj = new Model(data);
|
if (Model !== obj.constructor) obj = new Model(data);
|
||||||
|
|
||||||
Model.notifyObserversOf('before save', { Model: Model, instance: obj }, function(err) {
|
Model.notifyObserversOf('before save', {
|
||||||
|
Model: Model, instance: obj, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
data = obj.toObject(true);
|
data = obj.toObject(true);
|
||||||
|
@ -268,7 +271,9 @@ DataAccessObject.create = function (data, options, cb) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return cb(err, obj);
|
return cb(err, obj);
|
||||||
}
|
}
|
||||||
Model.notifyObserversOf('after save', { Model: Model, instance: obj }, function(err) {
|
Model.notifyObserversOf('after save', {
|
||||||
|
Model: Model, instance: obj, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
cb(err, obj);
|
cb(err, obj);
|
||||||
if(!err) Model.emit('changed', obj);
|
if(!err) Model.emit('changed', obj);
|
||||||
});
|
});
|
||||||
|
@ -345,20 +350,25 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var Model = this;
|
var Model = this;
|
||||||
|
var hookState = {};
|
||||||
|
|
||||||
var id = getIdValue(this, data);
|
var id = getIdValue(this, data);
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return this.create(data, options, cb);
|
return this.create(data, options, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
Model.notifyObserversOf('access', { Model: Model, query: byIdQuery(Model, id) }, doUpdateOrCreate);
|
Model.notifyObserversOf('access', {
|
||||||
|
Model: Model, query: byIdQuery(Model, id), hookState: hookState
|
||||||
|
}, doUpdateOrCreate);
|
||||||
|
|
||||||
function doUpdateOrCreate(err, ctx) {
|
function doUpdateOrCreate(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
var isOriginalQuery = isWhereByGivenId(Model, ctx.query.where, id)
|
var isOriginalQuery = isWhereByGivenId(Model, ctx.query.where, id)
|
||||||
if (Model.getDataSource().connector.updateOrCreate && isOriginalQuery) {
|
if (Model.getDataSource().connector.updateOrCreate && isOriginalQuery) {
|
||||||
var context = { Model: Model, where: ctx.query.where, data: data };
|
var context = {
|
||||||
|
Model: Model, where: ctx.query.where, data: data, hookState: hookState
|
||||||
|
};
|
||||||
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
@ -394,7 +404,9 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
|
||||||
Model.emit('changed', inst);
|
Model.emit('changed', inst);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Model.notifyObserversOf('after save', { Model: Model, instance: obj }, function(err) {
|
Model.notifyObserversOf('after save', {
|
||||||
|
Model: Model, instance: obj, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
cb(err, obj);
|
cb(err, obj);
|
||||||
if(!err) {
|
if(!err) {
|
||||||
Model.emit('changed', inst);
|
Model.emit('changed', inst);
|
||||||
|
@ -478,6 +490,7 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
|
||||||
|
|
||||||
var Model = this;
|
var Model = this;
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var hookState = {};
|
||||||
|
|
||||||
function _findOrCreate(query, data) {
|
function _findOrCreate(query, data) {
|
||||||
var modelName = self.modelName;
|
var modelName = self.modelName;
|
||||||
|
@ -493,8 +506,9 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (created) {
|
if (created) {
|
||||||
Model.notifyObserversOf('after save', { Model: Model, instance: obj },
|
Model.notifyObserversOf('after save', {
|
||||||
function(err) {
|
Model: Model, instance: obj, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
if (cb.promise) {
|
if (cb.promise) {
|
||||||
cb(err, [obj, created]);
|
cb(err, [obj, created]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -526,8 +540,9 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
|
||||||
|
|
||||||
this.applyScope(query);
|
this.applyScope(query);
|
||||||
|
|
||||||
Model.notifyObserversOf('access', { Model: Model, query: query },
|
Model.notifyObserversOf('access', {
|
||||||
function (err, ctx) {
|
Model: Model, query: query, hookState: hookState
|
||||||
|
}, function (err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
var query = ctx.query;
|
var query = ctx.query;
|
||||||
|
@ -539,8 +554,9 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
|
||||||
Model.applyProperties(enforced, obj);
|
Model.applyProperties(enforced, obj);
|
||||||
obj.setAttributes(enforced);
|
obj.setAttributes(enforced);
|
||||||
|
|
||||||
Model.notifyObserversOf('before save', { Model: Model, instance: obj },
|
Model.notifyObserversOf('before save', {
|
||||||
function(err, ctx) {
|
Model: Model, instance: obj, hookState: hookState
|
||||||
|
}, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
var obj = ctx.instance;
|
var obj = ctx.instance;
|
||||||
|
@ -1081,6 +1097,7 @@ DataAccessObject.find = function find(query, options, cb) {
|
||||||
assert(typeof cb === 'function', 'The cb argument must be a function');
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var hookState = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._normalize(query);
|
this._normalize(query);
|
||||||
|
@ -1105,7 +1122,9 @@ DataAccessObject.find = function find(query, options, cb) {
|
||||||
// using all documents
|
// using all documents
|
||||||
// TODO [fabien] use default scope here?
|
// TODO [fabien] use default scope here?
|
||||||
|
|
||||||
self.notifyObserversOf('access', { Model: self, query: query }, function(err, ctx) {
|
self.notifyObserversOf('access', {
|
||||||
|
Model: self, query: query, hookState: hookState
|
||||||
|
}, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
self.getDataSource().connector.all(self.modelName, {}, function (err, data) {
|
self.getDataSource().connector.all(self.modelName, {}, function (err, data) {
|
||||||
|
@ -1199,7 +1218,9 @@ DataAccessObject.find = function find(query, options, cb) {
|
||||||
if (options.notify === false) {
|
if (options.notify === false) {
|
||||||
self.getDataSource().connector.all(self.modelName, query, allCb);
|
self.getDataSource().connector.all(self.modelName, query, allCb);
|
||||||
} else {
|
} else {
|
||||||
this.notifyObserversOf('access', { Model: this, query: query }, function(err, ctx) {
|
this.notifyObserversOf('access', {
|
||||||
|
Model: this, query: query, hookState: hookState
|
||||||
|
}, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
var query = ctx.query;
|
var query = ctx.query;
|
||||||
self.getDataSource().connector.all(self.modelName, query, allCb);
|
self.getDataSource().connector.all(self.modelName, query, allCb);
|
||||||
|
@ -1296,16 +1317,22 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
|
||||||
this.applyScope(query);
|
this.applyScope(query);
|
||||||
where = query.where;
|
where = query.where;
|
||||||
|
|
||||||
var context = { Model: Model, where: whereIsEmpty(where) ? {} : where };
|
var hookState = {};
|
||||||
|
var context = {
|
||||||
|
Model: Model, where: whereIsEmpty(where) ? {} : where, hookState: hookState
|
||||||
|
};
|
||||||
|
|
||||||
if (options.notify === false) {
|
if (options.notify === false) {
|
||||||
doDelete(where);
|
doDelete(where);
|
||||||
} else {
|
} else {
|
||||||
query = { where: whereIsEmpty(where) ? {} : where };
|
query = { where: whereIsEmpty(where) ? {} : where };
|
||||||
Model.notifyObserversOf('access',
|
Model.notifyObserversOf('access', {
|
||||||
{ Model: Model, query: query },
|
Model: Model, query: query, hookState: hookState
|
||||||
function(err, ctx) {
|
}, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
var context = { Model: Model, where: ctx.query.where };
|
var context = {
|
||||||
|
Model: Model, where: ctx.query.where, hookState: hookState
|
||||||
|
};
|
||||||
Model.notifyObserversOf('before delete', context, function(err, ctx) {
|
Model.notifyObserversOf('before delete', context, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
doDelete(ctx.where);
|
doDelete(ctx.where);
|
||||||
|
@ -1338,7 +1365,9 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
|
||||||
return cb(err, data);
|
return cb(err, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Model.notifyObserversOf('after delete', { Model: Model, where: where }, function(err) {
|
Model.notifyObserversOf('after delete', {
|
||||||
|
Model: Model, where: where, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
if (!err)
|
if (!err)
|
||||||
Model.emit('deletedAll', whereIsEmpty(where) ? undefined : where);
|
Model.emit('deletedAll', whereIsEmpty(where) ? undefined : where);
|
||||||
|
@ -1459,7 +1488,11 @@ DataAccessObject.count = function (where, options, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var Model = this;
|
var Model = this;
|
||||||
this.notifyObserversOf('access', { Model: Model, query: { where: where } }, function(err, ctx) {
|
var hookState = {};
|
||||||
|
|
||||||
|
this.notifyObserversOf('access', {
|
||||||
|
Model: Model, query: { where: where }, hookState: hookState
|
||||||
|
}, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
where = ctx.query.where;
|
where = ctx.query.where;
|
||||||
Model.getDataSource().connector.count(Model.modelName, cb, where);
|
Model.getDataSource().connector.count(Model.modelName, cb, where);
|
||||||
|
@ -1506,8 +1539,11 @@ DataAccessObject.prototype.save = function (options, cb) {
|
||||||
|
|
||||||
var inst = this;
|
var inst = this;
|
||||||
var modelName = Model.modelName;
|
var modelName = Model.modelName;
|
||||||
|
var hookState = {};
|
||||||
|
|
||||||
Model.notifyObserversOf('before save', { Model: Model, instance: inst }, function(err) {
|
Model.notifyObserversOf('before save', {
|
||||||
|
Model: Model, instance: inst, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
var data = inst.toObject(true);
|
var data = inst.toObject(true);
|
||||||
|
@ -1542,7 +1578,9 @@ DataAccessObject.prototype.save = function (options, cb) {
|
||||||
return cb(err, inst);
|
return cb(err, inst);
|
||||||
}
|
}
|
||||||
inst._initProperties(data, { persisted: true });
|
inst._initProperties(data, { persisted: true });
|
||||||
Model.notifyObserversOf('after save', { Model: Model, instance: inst }, function(err) {
|
Model.notifyObserversOf('after save', {
|
||||||
|
Model: Model, instance: inst, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
if (err) return cb(err, inst);
|
if (err) return cb(err, inst);
|
||||||
updateDone.call(inst, function () {
|
updateDone.call(inst, function () {
|
||||||
saveDone.call(inst, function () {
|
saveDone.call(inst, function () {
|
||||||
|
@ -1625,15 +1663,19 @@ DataAccessObject.updateAll = function (where, data, options, cb) {
|
||||||
where = query.where;
|
where = query.where;
|
||||||
|
|
||||||
var Model = this;
|
var Model = this;
|
||||||
|
var hookState = {};
|
||||||
|
|
||||||
Model.notifyObserversOf('access', { Model: Model, query: { where: where } }, function(err, ctx) {
|
Model.notifyObserversOf('access', {
|
||||||
|
Model: Model, query: { where: where }, hookState: hookState
|
||||||
|
}, function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
Model.notifyObserversOf(
|
Model.notifyObserversOf(
|
||||||
'before save',
|
'before save',
|
||||||
{
|
{
|
||||||
Model: Model,
|
Model: Model,
|
||||||
where: ctx.query.where,
|
where: ctx.query.where,
|
||||||
data: data
|
data: data,
|
||||||
|
hookState: hookState
|
||||||
},
|
},
|
||||||
function(err, ctx) {
|
function(err, ctx) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
@ -1662,7 +1704,8 @@ DataAccessObject.updateAll = function (where, data, options, cb) {
|
||||||
{
|
{
|
||||||
Model: Model,
|
Model: Model,
|
||||||
where: where,
|
where: where,
|
||||||
data: data
|
data: data,
|
||||||
|
hookState: hookState
|
||||||
},
|
},
|
||||||
function(err, ctx) {
|
function(err, ctx) {
|
||||||
return cb(err, count);
|
return cb(err, count);
|
||||||
|
@ -1714,16 +1757,15 @@ DataAccessObject.prototype.remove =
|
||||||
var self = this;
|
var self = this;
|
||||||
var Model = this.constructor;
|
var Model = this.constructor;
|
||||||
var id = getIdValue(this.constructor, this);
|
var id = getIdValue(this.constructor, this);
|
||||||
|
var hookState = {};
|
||||||
|
|
||||||
Model.notifyObserversOf(
|
Model.notifyObserversOf('access', {
|
||||||
'access',
|
Model: Model, query: byIdQuery(Model, id), hookState: hookState
|
||||||
{ Model: Model, query: byIdQuery(Model, id) },
|
}, function(err, ctx) {
|
||||||
function(err, ctx) {
|
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
Model.notifyObserversOf(
|
Model.notifyObserversOf('before delete',{
|
||||||
'before delete',
|
Model: Model, where: ctx.query.where, hookState: hookState
|
||||||
{ Model: Model, where: ctx.query.where },
|
}, function(err, ctx) {
|
||||||
function(err, ctx) {
|
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
doDeleteInstance(ctx.where);
|
doDeleteInstance(ctx.where);
|
||||||
});
|
});
|
||||||
|
@ -1736,7 +1778,9 @@ DataAccessObject.prototype.remove =
|
||||||
// We must switch to full query-based delete.
|
// We must switch to full query-based delete.
|
||||||
Model.deleteAll(where, { notify: false }, function(err) {
|
Model.deleteAll(where, { notify: false }, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
Model.notifyObserversOf('after delete', { Model: Model, where: where }, function(err) {
|
Model.notifyObserversOf('after delete', {
|
||||||
|
Model: Model, where: where, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
if (!err) Model.emit('deleted', id);
|
if (!err) Model.emit('deleted', id);
|
||||||
});
|
});
|
||||||
|
@ -1751,7 +1795,9 @@ DataAccessObject.prototype.remove =
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyed(function () {
|
destroyed(function () {
|
||||||
Model.notifyObserversOf('after delete', { Model: Model, where: where }, function(err) {
|
Model.notifyObserversOf('after delete', {
|
||||||
|
Model: Model, where: where, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
if (!err) Model.emit('deleted', id);
|
if (!err) Model.emit('deleted', id);
|
||||||
});
|
});
|
||||||
|
@ -1858,6 +1904,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
|
||||||
var inst = this;
|
var inst = this;
|
||||||
var Model = this.constructor;
|
var Model = this.constructor;
|
||||||
var model = Model.modelName;
|
var model = Model.modelName;
|
||||||
|
var hookState = {};
|
||||||
|
|
||||||
// Convert the data to be plain object so that update won't be confused
|
// Convert the data to be plain object so that update won't be confused
|
||||||
if (data instanceof Model) {
|
if (data instanceof Model) {
|
||||||
|
@ -1868,7 +1915,8 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
|
||||||
var context = {
|
var context = {
|
||||||
Model: Model,
|
Model: Model,
|
||||||
where: byIdQuery(Model, getIdValue(Model, inst)).where,
|
where: byIdQuery(Model, getIdValue(Model, inst)).where,
|
||||||
data: data
|
data: data,
|
||||||
|
hookState: hookState
|
||||||
};
|
};
|
||||||
|
|
||||||
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
||||||
|
@ -1905,7 +1953,9 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
|
||||||
done.call(inst, function () {
|
done.call(inst, function () {
|
||||||
saveDone.call(inst, function () {
|
saveDone.call(inst, function () {
|
||||||
if (err) return cb(err, inst);
|
if (err) return cb(err, inst);
|
||||||
Model.notifyObserversOf('after save', { Model: Model, instance: inst }, function(err) {
|
Model.notifyObserversOf('after save', {
|
||||||
|
Model: Model, instance: inst, hookState: hookState
|
||||||
|
}, function(err) {
|
||||||
if(!err) Model.emit('changed', inst);
|
if(!err) Model.emit('changed', inst);
|
||||||
cb(err, inst);
|
cb(err, inst);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1095,6 +1095,31 @@ module.exports = function(dataSource, should) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('applies propagates hookState from `before delete` to `after delete` hook', function(done) {
|
||||||
|
TestModel.observe('before delete', pushContextAndNext(function(ctx) {
|
||||||
|
ctx.hookState.foo = 'bar';
|
||||||
|
}));
|
||||||
|
|
||||||
|
TestModel.observe('after delete', pushContextAndNext(function(ctx) {
|
||||||
|
ctx.hookState.foo = ctx.hookState.foo.toUpperCase();
|
||||||
|
}));
|
||||||
|
|
||||||
|
existingInstance.delete(function(err) {
|
||||||
|
if (err) return done(err);
|
||||||
|
observedContexts.should.eql([
|
||||||
|
aTestModelCtx({
|
||||||
|
hookState: { foo: 'bar', test: true },
|
||||||
|
where: { id: '1' }
|
||||||
|
}),
|
||||||
|
aTestModelCtx({
|
||||||
|
hookState: { foo: 'BAR', test: true },
|
||||||
|
where: { id: '1' }
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('triggers hooks only once', function(done) {
|
it('triggers hooks only once', function(done) {
|
||||||
TestModel.observe('access', pushNameAndNext('access'));
|
TestModel.observe('access', pushNameAndNext('access'));
|
||||||
TestModel.observe('after delete', pushNameAndNext('after delete'));
|
TestModel.observe('after delete', pushNameAndNext('after delete'));
|
||||||
|
@ -1202,9 +1227,14 @@ module.exports = function(dataSource, should) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function pushContextAndNext() {
|
function pushContextAndNext(fn) {
|
||||||
return function(context, next) {
|
return function(context, next) {
|
||||||
|
if (typeof fn === 'function') {
|
||||||
|
fn(context);
|
||||||
|
}
|
||||||
|
|
||||||
context = deepCloneToObject(context);
|
context = deepCloneToObject(context);
|
||||||
|
context.hookState.test = true;
|
||||||
|
|
||||||
if (typeof observedContexts === 'string') {
|
if (typeof observedContexts === 'string') {
|
||||||
observedContexts = context;
|
observedContexts = context;
|
||||||
|
@ -1246,6 +1276,9 @@ module.exports = function(dataSource, should) {
|
||||||
|
|
||||||
function aTestModelCtx(ctx) {
|
function aTestModelCtx(ctx) {
|
||||||
ctx.Model = TestModel;
|
ctx.Model = TestModel;
|
||||||
|
if (!ctx.hookState) {
|
||||||
|
ctx.hookState = { test: true };
|
||||||
|
}
|
||||||
return deepCloneToObject(ctx);
|
return deepCloneToObject(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue