From 47fb56e37eca9af40b298ab9d19ec0e9364a135a Mon Sep 17 00:00:00 2001 From: Fabien Franzen Date: Thu, 19 Mar 2015 17:51:41 +0100 Subject: [PATCH 1/4] Notify observers for embedded relations --- lib/relation-definition.js | 141 ++++++++++++++++++++++++++----------- test/relations.test.js | 80 +++++++++++++++++++++ 2 files changed, 178 insertions(+), 43 deletions(-) diff --git a/lib/relation-definition.js b/lib/relation-definition.js index ed0d7fef8..db759f26e 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -1856,27 +1856,35 @@ EmbedsOne.prototype.create = function (targetModelData, cb) { var inst = this.callScopeMethod('build', targetModelData); - var updateEmbedded = function() { + var updateEmbedded = function(callback) { modelInstance.updateAttribute(propertyName, inst, function(err) { - cb(err, err ? null : inst); + callback(err, err ? null : inst); }); }; if (this.definition.options.persistent) { inst.save(function(err) { // will validate if (err) return cb(err, inst); - updateEmbedded(); + updateEmbedded(cb); }); } else { - var err = inst.isValid() ? null : new ValidationError(inst); - if (err) { - process.nextTick(function() { - cb(err); - }); - } else { - updateEmbedded(); - } + var context = { Model: modelTo, instance: inst, hookState: {} }; + modelTo.notifyObserversOf('before save', context, function(err) { + err = err || (inst.isValid() ? null : new ValidationError(inst)); + if (err) { + process.nextTick(function() { + cb(err); + }); + } else { + updateEmbedded(function(err, inst) { + if (err) return cb(err, null); + modelTo.notifyObserversOf('after save', context, function(err) { + cb(err, err ? null : inst); + }); + }); + } + }); } }; @@ -1921,8 +1929,14 @@ EmbedsOne.prototype.update = function (targetModelData, cb) { if (embeddedInstance instanceof modelTo) { embeddedInstance.setAttributes(data); if (typeof cb === 'function') { - modelInstance.save(function(err, inst) { - cb(err, inst ? inst[propertyName] : embeddedInstance); + var context = { Model: modelTo, instance: embeddedInstance, hookState: {} }; + modelTo.notifyObserversOf('before save', context, function(err) { + if (err) return cb(err); + modelInstance.save(function(err, inst) { + modelTo.notifyObserversOf('after save', context, function(err) { + cb(err, inst ? inst[propertyName] : embeddedInstance); + }); + }); }); } } else if (!embeddedInstance && cb) { @@ -1933,11 +1947,23 @@ EmbedsOne.prototype.update = function (targetModelData, cb) { }; EmbedsOne.prototype.destroy = function (cb) { + var modelTo = this.definition.modelTo; var modelInstance = this.modelInstance; var propertyName = this.definition.keyFrom; + var embeddedInstance = modelInstance[propertyName]; + + if (!embeddedInstance) return cb && cb(null, 0); + modelInstance.unsetAttribute(propertyName, true); - modelInstance.save(function (err, result) { - cb && cb(err, result); + + var context = { Model: modelTo, instance: embeddedInstance, hookState: {} }; + modelTo.notifyObserversOf('before delete', context, function(err) { + if (err) return cb && cb(err); + modelInstance.save(function(err, result) { + modelTo.notifyObserversOf('after delete', context, function(err) { + cb && cb(err, result); + }); + }); }); }; @@ -2225,22 +2251,33 @@ EmbedsMany.prototype.updateById = function (fkId, data, cb) { var inst = this.findById(fkId); if (inst instanceof modelTo) { - if (typeof data === 'object') { - inst.setAttributes(data); - } - var err = inst.isValid() ? null : new ValidationError(inst); - if (err && typeof cb === 'function') { - return process.nextTick(function() { - cb(err, inst); - }); - } + var context = { Model: modelTo, instance: inst, hookState: {} }; + modelTo.notifyObserversOf('before save', context, function(err) { + if (err) return cb && cb(err); - if (typeof cb === 'function') { - modelInstance.updateAttribute(propertyName, - embeddedList, function(err) { - cb(err, inst); - }); - } + if (typeof data === 'object') { + inst.setAttributes(data); + } + + var err = inst.isValid() ? null : new ValidationError(inst); + if (err && typeof cb === 'function') { + return process.nextTick(function() { + cb(err, inst); + }); + } + + if (typeof cb === 'function') { + modelInstance.updateAttribute(propertyName, + embeddedList, function(err) { + if (err) return cb(err, inst); + modelTo.notifyObserversOf('after save', context, function(err) { + cb(err, inst); + }); + }); + } else { + modelTo.notifyObserversOf('after save', context); + } + }); } else if (typeof cb === 'function') { process.nextTick(function() { cb(null, null); // not found @@ -2259,15 +2296,23 @@ EmbedsMany.prototype.destroyById = function (fkId, cb) { var inst = (fkId instanceof modelTo) ? fkId : this.findById(fkId); if (inst instanceof modelTo) { - var index = embeddedList.indexOf(inst); - if (index > -1) embeddedList.splice(index, 1); - if (typeof cb === 'function') { - modelInstance.updateAttribute(propertyName, - embeddedList, function(err) { - cb(err); - modelTo.emit('deleted', inst.id, inst.toJSON()); - }); - } + var context = { Model: modelTo, instance: inst, hookState: {} }; + modelTo.notifyObserversOf('before delete', context, function(err) { + var index = embeddedList.indexOf(inst); + if (index > -1) embeddedList.splice(index, 1); + if (typeof cb === 'function') { + modelInstance.updateAttribute(propertyName, + embeddedList, function(err) { + if (err) return cb(err); + modelTo.notifyObserversOf('after delete', context, function(err) { + cb(err); + if (!err) modelTo.emit('deleted', inst.id, inst.toJSON()); + }); + }); + } else { + modelTo.notifyObserversOf('after delete', context); + } + }); } else if (typeof cb === 'function') { process.nextTick(cb); // not found } @@ -2312,17 +2357,17 @@ EmbedsMany.prototype.create = function (targetModelData, cb) { var inst = this.callScopeMethod('build', targetModelData); - var updateEmbedded = function() { + var updateEmbedded = function(callback) { modelInstance.updateAttribute(propertyName, embeddedList, function(err, modelInst) { - cb(err, err ? null : inst); + callback(err, err ? null : inst); }); }; if (this.definition.options.persistent) { inst.save(function(err) { // will validate if (err) return cb(err, inst); - updateEmbedded(); + updateEmbedded(cb); }); } else { var err = inst.isValid() ? null : new ValidationError(inst); @@ -2331,9 +2376,19 @@ EmbedsMany.prototype.create = function (targetModelData, cb) { cb(err); }); } else { - updateEmbedded(); + var context = { Model: modelTo, instance: inst, hookState: {} }; + modelTo.notifyObserversOf('before save', context, function(err) { + if (err) return cb(err); + updateEmbedded(function(err, inst) { + if (err) return cb(err, null); + modelTo.notifyObserversOf('after save', context, function(err) { + cb(err, err ? null : inst); + }); + }); + }); } } + }; EmbedsMany.prototype.build = function(targetModelData) { diff --git a/test/relations.test.js b/test/relations.test.js index c3b81b230..eeed8a874 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -2199,6 +2199,8 @@ describe('relations', function () { var Passport; var Other; + var hooks = []; + before(function () { tmp = getTransientDataSource(); db = getSchema(); @@ -2216,6 +2218,27 @@ describe('relations', function () { default: {name: 'Anonymous'} // a bit contrived }); Person.embedsOne(Address); // all by default + + Passport.observe('before save', function(ctx, next) { + hooks.push({ hook: 'before save', inst: ctx.instance.toObject() }); + next(); + }); + + Passport.observe('after save', function(ctx, next) { + hooks.push({ hook: 'after save', inst: ctx.instance.toObject() }); + next(); + }); + + Passport.observe('before delete', function(ctx, next) { + hooks.push({ hook: 'before delete', inst: ctx.instance.toObject() }); + next(); + }); + + Passport.observe('after delete', function(ctx, next) { + hooks.push({ hook: 'after delete', inst: ctx.instance.toObject() }); + next(); + }); + db.automigrate(done); }); @@ -2355,6 +2378,20 @@ describe('relations', function () { }); }); + it('should have notified hook observers', function() { + // the un-paired list of events stems from a ValidationError + var expected = [ + { hook: 'before save', inst: { name: 'Fredric' } }, + { hook: 'after save', inst: { name: 'Fredric' } }, + { hook: 'before save', inst: { name: undefined } }, + { hook: 'before save', inst: { name: 'Freddy' } }, + { hook: 'after save', inst: { name: 'Freddy' } }, + { hook: 'before delete', inst: { name: 'Freddy' } }, + { hook: 'after delete', inst: { name: 'Freddy' } } + ]; + hooks.should.eql(expected); + }); + }); describe('embedsOne - persisted model', function () { @@ -2435,6 +2472,8 @@ describe('relations', function () { describe('embedsMany', function () { var address1, address2; + + var hooks = []; before(function (done) { tmp = getTransientDataSource({defaultIdType: Number}); @@ -2450,6 +2489,27 @@ describe('relations', function () { it('can be declared', function (done) { Person.embedsMany(Address); + + Address.observe('before save', function(ctx, next) { + hooks.push({ hook: 'before save', inst: ctx.instance.toObject() }); + next(); + }); + + Address.observe('after save', function(ctx, next) { + hooks.push({ hook: 'after save', inst: ctx.instance.toObject() }); + next(); + }); + + Address.observe('before delete', function(ctx, next) { + hooks.push({ hook: 'before delete', inst: ctx.instance.toObject() }); + next(); + }); + + Address.observe('after delete', function(ctx, next) { + hooks.push({ hook: 'after delete', inst: ctx.instance.toObject() }); + next(); + }); + db.automigrate(done); }); @@ -2619,6 +2679,26 @@ describe('relations', function () { done(); }); }); + + it('should have notified hook observers', function() { + // the un-paired list of events stems from a ValidationError + var expected = [ + { hook: 'before save', inst: { street: 'Street 1', id: 1 } }, + { hook: 'after save', inst: { street: 'Street 1', id: 1 } }, + { hook: 'before save', inst: { street: 'Street 2', id: 2 } }, + { hook: 'after save', inst: { street: 'Street 2', id: 2 } }, + { hook: 'before save', inst: { street: 'Street 2', id: 2 } }, + { hook: 'after save', inst: { street: 'New Street', id: 2 } }, + { hook: 'before save', inst: { street: 'New Street', id: 2 } }, + { hook: 'before save', inst: { street: 'Street 1', id: 1 } }, + { hook: 'after save', inst: { street: 'Changed 1', id: 1 } }, + { hook: 'before save', inst: { street: 'New Street', id: 2 } }, + { hook: 'after save', inst: { street: 'Changed 2', id: 2 } }, + { hook: 'before delete', inst: { street: 'Street 1', id: 1 } }, + { hook: 'after delete', inst: { street: 'Street 1', id: 1 } } + ]; + hooks.should.eql(expected); + }); }); From 241bd65c3ebfd251bacaf763d512b1199beb36f2 Mon Sep 17 00:00:00 2001 From: Fabien Franzen Date: Fri, 20 Mar 2015 09:57:59 +0100 Subject: [PATCH 2/4] Embedded hooks: use data+currentInstance in 'before save' --- lib/relation-definition.js | 39 ++++++-- test/relations.test.js | 196 +++++++++++++++++++++++++++++++------ 2 files changed, 199 insertions(+), 36 deletions(-) diff --git a/lib/relation-definition.js b/lib/relation-definition.js index db759f26e..a8192b667 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -1924,20 +1924,40 @@ EmbedsOne.prototype.update = function (targetModelData, cb) { var isInst = targetModelData instanceof ModelBaseClass; var data = isInst ? targetModelData.toObject() : targetModelData; + data = typeof data === 'object' ? data : {}; var embeddedInstance = modelInstance[propertyName]; if (embeddedInstance instanceof modelTo) { - embeddedInstance.setAttributes(data); if (typeof cb === 'function') { - var context = { Model: modelTo, instance: embeddedInstance, hookState: {} }; + var hookState = {}; + var context = { + Model: modelTo, + currentInstance: embeddedInstance, + data: data, + hookState: hookState + }; modelTo.notifyObserversOf('before save', context, function(err) { if (err) return cb(err); + + embeddedInstance.setAttributes(data); + + var err = embeddedInstance.isValid() ? null : new ValidationError(embeddedInstance); + if (err && typeof cb === 'function') { + return process.nextTick(function() { + cb(err, embeddedInstance); + }); + } + modelInstance.save(function(err, inst) { + if (err) return cb(err); + context = { Model: modelTo, instance: embeddedInstance, hookState: hookState }; modelTo.notifyObserversOf('after save', context, function(err) { cb(err, inst ? inst[propertyName] : embeddedInstance); }); }); }); + } else { + embeddedInstance.setAttributes(data); } } else if (!embeddedInstance && cb) { this.callScopeMethod('create', data, cb); @@ -2249,15 +2269,20 @@ EmbedsMany.prototype.updateById = function (fkId, data, cb) { var embeddedList = this.embeddedList(); var inst = this.findById(fkId); + var data = typeof data === 'object' ? data : {}; if (inst instanceof modelTo) { - var context = { Model: modelTo, instance: inst, hookState: {} }; + var hookState = {}; + var context = { + Model: modelTo, + currentInstance: inst, + data: data, + hookState: hookState + }; modelTo.notifyObserversOf('before save', context, function(err) { if (err) return cb && cb(err); - if (typeof data === 'object') { - inst.setAttributes(data); - } + inst.setAttributes(data); var err = inst.isValid() ? null : new ValidationError(inst); if (err && typeof cb === 'function') { @@ -2266,6 +2291,8 @@ EmbedsMany.prototype.updateById = function (fkId, data, cb) { }); } + context = { Model: modelTo, instance: inst, hookState: hookState }; + if (typeof cb === 'function') { modelInstance.updateAttribute(propertyName, embeddedList, function(err) { diff --git a/test/relations.test.js b/test/relations.test.js index eeed8a874..69972745a 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -2200,6 +2200,14 @@ describe('relations', function () { var Other; var hooks = []; + var combinedHooks = []; + + function observeHook(Model, type) { + Model.observe(type, function(ctx, next) { + combinedHooks.push(ctx.Model.modelName + ': ' + type); + next(); + }); + }; before(function () { tmp = getTransientDataSource(); @@ -2213,29 +2221,62 @@ describe('relations', function () { Other = db.define('Other', {name: String}); }); + beforeEach(function() { + combinedHooks = []; + }); + it('can be declared using embedsOne method', function (done) { Person.embedsOne(Passport, { default: {name: 'Anonymous'} // a bit contrived }); Person.embedsOne(Address); // all by default + observeHook(Person, 'before save'); + observeHook(Person, 'after save'); + observeHook(Person, 'before delete'); + observeHook(Person, 'after delete'); + + observeHook(Passport, 'before save'); + observeHook(Passport, 'after save'); + observeHook(Passport, 'before delete'); + observeHook(Passport, 'after delete'); + Passport.observe('before save', function(ctx, next) { - hooks.push({ hook: 'before save', inst: ctx.instance.toObject() }); + var info = { hook: 'before save' }; + if (ctx.currentInstance) { + info.currentInstance = ctx.currentInstance.toObject(); + } else { + info.instance = ctx.instance.toObject(); + } + if (ctx.data) info.data = ctx.data; + hooks.push(info); next(); }); Passport.observe('after save', function(ctx, next) { - hooks.push({ hook: 'after save', inst: ctx.instance.toObject() }); + var info = { + hook: 'after save', + instance: ctx.instance.toObject() + }; + hooks.push(info); next(); }); Passport.observe('before delete', function(ctx, next) { - hooks.push({ hook: 'before delete', inst: ctx.instance.toObject() }); + var info = { + hook: 'before delete', + instance: ctx.instance.toObject() + }; + hooks.push(info); next(); }); Passport.observe('after delete', function(ctx, next) { - hooks.push({ hook: 'after delete', inst: ctx.instance.toObject() }); + var info = { + hook: 'after delete', + instance: ctx.instance.toObject() + }; + hooks.push(info); next(); }); @@ -2297,6 +2338,17 @@ describe('relations', function () { should.not.exist(err); p.passport.toObject().should.eql({name: 'Fredric'}); p.passport.should.be.an.instanceOf(Passport); + + var expected = [ + 'Person: before save', + 'Person: after save', + 'Passport: before save', + 'Person: before save', + 'Person: after save', + 'Passport: after save' + ]; + combinedHooks.should.eql(expected); + done(); }); }); @@ -2346,6 +2398,15 @@ describe('relations', function () { passport.toObject().should.eql({name: 'Freddy'}); passport.should.be.an.instanceOf(Passport); passport.should.equal(p.passport); + + var expected = [ + 'Passport: before save', + 'Person: before save', + 'Person: after save', + 'Passport: after save' + ]; + combinedHooks.should.eql(expected); + done(); }); }); @@ -2381,13 +2442,16 @@ describe('relations', function () { it('should have notified hook observers', function() { // the un-paired list of events stems from a ValidationError var expected = [ - { hook: 'before save', inst: { name: 'Fredric' } }, - { hook: 'after save', inst: { name: 'Fredric' } }, - { hook: 'before save', inst: { name: undefined } }, - { hook: 'before save', inst: { name: 'Freddy' } }, - { hook: 'after save', inst: { name: 'Freddy' } }, - { hook: 'before delete', inst: { name: 'Freddy' } }, - { hook: 'after delete', inst: { name: 'Freddy' } } + { hook: 'before save', instance: { name: 'Fredric' } }, + { hook: 'after save', instance: { name: 'Fredric' } }, + { hook: 'before save', instance: { name: undefined } }, + { hook: 'before save', + currentInstance: { name: 'Fredric' }, + data: { name: 'Freddy' } + }, + { hook: 'after save', instance: { name: 'Freddy' } }, + { hook: 'before delete', instance: { name: 'Freddy' } }, + { hook: 'after delete', instance: { name: 'Freddy' } } ]; hooks.should.eql(expected); }); @@ -2474,6 +2538,14 @@ describe('relations', function () { var address1, address2; var hooks = []; + var combinedHooks = []; + + function observeHook(Model, type) { + Model.observe(type, function(ctx, next) { + combinedHooks.push(ctx.Model.modelName + ': ' + type); + next(); + }); + }; before(function (done) { tmp = getTransientDataSource({defaultIdType: Number}); @@ -2487,29 +2559,62 @@ describe('relations', function () { }); }); + beforeEach(function() { + combinedHooks = []; + }); + it('can be declared', function (done) { Person.embedsMany(Address); - + + observeHook(Person, 'before save'); + observeHook(Person, 'after save'); + observeHook(Person, 'before delete'); + observeHook(Person, 'after delete'); + + observeHook(Address, 'before save'); + observeHook(Address, 'after save'); + observeHook(Address, 'before delete'); + observeHook(Address, 'after delete'); + Address.observe('before save', function(ctx, next) { - hooks.push({ hook: 'before save', inst: ctx.instance.toObject() }); + var info = { hook: 'before save' }; + if (ctx.currentInstance) { + info.currentInstance = ctx.currentInstance.toObject(); + } else { + info.instance = ctx.instance.toObject(); + } + if (ctx.data) info.data = ctx.data; + hooks.push(info); next(); }); Address.observe('after save', function(ctx, next) { - hooks.push({ hook: 'after save', inst: ctx.instance.toObject() }); + var info = { + hook: 'after save', + instance: ctx.instance.toObject() + }; + hooks.push(info); next(); }); Address.observe('before delete', function(ctx, next) { - hooks.push({ hook: 'before delete', inst: ctx.instance.toObject() }); + var info = { + hook: 'before delete', + instance: ctx.instance.toObject() + }; + hooks.push(info); next(); }); Address.observe('after delete', function(ctx, next) { - hooks.push({ hook: 'after delete', inst: ctx.instance.toObject() }); + var info = { + hook: 'after delete', + instance: ctx.instance.toObject() + }; + hooks.push(info); next(); }); - + db.automigrate(done); }); @@ -2533,6 +2638,17 @@ describe('relations', function () { address1 = address; should.exist(address1.id); address1.street.should.equal('Street 1'); + + var expected = [ + 'Person: before save', + 'Person: after save', + 'Address: before save', + 'Person: before save', + 'Person: after save', + 'Address: after save' + ]; + combinedHooks.should.eql(expected); + done(); }); }); @@ -2545,6 +2661,15 @@ describe('relations', function () { address2 = address; should.exist(address2.id); address2.street.should.equal('Street 2'); + + var expected = [ + 'Address: before save', + 'Person: before save', + 'Person: after save', + 'Address: after save' + ]; + combinedHooks.should.eql(expected); + done(); }); }); @@ -2683,19 +2808,30 @@ describe('relations', function () { it('should have notified hook observers', function() { // the un-paired list of events stems from a ValidationError var expected = [ - { hook: 'before save', inst: { street: 'Street 1', id: 1 } }, - { hook: 'after save', inst: { street: 'Street 1', id: 1 } }, - { hook: 'before save', inst: { street: 'Street 2', id: 2 } }, - { hook: 'after save', inst: { street: 'Street 2', id: 2 } }, - { hook: 'before save', inst: { street: 'Street 2', id: 2 } }, - { hook: 'after save', inst: { street: 'New Street', id: 2 } }, - { hook: 'before save', inst: { street: 'New Street', id: 2 } }, - { hook: 'before save', inst: { street: 'Street 1', id: 1 } }, - { hook: 'after save', inst: { street: 'Changed 1', id: 1 } }, - { hook: 'before save', inst: { street: 'New Street', id: 2 } }, - { hook: 'after save', inst: { street: 'Changed 2', id: 2 } }, - { hook: 'before delete', inst: { street: 'Street 1', id: 1 } }, - { hook: 'after delete', inst: { street: 'Street 1', id: 1 } } + { hook: 'before save', instance: { street: 'Street 1', id: 1 } }, + { hook: 'after save', instance: { street: 'Street 1', id: 1 } }, + { hook: 'before save', instance: { street: 'Street 2', id: 2 } }, + { hook: 'after save', instance: { street: 'Street 2', id: 2 } }, + { hook: 'before save', + currentInstance: { street: 'Street 2', id: 2 }, + data: { street: 'New Street' } }, + { hook: 'after save', + instance: { street: 'New Street', id: 2 } }, + { hook: 'before save', + currentInstance: { street: 'New Street', id: 2 }, + data: { street: null } }, + { hook: 'before save', + currentInstance: { street: 'Street 1', id: 1 }, + data: { street: 'Changed 1' } }, + { hook: 'after save', instance: { street: 'Changed 1', id: 1 } }, + { hook: 'before save', + currentInstance: { street: 'New Street', id: 2 }, + data: { street: 'Changed 2' } }, + { hook: 'after save', instance: { street: 'Changed 2', id: 2 } }, + { hook: 'before delete', + instance: { street: 'Street 1', id: 1 } }, + { hook: 'after delete', + instance: { street: 'Street 1', id: 1 } } ]; hooks.should.eql(expected); }); From 807e3b5ec6cdd39b478b98ad356db7dd705237fd Mon Sep 17 00:00:00 2001 From: Fabien Franzen Date: Fri, 20 Mar 2015 10:53:16 +0100 Subject: [PATCH 3/4] Refactor hook observing tests --- test/relations.test.js | 140 +++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 90 deletions(-) diff --git a/test/relations.test.js b/test/relations.test.js index 69972745a..01130f24c 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -2202,13 +2202,6 @@ describe('relations', function () { var hooks = []; var combinedHooks = []; - function observeHook(Model, type) { - Model.observe(type, function(ctx, next) { - combinedHooks.push(ctx.Model.modelName + ': ' + type); - next(); - }); - }; - before(function () { tmp = getTransientDataSource(); db = getSchema(); @@ -2241,44 +2234,10 @@ describe('relations', function () { observeHook(Passport, 'before delete'); observeHook(Passport, 'after delete'); - Passport.observe('before save', function(ctx, next) { - var info = { hook: 'before save' }; - if (ctx.currentInstance) { - info.currentInstance = ctx.currentInstance.toObject(); - } else { - info.instance = ctx.instance.toObject(); - } - if (ctx.data) info.data = ctx.data; - hooks.push(info); - next(); - }); - - Passport.observe('after save', function(ctx, next) { - var info = { - hook: 'after save', - instance: ctx.instance.toObject() - }; - hooks.push(info); - next(); - }); - - Passport.observe('before delete', function(ctx, next) { - var info = { - hook: 'before delete', - instance: ctx.instance.toObject() - }; - hooks.push(info); - next(); - }); - - Passport.observe('after delete', function(ctx, next) { - var info = { - hook: 'after delete', - instance: ctx.instance.toObject() - }; - hooks.push(info); - next(); - }); + observeEmbedded(Passport, 'before save'); + observeEmbedded(Passport, 'after save'); + observeEmbedded(Passport, 'before delete'); + observeEmbedded(Passport, 'after delete'); db.automigrate(done); }); @@ -2456,6 +2415,27 @@ describe('relations', function () { hooks.should.eql(expected); }); + function observeHook(Model, type) { + Model.observe(type, function(ctx, next) { + combinedHooks.push(ctx.Model.modelName + ': ' + type); + next(); + }); + }; + + function observeEmbedded(Model, type) { + Model.observe(type, function(ctx, next) { + var info = { hook: type }; + if (ctx.currentInstance) { + info.currentInstance = ctx.currentInstance.toObject(); + } else if (ctx.instance) { + info.instance = ctx.instance.toObject(); + } + if (ctx.data) info.data = ctx.data; + hooks.push(info); + next(); + }); + }; + }); describe('embedsOne - persisted model', function () { @@ -2540,13 +2520,6 @@ describe('relations', function () { var hooks = []; var combinedHooks = []; - function observeHook(Model, type) { - Model.observe(type, function(ctx, next) { - combinedHooks.push(ctx.Model.modelName + ': ' + type); - next(); - }); - }; - before(function (done) { tmp = getTransientDataSource({defaultIdType: Number}); db = getSchema(); @@ -2576,44 +2549,10 @@ describe('relations', function () { observeHook(Address, 'before delete'); observeHook(Address, 'after delete'); - Address.observe('before save', function(ctx, next) { - var info = { hook: 'before save' }; - if (ctx.currentInstance) { - info.currentInstance = ctx.currentInstance.toObject(); - } else { - info.instance = ctx.instance.toObject(); - } - if (ctx.data) info.data = ctx.data; - hooks.push(info); - next(); - }); - - Address.observe('after save', function(ctx, next) { - var info = { - hook: 'after save', - instance: ctx.instance.toObject() - }; - hooks.push(info); - next(); - }); - - Address.observe('before delete', function(ctx, next) { - var info = { - hook: 'before delete', - instance: ctx.instance.toObject() - }; - hooks.push(info); - next(); - }); - - Address.observe('after delete', function(ctx, next) { - var info = { - hook: 'after delete', - instance: ctx.instance.toObject() - }; - hooks.push(info); - next(); - }); + observeEmbedded(Address, 'before save'); + observeEmbedded(Address, 'after save'); + observeEmbedded(Address, 'before delete'); + observeEmbedded(Address, 'after delete'); db.automigrate(done); }); @@ -2836,6 +2775,27 @@ describe('relations', function () { hooks.should.eql(expected); }); + function observeHook(Model, type) { + Model.observe(type, function(ctx, next) { + combinedHooks.push(ctx.Model.modelName + ': ' + type); + next(); + }); + }; + + function observeEmbedded(Model, type) { + Model.observe(type, function(ctx, next) { + var info = { hook: type }; + if (ctx.currentInstance) { + info.currentInstance = ctx.currentInstance.toObject(); + } else if (ctx.instance) { + info.instance = ctx.instance.toObject(); + } + if (ctx.data) info.data = ctx.data; + hooks.push(info); + next(); + }); + }; + }); describe('embedsMany - numeric ids + forceId', function () { From 3cc6f101a6e651d46cf8c1132f5fbb824452216a Mon Sep 17 00:00:00 2001 From: Fabien Franzen Date: Fri, 20 Mar 2015 12:56:14 +0100 Subject: [PATCH 4/4] Add embedded delete tests --- test/relations.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/relations.test.js b/test/relations.test.js index 01130f24c..01d07a43c 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -2385,6 +2385,15 @@ describe('relations', function () { p.passportItem.destroy(function(err) { should.not.exist(err); should.equal(p.passport, null); + + var expected = [ + 'Passport: before delete', + 'Person: before save', + 'Person: after save', + 'Passport: after delete' + ]; + combinedHooks.should.eql(expected); + done(); }); }); @@ -2732,6 +2741,15 @@ describe('relations', function () { p.addressList.destroy(address1.id, function(err) { should.not.exist(err); p.addresses.should.have.length(1); + + var expected = [ + 'Address: before delete', + 'Person: before save', + 'Person: after save', + 'Address: after delete' + ]; + combinedHooks.should.eql(expected); + done(); }); });