ModelBaseClass: promise mode for notifyObserversOf

Support both promise and callback styles in
ModelBaseClass.notifyObserversOf.

When there is no callback supplied, the method returns a promise that
is resolved (or rejected) with the result.
This commit is contained in:
Miroslav Bajtoš 2015-02-17 17:41:46 +01:00
parent a2836fbb56
commit 76ebdcb91b
4 changed files with 58 additions and 2 deletions

View File

@ -561,6 +561,8 @@ ModelBaseClass.observe = function(operation, listener) {
ModelBaseClass.notifyObserversOf = function(operation, context, callback) {
var observers = this._observers && this._observers[operation];
if (!callback) callback = utils.createPromiseCallback();
this._notifyBaseObservers(operation, context, function doNotify(err) {
if (err) return callback(err, context);
if (!observers || !observers.length) return callback(null, context);
@ -579,6 +581,7 @@ ModelBaseClass.notifyObserversOf = function(operation, context, callback) {
function(err) { callback(err, context) }
);
});
return callback.promise;
}
ModelBaseClass._notifyBaseObservers = function(operation, context, callback) {

View File

@ -9,6 +9,7 @@ exports.defineCachedRelations = defineCachedRelations;
exports.sortObjectsByIds = sortObjectsByIds;
exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
exports.mergeQuery = mergeQuery;
exports.createPromiseCallback = createPromiseCallback
var traverse = require('traverse');
@ -340,3 +341,30 @@ function sortObjectsByIds(idName, ids, objects, strict) {
return heading.concat(tailing);
};
function createPromiseCallback() {
var cb;
if (!global.Promise) {
cb = function(){};
cb.promise = {};
Object.defineProperty(cb.promise, 'then', { get: throwPromiseNotDefined });
Object.defineProperty(cb.promise, 'catch', { get: throwPromiseNotDefined });
return cb;
}
var promise = new Promise(function (resolve, reject) {
cb = function (err, data) {
if (err) return reject(err);
return resolve(data);
};
});
cb.promise = promise;
return cb;
}
function throwPromiseNotDefined() {
throw new Error(
'Your Node runtime does support ES6 Promises. ' +
'Set "global.Promise" to your preferred implementation of promises.');
}

View File

@ -1,6 +1,5 @@
var ModelBuilder = require('../').ModelBuilder;
var should = require('./init');
var Promise = global.Promise || require('bluebird');
describe('async observer', function() {
var TestModel;
@ -115,6 +114,28 @@ describe('async observer', function() {
done();
});
});
it('returns a promise when no callback is provided', function() {
var context = { value: 'a-test-context' };
var p = TestModel.notifyObserversOf('event', context);
(p !== undefined).should.be.true;
return p.then(function(result) {
result.should.eql(context);
});
});
it('returns a rejected promise when no callback is provided', function() {
var testError = new Error('expected test error');
TestModel.observe('event', function(ctx, next) { next(testError); });
var p = TestModel.notifyObserversOf('event', context);
return p.then(
function(result) {
throw new Error('The promise should have been rejected.');
},
function(err) {
err.should.eql(testError);
});
});
});
function pushAndNext(array, value) {

View File

@ -25,4 +25,8 @@ if (!('getModelBuilder' in global)) {
global.getModelBuilder = function () {
return new ModelBuilder();
};
}
}
if (!('Promise' in global)) {
global.Promise = require('bluebird');
}