diff --git a/lib/dao.js b/lib/dao.js index 9e58fc4f..8e1197fe 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -2380,14 +2380,16 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op } } + context.data = typedData; + function updateAttributesCallback(err) { - var context = { + var ctx = { Model: Model, - data: data, + data: context.data, hookState: hookState, options: options }; - Model.notifyObserversOf('loaded', context, function(err) { + Model.notifyObserversOf('loaded', ctx, function(err) { if (!err) inst.__persisted = true; // By default, the instance passed to updateAttributes callback is NOT updated @@ -2395,7 +2397,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op // backwards compatibility, we introduced a new setting updateOnLoad, // which if set, will apply these changes to the model instance too. if(Model.settings.updateOnLoad) { - inst.setAttributes(context.data); + inst.setAttributes(ctx.data); } done.call(inst, function () { saveDone.call(inst, function () { @@ -2417,21 +2419,21 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op }); } - context = { + var ctx = { Model: Model, where: byIdQuery(Model, getIdValue(Model, inst)).where, - data: data, + data: context.data, currentInstance: inst, hookState: hookState, options: options }; - Model.notifyObserversOf('persist', context, function(err) { + Model.notifyObserversOf('persist', ctx, function(err) { if (connector.updateAttributes.length === 5) { connector.updateAttributes(model, getIdValue(inst.constructor, inst), - inst.constructor._forDB(typedData), options, updateAttributesCallback); + inst.constructor._forDB(context.data), options, updateAttributesCallback); } else { connector.updateAttributes(model, getIdValue(inst.constructor, inst), - inst.constructor._forDB(typedData), updateAttributesCallback); + inst.constructor._forDB(context.data), updateAttributesCallback); } }); }, data, cb); diff --git a/test/persistence-hooks.suite.js b/test/persistence-hooks.suite.js index ca5f7dd3..6bc4e494 100644 --- a/test/persistence-hooks.suite.js +++ b/test/persistence-hooks.suite.js @@ -1154,6 +1154,58 @@ module.exports = function(dataSource, should) { }); }); + it('applies updates from `persist` hook - for nested model instance', function(done) { + var Address = dataSource.createModel('Address', { + id: { type: String, id: true, default: 1 }, + city: { type: String, required: true }, + country: { type: String, required: true } + }); + + var User = dataSource.createModel('User', { + id: { type: String, id: true, default: uid() }, + name: { type: String, required: true }, + address: {type: Address, required: false} + }); + + User.create({name: 'Joe'}, function(err, instance) { + if (err) return done(err); + + var existingUser = instance; + + User.observe('persist', pushContextAndNext(function(ctx){ + should.exist(ctx.data.address) + ctx.data.address.should.be.type('object'); + ctx.data.address.should.not.be.instanceOf(Address); + + ctx.data.extra = 'hook data'; + })); + + // By default, the instance passed to updateAttributes callback is NOT updated + // with the changes made through persist/loaded hooks. To preserve + // backwards compatibility, we introduced a new setting updateOnLoad, + // which if set, will apply these changes to the model instance too. + User.settings.updateOnLoad = true; + existingUser.updateAttributes( + { address: new Address({city: 'Springfield', country: 'USA'}) }, + function(err, inst) { + if (err) return done(err); + + inst.should.have.property('extra', 'hook data'); + + User.findById(existingUser.id, function(err, dbInstance) { + if (err) return done(err); + dbInstance.toObject(true).should.eql({ + id: existingUser.id, + name: existingUser.name, + address: {id: '1', city: 'Springfield', country: 'USA'}, + extra: 'hook data' + }); + done(); + }); + }); + }); + }); + it('triggers `loaded` hook', function(done) { TestModel.observe('loaded', pushContextAndNext()); existingInstance.updateAttributes({ name: 'changed' }, function(err) {