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)
*/
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 self = this;
@ -167,9 +170,9 @@ DataAccessObject.create = function (data, options, cb) {
}
}
cb = cb || noCallback;
data = data || {};
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 options === 'object', 'The options argument must be an object');
@ -276,12 +279,27 @@ DataAccessObject.create = function (data, options, cb) {
}, obj, cb);
}
// Does this make any sense? How would chaining be used here? -partap
// for chaining
return obj;
return cb.promise || obj;
};
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
// compatible for angular SDK
DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(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) {
@ -316,7 +335,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
}
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
data = data || {};
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)
*/
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');
if (options === undefined && cb === undefined) {
assert(arguments.length >= 1, 'At least one argument is required');
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') {
// findOrCreate(data, cb);
// 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: {}};
data = data || {};
options = options || {};
@ -466,11 +495,19 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
if (created) {
Model.notifyObserversOf('after save', { Model: Model, instance: obj },
function(err) {
cb(err, obj, created);
if (cb.promise) {
cb(err, [obj, created]);
} else {
cb(err, obj, created);
}
if (!err) Model.emit('changed', obj);
});
} 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 {
this._normalize(query);
} catch (err) {
return process.nextTick(function () {
process.nextTick(function () {
cb(err);
});
return cb.promise;
}
this.applyScope(query);
@ -521,12 +559,23 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
} else {
Model.findOne(query, options, function (err, record) {
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) {
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)
*/
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');
if (cb === undefined) {
@ -548,7 +600,7 @@ DataAccessObject.exists = function exists(id, options, cb) {
}
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
options = options || {};
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);
});
} else {
return process.nextTick(function() {
process.nextTick(function() {
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)
*/
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');
if (cb === undefined) {
@ -590,18 +646,20 @@ DataAccessObject.findById = function find(id, options, cb) {
options = {};
}
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
options = options || {};
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
if (id == null || id === '') {
return process.nextTick(function() {
process.nextTick(function() {
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 || {};
query = query || {};
@ -638,7 +696,7 @@ DataAccessObject.findByIds = function(ids, query, options, cb) {
var pk = idName(this);
if (ids.length === 0) {
process.nextTick(function() { cb(null, []); });
return;
return cb.promise;;
}
var filter = { where: {} };
@ -647,6 +705,7 @@ DataAccessObject.findByIds = function(ids, query, options, cb) {
this.find(filter, options, function(err, results) {
cb(err, err ? results : utils.sortObjectsByIds(pk, ids, results));
});
return cb.promise;
};
function convertNullToNotFoundError(ctx, cb) {
@ -662,7 +721,7 @@ function convertNullToNotFoundError(ctx, cb) {
// alias function for backwards compat.
DataAccessObject.all = function () {
DataAccessObject.find.apply(this, arguments);
return DataAccessObject.find.apply(this, arguments);
};
var operators = {
@ -994,7 +1053,10 @@ DataAccessObject._coerce = function (where) {
*/
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 (typeof query === 'function') {
@ -1010,7 +1072,7 @@ DataAccessObject.find = function find(query, options, cb) {
}
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
query = query || {};
options = options || {};
@ -1023,9 +1085,10 @@ DataAccessObject.find = function find(query, options, cb) {
try {
this._normalize(query);
} catch (err) {
return process.nextTick(function () {
process.nextTick(function () {
cb(err);
});
return cb.promise;
}
this.applyScope(query);
@ -1073,7 +1136,7 @@ DataAccessObject.find = function find(query, options, cb) {
});
// 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);
});
}
return cb.promise;
};
/**
@ -1146,7 +1210,10 @@ DataAccessObject.find = function find(query, options, cb) {
* @param {Function} cb Callback function called with (err, instance)
*/
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 (typeof query === 'function') {
@ -1160,7 +1227,7 @@ DataAccessObject.findOne = function findOne(query, options, cb) {
}
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
query = query || {};
options = options || {};
@ -1173,6 +1240,7 @@ DataAccessObject.findOne = function findOne(query, options, cb) {
if (err || !collection || !collection.length > 0) return cb(err, null);
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)
*/
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;
@ -1206,7 +1277,7 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
}
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
where = where || {};
options = options || {};
@ -1267,6 +1338,7 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA
});
}
}
return cb.promise;
};
function whereIsEmpty(where) {
@ -1285,7 +1357,10 @@ function whereIsEmpty(where) {
// 'deleteById' will be used as the name for strong-remoting to keep it backward
// compatible for angular SDK
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');
if (cb === undefined) {
@ -1297,15 +1372,16 @@ DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.de
}
options = options || {};
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
assert(typeof options === 'object', 'The options argument must be an object');
assert(typeof cb === 'function', 'The cb argument must be a function');
if (id == null || id === '') {
return process.nextTick(function() {
process.nextTick(function() {
cb(new Error('Model::deleteById requires the id argument'));
});
return cb.promise;
}
var Model = this;
@ -1316,6 +1392,7 @@ DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.de
}
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)
*/
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 (typeof where === 'function') {
@ -1349,7 +1429,7 @@ DataAccessObject.count = function (where, options, cb) {
}
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
where = where || {};
options = options || {};
@ -1365,9 +1445,10 @@ DataAccessObject.count = function (where, options, cb) {
where = removeUndefined(where);
where = this._coerce(where);
} catch (err) {
return process.nextTick(function () {
process.nextTick(function () {
cb(err);
});
return cb.promise;
}
var Model = this;
@ -1376,6 +1457,7 @@ DataAccessObject.count = function (where, options, cb) {
where = ctx.query.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
*/
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;
if (typeof options === 'function') {
@ -1395,7 +1480,7 @@ DataAccessObject.prototype.save = function (options, cb) {
options = {};
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
options = options || {};
assert(typeof options === 'object', 'The options argument should be an object');
@ -1466,6 +1551,7 @@ DataAccessObject.prototype.save = function (options, cb) {
}, data, cb);
}
});
return cb.promise;
};
/**
@ -1486,27 +1572,39 @@ DataAccessObject.prototype.save = function (options, cb) {
*/
DataAccessObject.update =
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');
if (options === undefined && cb === undefined) {
assert(arguments.length >= 1, 'At least one argument is required');
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') {
// updateAll(data, cb);
cb = data;
data = where;
where = {};
}
} else if (cb === undefined) {
// One of:
// updateAll(where, data, options) -> Promise
// updateAll(where, data, cb)
if (typeof options === 'function') {
// updateAll(query, data, cb)
cb = options;
options = {};
}
}
cb = cb || noCallback;
data = data || {};
options = options || {};
cb = cb || utils.createPromiseCallback();
assert(typeof where === 'object', 'The where 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 () {
@ -1589,14 +1688,17 @@ DataAccessObject.prototype._adapter = function () {
DataAccessObject.prototype.remove =
DataAccessObject.prototype.delete =
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') {
cb = options;
options = {};
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
options = options || {};
assert(typeof options === 'object', 'The options argument should be an object');
@ -1650,6 +1752,7 @@ DataAccessObject.prototype.remove =
});
}, null, cb);
}
return cb.promise;
};
/**
@ -1674,7 +1777,7 @@ DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
DataAccessObject.prototype.updateAttribute = function updateAttribute(name, value, cb) {
var data = {};
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)
*/
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 (typeof data === 'function') {
@ -1734,7 +1840,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
}
}
cb = cb || noCallback;
cb = cb || utils.createPromiseCallback();
options = options || {};
assert((typeof data === 'object') && (data !== null),
@ -1803,6 +1909,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
}, data, cb);
}, data);
});
return cb.promise;
};
/**
@ -1812,11 +1919,12 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op
* @private
*/
DataAccessObject.prototype.reload = function reload(cb) {
if (stillConnecting(this.getDataSource(), this, arguments)) {
return;
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
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) {
var p = new Person({name: '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) {
var person = Person.create(function (err, p) {
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) {
var p = new Person({id: 123456});
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) {
Person.afterCreate = function (next) {
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) {
Person.afterCreate = function (next) {
this.should.be.an.instanceOf(Person);
@ -219,6 +290,7 @@ describe('manipulation', 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) {
Person.findOne(function (err, p) {
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) {
Person.findOne(function (err, p) {
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 () {
Person.findOne(function (err, p) {
should.not.exist(err);
@ -312,7 +433,7 @@ describe('manipulation', function () {
person.updateAttribute('name', 'Paul Graham', function (err, p) {
should.not.exist(err);
Person.all(function (e, ps) {
should.not.exist(err);
should.not.exist(e);
ps.should.have.lengthOf(1);
ps.pop().name.should.equal('Paul Graham');
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) {
person.updateAttributes({'name': 'John', age: undefined},
function(err, p) {
should.not.exist(err);
Person.findById(p.id, function(e, p) {
should.not.exist(err);
should.not.exist(e);
p.name.should.equal('John');
p.age.should.equal(15);
done();
@ -338,7 +471,7 @@ describe('manipulation', function () {
function(err, p) {
should.not.exist(err);
Person.findById(p.id, function(e, p) {
should.not.exist(err);
should.not.exist(e);
p.name.should.equal('John');
p.age.should.equal(15);
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() {
@ -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 () {
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) {
Person.destroyAll(function (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
it('should destroy filtered set of records');
});