diff --git a/lib/dao.js b/lib/dao.js index 118717e9..3a8c4a18 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -126,6 +126,7 @@ DataAccessObject.create = function (data, callback) { function modelCreated() { if (--wait === 0) { callback(gotError ? errors : null, instances); + if(!gotError) instances.forEach(Model.emit.bind('changed')); } } } @@ -168,6 +169,7 @@ DataAccessObject.create = function (data, callback) { saveDone.call(obj, function () { createDone.call(obj, function () { callback(err, obj); + if(!err) Model.emit('changed', obj); }); }); }, obj); @@ -627,6 +629,7 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyAll = function destroyAll(where, cb) { if (stillConnecting(this.getDataSource(), this, arguments)) return; + var Model = this; if (!cb && 'function' === typeof where) { cb = where; @@ -635,6 +638,7 @@ DataAccessObject.remove = if (!where) { this.getDataSource().connector.destroyAll(this.modelName, function (err, data) { cb && cb(err, data); + if(!err) Model.emit('deletedAll'); }.bind(this)); } else { // Support an optional where object @@ -642,6 +646,7 @@ DataAccessObject.remove = where = this._coerce(where); this.getDataSource().connector.destroyAll(this.modelName, where, function (err, data) { cb && cb(err, data); + if(!err) Model.emit('deletedAll', where); }.bind(this)); } }; @@ -655,11 +660,13 @@ DataAccessObject.removeById = DataAccessObject.deleteById = DataAccessObject.destroyById = function deleteById(id, cb) { if (stillConnecting(this.getDataSource(), this, arguments)) return; + var Model = this; this.getDataSource().connector.destroy(this.modelName, id, function (err) { if ('function' === typeof cb) { cb(err); } + if(!err) Model.emit('deleted', id); }.bind(this)); }; @@ -704,6 +711,7 @@ setRemoting(DataAccessObject.count, { */ DataAccessObject.prototype.save = function (options, callback) { if (stillConnecting(this.getDataSource(), this, arguments)) return; + var Model = this.constructor; if (typeof options == 'function') { callback = options; @@ -760,6 +768,9 @@ DataAccessObject.prototype.save = function (options, callback) { updateDone.call(inst, function () { saveDone.call(inst, function () { callback(err, inst); + if(!err) { + Model.emit('changed', inst); + } }); }); }); @@ -789,15 +800,18 @@ DataAccessObject.prototype.remove = DataAccessObject.prototype.delete = DataAccessObject.prototype.destroy = function (cb) { if (stillConnecting(this.getDataSource(), this, arguments)) return; + var Model = this.constructor; + var id = getIdValue(this.constructor, this); this.trigger('destroy', function (destroyed) { - this._adapter().destroy(this.constructor.modelName, getIdValue(this.constructor, this), function (err) { + this._adapter().destroy(this.constructor.modelName, id, function (err) { if (err) { return cb(err); } destroyed(function () { if (cb) cb(); + Model.emit('deleted', id); }); }.bind(this)); }); @@ -831,7 +845,8 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb if (stillConnecting(this.getDataSource(), this, arguments)) return; var inst = this; - var model = this.constructor.modelName; + var Model = this.constructor + var model = Model.modelName; if (typeof data === 'function') { cb = data; @@ -870,7 +885,8 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb } done.call(inst, function () { saveDone.call(inst, function () { - cb(err, inst); + if(cb) cb(err, inst); + if(!err) Model.emit('changed', inst); }); }); }); diff --git a/test/events.js b/test/events.js new file mode 100644 index 00000000..b2867591 --- /dev/null +++ b/test/events.js @@ -0,0 +1,81 @@ +var should = require('./init.js'); + +describe('events', function() { + beforeEach(function(done) { + var test = this; + this.db = getSchema(); + this.TestModel = this.db.define('TestModel'); + this.db.automigrate(function(err) { + if(err) return done(err); + test.TestModel.create(function(err, inst) { + if(err) return done(err); + test.inst = inst; + done(); + }); + }); + this.shouldEmitEvent = function(eventName, listener, done) { + var timeout = setTimeout(function() { + done(new Error('did not emit ' + eventName)); + }, 100); + this.TestModel.on(eventName, function() { + clearTimeout(timeout); + listener.apply(this, arguments); + done(); + }); + } + }); + + describe('changed', function() { + it('should be emitted after save', function(done) { + var model = new this.TestModel({name: 'foobar'}); + this.shouldEmitEvent('changed', assertValidChangedArgs, done); + model.save(); + }); + it('should be emitted after upsert', function(done) { + this.shouldEmitEvent('changed', assertValidChangedArgs, done); + this.TestModel.upsert({name: 'batbaz'}); + }); + it('should be emitted after create', function(done) { + this.shouldEmitEvent('changed', assertValidChangedArgs, done); + this.TestModel.create({name: '...'}); + }); + it('should be emitted after updateAttributes', function(done) { + var test = this; + this.TestModel.create({name: 'bazzy'}, function(err, model) { + // prevent getting the changed event from "create" + process.nextTick(function() { + test.shouldEmitEvent('changed', assertValidChangedArgs, done); + model.updateAttributes({name: 'foo'}); + }); + }); + }); + }); + + describe('deleted', function() { + it('should be emitted after destroy', function(done) { + this.shouldEmitEvent('deleted', assertValidDeletedArgs, done); + this.inst.destroy(); + }); + it('should be emitted after deleteById', function(done) { + this.shouldEmitEvent('deleted', assertValidDeletedArgs, done); + this.TestModel.deleteById(this.inst.id); + }); + }); + + describe('deletedAll', function() { + it('should be emitted after destroyAll', function(done) { + this.shouldEmitEvent('deletedAll', function(where) { + where.name.should.equal('foo'); + }, done); + this.TestModel.destroyAll({name: 'foo'}); + }); + }); +}); + +function assertValidChangedArgs(obj) { + obj.should.have.property('id'); +} + +function assertValidDeletedArgs(id) { + id.should.be.ok; +} \ No newline at end of file diff --git a/test/hooks.test.js b/test/hooks.test.js index 2610f1b3..f613122b 100644 --- a/test/hooks.test.js +++ b/test/hooks.test.js @@ -402,6 +402,8 @@ describe('hooks', function () { }); }); + + function addHooks(name, done) { var called = false, random = String(Math.floor(Math.random() * 1000)); User['before' + name] = function (next, data) {