Merge pull request #451 from partap/promises-dao

Promisify DAO
This commit is contained in:
Miroslav Bajtoš 2015-02-27 17:10:07 +01:00
commit 034bdcac07
2 changed files with 413 additions and 59 deletions

View File

@ -148,7 +148,10 @@ function noCallback(err, result) {
* - instance (null or Model) * - instance (null or Model)
*/ */
DataAccessObject.create = function (data, options, cb) { DataAccessObject.create = function (data, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
var Model = this; var Model = this;
var self = this; var self = this;
@ -167,9 +170,9 @@ DataAccessObject.create = function (data, options, cb) {
} }
} }
cb = cb || noCallback;
data = data || {}; data = data || {};
options = options || {}; options = options || {};
cb = cb || (Array.isArray(data) ? noCallback : utils.createPromiseCallback());
assert(typeof data === 'object', 'The data argument must be an object or array'); assert(typeof data === 'object', 'The data argument must be an object or array');
assert(typeof options === 'object', 'The options argument must be an object'); assert(typeof options === 'object', 'The options argument must be an object');
@ -276,12 +279,27 @@ DataAccessObject.create = function (data, options, cb) {
}, obj, cb); }, obj, cb);
} }
// Does this make any sense? How would chaining be used here? -partap
// for chaining // for chaining
return obj; return cb.promise || obj;
}; };
function stillConnecting(dataSource, obj, args) { function stillConnecting(dataSource, obj, args) {
return dataSource.ready(obj, args); if (typeof args[args.length-1] === 'function') {
return dataSource.ready(obj, args);
}
// promise variant
var promiseArgs = Array.prototype.slice.call(args);
promiseArgs.callee = args.callee
var cb = utils.createPromiseCallback();
promiseArgs.push(cb);
if (dataSource.ready(obj, promiseArgs)) {
return cb.promise;
} else {
return false;
}
} }
/** /**
@ -298,8 +316,9 @@ function stillConnecting(dataSource, obj, args) {
// 'upsert' will be used as the name for strong-remoting to keep it backward // 'upsert' will be used as the name for strong-remoting to keep it backward
// compatible for angular SDK // compatible for angular SDK
DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data, options, cb) { DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) { var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
return; if (connectionPromise) {
return connectionPromise;
} }
if (options === undefined && cb === undefined) { if (options === undefined && cb === undefined) {
@ -316,7 +335,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
data = data || {}; data = data || {};
options = options || {}; options = options || {};
@ -404,6 +423,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
}); });
} }
} }
return cb.promise;
}; };
/** /**
@ -418,10 +438,19 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
* @param {Function} cb Callback called with (err, instance, created) * @param {Function} cb Callback called with (err, instance, created)
*/ */
DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) { DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
assert(arguments.length >= 2, 'At least two arguments are required'); assert(arguments.length >= 1, 'At least one argument is required');
if (options === undefined && cb === undefined) { if (data === undefined && options === undefined && cb === undefined) {
assert(typeof query === 'object', 'Single argument must be data object');
// findOrCreate(data);
// query will be built from data, and method will return Promise
data = query;
query = {where: data};
} else if (options === undefined && cb === undefined) {
if (typeof data === 'function') { if (typeof data === 'function') {
// findOrCreate(data, cb); // findOrCreate(data, cb);
// query will be built from data // query will be built from data
@ -437,7 +466,7 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
query = query || {where: {}}; query = query || {where: {}};
data = data || {}; data = data || {};
options = options || {}; options = options || {};
@ -466,11 +495,19 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
if (created) { if (created) {
Model.notifyObserversOf('after save', { Model: Model, instance: obj }, Model.notifyObserversOf('after save', { Model: Model, instance: obj },
function(err) { function(err) {
cb(err, obj, created); if (cb.promise) {
cb(err, [obj, created]);
} else {
cb(err, obj, created);
}
if (!err) Model.emit('changed', obj); if (!err) Model.emit('changed', obj);
}); });
} else { } else {
cb(err, obj, created); if (cb.promise) {
cb(err, [obj, created]);
} else {
cb(err, obj, created);
}
} }
}); });
} }
@ -481,9 +518,10 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
try { try {
this._normalize(query); this._normalize(query);
} catch (err) { } catch (err) {
return process.nextTick(function () { process.nextTick(function () {
cb(err); cb(err);
}); });
return cb.promise;
} }
this.applyScope(query); this.applyScope(query);
@ -521,12 +559,23 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
} else { } else {
Model.findOne(query, options, function (err, record) { Model.findOne(query, options, function (err, record) {
if (err) return cb(err); if (err) return cb(err);
if (record) return cb(null, record, false); if (record) {
if (cb.promise) {
return cb(null, [record, false]);
} else {
return cb(null, record, false);
}
}
Model.create(data, options, function (err, record) { Model.create(data, options, function (err, record) {
cb(err, record, record != null); if (cb.promise) {
cb(err, [record, record != null]);
} else {
cb(err, record, record != null);
}
}); });
}); });
} }
return cb.promise;
}; };
/** /**
@ -537,7 +586,10 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
* @param {Function} cb Callback function called with (err, exists: Bool) * @param {Function} cb Callback function called with (err, exists: Bool)
*/ */
DataAccessObject.exists = function exists(id, options, cb) { DataAccessObject.exists = function exists(id, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
assert(arguments.length >= 1, 'The id argument is required'); assert(arguments.length >= 1, 'The id argument is required');
if (cb === undefined) { if (cb === undefined) {
@ -548,7 +600,7 @@ DataAccessObject.exists = function exists(id, options, cb) {
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
options = options || {}; options = options || {};
assert(typeof options === 'object', 'The options argument must be an object'); assert(typeof options === 'object', 'The options argument must be an object');
@ -559,10 +611,11 @@ DataAccessObject.exists = function exists(id, options, cb) {
cb(err, err ? false : count === 1); cb(err, err ? false : count === 1);
}); });
} else { } else {
return process.nextTick(function() { process.nextTick(function() {
cb(new Error('Model::exists requires the id argument')); cb(new Error('Model::exists requires the id argument'));
}); });
} }
return cb.promise;
}; };
/** /**
@ -580,7 +633,10 @@ DataAccessObject.exists = function exists(id, options, cb) {
* @param {Function} cb Callback called with (err, instance) * @param {Function} cb Callback called with (err, instance)
*/ */
DataAccessObject.findById = function find(id, options, cb) { DataAccessObject.findById = function find(id, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
assert(arguments.length >= 1, 'The id argument is required'); assert(arguments.length >= 1, 'The id argument is required');
if (cb === undefined) { if (cb === undefined) {
@ -590,18 +646,20 @@ DataAccessObject.findById = function find(id, options, cb) {
options = {}; options = {};
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
options = options || {}; options = options || {};
assert(typeof options === 'object', 'The options argument must be an object'); assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function'); assert(typeof cb === 'function', 'The cb argument must be a function');
if (id == null || id === '') { if (id == null || id === '') {
return process.nextTick(function() { process.nextTick(function() {
cb(new Error('Model::findById requires the id argument')); cb(new Error('Model::findById requires the id argument'));
}); });
} else {
this.findOne(byIdQuery(this, id), options, cb);
} }
this.findOne(byIdQuery(this, id), options, cb); return cb.promise;
}; };
/** /**
@ -626,7 +684,7 @@ DataAccessObject.findByIds = function(ids, query, options, cb) {
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
options = options || {}; options = options || {};
query = query || {}; query = query || {};
@ -638,7 +696,7 @@ DataAccessObject.findByIds = function(ids, query, options, cb) {
var pk = idName(this); var pk = idName(this);
if (ids.length === 0) { if (ids.length === 0) {
process.nextTick(function() { cb(null, []); }); process.nextTick(function() { cb(null, []); });
return; return cb.promise;;
} }
var filter = { where: {} }; var filter = { where: {} };
@ -647,6 +705,7 @@ DataAccessObject.findByIds = function(ids, query, options, cb) {
this.find(filter, options, function(err, results) { this.find(filter, options, function(err, results) {
cb(err, err ? results : utils.sortObjectsByIds(pk, ids, results)); cb(err, err ? results : utils.sortObjectsByIds(pk, ids, results));
}); });
return cb.promise;
}; };
function convertNullToNotFoundError(ctx, cb) { function convertNullToNotFoundError(ctx, cb) {
@ -662,7 +721,7 @@ function convertNullToNotFoundError(ctx, cb) {
// alias function for backwards compat. // alias function for backwards compat.
DataAccessObject.all = function () { DataAccessObject.all = function () {
DataAccessObject.find.apply(this, arguments); return DataAccessObject.find.apply(this, arguments);
}; };
var operators = { var operators = {
@ -994,7 +1053,10 @@ DataAccessObject._coerce = function (where) {
*/ */
DataAccessObject.find = function find(query, options, cb) { DataAccessObject.find = function find(query, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
if (options === undefined && cb === undefined) { if (options === undefined && cb === undefined) {
if (typeof query === 'function') { if (typeof query === 'function') {
@ -1010,7 +1072,7 @@ DataAccessObject.find = function find(query, options, cb) {
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
query = query || {}; query = query || {};
options = options || {}; options = options || {};
@ -1023,9 +1085,10 @@ DataAccessObject.find = function find(query, options, cb) {
try { try {
this._normalize(query); this._normalize(query);
} catch (err) { } catch (err) {
return process.nextTick(function () { process.nextTick(function () {
cb(err); cb(err);
}); });
return cb.promise;
} }
this.applyScope(query); this.applyScope(query);
@ -1073,7 +1136,7 @@ DataAccessObject.find = function find(query, options, cb) {
}); });
// already handled // already handled
return; return cb.promise;
} }
} }
@ -1135,6 +1198,7 @@ DataAccessObject.find = function find(query, options, cb) {
self.getDataSource().connector.all(self.modelName, query, allCb); self.getDataSource().connector.all(self.modelName, query, allCb);
}); });
} }
return cb.promise;
}; };
/** /**
@ -1146,7 +1210,10 @@ DataAccessObject.find = function find(query, options, cb) {
* @param {Function} cb Callback function called with (err, instance) * @param {Function} cb Callback function called with (err, instance)
*/ */
DataAccessObject.findOne = function findOne(query, options, cb) { DataAccessObject.findOne = function findOne(query, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
if (options === undefined && cb === undefined) { if (options === undefined && cb === undefined) {
if (typeof query === 'function') { if (typeof query === 'function') {
@ -1160,7 +1227,7 @@ DataAccessObject.findOne = function findOne(query, options, cb) {
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
query = query || {}; query = query || {};
options = options || {}; options = options || {};
@ -1173,6 +1240,7 @@ DataAccessObject.findOne = function findOne(query, options, cb) {
if (err || !collection || !collection.length > 0) return cb(err, null); if (err || !collection || !collection.length > 0) return cb(err, null);
cb(err, collection[0]); cb(err, collection[0]);
}); });
return cb.promise;
}; };
/** /**
@ -1190,7 +1258,10 @@ DataAccessObject.findOne = function findOne(query, options, cb) {
* @param {Function} [cb] Callback called with (err) * @param {Function} [cb] Callback called with (err)
*/ */
DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyAll = function destroyAll(where, options, cb) { DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyAll = function destroyAll(where, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
var Model = this; var Model = this;
@ -1206,7 +1277,7 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
where = where || {}; where = where || {};
options = options || {}; options = options || {};
@ -1267,6 +1338,7 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
}); });
} }
} }
return cb.promise;
}; };
function whereIsEmpty(where) { function whereIsEmpty(where) {
@ -1285,7 +1357,10 @@ function whereIsEmpty(where) {
// 'deleteById' will be used as the name for strong-remoting to keep it backward // 'deleteById' will be used as the name for strong-remoting to keep it backward
// compatible for angular SDK // compatible for angular SDK
DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.deleteById = function deleteById(id, options, cb) { DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.deleteById = function deleteById(id, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
assert(arguments.length >= 1, 'The id argument is required'); assert(arguments.length >= 1, 'The id argument is required');
if (cb === undefined) { if (cb === undefined) {
@ -1297,15 +1372,16 @@ DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.de
} }
options = options || {}; options = options || {};
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
assert(typeof options === 'object', 'The options argument must be an object'); assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function'); assert(typeof cb === 'function', 'The cb argument must be a function');
if (id == null || id === '') { if (id == null || id === '') {
return process.nextTick(function() { process.nextTick(function() {
cb(new Error('Model::deleteById requires the id argument')); cb(new Error('Model::deleteById requires the id argument'));
}); });
return cb.promise;
} }
var Model = this; var Model = this;
@ -1316,6 +1392,7 @@ DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.de
} }
if(!err) Model.emit('deleted', id); if(!err) Model.emit('deleted', id);
}); });
return cb.promise;
}; };
/** /**
@ -1333,7 +1410,10 @@ DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.de
* @param {Function} cb Callback, called with (err, count) * @param {Function} cb Callback, called with (err, count)
*/ */
DataAccessObject.count = function (where, options, cb) { DataAccessObject.count = function (where, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
if (options === undefined && cb === undefined) { if (options === undefined && cb === undefined) {
if (typeof where === 'function') { if (typeof where === 'function') {
@ -1349,7 +1429,7 @@ DataAccessObject.count = function (where, options, cb) {
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
where = where || {}; where = where || {};
options = options || {}; options = options || {};
@ -1365,9 +1445,10 @@ DataAccessObject.count = function (where, options, cb) {
where = removeUndefined(where); where = removeUndefined(where);
where = this._coerce(where); where = this._coerce(where);
} catch (err) { } catch (err) {
return process.nextTick(function () { process.nextTick(function () {
cb(err); cb(err);
}); });
return cb.promise;
} }
var Model = this; var Model = this;
@ -1376,6 +1457,7 @@ DataAccessObject.count = function (where, options, cb) {
where = ctx.query.where; where = ctx.query.where;
Model.getDataSource().connector.count(Model.modelName, cb, where); Model.getDataSource().connector.count(Model.modelName, cb, where);
}); });
return cb.promise;
}; };
/** /**
@ -1387,7 +1469,10 @@ DataAccessObject.count = function (where, options, cb) {
* @param {Function} cb Callback function with err and object arguments * @param {Function} cb Callback function with err and object arguments
*/ */
DataAccessObject.prototype.save = function (options, cb) { DataAccessObject.prototype.save = function (options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
var Model = this.constructor; var Model = this.constructor;
if (typeof options === 'function') { if (typeof options === 'function') {
@ -1395,7 +1480,7 @@ DataAccessObject.prototype.save = function (options, cb) {
options = {}; options = {};
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
options = options || {}; options = options || {};
assert(typeof options === 'object', 'The options argument should be an object'); assert(typeof options === 'object', 'The options argument should be an object');
@ -1466,6 +1551,7 @@ DataAccessObject.prototype.save = function (options, cb) {
}, data, cb); }, data, cb);
} }
}); });
return cb.promise;
}; };
/** /**
@ -1486,27 +1572,39 @@ DataAccessObject.prototype.save = function (options, cb) {
*/ */
DataAccessObject.update = DataAccessObject.update =
DataAccessObject.updateAll = function (where, data, options, cb) { DataAccessObject.updateAll = function (where, data, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
assert(arguments.length >= 2, 'At least two arguments are required'); assert(arguments.length >= 1, 'At least one argument is required');
if (options === undefined && cb === undefined) {
if (data === undefined && options === undefined && cb === undefined && arguments.length === 1) {
data = where;
where = {};
} else if (options === undefined && cb === undefined) {
// One of:
// updateAll(data, cb)
// updateAll(where, data) -> Promise
if (typeof data === 'function') { if (typeof data === 'function') {
// updateAll(data, cb);
cb = data; cb = data;
data = where; data = where;
where = {}; where = {};
} }
} else if (cb === undefined) { } else if (cb === undefined) {
// One of:
// updateAll(where, data, options) -> Promise
// updateAll(where, data, cb)
if (typeof options === 'function') { if (typeof options === 'function') {
// updateAll(query, data, cb)
cb = options; cb = options;
options = {}; options = {};
} }
} }
cb = cb || noCallback;
data = data || {}; data = data || {};
options = options || {}; options = options || {};
cb = cb || utils.createPromiseCallback();
assert(typeof where === 'object', 'The where argument must be an object'); assert(typeof where === 'object', 'The where argument must be an object');
assert(typeof data === 'object', 'The data argument must be an object'); assert(typeof data === 'object', 'The data argument must be an object');
@ -1564,6 +1662,7 @@ DataAccessObject.updateAll = function (where, data, options, cb) {
}); });
}); });
} }
return cb.promise;
}; };
DataAccessObject.prototype.isNewRecord = function () { DataAccessObject.prototype.isNewRecord = function () {
@ -1589,14 +1688,17 @@ DataAccessObject.prototype._adapter = function () {
DataAccessObject.prototype.remove = DataAccessObject.prototype.remove =
DataAccessObject.prototype.delete = DataAccessObject.prototype.delete =
DataAccessObject.prototype.destroy = function (options, cb) { DataAccessObject.prototype.destroy = function (options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
if (cb === undefined && typeof options === 'function') { if (cb === undefined && typeof options === 'function') {
cb = options; cb = options;
options = {}; options = {};
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
options = options || {}; options = options || {};
assert(typeof options === 'object', 'The options argument should be an object'); assert(typeof options === 'object', 'The options argument should be an object');
@ -1650,6 +1752,7 @@ DataAccessObject.prototype.remove =
}); });
}, null, cb); }, null, cb);
} }
return cb.promise;
}; };
/** /**
@ -1674,7 +1777,7 @@ DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
DataAccessObject.prototype.updateAttribute = function updateAttribute(name, value, cb) { DataAccessObject.prototype.updateAttribute = function updateAttribute(name, value, cb) {
var data = {}; var data = {};
data[name] = value; data[name] = value;
this.updateAttributes(data, cb); return this.updateAttributes(data, cb);
}; };
/** /**
@ -1718,7 +1821,10 @@ DataAccessObject.prototype.unsetAttribute = function unsetAttribute(name, nullif
* @param {Function} cb Callback function called with (err, instance) * @param {Function} cb Callback function called with (err, instance)
*/ */
DataAccessObject.prototype.updateAttributes = function updateAttributes(data, options, cb) { DataAccessObject.prototype.updateAttributes = function updateAttributes(data, options, cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) return; var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
if (connectionPromise) {
return connectionPromise;
}
if (options === undefined && cb === undefined) { if (options === undefined && cb === undefined) {
if (typeof data === 'function') { if (typeof data === 'function') {
@ -1734,7 +1840,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
} }
} }
cb = cb || noCallback; cb = cb || utils.createPromiseCallback();
options = options || {}; options = options || {};
assert((typeof data === 'object') && (data !== null), assert((typeof data === 'object') && (data !== null),
@ -1803,6 +1909,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
}, data, cb); }, data, cb);
}, data); }, data);
}); });
return cb.promise;
}; };
/** /**
@ -1812,11 +1919,12 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
* @private * @private
*/ */
DataAccessObject.prototype.reload = function reload(cb) { DataAccessObject.prototype.reload = function reload(cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) { var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
return; if (connectionPromise) {
return connectionPromise;
} }
this.constructor.findById(getIdValue(this.constructor, this), cb); return this.constructor.findById(getIdValue(this.constructor, this), cb);
}; };

View File

@ -59,6 +59,21 @@ describe('manipulation', function () {
}); });
}); });
it('should create instance (promise variant)', function (done) {
Person.create({name: 'Anatoliy'})
.then (function (p) {
p.name.should.equal('Anatoliy');
should.exist(p);
return Person.findById(p.id)
.then (function (person) {
person.id.should.eql(p.id);
person.name.should.equal('Anatoliy');
done();
});
})
.catch(done);
});
it('should instantiate an object', function (done) { it('should instantiate an object', function (done) {
var p = new Person({name: 'Anatoliy'}); var p = new Person({name: 'Anatoliy'});
p.name.should.equal('Anatoliy'); p.name.should.equal('Anatoliy');
@ -71,6 +86,20 @@ describe('manipulation', function () {
}); });
}); });
it('should instantiate an object (promise variant)', function (done) {
var p = new Person({name: 'Anatoliy'});
p.name.should.equal('Anatoliy');
p.isNewRecord().should.be.true;
p.save()
.then (function(inst) {
inst.isNewRecord().should.be.false;
inst.should.equal(p);
done();
})
.catch(done);
});
it('should return instance of object', function (done) { it('should return instance of object', function (done) {
var person = Person.create(function (err, p) { var person = Person.create(function (err, p) {
p.id.should.eql(person.id); p.id.should.eql(person.id);
@ -93,6 +122,19 @@ describe('manipulation', function () {
}); });
}); });
it('should not allow user-defined value for the id of object - create (promise variant)', function (done) {
Person.create({id: 123456})
.then (function (p) {
done(new Error('Person.create should have failed.'));
}, function (err) {
err.should.be.instanceof(ValidationError);
err.statusCode.should.equal(422);
err.details.messages.id.should.eql(['can\'t be set']);
done();
})
.catch(done);
});
it('should not allow user-defined value for the id of object - save', function (done) { it('should not allow user-defined value for the id of object - save', function (done) {
var p = new Person({id: 123456}); var p = new Person({id: 123456});
p.isNewRecord().should.be.true; p.isNewRecord().should.be.true;
@ -106,6 +148,21 @@ describe('manipulation', function () {
}); });
}); });
it('should not allow user-defined value for the id of object - save (promise variant)', function (done) {
var p = new Person({id: 123456});
p.isNewRecord().should.be.true;
p.save()
.then (function(inst) {
done(new Error('save should have failed.'));
}, function (err) {
err.should.be.instanceof(ValidationError);
err.statusCode.should.equal(422);
err.details.messages.id.should.eql(['can\'t be set']);
done();
})
.catch(done);
});
it('should work when called without callback', function (done) { it('should work when called without callback', function (done) {
Person.afterCreate = function (next) { Person.afterCreate = function (next) {
this.should.be.an.instanceOf(Person); this.should.be.an.instanceOf(Person);
@ -131,6 +188,20 @@ describe('manipulation', function () {
}); });
}); });
it('should create instance with blank data (promise variant)', function (done) {
Person.create()
.then (function (p) {
should.exist(p);
should.not.exists(p.name);
return Person.findById(p.id)
.then (function (person) {
person.id.should.eql(p.id);
should.not.exists(person.name);
done();
});
}).catch(done);
});
it('should work when called with no data and callback', function (done) { it('should work when called with no data and callback', function (done) {
Person.afterCreate = function (next) { Person.afterCreate = function (next) {
this.should.be.an.instanceOf(Person); this.should.be.an.instanceOf(Person);
@ -219,6 +290,7 @@ describe('manipulation', function () {
}); });
}); });
}); });
}); });
describe('save', function () { describe('save', function () {
@ -232,6 +304,16 @@ describe('manipulation', function () {
}); });
}); });
it('should save new object (promise variant)', function (done) {
var p = new Person;
p.save()
.then(function () {
should.exist(p.id);
done();
})
.catch(done);
});
it('should save existing object', function (done) { it('should save existing object', function (done) {
Person.findOne(function (err, p) { Person.findOne(function (err, p) {
should.not.exist(err); should.not.exist(err);
@ -247,6 +329,22 @@ describe('manipulation', function () {
}); });
}); });
it('should save existing object (promise variant)', function (done) {
Person.findOne()
.then(function (p) {
p.name = 'Fritz';
return p.save()
.then(function () {
return Person.findOne()
.then(function (p) {
p.name.should.equal('Fritz');
done();
});
});
})
.catch(done);
});
it('should save invalid object (skipping validation)', function (done) { it('should save invalid object (skipping validation)', function (done) {
Person.findOne(function (err, p) { Person.findOne(function (err, p) {
should.not.exist(err); should.not.exist(err);
@ -265,6 +363,29 @@ describe('manipulation', function () {
}); });
}); });
it('should save invalid object (skipping validation - promise variant)', function (done) {
Person.findOne()
.then(function (p) {
p.isValid = function (done) {
process.nextTick(done);
return false;
};
p.name = 'Nana';
return p.save()
.then(function (d) {
done(new Error('save should have failed.'));
}, function (err) {
should.exist(err);
p.save({validate: false})
.then(function (d) {
should.exist(d);
done();
});
});
})
.catch(done);
});
it('should save throw error on validation', function () { it('should save throw error on validation', function () {
Person.findOne(function (err, p) { Person.findOne(function (err, p) {
should.not.exist(err); should.not.exist(err);
@ -312,7 +433,7 @@ describe('manipulation', function () {
person.updateAttribute('name', 'Paul Graham', function (err, p) { person.updateAttribute('name', 'Paul Graham', function (err, p) {
should.not.exist(err); should.not.exist(err);
Person.all(function (e, ps) { Person.all(function (e, ps) {
should.not.exist(err); should.not.exist(e);
ps.should.have.lengthOf(1); ps.should.have.lengthOf(1);
ps.pop().name.should.equal('Paul Graham'); ps.pop().name.should.equal('Paul Graham');
done(); done();
@ -320,12 +441,24 @@ describe('manipulation', function () {
}); });
}); });
it('should update one attribute (promise variant)', function (done) {
person.updateAttribute('name', 'Teddy Graham')
.then(function (p) {
return Person.all()
.then(function (ps) {
ps.should.have.lengthOf(1);
ps.pop().name.should.equal('Teddy Graham');
done();
});
}).catch(done);
});
it('should ignore undefined values on updateAttributes', function(done) { it('should ignore undefined values on updateAttributes', function(done) {
person.updateAttributes({'name': 'John', age: undefined}, person.updateAttributes({'name': 'John', age: undefined},
function(err, p) { function(err, p) {
should.not.exist(err); should.not.exist(err);
Person.findById(p.id, function(e, p) { Person.findById(p.id, function(e, p) {
should.not.exist(err); should.not.exist(e);
p.name.should.equal('John'); p.name.should.equal('John');
p.age.should.equal(15); p.age.should.equal(15);
done(); done();
@ -338,7 +471,7 @@ describe('manipulation', function () {
function(err, p) { function(err, p) {
should.not.exist(err); should.not.exist(err);
Person.findById(p.id, function(e, p) { Person.findById(p.id, function(e, p) {
should.not.exist(err); should.not.exist(e);
p.name.should.equal('John'); p.name.should.equal('John');
p.age.should.equal(15); p.age.should.equal(15);
done(); done();
@ -346,6 +479,19 @@ describe('manipulation', function () {
}); });
}); });
it('should allows model instance on updateAttributes (promise variant)', function(done) {
person.updateAttributes(new Person({'name': 'Jane', age: undefined}))
.then(function(p) {
return Person.findById(p.id)
.then(function(p) {
p.name.should.equal('Jane');
p.age.should.equal(15);
done();
});
})
.catch(done);
});
}); });
describe('updateOrCreate', function() { describe('updateOrCreate', function() {
@ -404,6 +550,71 @@ describe('manipulation', function () {
}); });
}); });
describe('findOrCreate', function() {
it('should create a record with if new', function(done) {
Person.findOrCreate({ name: 'Zed', gender: 'male' },
function(err, p, created) {
if (err) return done(err);
should.exist(p);
p.should.be.instanceOf(Person);
p.name.should.equal('Zed');
p.gender.should.equal('male');
created.should.equal(true);
done();
});
});
it('should find a record if exists', function(done) {
Person.findOrCreate(
{where: {name: 'Zed'}},
{name: 'Zed', gender: 'male'},
function(err, p, created) {
if (err) return done(err);
should.exist(p);
p.should.be.instanceOf(Person);
p.name.should.equal('Zed');
p.gender.should.equal('male');
created.should.equal(false);
done();
});
});
it('should create a record with if new (promise variant)', function(done) {
Person.findOrCreate({ name: 'Jed', gender: 'male' })
.then(function(res) {
should.exist(res);
res.should.be.instanceOf(Array);
res.should.have.lengthOf(2);
var p = res[0];
var created = res[1];
p.should.be.instanceOf(Person);
p.name.should.equal('Jed');
p.gender.should.equal('male');
created.should.equal(true);
done();
})
.catch(done);
});
it('should find a record if exists (promise variant)', function(done) {
Person.findOrCreate(
{where: {name: 'Jed'}},
{name: 'Jed', gender: 'male'})
.then(function(res) {
res.should.be.instanceOf(Array);
res.should.have.lengthOf(2);
var p = res[0];
var created = res[1];
p.should.be.instanceOf(Person);
p.name.should.equal('Jed');
p.gender.should.equal('male');
created.should.equal(false);
done();
})
.catch(done);
});
});
describe('destroy', function () { describe('destroy', function () {
it('should destroy record', function (done) { it('should destroy record', function (done) {
@ -418,6 +629,21 @@ describe('manipulation', function () {
}); });
}); });
it('should destroy record (promise variant)', function (done) {
Person.create()
.then(function (p) {
return p.destroy()
.then(function () {
return Person.exists(p.id)
.then(function (ex) {
ex.should.not.be.ok;
done();
});
});
})
.catch(done);
});
it('should destroy all records', function (done) { it('should destroy all records', function (done) {
Person.destroyAll(function (err) { Person.destroyAll(function (err) {
should.not.exist(err); should.not.exist(err);
@ -431,6 +657,26 @@ describe('manipulation', function () {
}); });
}); });
it('should destroy all records (promise variant)', function (done) {
Person.create()
.then(function() {
return Person.destroyAll()
.then(function () {
return Person.all()
.then(function (ps) {
ps.should.have.lengthOf(0);
return Person.count()
.then(function (count) {
count.should.eql(0);
done();
});
});
});
})
.catch(done);
});
// TODO: implement destroy with filtered set // TODO: implement destroy with filtered set
it('should destroy filtered set of records'); it('should destroy filtered set of records');
}); });