UNPKG

reheat

Version:

A red hot Node.js ORM for RethinkDB.

201 lines (184 loc) 6.97 kB
module.exports = function (container, Promise, utils, errors) { var IllegalArgumentError = errors.IllegalArgumentError; var RuntimeError = errors.RuntimeError; var errorPrefix = 'Model.load(relations[, options][, cb]): '; /** * @doc method * @id Model.instance_methods:load * @name load * @description * Load the specified relations of this model instance. * * ## Signature: * ```js * Model#load(relations[, options][, cb]) * ``` * * ## Examples: * * ### Promise-style: * ```js * post.load('Comment').then(function (post) { * post.get('comments'); // [...] * }); * * post.load(['Comment', 'User']).then(function (post) { * post.get('comments'); // [...] * post.get('user'); // {...} * }); * ``` * * ### Node-style: * ```js * post.load('Comment', function (err, post) { * if (err) { * // handle error * } else { * post.get('comments'); // [...] * } * }); * ``` * * ## Throws/Rejects with: * * - `{IllegalArgumentError}` * - `{UnhandledError}` * * @param {string|array} relations The names of the Models of the relations to load. * @param {object=} options Optional configuration. Properties: * * - `{boolean=false}` - `profile` - If `true` the query profile will be appended to `instance.queries`. * * @param {function=} cb Optional callback function for Node-style usage. Signature: `cb(err, instance)`. Arguments: * * - `{IllegalArgumentError|UnhandledError}` - `err` - `null` if no error occurs. * - `{object}` - `instance` - If no error occurs, this model instance, with relations loaded. * @returns {Promise} Promise. */ function load(relations, options, cb) { var models = container.get('models'); var Model = this.constructor; var newModels = {}; var merge = {}; var query; if (utils.isFunction(options)) { cb = options; options = {}; } options = options || {}; if (utils.isString(relations)) { relations = [relations]; } if (cb && !utils.isFunction(cb)) { throw new IllegalArgumentError(errorPrefix + 'cb: Must be a function!', { actual: typeof cb, expected: 'function' }); } return Promise.resolve().bind(this) .then(function () { if (!utils.isString(relations) && !utils.isArray(relations)) { throw new IllegalArgumentError(errorPrefix + 'relations: Must be a string or an array!', { actual: typeof relations, expected: 'string' }); } else if (!utils.isObject(options)) { throw new IllegalArgumentError(errorPrefix + 'options: Must be an object!', { actual: typeof options, expected: 'object' }); } }) .then(function () { if (Model.relations.belongsTo) { utils.forOwn(Model.relations.belongsTo, function (relation, modelName) { if (!models[modelName]) { throw new RuntimeError(Model.name + ' Model defined belongsTo relationship to nonexistent ' + modelName + ' Model!'); } else if (utils.contains(relations, modelName)) { var localField = relation.localField; var localKey = relation.localKey; merge[localField] = Model.r.table(models[modelName].tableName).get(this.get(localKey) || ''); newModels[localField] = { modelName: modelName, relation: 'belongsTo' }; } }, this); } if (Model.relations.hasMany) { utils.forOwn(Model.relations.hasMany, function (relation, modelName) { if (!models[modelName]) { throw new RuntimeError(Model.name + ' Model defined hasMany relationship to nonexistent ' + modelName + ' Model!'); } else if (utils.contains(relations, modelName)) { var localField = relation.localField; var foreignKey = relation.foreignKey; merge[localField] = Model.r.table(models[modelName].tableName).getAll(this.get(Model.idAttribute) || '', { index: foreignKey }).coerceTo('ARRAY'); newModels[localField] = { modelName: modelName, relation: 'hasMany' }; } }, this); } if (Model.relations.hasOne) { utils.forOwn(Model.relations.hasOne, function (relation, modelName) { if (!models[modelName]) { throw new RuntimeError(Model.name + ' Model defined hasOne relationship to nonexistent ' + modelName + ' Model!'); } else if (utils.contains(relations, modelName)) { var localField = relation.localField; merge[localField] = Model.r.table(models[modelName].tableName); if (relation.localKey) { merge[localField] = merge[localField].get(relation.localKey); } else { var foreignKey = relation.foreignKey; merge[localField] = merge[localField].getAll(this.get(Model.idAttribute) || '', { index: foreignKey }).coerceTo('ARRAY'); } newModels[localField] = { modelName: modelName, relation: 'hasOne' }; } }, this); } query = Model.r.expr({}).merge(merge); if (Model.tableReady && Model.tableReady !== true) { return Model.tableReady; } }) .then(function () { if (!utils.isEmpty(merge)) { return Model.connection.run(Model.r.table(Model.tableName).indexWait()); } else { return null; } }) .then(function () { return Model.connection.run(query, options); }) .then(function (document) { var doc = document; if (options.profile) { doc = document.result; } if (!doc) { return null; } else { utils.forOwn(doc, function (localValue, localKey) { if (localKey in newModels) { if (utils.isObject(localValue)) { doc[localKey] = new models[newModels[localKey].modelName](doc[localKey]); } else if (utils.isArray(localValue)) { if (newModels[localKey].relation === 'hasOne' && localValue.length) { doc[localKey] = new models[newModels[localKey].modelName](localValue[0]); } else { doc[localKey] = new models[newModels[localKey].modelName].collection(localValue); } } } }); this.setSync(doc); if (options.profile) { this.queries = this.queries || []; this.queries.push(document.profile); } return this; } }) .finally(function () { models = newModels = merge = query = null; }).nodeify(cb); } return load; } ;