UNPKG

reheat

Version:

A red hot Node.js ORM for RethinkDB.

764 lines (738 loc) 24.2 kB
module.exports = function (utils, errors, Model_set, Model_setSync, Model_unset, Model_clear, Model_save, Model_destroy, Model_load) { return { /** * @doc method * @id Model.instance_methods:initialize * @name initialize * @description * Called at the end of construction of Model instances. Override this method via Model#extend([proto], static) to * execute custom initialization logic when instantiating new instances of Model. * * ## Signature: * ```js * Model#initialize() * ``` * * ## Example: * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * initialize: function () { * this.dirty = false; * } * }); * * var post = new Post(); * * post.dirty; // false * ``` */ initialize: function () { }, /** * @doc method * @id Model.instance_methods:escape * @name escape * @description * Return the HTML-escaped version of one of this instance's attributes. * * ## Signature: * ```js * Model#escape(key) * ``` * * ## Throws: * * - `{IllegalArgumentError}` - Argument `key` must be a string. * @param {string} key The key of the attribute to retrieve. Supports nested keys, e.g. `"address.state"`. * @returns {string} The HTML-escaped version of one of this instance's attributes. */ escape: function (key) { if (!utils.isString(key)) { throw new errors.IllegalArgumentError('Model#escapeHtml(key): key: Must be a string!', { actual: typeof key, expected: 'string' }); } try { return utils.escapeHtml(this.get(key)); } catch (err) { throw new errors.UnhandledError(err); } }, /** * @doc method * @id Model.instance_methods:toJSON * @name toJSON * @description * Return the plain attributes of this instance. Override this method to se your own custom serialization. * * ## Signature: * ```js * Model#toJSON() * ``` * * ## Examples: * ```js * var post = new Post({ author: 'John Anderson' }); * * post; // { * // attributes: { author: 'John Anderson' }, * // ... * // } * * post.toJSON(); // { author: 'John Anderson' } * ``` * * You can override the `toJSON()` method for custom serialization. * ```js * // Example of overriding toJSON() * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * toJSON: function () { * var attrs = this.constructor.__super__.toJSON.apply(this); * delete attrs.secretField; * return attrs; * } * }); * * var post = new Post({ author: 'John Anderson', secretField: 'mySecret' }); * * post; // { * // attributes: { author: 'John Anderson', secretField: 'mySecret' }, * // ... * // } * * post.toJSON(); // { author: 'John Anderson' } * ``` * * ## Throws: * * - `{UnhandledError}` - Thrown for any uncaught exception. * * @returns {object} The plain attributes of this instance. */ toJSON: function () { try { var toKeep = []; var clone = {}; utils.forOwn(this.attributes, function (value, key) { if (utils.isObject(value) && value.constructor.__reheat_super__) { clone[key] = value.toJSON(); } else if (utils.isArray(value) && value.length && utils.isObject(value[0]) && value[0].constructor.__reheat_super__) { clone[key] = []; for (var i = 0; i < value.length; i++) { clone[key].push(value[i].toJSON()); } } else { toKeep.push(key); } }); clone = utils.deepMixIn(clone, utils.pick(this.attributes, toKeep)); return clone; } catch (err) { throw new errors.UnhandledError(err); } }, /** * @doc method * @id Model.instance_methods:functions * @name functions * @description * Return an array of available methods on this instance. * * ## Signature: * ```js * Model#functions() * ``` * * ## Throws: * * - `{UnhandledError}` - Thrown for any uncaught exception. * * @returns {array} Array of available functions on this instance. */ functions: function () { try { return utils.functions(this); } catch (err) { throw new errors.UnhandledError(err); } }, /** * @doc method * @id Model.instance_methods:get * @name get * @description * Return the attribute specified by the given key. * * ## Signature: * ```js * Model#get(key) * ``` * * ## Example: * * ```js * var contanct = new Contact({ * firstName: 'John', * address: { * state: 'NY' * } * }); * * contact.get('firstName'); // John * * contact.get('address.state'); // NY * ``` * * ## Throws: * * - `{IllegalArgumentError}` - Argument `key` must be a string. * - `{UnhandledError}` - Thrown for any uncaught exception. * * @param {string} key The key of the attribute to retrieve. Supports nested keys, e.g. `"address.state"`. * @returns {*} The attribute specified by the given key. */ get: function (key) { if (!utils.isString(key)) { throw new errors.IllegalArgumentError('Model#get(key): key: Must be a string!', { actual: typeof key, expected: 'string' }); } try { return utils.get(this.attributes, key); } catch (err) { throw new errors.UnhandledError(err); } }, // See reheat/lib/model/prototype/set.js set: Model_set, // See reheat/lib/model/prototype/setSync.js setSync: Model_setSync, // See reheat/lib/model/prototype/unset.js unset: Model_unset, // See reheat/lib/model/prototype/clear.js clear: Model_clear, // See reheat/lib/model/prototype/save.js save: Model_save, // See reheat/lib/model/prototype/destroy.js destroy: Model_destroy, // See reheat/lib/model/prototype/load.js load: Model_load, /** * @doc method * @id Model.instance_methods:clone * @name clone * @description * Clone this instance. * * ## Signature: * ```js * Model#clone() * ``` * * ## Example: * ```js * contact.toJSON(); // { address: { state: 'NY' }, firstName: 'John' } * * var cloned = contact.clone(); * * cloned.toJSON(); // { address: { state: 'NY' }, firstName: 'John' } * cloned.setSync('firstName', 'Sally'); * * cloned.toJSON(); // { address: { state: 'NY' }, firstName: 'Sally' } * contact.toJSON(); // { address: { state: 'NY' }, firstName: 'John' } * ``` * * ## Throws: * * - `{UnhandledError}` - Thrown for any uncaught exception. * * @returns {*} A new instance identical to this instance. */ clone: function () { try { var toKeep = []; var clone = {}; utils.forOwn(this.attributes, function (value, key) { if (utils.isObject(value) && value.constructor.__reheat_super__) { clone[key] = value.clone(); } else if (utils.isArray(value) && value.length && utils.isObject(value[0]) && value[0].constructor.__reheat_super__) { clone[key] = []; for (var i = 0; i < value.length; i++) { clone[key].push(value[i].clone()); } } else { toKeep.push(key); } }); var model = new this.constructor(utils.merge({}, utils.pick(this.attributes, toKeep))); utils.deepMixIn(model.attributes, clone); return model; } catch (err) { throw new errors.UnhandledError(err); } }, /** * @doc method * @id Model.instance_methods:isNew * @name isNew * @description * Return `true` if this instance has not yet been saved to the database (lacks the property specified by * `Model.idAttribute`, which defaults to `"id"`). * * ## Signature: * ```js * Model#isNew() * ``` * * ## Example: * ```js * contact.toJSON(); // { address: { state: 'NY' }, firstName: 'John' } * contact.isNew(); // true * * contact.save(function (err, contact) { * contact.toJSON(); // { id: 45, address: { state: 'NY' }, firstName: 'John' } * contact.isNew(); // false * }); * ``` * * @returns {boolean} Whether this instance has been saved to the database. */ isNew: function () { return !this.attributes[this.constructor.idAttribute]; }, /** * @doc method * @id Model.instance_methods:beforeValidate * @name beforeValidate * @summary `beforeValidate` Model lifecycle step. * @description * `beforeValidate` Model lifecycle step. * * Called immediately before `Model#validate(cb)` is called and executes in the context this instance. The * default implementation does nothing. This method can be overridden via `reheat.defineModel()`. This method should * not pass anything to the callback function unless there is an error, in which case the lifecycle will be aborted * with the error. * * ## Signature: * ```js * Model#beforeValidate(cb) * ``` * * ## Example: * * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * beforeValidate: function (cb) { * // "this" refers to an instance of Post * if (this.get('author') === 'Walt Disney') { * cb('Impossible!'); * } else { * cb(); * } * } * }); * * var post = new Post({ author: 'Walt Disney' }); * * post.save(function (err, post) { * err; // Impossible! * }); * ``` * * @param {function} cb Callback function. Signature: `cb(err)`. */ beforeValidate: function (cb) { cb(null); }, /** * @doc method * @id Model.instance_methods:afterValidate * @name afterValidate * @description * `afterValidate` Model lifecycle step. * * Called immediately after `Model#validate(cb)` is called and executes in the context of this instance. The * default implementation does nothing. This method can be overridden via `reheat.defineModel()`. This method should * not pass anything to the callback function unless there is an error, in which case the lifecycle will be aborted * with the error. * * ## Signature: * ```js * Model#afterValidate(cb) * ``` * * ## Example: * * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * afterValidate: function (cb) { * // "this" refers to an instance of Post * if (this.get('author') === 'Walt Disney') { * cb('Impossible!'); * } else { * cb(); * } * } * }); * * var post = new Post({ author: 'Walt Disney' }); * * post.save(function (err, post) { * err; // Impossible! * }); * ``` * * @param {function} cb Callback function. Signature: `cb(err)`. */ afterValidate: function (cb) { cb(null); }, /** * @doc method * @id Model.instance_methods:validate * @name validate * @description * Validate the current attributes of this instance against the schema of this instance's Model, if any is * specified. * * This method does nothing if the Model of this instance does not have an instance of Schema. If this Model * has an instance of Schema, the current attributes of this instance will be validated with the Schema instance. * This method can be overridden for complete custom validation behavior. * * ## Signature: * ```js * Model#validate(cb) * ``` * * ## Example: * * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * validate: function (cb) { * // "this" refers to an instance of Post * if (typeof this.get('author') !== 'string') { * cb('type error'); * } else { * cb(); * } * } * }); * * var post = new Post({ author: 4435 }); * * post.save(function (err, post) { * err; // type error * }); * ``` * * @param {function} cb Callback function. Signature: `cb(err)`. Arguments: * * - `{ValidationError|UnhandledError}` - `err` - `null` if no error occurs. `ValidationError` if a validation * error occurs and `UnhandledError` for any other error. */ validate: function (cb) { var _this = this; if (this.constructor.schema) { this.constructor.schema.validate(this.attributes, function (err) { if (err) { _this.validationError = new errors.ValidationError(_this.constructor.name + '#validate(cb): Validation failed!', err); cb(_this.validationError); } else { cb(null); } }); } else { cb(null); } }, /** * @doc method * @id Model.instance_methods:beforeCreate * @name beforeCreate * @description * `beforeCreate` Model lifecycle step. * * Called immediately before this instance is saved to the database for the first time and executes in the * context of this instance. The default implementation does nothing. This method can be overridden via * `reheat.defineModel()`. This method should not pass anything to the callback function unless there is an error, in * which case the lifecycle will be aborted with the error. * * ## Signature: * ```js * Model#beforeCreate(cb) * ``` * * ## Example: * * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * beforeCreate: function (cb) { * // "this" refers to an instance of Post * if (this.get('author') === 'Walt Disney') { * cb('Impossible!'); * } else { * cb(); * } * } * }); * * var post = new Post({ author: 'Walt Disney' }); * * post.save(function (err, post) { * err; // Impossible! * }); * ``` * * @param {function} cb Callback function. Signature: `cb(err)`. */ beforeCreate: function (cb) { cb(null); }, /** * @doc method * @id Model.instance_methods:afterCreate * @name afterCreate * @description * `afterCreate` Model lifecycle step. * * Called immediately after this instance is saved to the database for the first time. The default * implementation does nothing. This method can be overridden via `reheat.defineModel()`. This method should pass * along the instance argument to the callback function, unless there is an error, in which case the * lifecycle will be aborted with the error. The inserted row is still in the database at this point, and it is * currently up to the developer to remove it. * * ## Signature: * ```js * Model#afterCreate(instance, cb) * ``` * * ## Example: * * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * afterCreate: function (instance, cb) { * // "this" refers to an instance of Post, and in this case, the "instance" argument is also a reference * // to "this" * * // e.g. send a transactional email, log the activity, etc. * * cb(null, instance); * } * }); * ``` * * @param {object} instance The instance of the Model. * @param {function} cb Callback function. Signature: `cb(err, instance)`. Arguments: * * - `{UnhandledError}` - `err` - `null` if no errors occur. * - `{object}` - `instance` - If no error occurs, a reference to the instance on which `save([options][, cb])` was called. */ afterCreate: function (instance, cb) { cb(null, instance); }, /** * @doc method * @id Model.instance_methods:beforeUpdate * @name beforeUpdate * @description * `beforeUpdate` Model lifecycle step. * * Called immediately before the row specified by this instance's primary key is updated with this instance's * current attributes. The default implementation does nothing. This method can be overridden via * `reheat.defineModel()`. This method should not pass anything to the callback function unless there is an error, in * which case the lifecycle will be aborted with the error. * * ## Signature: * ```js * Model#beforeUpdate(cb) * ``` * * ## Example: * * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * beforeUpdate: function (cb) { * // "this" refers to an instance of Post * if (this.get('author') === 'Walt Disney') { * cb('Impossible!'); * } else { * cb(); * } * } * }); * * Post.get(45, function (err, post) { * * post.setSync('author', 'Walt Disney'); * post.save(function (err, post) { * err; // Impossible! * }); * }); * ``` * * @param {function} cb Callback function. Signature: `cb(err)`. */ beforeUpdate: function (cb) { cb(null); }, /** * @doc method * @id Model.instance_methods:afterUpdate * @name afterUpdate * @description * `afterUpdate` Model lifecycle step. * * Called immediately after the row specified by this instance's primary key is updated with this instance's * current attributes and executes in the context this instance. The default implementation does nothing. This * method can be overridden via `reheat.defineModel()`. This method should pass along the instance * argument to the callback function, unless there is an error, in which case the lifecycle will be aborted with * the error. The row in the database has still been updated at this point, and for now it is up to the developer to * roll back the changes in case of an error. The old value of the updated row is in `instance.meta.old_val` or * `this.previousAttributes`. * * ## Signature: * ```js * Model#afterValidate(instance, cb) * ``` * * ## Example: * * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * afterUpdate: function (instance, cb) { * // "this" refers to an instance of Post, and in this case, the "instance" argument is also a reference * // to "this" * * // e.g. send a transactional email, log the activity, etc. * * cb(null, instance); * } * }); * ``` * * @param {object} instance The instance of the Model. * @param {function} cb Callback function. Signature: `cb(err, instance)`. Arguments: * * - `{UnhandledError}` - `err` - `null` if no errors occur. * - `{object}` - `instance` - If no error occurs, a reference to the instance on which `save([options][, cb])` was called. */ afterUpdate: function (instance, cb) { cb(null, instance); }, /** * @doc method * @id Model.instance_methods:beforeDestroy * @name beforeDestroy * @description * `beforeDestroy` Model lifecycle step. * * Called immediately before `Model#destroy(cb)` and executes in the context of this instance. The default * implementation does nothing. This method can be overridden via `reheat.defineModel()`. This method should not pass * anything to the callback function unless there is an error, in which case the lifecycle will be aborted with the * error. See [r#delete](http://rethinkdb.com/api/javascript/#delete). * * ## Signature: * ```js * Model#beforeDestroy(cb) * ```js * * ## Example: * * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * beforeDestroy: function (cb) { * // "this" refers to an instance of Post * if (this.get('author') === 'Walt Disney') { * cb('Impossible!'); * } else { * cb(); * } * } * }); * * Post.get(45, function (err, post) { * post.destroy(function (err, post) { * err; // Impossible! * }); * }); * ``` * * @param {function} cb Callback function. Signature: `cb(err)`. */ beforeDestroy: function (cb) { cb(null); }, /** * @doc method * @id Model.instance_methods:afterDestroy * @name afterDestroy * @description * `afterDestroy` Model lifecycle step. * * Called immediately after `reheat.defineModel()` and executes in the context of this instance. The default * implementation does nothing. This method can be overridden via `reheat.defineModel()`. This method should pass * along the instance argument to the callback function, unless there is an error, in which case the * lifecycle will be aborted with the error. The row in the database has still been updated/removed at this point, * and for now it is up to the developer to roll back the changes in case of an error. The old value of the updated/ * deleted row is in `instance.meta.old_val` or `this.previousAttributes`. * * ## Signature: * ```js * Model#afterDestroy(instance, cb) * ``` * * ## Example: * * ```js * var Post = reheat.defineModel('Post', { * connection: new reheat.Connection(), * tableName: 'post' * }, { * afterDestroy: function (instance, cb) { * // "this" refers to an instance of Post, and in this case, the "instance" argument is also a reference * // to "this" * * // e.g. send a transactional email, log the activity, etc. * * cb(null, instance); * } * }); * ``` * * @param {object} instance This instance. * @param {function} cb Callback function. Signature: `cb(err, instance)`. Arguments: * * - `{UnhandledError}` - `err` - `null` if no errors occur. * - `{object}` - `instance` - If no error occurs, a reference to the instance on which `destroy([options][, cb])` was called. */ afterDestroy: function (instance, cb) { cb(null, instance); } }; };