diff --git a/lib/connectors/memory.js b/lib/connectors/memory.js index 613798a1..622e6273 100644 --- a/lib/connectors/memory.js +++ b/lib/connectors/memory.js @@ -366,7 +366,7 @@ Memory.prototype.all = function all(model, filter, options, callback) { process.nextTick(function () { if (filter && filter.include) { - self._models[model].model.include(nodes, filter.include, callback); + self._models[model].model.include(nodes, filter.include, options, callback); } else { callback(null, nodes); } diff --git a/lib/dao.js b/lib/dao.js index 102c7c02..9bf0467a 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -2068,10 +2068,10 @@ DataAccessObject.prototype.setAttribute = function setAttribute(name, value) { * @param {Mixed} value Value of property * @param {Function} cb Callback function called with (err, instance) */ -DataAccessObject.prototype.updateAttribute = function updateAttribute(name, value, cb) { +DataAccessObject.prototype.updateAttribute = function updateAttribute(name, value, options, cb) { var data = {}; data[name] = value; - return this.updateAttributes(data, cb); + return this.updateAttributes(data, options, cb); }; /** diff --git a/lib/include.js b/lib/include.js index fcc1fe24..b989f5b1 100644 --- a/lib/include.js +++ b/lib/include.js @@ -2,7 +2,6 @@ var async = require('async'); var utils = require('./utils'); var isPlainObject = utils.isPlainObject; var defineCachedRelations = utils.defineCachedRelations; -var debug = require('debug')('loopback:include'); /*! * Normalize the include to be an array @@ -145,11 +144,15 @@ Inclusion.normalizeInclude = normalizeInclude; * * @param {Array} objects Array of instances * @param {String|Object|Array} include Which relations to load. + * @param {Object} [options] Options for CRUD * @param {Function} cb Callback called when relations are loaded * */ -Inclusion.include = function (objects, include, cb) { - debug('include', include); +Inclusion.include = function (objects, include, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var self = this; if (!include || (Array.isArray(include) && include.length === 0) || @@ -163,12 +166,12 @@ Inclusion.include = function (objects, include, cb) { include = normalizeInclude(include); async.each(include, function(item, callback) { - processIncludeItem(objects, item, callback); + processIncludeItem(objects, item, options, callback); }, function(err) { cb && cb(err, objects); }); - function processIncludeItem(objs, include, cb) { + function processIncludeItem(objs, include, options, cb) { var relations = self.relations; var relationName; @@ -214,8 +217,7 @@ Inclusion.include = function (objects, include, cb) { // Just skip if inclusion is disabled if (relation.options.disableInclude) { - cb(); - return; + return cb(); } //prepare filter and fields for making DB Call var filter = (scope && scope.conditions()) || {}; @@ -372,7 +374,7 @@ Inclusion.include = function (objects, include, cb) { //process. if (subInclude && targets) { tasks.push(function subIncludesTask(next) { - relation.modelTo.include(targets, subInclude, next); + relation.modelTo.include(targets, subInclude, options, next); }); } //process & link each target with object @@ -450,7 +452,7 @@ Inclusion.include = function (objects, include, cb) { //simultaneously process subIncludes if (subInclude && targets) { tasks.push(function subIncludesTask(next) { - relation.modelTo.include(targets, subInclude, next); + relation.modelTo.include(targets, subInclude, options, next); }); } //process each target object @@ -511,7 +513,7 @@ Inclusion.include = function (objects, include, cb) { //simultaneously process subIncludes if (subInclude && targets) { tasks.push(function subIncludesTask(next) { - relation.modelTo.include(targets, subInclude, next); + relation.modelTo.include(targets, subInclude, options, next); }); } //process each target object @@ -597,7 +599,7 @@ Inclusion.include = function (objects, include, cb) { //simultaneously process subIncludes if (subInclude && targets) { tasks.push(function subIncludesTask(next) { - Model.include(targets, subInclude, next); + Model.include(targets, subInclude, options, next); }); } //process each target object @@ -665,7 +667,7 @@ Inclusion.include = function (objects, include, cb) { //simultaneously process subIncludes if (subInclude && targets) { tasks.push(function subIncludesTask(next) { - relation.modelTo.include(targets, subInclude, next); + relation.modelTo.include(targets, subInclude, options, next); }); } //process each target object @@ -769,10 +771,10 @@ Inclusion.include = function (objects, include, cb) { related = inst[relationName].bind(inst, filter); } else { - related = inst[relationName].bind(inst); + related = inst[relationName].bind(inst, undefined); } - related(function (err, result) { + related(options, function (err, result) { if (err) { return callback(err); } else { diff --git a/lib/relation-definition.js b/lib/relation-definition.js index 1a727f39..fc369bce 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -297,14 +297,15 @@ Relation.prototype.callScopeMethod = function(methodName) { } else { throw new Error('Unknown scope method: ' + methodName); } -} +}; /** * Fetch the related model(s) - this is a helper method to unify access. * @param (Boolean|Object} condOrRefresh refresh or conditions object + * @param {Object} [options] Options * @param {Function} cb callback */ -Relation.prototype.fetch = function(condOrRefresh, cb) { +Relation.prototype.fetch = function(condOrRefresh, options, cb) { this.modelInstance[this.definition.name].apply(this.modelInstance, arguments); }; @@ -738,14 +739,19 @@ function sharedMethod(definition, methodName, method, relationMethod) { method.returns = relationMethod.returns; method.http = relationMethod.http; method.description = relationMethod.description; -}; +} /** * Find a related item by foreign key * @param {*} fkId The foreign key + * @param {Object} [options] Options * @param {Function} cb The callback function */ -HasMany.prototype.findById = function (fkId, cb) { +HasMany.prototype.findById = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var modelTo = this.definition.modelTo; var modelFrom = this.definition.modelFrom; var fk = this.definition.keyTo; @@ -767,7 +773,7 @@ HasMany.prototype.findById = function (fkId, cb) { } this.definition.applyScope(modelInstance, filter); - modelTo.findOne(filter, function (err, inst) { + modelTo.findOne(filter, options, function (err, inst) { if (err) { return cb(err); } @@ -793,9 +799,14 @@ HasMany.prototype.findById = function (fkId, cb) { /** * Find a related item by foreign key * @param {*} fkId The foreign key + * @param {Object} [options] Options * @param {Function} cb The callback function */ -HasMany.prototype.exists = function (fkId, cb) { +HasMany.prototype.exists = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var fk = this.definition.keyTo; var pk = this.definition.keyFrom; var modelInstance = this.modelInstance; @@ -822,15 +833,21 @@ HasMany.prototype.exists = function (fkId, cb) { /** * Update a related item by foreign key * @param {*} fkId The foreign key + * @param {Object} Changes to the data + * @param {Object} [options] Options * @param {Function} cb The callback function */ -HasMany.prototype.updateById = function (fkId, data, cb) { +HasMany.prototype.updateById = function (fkId, data, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } cb = cb || utils.createPromiseCallback(); - this.findById(fkId, function (err, inst) { + this.findById(fkId, options, function (err, inst) { if (err) { return cb && cb(err); } - inst.updateAttributes(data, cb); + inst.updateAttributes(data, options, cb); }); return cb.promise; }; @@ -838,17 +855,22 @@ HasMany.prototype.updateById = function (fkId, data, cb) { /** * Delete a related item by foreign key * @param {*} fkId The foreign key + * @param {Object} [options] Options * @param {Function} cb The callback function */ -HasMany.prototype.destroyById = function (fkId, cb) { +HasMany.prototype.destroyById = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } cb = cb || utils.createPromiseCallback(); var self = this; - this.findById(fkId, function(err, inst) { + this.findById(fkId, options, function(err, inst) { if (err) { return cb(err); } self.removeFromCache(inst[fkId]); - inst.destroy(cb); + inst.destroy(options, cb); }); return cb.promise; }; @@ -883,9 +905,14 @@ var throughKeys = function(definition) { /** * Find a related item by foreign key * @param {*} fkId The foreign key value + * @param {Object} [options] Options * @param {Function} cb The callback function */ -HasManyThrough.prototype.findById = function (fkId, cb) { +HasManyThrough.prototype.findById = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var self = this; var modelTo = this.definition.modelTo; var pk = this.definition.keyFrom; @@ -894,7 +921,7 @@ HasManyThrough.prototype.findById = function (fkId, cb) { cb = cb || utils.createPromiseCallback(); - self.exists(fkId, function (err, exists) { + self.exists(fkId, options, function (err, exists) { if (err || !exists) { if (!err) { err = new Error('No relation found in ' + modelThrough.modelName @@ -904,7 +931,7 @@ HasManyThrough.prototype.findById = function (fkId, cb) { } return cb(err); } - modelTo.findById(fkId, function (err, inst) { + modelTo.findById(fkId, options, function (err, inst) { if (err) { return cb(err); } @@ -922,9 +949,14 @@ HasManyThrough.prototype.findById = function (fkId, cb) { /** * Delete a related item by foreign key * @param {*} fkId The foreign key + * @param {Object} [options] Options * @param {Function} cb The callback function */ -HasManyThrough.prototype.destroyById = function (fkId, cb) { +HasManyThrough.prototype.destroyById = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var self = this; var modelTo = this.definition.modelTo; var pk = this.definition.keyFrom; @@ -933,7 +965,7 @@ HasManyThrough.prototype.destroyById = function (fkId, cb) { cb = cb || utils.createPromiseCallback(); - self.exists(fkId, function (err, exists) { + self.exists(fkId, options, function (err, exists) { if (err || !exists) { if (!err) { err = new Error('No record found in ' + modelThrough.modelName @@ -943,11 +975,11 @@ HasManyThrough.prototype.destroyById = function (fkId, cb) { } return cb(err); } - self.remove(fkId, function(err) { + self.remove(fkId, options, function(err) { if(err) { return cb(err); } - modelTo.deleteById(fkId, cb); + modelTo.deleteById(fkId, options, cb); }); }); return cb.promise; @@ -955,24 +987,28 @@ HasManyThrough.prototype.destroyById = function (fkId, cb) { // Create an instance of the target model and connect it to the instance of // the source model by creating an instance of the through model -HasManyThrough.prototype.create = function create(data, done) { +HasManyThrough.prototype.create = function create(data, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var self = this; var definition = this.definition; var modelTo = definition.modelTo; var modelThrough = definition.modelThrough; - if (typeof data === 'function' && !done) { - done = data; + if (typeof data === 'function' && !cb) { + cb = data; data = {}; } - done = done || utils.createPromiseCallback(); + cb = cb || utils.createPromiseCallback(); var modelInstance = this.modelInstance; // First create the target model - modelTo.create(data, function (err, to) { + modelTo.create(data, options, function (err, to) { if (err) { - return done(err, to); + return cb(err, to); } // The primary key for the target model var pk2 = definition.modelTo.definition.idName(); @@ -988,10 +1024,10 @@ HasManyThrough.prototype.create = function create(data, done) { definition.applyScope(modelInstance, filter); // Then create the through model - modelThrough.findOrCreate(filter, d, function (e, through) { + modelThrough.findOrCreate(filter, d, options, function (e, through) { if (e) { // Undo creation of the target model - to.destroy(function () { + to.destroy(options, function () { next(e); }); } else { @@ -1003,11 +1039,11 @@ HasManyThrough.prototype.create = function create(data, done) { // process array or single item if (!Array.isArray(to)) - createRelation(to, done); + createRelation(to, cb); else - async.map(to, createRelation, done); + async.map(to, createRelation, cb); }); - return done.promise; + return cb.promise; }; @@ -1016,21 +1052,27 @@ HasManyThrough.prototype.create = function create(data, done) { * Add the target model instance to the 'hasMany' relation * @param {Object|ID} acInst The actual instance or id value * @param {Object} [data] Optional data object for the through model to be created + * @param {Object} [options] Options + * @param {Function} [cb] Callback function */ -HasManyThrough.prototype.add = function (acInst, data, done) { +HasManyThrough.prototype.add = function (acInst, data, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var self = this; var definition = this.definition; var modelThrough = definition.modelThrough; var pk1 = definition.keyFrom; if (typeof data === 'function') { - done = data; + cb = data; data = {}; } var query = {}; data = data || {}; - done = done || utils.createPromiseCallback(); + cb = cb || utils.createPromiseCallback(); // The primary key for the target model var pk2 = definition.modelTo.definition.idName(); @@ -1052,22 +1094,26 @@ HasManyThrough.prototype.add = function (acInst, data, done) { definition.applyProperties(this.modelInstance, data); // Create an instance of the through model - modelThrough.findOrCreate(filter, data, function(err, ac) { + modelThrough.findOrCreate(filter, data, options, function(err, ac) { if(!err) { if (acInst instanceof definition.modelTo) { self.addToCache(acInst); } } - done(err, ac); + cb(err, ac); }); - return done.promise; + return cb.promise; }; /** * Check if the target model instance is related to the 'hasMany' relation * @param {Object|ID} acInst The actual instance or id value */ -HasManyThrough.prototype.exists = function (acInst, done) { +HasManyThrough.prototype.exists = function (acInst, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var definition = this.definition; var modelThrough = definition.modelThrough; var pk1 = definition.keyFrom; @@ -1088,19 +1134,23 @@ HasManyThrough.prototype.exists = function (acInst, done) { definition.applyScope(this.modelInstance, filter); - done = done || utils.createPromiseCallback(); + cb = cb || utils.createPromiseCallback(); - modelThrough.count(filter.where, function(err, ac) { - done(err, ac > 0); + modelThrough.count(filter.where, options, function(err, ac) { + cb(err, ac > 0); }); - return done.promise; + return cb.promise; }; /** * Remove the target model instance from the 'hasMany' relation * @param {Object|ID) acInst The actual instance or id value */ -HasManyThrough.prototype.remove = function (acInst, done) { +HasManyThrough.prototype.remove = function (acInst, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var self = this; var definition = this.definition; var modelThrough = definition.modelThrough; @@ -1122,15 +1172,15 @@ HasManyThrough.prototype.remove = function (acInst, done) { definition.applyScope(this.modelInstance, filter); - done = done || utils.createPromiseCallback(); + cb = cb || utils.createPromiseCallback(); - modelThrough.deleteAll(filter.where, function (err) { + modelThrough.deleteAll(filter.where, options, function (err) { if (!err) { self.removeFromCache(query[fk2]); } - done(err); + cb(err); }); - return done.promise; + return cb.promise; }; @@ -1245,7 +1295,11 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) { return definition; }; -BelongsTo.prototype.create = function(targetModelData, cb) { +BelongsTo.prototype.create = function(targetModelData, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var self = this; var modelTo = this.definition.modelTo; var fk = this.definition.keyFrom; @@ -1260,14 +1314,14 @@ BelongsTo.prototype.create = function(targetModelData, cb) { this.definition.applyProperties(modelInstance, targetModelData || {}); - modelTo.create(targetModelData, function(err, targetModel) { + modelTo.create(targetModelData, options, function(err, targetModel) { if(!err) { modelInstance[fk] = targetModel[pk]; if (modelInstance.isNewRecord()) { self.resetCache(targetModel); cb && cb(err, targetModel); } else { - modelInstance.save(function(err, inst) { + modelInstance.save(options, function(err, inst) { if (cb && err) return cb && cb(err); self.resetCache(targetModel); cb && cb(err, targetModel); @@ -1286,12 +1340,16 @@ BelongsTo.prototype.build = function(targetModelData) { return new modelTo(targetModelData); }; -BelongsTo.prototype.update = function (targetModelData, cb) { +BelongsTo.prototype.update = function (targetModelData, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } cb = cb || utils.createPromiseCallback(); var definition = this.definition; - this.fetch(function(err, inst) { + this.fetch(options, function(err, inst) { if (inst instanceof ModelBaseClass) { - inst.updateAttributes(targetModelData, cb); + inst.updateAttributes(targetModelData, options, cb); } else { cb(new Error('BelongsTo relation ' + definition.name + ' is empty')); @@ -1300,17 +1358,21 @@ BelongsTo.prototype.update = function (targetModelData, cb) { return cb.promise; }; -BelongsTo.prototype.destroy = function (cb) { +BelongsTo.prototype.destroy = function (options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } var modelTo = this.definition.modelTo; var modelInstance = this.modelInstance; var fk = this.definition.keyFrom; cb = cb || utils.createPromiseCallback(); - this.fetch(function(err, targetModel) { + this.fetch(options, function(err, targetModel) { if (targetModel instanceof ModelBaseClass) { modelInstance[fk] = null; - modelInstance.save(function(err, targetModel) { + modelInstance.save(options, function(err, targetModel) { if (cb && err) return cb && cb(err); cb && cb(err, targetModel); }); @@ -1325,7 +1387,7 @@ BelongsTo.prototype.destroy = function (cb) { /** * Define the method for the belongsTo relation itself * It will support one of the following styles: - * - order.customer(refresh, callback): Load the target model instance asynchronously + * - order.customer(refresh, options, callback): Load the target model instance asynchronously * - order.customer(customer): Synchronous setter of the target model instance * - order.customer(): Synchronous getter of the target model instance * @@ -1333,7 +1395,7 @@ BelongsTo.prototype.destroy = function (cb) { * @param params * @returns {*} */ -BelongsTo.prototype.related = function (refresh, params) { +BelongsTo.prototype.related = function (condOrRefresh, options, cb) { var self = this; var modelFrom = this.definition.modelFrom; var modelTo = this.definition.modelTo; @@ -1342,15 +1404,25 @@ BelongsTo.prototype.related = function (refresh, params) { var modelInstance = this.modelInstance; var discriminator; var scopeQuery = null; + var newValue; - if (arguments.length === 1) { - params = refresh; - refresh = false; - } else if (arguments.length === 2 - && typeof refresh === 'object' && typeof params === 'function') { - scopeQuery = refresh; - } else if (arguments.length > 2) { - throw new Error('Method can\'t be called with more than two arguments'); + if ((condOrRefresh instanceof ModelBaseClass) && + options === undefined && cb === undefined) { + // order.customer(customer) + newValue = condOrRefresh; + condOrRefresh = false; + } else if (typeof condOrRefresh === 'function' && + options === undefined && cb === undefined) { + // order.customer(cb) + cb = condOrRefresh; + condOrRefresh = false; + } else if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } + if (!newValue) { + scopeQuery = condOrRefresh; } if (typeof this.definition.polymorphic === 'object') { @@ -1358,20 +1430,20 @@ BelongsTo.prototype.related = function (refresh, params) { } var cachedValue; - if (!refresh) { + if (!condOrRefresh) { cachedValue = self.getCache(); } - if (params instanceof ModelBaseClass) { // acts as setter - modelInstance[fk] = params[pk]; + if (newValue) { // acts as setter + modelInstance[fk] = newValue[pk]; if (discriminator) { - modelInstance[discriminator] = params.constructor.modelName; + modelInstance[discriminator] = newValue.constructor.modelName; } - this.definition.applyProperties(modelInstance, params); + this.definition.applyProperties(modelInstance, newValue); - self.resetCache(params); - } else if (typeof params === 'function') { // acts as async getter + self.resetCache(newValue); + } else if (typeof cb === 'function') { // acts as async getter if (discriminator) { var modelToName = modelInstance[discriminator]; @@ -1385,7 +1457,6 @@ BelongsTo.prototype.related = function (refresh, params) { } } - var cb = params; if (cachedValue === undefined || !(cachedValue instanceof ModelBaseClass)) { var query = {where: {}}; query.where[pk] = modelInstance[fk]; @@ -1404,7 +1475,7 @@ BelongsTo.prototype.related = function (refresh, params) { query.fields.push(pk); // always include the pk } - modelTo.findOne(query, function (err, inst) { + modelTo.findOne(query, options, function (err, inst) { if (err) { return cb(err); } @@ -1429,10 +1500,10 @@ BelongsTo.prototype.related = function (refresh, params) { cb(null, cachedValue); return cachedValue; } - } else if (params === undefined) { // acts as sync getter + } else if (condOrRefresh === undefined) { // acts as sync getter return cachedValue; } else { // setter - modelInstance[fk] = params; + modelInstance[fk] = newValue; self.resetCache(); } }; @@ -1445,9 +1516,14 @@ BelongsTo.prototype.related = function (refresh, params) { * @param {Function} cb Callback of the form function (err, inst) * @returns {Promise | Undefined} returns promise if callback is omitted */ -BelongsTo.prototype.getAsync = function (cb) { +BelongsTo.prototype.getAsync = function (options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } cb = cb || utils.createPromiseCallback(); - this.related(true, cb); + this.related(true, options, cb); return cb.promise; } @@ -1606,7 +1682,12 @@ RelationDefinition.hasOne = function (modelFrom, modelTo, params) { * @param {String|Object} err Error string or object * @param {Object} The newly created target model instance */ -HasOne.prototype.create = function (targetModelData, cb) { +HasOne.prototype.create = function (targetModelData, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var self = this; var modelTo = this.definition.modelTo; var fk = this.definition.keyTo; @@ -1627,23 +1708,29 @@ HasOne.prototype.create = function (targetModelData, cb) { this.definition.applyScope(modelInstance, query); this.definition.applyProperties(modelInstance, targetModelData); - modelTo.findOrCreate(query, targetModelData, function (err, targetModel, created) { - if (err) { - return cb && cb(err); - } - if (created) { - // Refresh the cache - self.resetCache(targetModel); - cb && cb(err, targetModel); - } else { - cb && cb(new Error('HasOne relation cannot create more than one instance of ' - + modelTo.modelName)); - } - }); + modelTo.findOrCreate(query, targetModelData, options, + function(err, targetModel, created) { + if (err) { + return cb && cb(err); + } + if (created) { + // Refresh the cache + self.resetCache(targetModel); + cb && cb(err, targetModel); + } else { + cb && cb(new Error('HasOne relation cannot create more than one instance of ' + + modelTo.modelName)); + } + }); return cb.promise; }; -HasOne.prototype.update = function(targetModelData, cb) { +HasOne.prototype.update = function(targetModelData, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } cb = cb || utils.createPromiseCallback(); var definition = this.definition; var fk = this.definition.keyTo; @@ -1659,11 +1746,16 @@ HasOne.prototype.update = function(targetModelData, cb) { return cb.promise; }; -HasOne.prototype.destroy = function (cb) { +HasOne.prototype.destroy = function (options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } cb = cb || utils.createPromiseCallback(); this.fetch(function(err, targetModel) { if (targetModel instanceof ModelBaseClass) { - targetModel.destroy(cb); + targetModel.destroy(options, cb); } else { cb(new Error('HasOne relation ' + definition.name + ' is empty')); @@ -1679,7 +1771,12 @@ HasOne.prototype.destroy = function (cb) { * @param {String|Object} err Error string or object * @param {Object} The newly created target model instance */ -HasMany.prototype.create = function (targetModelData, cb) { +HasMany.prototype.create = function (targetModelData, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var self = this; var modelTo = this.definition.modelTo; var fk = this.definition.keyTo; @@ -1708,7 +1805,7 @@ HasMany.prototype.create = function (targetModelData, cb) { apply(targetModelData, fkAndProps); - modelTo.create(targetModelData, function(err, targetModel) { + modelTo.create(targetModelData, options, function(err, targetModel) { if(!err) { //Refresh the cache apply(targetModel, self.addToCache.bind(self)); @@ -1748,35 +1845,44 @@ HasMany.prototype.build = HasOne.prototype.build = function(targetModelData) { * @param {Object|Function} params Query parameters * @returns {Object} */ -HasOne.prototype.related = function (refresh, params) { +HasOne.prototype.related = function (condOrRefresh, options, cb) { var self = this; var modelTo = this.definition.modelTo; var fk = this.definition.keyTo; var pk = this.definition.keyFrom; var definition = this.definition; var modelInstance = this.modelInstance; + var newValue; - if (arguments.length === 1) { - params = refresh; - refresh = false; - } else if (arguments.length > 2) { - throw new Error('Method can\'t be called with more than two arguments'); + if ((condOrRefresh instanceof ModelBaseClass) && + options === undefined && cb === undefined) { + // order.customer(customer) + newValue = condOrRefresh; + condOrRefresh = false; + } else if (typeof condOrRefresh === 'function' && + options === undefined && cb === undefined) { + // order.customer(cb) + cb = condOrRefresh; + condOrRefresh = false; + } else if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; } var cachedValue; - if (!refresh) { + if (!condOrRefresh) { cachedValue = self.getCache(); } - if (params instanceof ModelBaseClass) { // acts as setter - params[fk] = modelInstance[pk]; - self.resetCache(params); - } else if (typeof params === 'function') { // acts as async getter - var cb = params; + if (newValue) { // acts as setter + newValue[fk] = modelInstance[pk]; + self.resetCache(newValue); + } else if (typeof cb === 'function') { // acts as async getter if (cachedValue === undefined) { var query = {where: {}}; query.where[fk] = modelInstance[pk]; definition.applyScope(modelInstance, query); - modelTo.findOne(query, function (err, inst) { + modelTo.findOne(query, options, function (err, inst) { if (err) { return cb(err); } @@ -1801,10 +1907,10 @@ HasOne.prototype.related = function (refresh, params) { cb(null, cachedValue); return cachedValue; } - } else if (params === undefined) { // acts as sync getter + } else if (condOrRefresh === undefined) { // acts as sync getter return cachedValue; } else { // setter - params[fk] = modelInstance[pk]; + newValue[fk] = modelInstance[pk]; self.resetCache(); } }; @@ -1817,7 +1923,12 @@ HasOne.prototype.related = function (refresh, params) { * @returns {Promise | Undefined} Returns promise if cb is omitted */ -HasOne.prototype.getAsync = function (cb) { +HasOne.prototype.getAsync = function (options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } cb = cb || utils.createPromiseCallback(); this.related(true, cb); return cb.promise; @@ -1926,22 +2037,32 @@ RelationDefinition.embedsOne = function (modelFrom, modelTo, params) { return definition; }; -EmbedsOne.prototype.related = function (refresh, params) { +EmbedsOne.prototype.related = function (condOrRefresh, options, cb) { var modelTo = this.definition.modelTo; var modelInstance = this.modelInstance; var propertyName = this.definition.keyFrom; + var newValue; - if (arguments.length === 1) { - params = refresh; - refresh = false; - } else if (arguments.length > 2) { - throw new Error('Method can\'t be called with more than two arguments'); + if ((condOrRefresh instanceof ModelBaseClass) && + options === undefined && cb === undefined) { + // order.customer(customer) + newValue = condOrRefresh; + condOrRefresh = false; + } else if (typeof condOrRefresh === 'function' && + options === undefined && cb === undefined) { + // order.customer(cb) + cb = condOrRefresh; + condOrRefresh = false; + } else if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; } - if (params instanceof ModelBaseClass) { // acts as setter - if (params instanceof modelTo) { - this.definition.applyProperties(modelInstance, params); - modelInstance.setAttribute(propertyName, params); + if (newValue) { // acts as setter + if (newValue instanceof modelTo) { + this.definition.applyProperties(modelInstance, newValue); + modelInstance.setAttribute(propertyName, newValue); } return; } @@ -1952,12 +2073,11 @@ EmbedsOne.prototype.related = function (refresh, params) { embeddedInstance.__persisted = true; } - if (typeof params === 'function') { // acts as async getter - var cb = params; + if (typeof cb === 'function') { // acts as async getter process.nextTick(function() { cb(null, embeddedInstance); }); - } else if (params === undefined) { // acts as sync getter + } else if (condOrRefresh === undefined) { // acts as sync getter return embeddedInstance; } }; @@ -1967,7 +2087,12 @@ EmbedsOne.prototype.embeddedValue = function(modelInstance) { return modelInstance[this.definition.keyFrom]; }; -EmbedsOne.prototype.create = function (targetModelData, cb) { +EmbedsOne.prototype.create = function (targetModelData, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var modelTo = this.definition.modelTo; var propertyName = this.definition.keyFrom; var modelInstance = this.modelInstance; @@ -1985,19 +2110,19 @@ EmbedsOne.prototype.create = function (targetModelData, cb) { var updateEmbedded = function() { if (modelInstance.isNewRecord()) { modelInstance.setAttribute(propertyName, inst); - modelInstance.save(function(err) { + modelInstance.save(options, function(err) { cb(err, err ? null : inst); }); } else { modelInstance.updateAttribute(propertyName, - inst, function(err) { + inst, options, function(err) { cb(err, err ? null : inst); }); } }; if (this.definition.options.persistent) { - inst.save(function(err) { // will validate + inst.save(options, function(err) { // will validate if (err) return cb(err, inst); updateEmbedded(); }); @@ -2043,7 +2168,12 @@ EmbedsOne.prototype.build = function (targetModelData) { return embeddedInstance; }; -EmbedsOne.prototype.update = function (targetModelData, cb) { +EmbedsOne.prototype.update = function (targetModelData, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var modelTo = this.definition.modelTo; var modelInstance = this.modelInstance; var propertyName = this.definition.keyFrom; @@ -2056,7 +2186,7 @@ EmbedsOne.prototype.update = function (targetModelData, cb) { embeddedInstance.setAttributes(data); cb = cb || utils.createPromiseCallback(); if (typeof cb === 'function') { - modelInstance.save(function(err, inst) { + modelInstance.save(options, function(err, inst) { cb(err, inst ? inst[propertyName] : embeddedInstance); }); } @@ -2068,7 +2198,12 @@ EmbedsOne.prototype.update = function (targetModelData, cb) { return cb.promise; }; -EmbedsOne.prototype.destroy = function (cb) { +EmbedsOne.prototype.destroy = function (options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var modelInstance = this.modelInstance; var propertyName = this.definition.keyFrom; modelInstance.unsetAttribute(propertyName, true); @@ -2281,21 +2416,27 @@ EmbedsMany.prototype.embeddedList = return embeddedList; }; -EmbedsMany.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) { +EmbedsMany.prototype.related = function(receiver, scopeParams, condOrRefresh, options, cb) { var modelTo = this.definition.modelTo; var modelInstance = this.modelInstance; var actualCond = {}; - if (arguments.length === 3) { + + if (typeof condOrRefresh === 'function' && + options === undefined && cb === undefined) { cb = condOrRefresh; - } else if (arguments.length === 4) { - if (typeof condOrRefresh === 'object') { - actualCond = condOrRefresh; - } - } else { - throw new Error('Method can be only called with one or two arguments'); + condOrRefresh = false; + } else if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; } + if (typeof condOrRefresh === 'object') { + actualCond = condOrRefresh; + } + + var embeddedList = this.embeddedList(receiver); this.definition.applyScope(receiver, actualCond); @@ -2308,7 +2449,7 @@ EmbedsMany.prototype.related = function(receiver, scopeParams, condOrRefresh, cb var returnRelated = function(list) { if (params.include) { - modelTo.include(list, params.include, cb); + modelTo.include(list, params.include, options, cb); } else { process.nextTick(function() { cb(null, list); }); } @@ -2317,7 +2458,12 @@ EmbedsMany.prototype.related = function(receiver, scopeParams, condOrRefresh, cb returnRelated(embeddedList); }; -EmbedsMany.prototype.findById = function (fkId, cb) { +EmbedsMany.prototype.findById = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var pk = this.definition.keyTo; var modelTo = this.definition.modelTo; var modelInstance = this.modelInstance; @@ -2344,15 +2490,25 @@ EmbedsMany.prototype.findById = function (fkId, cb) { return item; // sync }; -EmbedsMany.prototype.exists = function (fkId, cb) { +EmbedsMany.prototype.exists = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var modelTo = this.definition.modelTo; - var inst = this.findById(fkId, function (err, inst) { + var inst = this.findById(fkId, options, function (err, inst) { if (cb) cb(err, inst instanceof modelTo); }); return inst instanceof modelTo; // sync }; -EmbedsMany.prototype.updateById = function (fkId, data, cb) { +EmbedsMany.prototype.updateById = function (fkId, data, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } if (typeof data === 'function') { cb = data; data = {}; @@ -2379,7 +2535,7 @@ EmbedsMany.prototype.updateById = function (fkId, data, cb) { if (typeof cb === 'function') { modelInstance.updateAttribute(propertyName, - embeddedList, function(err) { + embeddedList, options, function(err) { cb(err, inst); }); } @@ -2391,7 +2547,12 @@ EmbedsMany.prototype.updateById = function (fkId, data, cb) { return inst; // sync }; -EmbedsMany.prototype.destroyById = function (fkId, cb) { +EmbedsMany.prototype.destroyById = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var modelTo = this.definition.modelTo; var propertyName = this.definition.keyFrom; var modelInstance = this.modelInstance; @@ -2416,7 +2577,12 @@ EmbedsMany.prototype.destroyById = function (fkId, cb) { return inst; // sync }; -EmbedsMany.prototype.destroyAll = function(where, cb) { +EmbedsMany.prototype.destroyAll = function(where, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } if (typeof where === 'function') cb = where, where = {}; var propertyName = this.definition.keyFrom; var modelInstance = this.modelInstance; @@ -2463,12 +2629,18 @@ EmbedsMany.prototype.at = function (index, cb) { return item; // sync }; -EmbedsMany.prototype.create = function (targetModelData, cb) { +EmbedsMany.prototype.create = function (targetModelData, options, cb) { var pk = this.definition.keyTo; var modelTo = this.definition.modelTo; var propertyName = this.definition.keyFrom; var modelInstance = this.modelInstance; + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } + if (typeof targetModelData === 'function' && !cb) { cb = targetModelData; targetModelData = {}; @@ -2483,12 +2655,12 @@ EmbedsMany.prototype.create = function (targetModelData, cb) { var updateEmbedded = function() { if (modelInstance.isNewRecord()) { modelInstance.setAttribute(propertyName, embeddedList); - modelInstance.save(function(err) { + modelInstance.save(options, function(err) { cb(err, err ? null : inst); }); } else { modelInstance.updateAttribute(propertyName, - embeddedList, function(err) { + embeddedList, options, function(err) { cb(err, err ? null : inst); }); } @@ -2563,7 +2735,12 @@ EmbedsMany.prototype.build = function(targetModelData) { * Add the target model instance to the 'embedsMany' relation * @param {Object|ID} acInst The actual instance or id value */ -EmbedsMany.prototype.add = function (acInst, data, cb) { +EmbedsMany.prototype.add = function (acInst, data, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } if (typeof data === 'function') { cb = data; data = {}; @@ -2593,7 +2770,7 @@ EmbedsMany.prototype.add = function (acInst, data, cb) { belongsTo.applyScope(modelInstance, filter); - belongsTo.modelTo.findOne(filter, function(err, ref) { + belongsTo.modelTo.findOne(filter, options, function(err, ref) { if (ref instanceof belongsTo.modelTo) { var inst = self.build(data || {}); inst[options.belongsTo](ref); @@ -2611,7 +2788,12 @@ EmbedsMany.prototype.add = function (acInst, data, cb) { * Remove the target model instance from the 'embedsMany' relation * @param {Object|ID) acInst The actual instance or id value */ -EmbedsMany.prototype.remove = function (acInst, cb) { +EmbedsMany.prototype.remove = function (acInst, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var self = this; var definition = this.definition; var modelTo = this.definition.modelTo; @@ -2637,14 +2819,14 @@ EmbedsMany.prototype.remove = function (acInst, cb) { cb = cb || utils.createPromiseCallback(); - modelInstance[definition.name](filter, function(err, items) { + modelInstance[definition.name](filter, options, function(err, items) { if (err) return cb(err); items.forEach(function(item) { self.unset(item); }); - modelInstance.save(function(err) { + modelInstance.save(options, function(err) { cb(err); }); }); @@ -2740,7 +2922,7 @@ RelationDefinition.referencesMany = function referencesMany(modelFrom, modelTo, return definition; }; -ReferencesMany.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) { +ReferencesMany.prototype.related = function(receiver, scopeParams, condOrRefresh, options, cb) { var fk = this.definition.keyFrom; var modelTo = this.definition.modelTo; var relationName = this.definition.name; @@ -2750,35 +2932,43 @@ ReferencesMany.prototype.related = function(receiver, scopeParams, condOrRefresh var actualCond = {}; var actualRefresh = false; - if (arguments.length === 3) { + if (typeof condOrRefresh === 'function' && + options === undefined && cb === undefined) { + // order.customer(cb) cb = condOrRefresh; - } else if (arguments.length === 4) { + condOrRefresh = undefined; + } else if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; if (typeof condOrRefresh === 'boolean') { actualRefresh = condOrRefresh; + condOrRefresh = {}; } else { - actualCond = condOrRefresh; actualRefresh = true; } - } else { - throw new Error('Method can be only called with one or two arguments'); } + actualCond = condOrRefresh || {}; var ids = self[fk] || []; this.definition.applyScope(modelInstance, actualCond); var params = mergeQuery(actualCond, scopeParams); - - modelTo.findByIds(ids, params, cb); + return modelTo.findByIds(ids, params, options, cb); }; -ReferencesMany.prototype.findById = function (fkId, cb) { +ReferencesMany.prototype.findById = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var modelTo = this.definition.modelTo; var modelFrom = this.definition.modelFrom; var relationName = this.definition.name; var modelInstance = this.modelInstance; - var modelTo = this.definition.modelTo; var pk = this.definition.keyTo; var fk = this.definition.keyFrom; @@ -2794,7 +2984,7 @@ ReferencesMany.prototype.findById = function (fkId, cb) { cb = cb || utils.createPromiseCallback(); - modelTo.findByIds(ids, filter, function (err, instances) { + modelTo.findByIds(ids, filter, options, function (err, instances) { if (err) { return cb(err); } @@ -2823,7 +3013,12 @@ ReferencesMany.prototype.findById = function (fkId, cb) { return cb.promise; }; -ReferencesMany.prototype.exists = function (fkId, cb) { +ReferencesMany.prototype.exists = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var fk = this.definition.keyFrom; var ids = this.modelInstance[fk] || []; var currentIds = ids.map(function(id) { return id.toString(); }); @@ -2834,21 +3029,31 @@ ReferencesMany.prototype.exists = function (fkId, cb) { return cb.promise; }; -ReferencesMany.prototype.updateById = function (fkId, data, cb) { +ReferencesMany.prototype.updateById = function (fkId, data, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } if (typeof data === 'function') { cb = data; data = {}; } cb = cb || utils.createPromiseCallback(); - this.findById(fkId, function(err, inst) { + this.findById(fkId, options, function(err, inst) { if (err) return cb(err); - inst.updateAttributes(data, cb); + inst.updateAttributes(data, options, cb); }); return cb.promise; }; -ReferencesMany.prototype.destroyById = function (fkId, cb) { +ReferencesMany.prototype.destroyById = function (fkId, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var self = this; cb = cb || utils.createPromiseCallback(); this.findById(fkId, function(err, inst) { @@ -2860,15 +3065,25 @@ ReferencesMany.prototype.destroyById = function (fkId, cb) { return cb.promise; }; -ReferencesMany.prototype.at = function (index, cb) { +ReferencesMany.prototype.at = function (index, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var fk = this.definition.keyFrom; var ids = this.modelInstance[fk] || []; cb = cb || utils.createPromiseCallback(); - this.findById(ids[index], cb); + this.findById(ids[index], options, cb); return cb.promise; }; -ReferencesMany.prototype.create = function (targetModelData, cb) { +ReferencesMany.prototype.create = function (targetModelData, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var definition = this.definition; var modelTo = this.definition.modelTo; var relationName = this.definition.name; @@ -2888,7 +3103,7 @@ ReferencesMany.prototype.create = function (targetModelData, cb) { var inst = this.callScopeMethod('build', targetModelData); - inst.save(function(err, inst) { + inst.save(options, function(err, inst) { if (err) return cb(err, inst); var id = inst[pk]; @@ -2904,7 +3119,7 @@ ReferencesMany.prototype.create = function (targetModelData, cb) { } modelInstance.updateAttribute(fk, - ids, function(err, modelInst) { + ids, options, function(err, modelInst) { cb(err, inst); }); }); @@ -2924,7 +3139,12 @@ ReferencesMany.prototype.build = function(targetModelData) { * Add the target model instance to the 'embedsMany' relation * @param {Object|ID} acInst The actual instance or id value */ -ReferencesMany.prototype.add = function (acInst, cb) { +ReferencesMany.prototype.add = function (acInst, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var self = this; var definition = this.definition; var modelTo = this.definition.modelTo; @@ -2948,7 +3168,7 @@ ReferencesMany.prototype.add = function (acInst, cb) { ids.push(id); } - modelInstance.updateAttribute(fk, ids, function(err) { + modelInstance.updateAttribute(fk, ids, options, function(err) { done(err, err ? null : inst); }); }; @@ -2963,7 +3183,7 @@ ReferencesMany.prototype.add = function (acInst, cb) { definition.applyScope(modelInstance, filter); - modelTo.findOne(filter, function (err, inst) { + modelTo.findOne(filter, options, function (err, inst) { if (err || !inst) return cb(err, null); insert(inst, cb); }); @@ -2975,7 +3195,12 @@ ReferencesMany.prototype.add = function (acInst, cb) { * Remove the target model instance from the 'embedsMany' relation * @param {Object|ID) acInst The actual instance or id value */ -ReferencesMany.prototype.remove = function (acInst, cb) { +ReferencesMany.prototype.remove = function (acInst, options, cb) { + if (typeof options === 'function' && cb === undefined) { + // order.customer(options, cb) + cb = options; + options = {}; + } var definition = this.definition; var modelInstance = this.modelInstance; @@ -2994,7 +3219,7 @@ ReferencesMany.prototype.remove = function (acInst, cb) { var index = currentIds.indexOf(id); if (index > -1) { ids.splice(index, 1); - modelInstance.updateAttribute(fk, ids, function(err, inst) { + modelInstance.updateAttribute(fk, ids, options, function(err, inst) { cb(err, inst[fk] || []); }); } else { diff --git a/lib/scope.js b/lib/scope.js index c0b2a56d..0d4b941d 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -21,10 +21,11 @@ function ScopeDefinition(definition) { } ScopeDefinition.prototype.targetModel = function(receiver) { + var modelTo; if (typeof this.options.modelTo === 'function') { - var modelTo = this.options.modelTo.call(this, receiver) || this.modelTo; + modelTo = this.options.modelTo.call(this, receiver) || this.modelTo; } else { - var modelTo = this.modelTo; + modelTo = this.modelTo; } if (!(modelTo.prototype instanceof DefaultModelBaseClass)) { var msg = 'Invalid target model for scope `'; @@ -36,16 +37,34 @@ ScopeDefinition.prototype.targetModel = function(receiver) { return modelTo; }; -ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) { +/*! + * Find related model instances + * @param {*} receiver The target model class/prototype + * @param {Object|Function} scopeParams + * @param {Boolean|Object} [condOrRefresh] true for refresh or object as a filter + * @param {Object} [options] + * @param {Function} cb + * @returns {*} + */ +ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefresh, options, cb) { var name = this.name; var self = receiver; var actualCond = {}; var actualRefresh = false; var saveOnCache = true; - if (arguments.length === 3) { + if (typeof condOrRefresh === 'function' && + options === undefined && cb === undefined) { + // related(receiver, scopeParams, cb) cb = condOrRefresh; - } else if (arguments.length === 4) { + options = {}; + condOrRefresh = undefined; + } else if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } + options = options || {}; + if (condOrRefresh !== undefined) { if (typeof condOrRefresh === 'boolean') { actualRefresh = condOrRefresh; } else { @@ -53,16 +72,15 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres actualRefresh = true; saveOnCache = false; } - } else { - throw new Error('Method can be only called with one or two arguments'); } + cb = cb || utils.createPromiseCallback(); if (!self.__cachedRelations || self.__cachedRelations[name] === undefined || actualRefresh) { // It either doesn't hit the cache or refresh is required var params = mergeQuery(actualCond, scopeParams, {nestedInclude: true}); var targetModel = this.targetModel(receiver); - targetModel.find(params, function (err, data) { + targetModel.find(params, options, function (err, data) { if (!err && saveOnCache) { defineCachedRelations(self); self.__cachedRelations[name] = data; @@ -73,6 +91,7 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres // Return from cache cb(null, self.__cachedRelations[name]); } + return cb.promise; } /** @@ -149,7 +168,7 @@ function defineScope(cls, targetClass, name, params, methods, options) { var targetModel = definition.targetModel(this); var self = this; - var f = function(condOrRefresh, cb) { + var f = function(condOrRefresh, options, cb) { if (arguments.length === 0) { if (typeof f.value === 'function') { return f.value(self); @@ -157,6 +176,18 @@ function defineScope(cls, targetClass, name, params, methods, options) { return self.__cachedRelations[name]; } } else { + if (typeof condOrRefresh === 'function' + && options === undefined && cb === undefined) { + // customer.orders(cb) + cb = condOrRefresh; + options = {}; + condOrRefresh = undefined; + } else if (typeof options === 'function' && cb === undefined) { + // customer.orders(condOrRefresh, cb); + cb = options; + options = {}; + } + options = options || {} // Check if there is a through model // see https://github.com/strongloop/loopback/issues/1076 if (f._scope.collect && @@ -169,11 +200,7 @@ function defineScope(cls, targetClass, name, params, methods, options) { }; condOrRefresh = {}; } - if (arguments.length === 1) { - return definition.related(self, f._scope, condOrRefresh); - } else { - return definition.related(self, f._scope, condOrRefresh, cb); - } + return definition.related(self, f._scope, condOrRefresh, options, cb); } }; @@ -186,23 +213,20 @@ function defineScope(cls, targetClass, name, params, methods, options) { f._targetClass = i8n.camelize(f._scope.collect); } - f.getAsync = function (cond, cb) { - if (cb === undefined) { - if (cond === undefined) { - // getAsync() - cb = utils.createPromiseCallback(); - cond = true; - } else if (typeof cond !== 'function') { - // getAsync({where:{}}) - cb = utils.createPromiseCallback(); - } else { - // getAsync(function(){}) - cb = cond; - cond = true; - } + f.getAsync = function(condOrRefresh, options, cb) { + if (typeof condOrRefresh === 'function' + && options === undefined && cb === undefined) { + // customer.orders.getAsync(cb) + cb = condOrRefresh; + options = {}; + condOrRefresh = {}; + } else if (typeof options === 'function' && cb === undefined) { + // customer.orders.getAsync(condOrRefresh, cb); + cb = options; + options = {}; } - definition.related(self, f._scope, cond, cb); - return cb.promise; + options = options || {} + return definition.related(self, f._scope, condOrRefresh, options, cb); } f.build = build; @@ -298,13 +322,19 @@ function defineScope(cls, targetClass, name, params, methods, options) { return new targetModel(data); } - function create(data, cb) { - if (typeof data === 'function') { + function create(data, options, cb) { + if (typeof data === 'function' && + options === undefined && cb === undefined) { + // create(cb) cb = data; data = {}; + } else if (typeof options === 'function' && cb === undefined) { + // create(data, cb) + cb = options; + options = {}; } - cb = cb || utils.createPromiseCallback(); - return this.build(data).save(cb); + options = options || {}; + return this.build(data).save(options, cb); } /* @@ -313,53 +343,108 @@ function defineScope(cls, targetClass, name, params, methods, options) { - For every destroy call which results in an error - If fetching the Elements on which destroyAll is called results in an error */ - function destroyAll(where, cb) { - if (typeof where === 'function') cb = where, where = {}; - cb = cb || utils.createPromiseCallback(); + function destroyAll(where, options, cb) { + if (typeof where === 'function') { + // destroyAll(cb) + cb = where; + where = {}; + } else if (typeof options === 'function' && cb === undefined) { + // destroyAll(where, cb) + cb = options; + options = {}; + } + options = options || {}; var targetModel = definition.targetModel(this._receiver); var scoped = (this._scope && this._scope.where) || {}; var filter = mergeQuery({ where: scoped }, { where: where || {} }); - return targetModel.destroyAll(filter.where, cb); + return targetModel.destroyAll(filter.where, options, cb); } - function updateAll(where, data, cb) { - if (arguments.length === 2) { - // Handle updateAll(data, cb) + function updateAll(where, data, options, cb) { + if (typeof data === 'function' && + options === undefined && cb === undefined) { + // updateAll(data, cb) cb = data; data = where; where = {}; + options = {}; + } else if (typeof options === 'function' && cb === undefined) { + // updateAll(where, data, cb) + cb = options; + options = {}; } + options = options || {}; var targetModel = definition.targetModel(this._receiver); var scoped = (this._scope && this._scope.where) || {}; var filter = mergeQuery({ where: scoped }, { where: where || {} }); - targetModel.updateAll(filter.where, data, cb); + return targetModel.updateAll(filter.where, data, options, cb); } - function findById(id, cb) { + function findById(id, filter, options, cb) { + if (options === undefined && cb === undefined) { + if (typeof filter === 'function') { + // findById(id, cb) + cb = filter; + filter = {}; + } + } else if (cb === undefined) { + if (typeof options === 'function') { + // findById(id, query, cb) + cb = options; + options = {}; + if (typeof filter === 'object' && !(filter.include || filter.fields)) { + // If filter doesn't have include or fields, assuming it's options + options = filter; + filter = {}; + } + } + } + + options = options || {}; + filter = filter || {}; var targetModel = definition.targetModel(this._receiver); var idName = targetModel.definition.idName(); - var filter = { where: {} }; - filter.where[idName] = id; - this.findOne(filter, cb); + var query = {where: {}}; + query.where[idName] = id; + query = mergeQuery(query, filter); + return this.findOne(query, options, cb); } - function findOne(filter, cb) { - if (typeof filter === 'function') cb = filter, filter = {}; + function findOne(filter, options, cb) { + if (typeof filter === 'function') { + // findOne(cb) + cb = filter; + filter = {}; + options = {}; + } else if (typeof options === 'function' && cb === undefined) { + // findOne(filter, cb) + cb = options; + options = {}; + } + options = options || {}; var targetModel = definition.targetModel(this._receiver); var scoped = (this._scope && this._scope.where) || {}; - var filter = mergeQuery({ where: scoped }, filter || {}); - targetModel.findOne(filter, cb); + filter = mergeQuery({ where: scoped }, filter || {}); + return targetModel.findOne(filter, options, cb); } - function count(where, cb) { - if (typeof where === 'function') cb = where, where = {}; - cb = cb || utils.createPromiseCallback(); + function count(where, options, cb) { + if (typeof where === 'function') { + // count(cb) + cb = where; + where = {}; + } else if (typeof options === 'function' && cb === undefined) { + // count(where, cb) + cb = options; + options = {}; + } + options = options || {}; var targetModel = definition.targetModel(this._receiver); var scoped = (this._scope && this._scope.where) || {}; var filter = mergeQuery({ where: scoped }, { where: where || {} }); - return targetModel.count(filter.where, cb); + return targetModel.count(filter.where, options, cb); } return definition;