diff --git a/lib/dao.js b/lib/dao.js index 9025cbf7..2df44e62 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -1,9 +1,9 @@ -/** +/*! * Module exports class Model */ module.exports = DataAccessObject; -/** +/*! * Module dependencies */ var jutil = require('./jutil'); @@ -19,15 +19,14 @@ var fieldsToArray = utils.fieldsToArray; var removeUndefined = utils.removeUndefined; /** - * DAO class - base class for all persist objects - * provides **common API** to access any database connector. - * This class describes only abstract behavior layer, refer to `lib/connectors/*.js` - * to learn more about specific connector implementations + * Base class for all persistent objects. + * Provides a common API to access any database connector. + * This class describes only abstract behavior. Refer to the specific connector (`lib/connectors/*.js`) for details. * - * `DataAccessObject` mixes `Inclusion` classes methods + * `DataAccessObject` mixes `Inclusion` classes methods. * - * @constructor - * @param {Object} data - initial object data + * @class DataAccessObject + * @param {Object} data Initial object data */ function DataAccessObject() { if (DataAccessObject._mixins) { @@ -71,14 +70,15 @@ DataAccessObject._forDB = function (data) { }; /** - * Create new instance of Model class, saved in database - * - * @param data [optional] - * @param callback(err, obj) - * callback called with arguments: + * Create new instance of Model class, saved in database. + * The callback function is called with arguments: * * - err (null or Error) * - instance (null or Model) + * + * @param data {Object} Optional data object + * @param callback {Function} Callback function + */ DataAccessObject.create = function (data, callback) { if (stillConnecting(this.getDataSource(), this, arguments)) return; @@ -208,9 +208,9 @@ function stillConnecting(dataSource, obj, args) { } /** - * Update or insert a model instance + * Update or insert a model instance. * @param {Object} data The model instance data - * @param {Function} [callback] The callback function + * @param {Function} callback The callback function (optional). */ DataAccessObject.upsert = DataAccessObject.updateOrCreate = function upsert(data, callback) { if (stillConnecting(this.getDataSource(), this, arguments)) return; @@ -254,9 +254,9 @@ setRemoting(DataAccessObject.upsert, { * Find one record, same as `all`, limited by 1 and return object, not collection, * if not found, create using data provided as second argument * - * @param {Object} query - search conditions: {where: {test: 'me'}}. - * @param {Object} data - object to create. - * @param {Function} cb - callback called with (err, instance) + * @param {Object} query Search conditions: {where: {test: 'me'}}. + * @param {Object} data Object to create. + * @param {Function} cb Callback called with (err, instance) */ DataAccessObject.findOrCreate = function findOrCreate(query, data, callback) { if (query === undefined) { @@ -282,8 +282,8 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, callback) { /** * Check whether a model instance exists in database * - * @param {id} id - identifier of object (primary key value) - * @param {Function} cb - callbacl called with (err, exists: Bool) + * @param {id} id Identifier of object (primary key value) + * @param {Function} cb Callback function called with (err, exists: Bool) */ DataAccessObject.exists = function exists(id, cb) { if (stillConnecting(this.getDataSource(), this, arguments)) return; @@ -306,8 +306,8 @@ setRemoting(DataAccessObject.exists, { /** * Find object by id * - * @param {*} id - primary key value - * @param {Function} cb - callback called with (err, instance) + * @param {*} id Primary key value + * @param {Function} cb Callback called with (err, instance) */ DataAccessObject.findById = function find(id, cb) { if (stillConnecting(this.getDataSource(), this, arguments)) return; @@ -458,20 +458,16 @@ DataAccessObject._coerce = function (where) { /** * Find all instances of Model, matched by query - * make sure you have marked as `index: true` fields for filter or sort - * - * @param {Object} params (optional) - * + * make sure you have marked as `index: true` fields for filter or sort. + * The params object: * - where: Object `{ key: val, key2: {gt: 'val2'}}` - * - include: String, Object or Array. See DataAccessObject.include documentation. + * - include: String, Object or Array. See `DataAccessObject.include()`. * - order: String * - limit: Number * - skip: Number - * - * @param {Function} callback (required) called with arguments: - * - * - err (null or Error) - * - Array of instances + * + * @param {Object} params (optional) + * @param {Function} callback (required) called with two arguments: err (null or Error), array of instances */ DataAccessObject.find = function find(params, cb) { @@ -598,8 +594,8 @@ setRemoting(DataAccessObject.find, { /** * Find one record, same as `all`, limited by 1 and return object, not collection * - * @param {Object} params - search conditions: {where: {test: 'me'}} - * @param {Function} cb - callback called with (err, instance) + * @param {Object} params Search conditions: {where: {test: 'me'}} + * @param {Function} cb Callback called with (err, instance) */ DataAccessObject.findOne = function findOne(params, cb) { if (stillConnecting(this.getDataSource(), this, arguments)) return; @@ -626,7 +622,7 @@ setRemoting(DataAccessObject.findOne, { /** * Destroy all matching records * @param {Object} [where] An object that defines the criteria - * @param {Function} [cb] - callback called with (err) + * @param {Function} [cb] Callback called with (err) */ DataAccessObject.remove = DataAccessObject.deleteAll = @@ -657,7 +653,7 @@ DataAccessObject.remove = /** * Destroy a record by id * @param {*} id The id value - * @param {Function} cb - callback called with (err) + * @param {Function} cb Callback called with (err) */ DataAccessObject.removeById = DataAccessObject.deleteById = @@ -683,8 +679,8 @@ setRemoting(DataAccessObject.deleteById, { /** * Return count of matched records * - * @param {Object} where - search conditions (optional) - * @param {Function} cb - callback, called with (err, count) + * @param {Object} where Search conditions (optional) + * @param {Function} cb Callback, called with (err, count) */ DataAccessObject.count = function (where, cb) { if (stillConnecting(this.getDataSource(), this, arguments)) return; @@ -797,7 +793,7 @@ DataAccessObject.prototype._adapter = function () { /** * Delete object from persistence * - * @triggers `destroy` hook (async) before and after destroying object + * Triggers `destroy` hook (async) before and after destroying object */ DataAccessObject.prototype.remove = DataAccessObject.prototype.delete = @@ -825,9 +821,9 @@ DataAccessObject.prototype.remove = * * equals to `updateAttributes({name: value}, cb) * - * @param {String} name - name of property - * @param {Mixed} value - value of property - * @param {Function} callback - callback called with (err, instance) + * @param {String} name Name of property + * @param {Mixed} value Value of property + * @param {Function} callback Callback function called with (err, instance) */ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, value, callback) { var data = {}; @@ -841,8 +837,8 @@ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, valu * this method performs validation before updating * * @trigger `validation`, `save` and `update` hooks - * @param {Object} data - data to update - * @param {Function} callback - callback called with (err, instance) + * @param {Object} data Data to update + * @param {Function} callback Callback function called with (err, instance) */ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb) { if (stillConnecting(this.getDataSource(), this, arguments)) return; @@ -911,9 +907,8 @@ setRemoting(DataAccessObject.prototype.updateAttributes, { /** * Reload object from persistence - * - * @requires `id` member of `object` to be able to call `find` - * @param {Function} callback - called with (err, instance) arguments + * Requires `id` member of `object` to be able to call `find` + * @param {Function} callback Called with (err, instance) arguments */ DataAccessObject.prototype.reload = function reload(callback) { if (stillConnecting(this.getDataSource(), this, arguments)) return; @@ -946,8 +941,8 @@ function defineReadonlyProp(obj, key, value) { var defineScope = require('./scope.js').defineScope; -/** - * Define scope +/*! + * Define scope. N.B. Not clear if this needs to be exposed in API doc. */ DataAccessObject.scope = function (name, filter, targetClass) { defineScope(this, targetClass || this, name, filter); diff --git a/lib/datasource.js b/lib/datasource.js index 42c612f6..32a0ee4b 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -19,7 +19,7 @@ if (process.env.DEBUG === 'loopback') { } var debug = require('debug')('loopback:datasource'); -/** +/*! * Export public API */ exports.DataSource = DataSource; @@ -30,23 +30,32 @@ exports.DataSource = DataSource; var slice = Array.prototype.slice; /** - * DataSource - connector-specific classes factory. + * LoopBack models can manipulate data via the DataSource object. + * Attaching a `DataSource` to a `Model` adds instance methods and static methods to the `Model`; + * some of the added methods may be remote methods. * - * All classes in single dataSource shares same connector type and - * one database connection - * - * @param {String} name - type of dataSource connector (mysql, mongoose, oracle, redis) - * @param {Object} settings - any database-specific settings which we need to - * establish connection (of course it depends on specific connector) + * Define a data source for persisting models. + * Typically, you create a DataSource by calling createDataSource() on the LoopBack object; for example: + * ```js + * var oracle = loopback.createDataSource({ + * connector: 'oracle', + * host: '111.22.333.44', + * database: 'MYDB', + * username: 'username', + * password: 'password' + * }); + * ``` * + * All classes in single dataSource share same the connector type and + * one database connection. The `settings` argument is an object that can have the following properties: * - host * - port * - username * - password * - database - * - debug {Boolean} = false + * - debug (Boolean, default is false) * - * @example DataSource creation, waiting for connection callback + * @desc For example, the following creates a DataSource, and waits for a connection callback. * ``` * var dataSource = new DataSource('mysql', { database: 'myapp_test' }); * dataSource.define(...); @@ -54,6 +63,9 @@ var slice = Array.prototype.slice; * // work with database * }); * ``` + * @class Define new DataSource + * @param {String} name Type of dataSource connector (mysql, mongoose, oracle, redis) + * @param {Object} settings Database-specific settings to establish connection (settings depend on specific connector). See above. */ function DataSource(name, settings, modelBuilder) { if (!(this instanceof DataSource)) { @@ -477,28 +489,19 @@ DataSource.prototype.setupDataAccess = function (modelClass, settings) { }; /** - * Define a model class - * - * @param {String} className - * @param {Object} properties - hash of class properties in format - * `{property: Type, property2: Type2, ...}` - * or - * `{property: {type: Type}, property2: {type: Type2}, ...}` - * @param {Object} settings - other configuration of class - * @return newly created class - * - * @example simple case + * Define a model class. + * Simple example: * ``` - * var User = dataSource.define('User', { + * var User = dataSource.createModel('User', { * email: String, * password: String, * birthDate: Date, * activated: Boolean * }); * ``` - * @example more advanced case + * More advanced example * ``` - * var User = dataSource.define('User', { + * var User = dataSource.createModel('User', { * email: { type: String, limit: 150, index: true }, * password: { type: String, limit: 50 }, * birthDate: Date, @@ -506,6 +509,29 @@ DataSource.prototype.setupDataAccess = function (modelClass, settings) { * activated: { type: Boolean, default: false } * }); * ``` + * You can also define an ACL when you create a new data source with the `DataSource.create()` method. For example: + * + * ```js + * var Customer = ds.createModel('Customer', { + * name: { + * type: String, + * acls: [ + * {principalType: ACL.USER, principalId: 'u001', accessType: ACL.WRITE, permission: ACL.DENY}, + * {principalType: ACL.USER, principalId: 'u001', accessType: ACL.ALL, permission: ACL.ALLOW} + * ] + * } + * }, { + * acls: [ + * {principalType: ACL.USER, principalId: 'u001', accessType: ACL.ALL, permission: ACL.ALLOW} + * ] + * }); + * ``` + * + * @param {String} className Name of the model to create. + * @param {Object} properties Hash of class properties in format `{property: Type, property2: Type2, ...}` or `{property: {type: Type}, property2: {type: Type2}, ...}` + * @param {Object} settings Other configuration settings. + * @returns newly created class + * */ DataSource.prototype.createModel = DataSource.prototype.define = function defineClass(className, properties, settings) { @@ -591,14 +617,14 @@ DataSource.prototype.mixin = function (ModelCtor) { }; /** - * @see ModelBuilder.prototype.getModel + * See ModelBuilder.getModel */ DataSource.prototype.getModel = function (name, forceCreate) { return this.modelBuilder.getModel(name, forceCreate); }; /** - * @see ModelBuilder.prototype.getModelDefinition + * See ModelBuilder.getModelDefinition */ DataSource.prototype.getModelDefinition = function (name) { return this.modelBuilder.getModelDefinition(name); @@ -618,9 +644,9 @@ DataSource.prototype.getTypes = function () { }; /** - * Check the data source supports the given types - * @param String|String[]) types A type name or an array of type names - * @return {Boolean} true if all types are supported by the data source + * Check the data source supports the specified types. + * @param {String} types Type name or an array of type names. Can also be array of Strings. + * @returns {Boolean} true if all types are supported by the data source */ DataSource.prototype.supportTypes = function (types) { var supportedTypes = this.getTypes(); @@ -670,9 +696,9 @@ DataSource.prototype.attach = function (modelClass) { /** * Define single property named `prop` on `model` * - * @param {String} model - name of model - * @param {String} prop - name of property - * @param {Object} params - property settings + * @param {String} model Name of model + * @param {String} prop Name of property + * @param {Object} params Property settings */ DataSource.prototype.defineProperty = function (model, prop, params) { this.modelBuilder.defineProperty(model, prop, params); @@ -685,12 +711,12 @@ DataSource.prototype.defineProperty = function (model, prop, params) { /** * Drop each model table and re-create. - * This method make sense only for sql connectors. + * This method applies only to SQL connectors. * - * @param {String} or {[String]} Models to be migrated, if not present, apply to all models - * @param {Function} [cb] The callback function + * @param {String} Models to be migrated, if not present, apply to all models. This can also be an array of Strings. + * @param {Function} cb Callback function. Optional. * - * @warning All data will be lost! Use autoupdate if you need your data. + * WARNING: Calling this function will cause all data to be lost! Use autoupdate if you need to preserve data. */ DataSource.prototype.automigrate = function (models, cb) { this.freeze(); @@ -709,7 +735,7 @@ DataSource.prototype.automigrate = function (models, cb) { * Update existing database tables. * This method make sense only for sql connectors. * - * @param {String} or {[String]} Models to be migrated, if not present, apply to all models + * @param {String} Models to be migrated, if not present, apply to all models. This can also be an array of Strings. * @param {Function} [cb] The callback function */ DataSource.prototype.autoupdate = function (models, cb) { @@ -729,15 +755,15 @@ DataSource.prototype.autoupdate = function (models, cb) { * Discover existing database tables. * This method returns an array of model objects, including {type, name, onwer} * - * `options` + * Kyes in options object: * - * all: true - Discovering all models, false - Discovering the models owned by the current user - * views: true - Including views, false - only tables - * limit: The page size - * offset: The starting index + * - all: true - Discovering all models, false - Discovering the models owned by the current user + * - views: true - Including views, false - only tables + * - limit: The page size + * - offset: The starting index * * @param {Object} options The options - * @param {Function} [cb] The callback function + * @param {Function} Callback function. Optional. * */ DataSource.prototype.discoverModelDefinitions = function (options, cb) { @@ -765,24 +791,25 @@ DataSource.prototype.discoverModelDefinitionsSync = function (options) { /** * Discover properties for a given model. * - * `property description` + * property description: * - * owner {String} The database owner or schema - * tableName {String} The table/view name - * columnName {String} The column name - * dataType {String} The data type - * dataLength {Number} The data length - * dataPrecision {Number} The numeric data precision - * dataScale {Number} The numeric data scale - * nullable {Boolean} If the data can be null +*| Key | Type | Description | +*|-----|------|-------------| +*|owner | String | Database owner or schema| +*|tableName | String | Table/view name| +*|columnName | String | Column name| +*|dataType | String | Data type| +*|dataLength | Number | Data length| +*|dataPrecision | Number | Numeric data precision| +*|dataScale |Number | Numeric data scale| +*|nullable |Boolean | If true, then the data can be null| * - * `options` - * - * owner/schema The database owner/schema + * Options: + * - owner/schema The database owner/schema * * @param {String} modelName The table/view name * @param {Object} options The options - * @param {Function} [cb] The callback function + * @param {Function} cb Callback function. Optional * */ DataSource.prototype.discoverModelProperties = function (modelName, options, cb) { @@ -809,21 +836,20 @@ DataSource.prototype.discoverModelPropertiesSync = function (modelName, options) }; /** - * Discover primary keys for a given owner/modelName + * Discover primary keys for a given owner/modelName. * * Each primary key column description has the following columns: * - * owner {String} => table schema (may be null) - * tableName {String} => table name - * columnName {String} => column name - * keySeq {Number} => sequence number within primary key( a value of 1 represents the first column of the primary key, a value of 2 would represent the second column within the primary key). - * pkName {String} => primary key name (may be null) + * - owner {String} => table schema (may be null) + * - tableName {String} => table name + * - columnName {String} => column name + * - keySeq {Number} => sequence number within primary key( a value of 1 represents the first column of the primary key, a value of 2 would represent the second column within the primary key). + * - pkName {String} => primary key name (may be null) * - * The owner, default to current user + * The owner defaults to current user. * - * `options` - * - * owner/schema The database owner/schema + * Options: + * - owner/schema The database owner/schema * * @param {String} modelName The model name * @param {Object} options The options @@ -968,7 +994,58 @@ function fromDBName(dbName, camelCase) { } /** - * Discover one schema from the given model without following the relations + * Discover one schema from the given model without following the relations. +**Example schema from oracle connector:** + * + * ```js + * { + * "name": "Product", + * "options": { + * "idInjection": false, + * "oracle": { + * "schema": "BLACKPOOL", + * "table": "PRODUCT" + * } + * }, + * "properties": { + * "id": { + * "type": "String", + * "required": true, + * "length": 20, + * "id": 1, + * "oracle": { + * "columnName": "ID", + * "dataType": "VARCHAR2", + * "dataLength": 20, + * "nullable": "N" + * } + * }, + * "name": { + * "type": "String", + * "required": false, + * "length": 64, + * "oracle": { + * "columnName": "NAME", + * "dataType": "VARCHAR2", + * "dataLength": 64, + * "nullable": "Y" + * } + * }, + * ... + * "fireModes": { + * "type": "String", + * "required": false, + * "length": 64, + * "oracle": { + * "columnName": "FIRE_MODES", + * "dataType": "VARCHAR2", + * "dataLength": 64, + * "nullable": "Y" + * } + * } + * } + * } + * ``` * * @param {String} modelName The model name * @param {Object} [options] The options @@ -1489,7 +1566,7 @@ DataSource.prototype.idNames = function (modelName) { /** * Define foreign key to another model * @param {String} className The model name that owns the key - * @param {String} key - name of key field + * @param {String} key Name of key field * @param {String} foreignClassName The foreign model name */ DataSource.prototype.defineForeignKey = function defineForeignKey(className, key, foreignClassName) { @@ -1528,7 +1605,7 @@ DataSource.prototype.defineForeignKey = function defineForeignKey(className, key /** * Close database connection - * @param {Fucntion} [cb] The callback function + * @param {Fucntion} cb The callback function. Optional. */ DataSource.prototype.disconnect = function disconnect(cb) { var self = this; @@ -1545,7 +1622,7 @@ DataSource.prototype.disconnect = function disconnect(cb) { }; /** - * Copy the model from Master + * Copy the model from Master. * @param {Function} Master The model constructor * @returns {Function} The copy of the model constructor * @@ -1625,7 +1702,8 @@ DataSource.prototype.transaction = function () { }; /** - * Enable a data source operation to be remote. + * Enable remote access to a data source operation. Each [connector](#connector) has its own set of set + * remotely enabled and disabled operations. To list the operations, call `dataSource.operations()`. * @param {String} operation The operation name */ @@ -1639,7 +1717,23 @@ DataSource.prototype.enableRemote = function (operation) { } /** - * Disable a data source operation to be remote. + * Disable remote access to a data source operation. Each [connector](#connector) has its own set of set enabled + * and disabled operations. To list the operations, call `dataSource.operations()`. + * + *```js + * + * var oracle = loopback.createDataSource({ + * connector: require('loopback-connector-oracle'), + * host: '...', + * ... + * }); + * oracle.disableRemote('destroyAll'); + * ``` + * **Notes:** + * + * - Disabled operations will not be added to attached models. + * - Disabling the remoting for a method only affects client access (it will still be available from server models). + * - Data sources must enable / disable operations before attaching or creating models. * @param {String} operation The operation name */ @@ -1671,7 +1765,27 @@ DataSource.prototype.getOperation = function (operation) { } /** - * Get all operations. + * Return JSON object describing all operations. + * + * Example return value: + * ```js + * { + * find: { + * remoteEnabled: true, + * accepts: [...], + * returns: [...] + * enabled: true + * }, + * save: { + * remoteEnabled: true, + * prototype: true, + * accepts: [...], + * returns: [...], + * enabled: true + * }, + * ... + * } + * ``` */ DataSource.prototype.operations = function () { return this._operations; @@ -1681,7 +1795,7 @@ DataSource.prototype.operations = function () { * Define an operation to the data source * @param {String} name The operation name * @param {Object} options The options - * @param [Function} fn The function + * @param {Function} fn The function */ DataSource.prototype.defineOperation = function (name, options, fn) { options.fn = fn; @@ -1698,10 +1812,10 @@ DataSource.prototype.isRelational = function () { }; /** - * Check if the data source is ready - * @param obj - * @param args - * @returns {boolean} + * Check if the data source is ready. + * Returns a Boolean value. + * @param obj {Object} ? + * @param args {Object} ? */ DataSource.prototype.ready = function (obj, args) { var self = this; diff --git a/lib/geo.js b/lib/geo.js index 84f7656f..30d6931d 100644 --- a/lib/geo.js +++ b/lib/geo.js @@ -1,7 +1,3 @@ -/** - * Dependencies. - */ - var assert = require('assert'); /*! @@ -80,12 +76,41 @@ exports.filter = function (arr, filter) { }); } -/** - * Export the `GeoPoint` class. - */ - exports.GeoPoint = GeoPoint; +/** + * The GeoPoint object represents a physical location. + * + * For example: + * + * ```js + * var here = new GeoPoint({lat: 10.32424, lng: 5.84978}); + * ``` + * + * Embed a latitude / longitude point in a model. + * + * ```js + * var CoffeeShop = loopback.createModel('coffee-shop', { + * location: 'GeoPoint' + * }); + * ``` + * + * You can query LoopBack models with a GeoPoint property and an attached data source using geo-spatial filters and + * sorting. For example, the following code finds the three nearest coffee shops. + * + * ```js + * CoffeeShop.attachTo(oracle); + * var here = new GeoPoint({lat: 10.32424, lng: 5.84978}); + * CoffeeShop.find( {where: {location: {near: here}}, limit:3}, function(err, nearbyShops) { + * console.info(nearbyShops); // [CoffeeShop, ...] + * }); + * ``` + * @class GeoPoint + * @param {Object} latlong Object with two Number properties: lat and long. + * @prop {Number} lat + * @prop {Number} lng + */ + function GeoPoint(data) { if (!(this instanceof GeoPoint)) { return new GeoPoint(data); @@ -118,7 +143,18 @@ function GeoPoint(data) { } /** - * Determine the spherical distance between two geo points. + * Determine the spherical distance between two GeoPoints. + * Specify units of measurement with the 'type' property in options object. Type can be: + * - `miles` (default) + * - `radians` + * - `kilometers` + * - `meters` + * - `miles` + * - `feet` + * - `degrees` + * @param {GeoPoint} pointA Point A + * @param {GeoPoint} pointB Point B + * @param {Object} options Options object; has one key, 'type', to specify the units of measurment (see above). Default is miles. */ GeoPoint.distanceBetween = function distanceBetween(a, b, options) { @@ -140,6 +176,14 @@ GeoPoint.distanceBetween = function distanceBetween(a, b, options) { /** * Determine the spherical distance to the given point. + * Example: + * ```js + * var here = new GeoPoint({lat: 10, lng: 10}); + * var there = new GeoPoint({lat: 5, lng: 5}); + * GeoPoint.distanceBetween(here, there, {type: 'miles'}) // 438 + * ``` + * @param {Object} point GeoPoint object to which to measure distance. + * @param {Object} options Use type key to specify units of measurment (default is miles). */ GeoPoint.prototype.distanceTo = function (point, options) { @@ -155,16 +199,19 @@ GeoPoint.prototype.toString = function () { } /** - * Si - */ + * @property {Number} PI - Ratio of a circle's circumference to its diameter. + * @property {Number} DEG2RAD - Factor to convert degrees to radians. + * @property {Number} RAD2DEG - Factor to convert radians to degrees. + * @property {Object} EARTH_RADIUS - Radius of the earth. +*/ // ratio of a circle's circumference to its diameter var PI = 3.1415926535897932384626433832795; -// factor to convert decimal degrees to radians +// factor to convert degrees to radians var DEG2RAD = 0.01745329252; -// factor to convert decimal degrees to radians +// factor to convert radians degrees to degrees var RAD2DEG = 57.29577951308; // radius of the earth diff --git a/lib/include.js b/lib/include.js index 4985ff2a..62926ccc 100644 --- a/lib/include.js +++ b/lib/include.js @@ -2,32 +2,42 @@ var utils = require('./utils'); var isPlainObject = utils.isPlainObject; var defineCachedRelations = utils.defineCachedRelations; -/** +/*! * Include mixin for ./model.js */ module.exports = Inclusion; +/** + * Inclusion - Model mixin. + * + * @class + */ + function Inclusion() { } /** - * Allows you to load relations of several objects and optimize numbers of requests. - * - * @param {Array} objects - array of instances - * @param {String}, {Object} or {Array} include - which relations you want to load. - * @param {Function} cb - Callback called when relations are loaded + * Enables you to load relations of several objects and optimize numbers of requests. * * Examples: * - * - User.include(users, 'posts', function() {}); will load all users posts with only one additional request. - * - User.include(users, ['posts'], function() {}); // same - * - User.include(users, ['posts', 'passports'], function() {}); // will load all users posts and passports with two - * additional requests. - * - Passport.include(passports, {owner: 'posts'}, function() {}); // will load all passports owner (users), and all - * posts of each owner loaded - * - Passport.include(passports, {owner: ['posts', 'passports']}); // ... - * - Passport.include(passports, {owner: [{posts: 'images'}, 'passports']}); // ... + * Load all users' posts with only one additional request: + * `User.include(users, 'posts', function() {});` + * Or + * `User.include(users, ['posts'], function() {});` * + * Load all users posts and passports with two additional requests: + * `User.include(users, ['posts', 'passports'], function() {});` + * + * Load all passports owner (users), and all posts of each owner loaded: + *```Passport.include(passports, {owner: 'posts'}, function() {}); + *``` Passport.include(passports, {owner: ['posts', 'passports']}); + *``` Passport.include(passports, {owner: [{posts: 'images'}, 'passports']}); + * + * @param {Array} objects Array of instances + * @param {String}, {Object} or {Array} include Which relations to load. + * @param {Function} cb Callback called when relations are loaded + * */ Inclusion.include = function (objects, include, cb) { var self = this; diff --git a/lib/model-builder.js b/lib/model-builder.js index 8b86e508..786978ae 100644 --- a/lib/model-builder.js +++ b/lib/model-builder.js @@ -16,7 +16,7 @@ require('./types')(ModelBuilder); var introspect = require('./introspection')(ModelBuilder); -/** +/*! * Export public API */ exports.ModelBuilder = exports.Schema = ModelBuilder; @@ -27,9 +27,9 @@ exports.ModelBuilder = exports.Schema = ModelBuilder; var slice = Array.prototype.slice; /** - * ModelBuilder - A builder to define data models + * ModelBuilder - A builder to define data models. * - * @constructor + * @class */ function ModelBuilder() { // create blank models pool @@ -57,11 +57,11 @@ function isModelClass(cls) { } /** - * Get a model by name + * Get a model by name. + * * @param {String} name The model name - * @param {Boolean} forceCreate Indicate if a stub should be created for the - * given name if a model doesn't exist - * @returns {*} The model class + * @param {Boolean} forceCreate Whether the create a stub for the given name if a model doesn't exist. + * Returns {*} The model class */ ModelBuilder.prototype.getModel = function (name, forceCreate) { var model = this.models[name]; @@ -81,17 +81,8 @@ ModelBuilder.prototype.getModelDefinition = function (name) { }; /** - * Define a model class - * - * @param {String} className - * @param {Object} properties - hash of class properties in format - * `{property: Type, property2: Type2, ...}` - * or - * `{property: {type: Type}, property2: {type: Type2}, ...}` - * @param {Object} settings - other configuration of class - * @return newly created class - * - * @example simple case + * Define a model class. + * Simple example: * ``` * var User = modelBuilder.define('User', { * email: String, @@ -100,7 +91,7 @@ ModelBuilder.prototype.getModelDefinition = function (name) { * activated: Boolean * }); * ``` - * @example more advanced case + * More advanced example: * ``` * var User = modelBuilder.define('User', { * email: { type: String, limit: 150, index: true }, @@ -110,6 +101,12 @@ ModelBuilder.prototype.getModelDefinition = function (name) { * activated: { type: Boolean, default: false } * }); * ``` + * + * @param {String} className Name of class + * @param {Object} properties Hash of class properties in format `{property: Type, property2: Type2, ...}` or `{property: {type: Type}, property2: {type: Type2}, ...}` + * @param {Object} settings Other configuration of class + * @return newly created class + * */ ModelBuilder.prototype.define = function defineClass(className, properties, settings, parent) { var modelBuilder = this; @@ -331,7 +328,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett /** * Register a property for the model class - * @param propertyName + * @param {String} propertyName Name of the property. */ ModelClass.registerProperty = function (propertyName) { var properties = modelDefinition.build(); @@ -426,9 +423,9 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett /** * Define single property named `propertyName` on `model` * - * @param {String} model - name of model - * @param {String} propertyName - name of property - * @param {Object} propertyDefinition - property settings + * @param {String} model Name of model + * @param {String} propertyName Name of property + * @param {Object} propertyDefinition Property settings */ ModelBuilder.prototype.defineProperty = function (model, propertyName, propertyDefinition) { this.definitions[model].defineProperty(propertyName, propertyDefinition); @@ -438,25 +435,25 @@ ModelBuilder.prototype.defineProperty = function (model, propertyName, propertyD /** * Extend existing model with bunch of properties * - * @param {String} model - name of model - * @param {Object} props - hash of properties - * * Example: - * - * // Instead of doing this: - * - * // amend the content model with competition attributes + * Instead of doing amending the content model with competition attributes like this: + * + * ```js * db.defineProperty('Content', 'competitionType', { type: String }); * db.defineProperty('Content', 'expiryDate', { type: Date, index: true }); * db.defineProperty('Content', 'isExpired', { type: Boolean, index: true }); - * - * // modelBuilder.extend allows to - * // extend the content model with competition attributes + *``` + * The extendModel() method enables you to extend the content model with competition attributes. + * ```js * db.extendModel('Content', { * competitionType: String, * expiryDate: { type: Date, index: true }, * isExpired: { type: Boolean, index: true } * }); + *``` + * + * @param {String} model Name of model + * @param {Object} props Hash of properties */ ModelBuilder.prototype.extendModel = function (model, props) { var t = this; @@ -523,9 +520,9 @@ ModelBuilder.prototype.getSchemaName = function (name) { }; /** - * Resolve the type string to be a function, for example, 'String' to String + * Resolve the type string to be a function, for example, 'String' to String. + * Returns {Function} if the type is resolved * @param {String} type The type string, such as 'number', 'Number', 'boolean', or 'String'. It's case insensitive - * @returns {Function} if the type is resolved */ ModelBuilder.prototype.resolveType = function (type) { if (!type) { @@ -620,10 +617,10 @@ ModelBuilder.prototype.buildModels = function (schemas) { }; /** - * Introspect the json document to build a corresponding model + * Introspect the JSON document to build a corresponding model. * @param {String} name The model name - * @param {Object} json The json object - * @param [Object} options The options + * @param {Object} json The JSON object + * @param {Object} options The options * @returns {} */ ModelBuilder.prototype.buildModelFromInstance = function (name, json, options) { diff --git a/lib/model.js b/lib/model.js index 258d1390..a587c826 100644 --- a/lib/model.js +++ b/lib/model.js @@ -1,9 +1,9 @@ -/** +/*! * Module exports class Model */ module.exports = ModelBaseClass; -/** +/*! * Module dependencies */ @@ -17,15 +17,12 @@ var validations = require('./validations.js'); var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text']; /** - * Model class - base class for all persist objects - * provides **common API** to access any database connector. - * This class describes only abstract behavior layer, refer to `lib/connectors/*.js` - * to learn more about specific connector implementations + * Model class: base class for all persistent objects. * * `ModelBaseClass` mixes `Validatable` and `Hookable` classes methods * - * @constructor - * @param {Object} data - initial object data + * @class + * @param {Object} data Initial object data */ function ModelBaseClass(data, options) { options = options || {}; @@ -195,8 +192,9 @@ ModelBaseClass.prototype._initProperties = function (data, options) { }; /** - * @param {String} prop - property name - * @param {Object} params - various property configuration + * Define a property on the model. + * @param {String} prop Property name + * @param {Object} params Various property configuration */ ModelBaseClass.defineProperty = function (prop, params) { this.dataSource.defineProperty(this.modelName, prop, params); @@ -221,20 +219,17 @@ ModelBaseClass.prototype.getPropertyType = function (propName) { /** * Return string representation of class - * - * @override default toString method + * This overrides the default `toString()` method */ ModelBaseClass.toString = function () { return '[Model ' + this.modelName + ']'; }; /** - * Convert model instance to a plain JSON object + * Convert model instance to a plain JSON object. + * Returns a canonical object representation (no getters and setters). * - * @param {Boolean} onlySchema - restrict properties to dataSource only, - * default to false. When onlySchema is true, only properties defined in - * the schema are returned, otherwise all enumerable properties returned - * @returns {Object} - canonical object representation (no getters and setters) + * @param {Boolean} onlySchema Restrict properties to dataSource only. Default is false. If true, the function returns only properties defined in the schema; Otherwise it returns all enumerable properties. */ ModelBaseClass.prototype.toObject = function (onlySchema) { if(onlySchema === undefined) { @@ -303,7 +298,7 @@ ModelBaseClass.prototype.fromObject = function (obj) { /** * Checks is property changed based on current property and initial value * - * @param {String} propertyName - property name + * @param {String} propertyName Property name * @return Boolean */ ModelBaseClass.prototype.propertyChanged = function propertyChanged(propertyName) { @@ -311,10 +306,9 @@ ModelBaseClass.prototype.propertyChanged = function propertyChanged(propertyName }; /** - * Reset dirty attributes - * - * this method does not perform any database operation it just reset object to it's - * initial state + * Reset dirty attributes. + * This method does not perform any database operations; it just resets the object to its + * initial state. */ ModelBaseClass.prototype.reset = function () { var obj = this; diff --git a/lib/relations.js b/lib/relations.js index 5b799556..e8b44ec1 100644 --- a/lib/relations.js +++ b/lib/relations.js @@ -1,4 +1,4 @@ -/** +/*! * Dependencies */ var i8n = require('inflection'); @@ -7,6 +7,11 @@ var ModelBaseClass = require('./model.js'); module.exports = Relation; +/** + * Relations class + * + * @class Relation + */ function Relation() { } @@ -31,11 +36,12 @@ function lookupModel(models, modelName) { } /** - * Declare hasMany relation - * - * @param {Relation} anotherClass - class to has many - * @param {Object} params - configuration {as:, foreignKey:} - * @example `User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});` + * Declare "hasMany" relation. + * Example: + * ```User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});``` + * + * @param {Relation} anotherClass Class to has many + * @param {Object} params Configuration {as:, foreignKey:} */ Relation.hasMany = function hasMany(anotherClass, params) { var thisClassName = this.modelName; @@ -172,27 +178,34 @@ Relation.hasMany = function hasMany(anotherClass, params) { }; /** - * Declare belongsTo relation + * Declare "belongsTo" relation. * - * @param {Class} anotherClass - class to belong - * @param {Object} params - configuration {as: 'propertyName', foreignKey: 'keyName'} - * - * **Usage examples** - * Suppose model Post have a *belongsTo* relationship with User (the author of the post). You could declare it this way: + * **Examples** + * + * Suppose the model Post has a *belongsTo* relationship with User (the author of the post). You could declare it this way: + * ```js * Post.belongsTo(User, {as: 'author', foreignKey: 'userId'}); + * ``` * * When a post is loaded, you can load the related author with: + * ```js * post.author(function(err, user) { * // the user variable is your user object * }); + * ``` * * The related object is cached, so if later you try to get again the author, no additional request will be made. * But there is an optional boolean parameter in first position that set whether or not you want to reload the cache: + * ```js * post.author(true, function(err, user) { * // The user is reloaded, even if it was already cached. * }); - * + * ``` * This optional parameter default value is false, so the related object will be loaded from cache if available. + * + * @param {Class} anotherClass Class to belong + * @param {Object} params Configuration {as: 'propertyName', foreignKey: 'keyName'} + * */ Relation.belongsTo = function (anotherClass, params) { params = params || {}; @@ -291,7 +304,13 @@ Relation.belongsTo = function (anotherClass, params) { /** * Many-to-many relation * - * Post.hasAndBelongsToMany('tags'); creates connection model 'PostTag' + * For example, this creates connection model 'PostTag': + * ```js + * Post.hasAndBelongsToMany('tags'); + * ``` + * @param {String} anotherClass + * @param {Object} params + * */ Relation.hasAndBelongsToMany = function hasAndBelongsToMany(anotherClass, params) { params = params || {}; diff --git a/lib/sql.js b/lib/sql.js index 3f32882b..701c8dc0 100644 --- a/lib/sql.js +++ b/lib/sql.js @@ -5,7 +5,7 @@ module.exports = BaseSQL; /** * Base class for connectors that are backed by relational databases/SQL - * @constructor + * @class */ function BaseSQL() { Connector.apply(this, [].slice.call(arguments)); @@ -21,7 +21,7 @@ BaseSQL.prototype.relational = true; /** * Get types associated with the connector - * @returns {String[]} The types for the connector + * Returns {String[]} The types for the connector */ BaseSQL.prototype.getTypes = function() { return ['db', 'rdbms', 'sql']; @@ -29,7 +29,7 @@ BaseSQL.prototype.relational = true; /*! * Get the default data type for ID - * @returns {Function} + * Returns {Function} */ BaseSQL.prototype.getDefaultIdType = function() { return Number; @@ -51,9 +51,9 @@ BaseSQL.prototype.queryOne = function (sql, callback) { }; /** - * Get the table name for a given model + * Get the table name for a given model. + * Returns the table name (String). * @param {String} model The model name - * @returns {String} The table name */ BaseSQL.prototype.table = function (model) { var name = this.getDataSource(model).tableName(model); diff --git a/lib/validations.js b/lib/validations.js index c7a43157..9582c2c3 100644 --- a/lib/validations.js +++ b/lib/validations.js @@ -1,15 +1,15 @@ var util = require('util'); -/** +/*! * Module exports */ exports.ValidationError = ValidationError; +exports.Validatable = Validatable; /** - * Validation mixins for model.js + * Validation mixins for LoopBack models. * - * Basically validation configurators is just class methods, which adds validations - * configs to AbstractClass._validations. Each of this validations run when - * `obj.isValid()` method called. + * This class provides methods that add validation cababilities to models. + * Each of this validations run when `obj.isValid()` method called. * * Each configurator can accept n params (n-1 field names and one config). Config * is {Object} depends on specific validation, but all of them has one common part: @@ -18,9 +18,8 @@ exports.ValidationError = ValidationError; * * In more complicated cases it can be {Hash} of messages (for each case): * `User.validatesLengthOf('password', { min: 6, max: 20, message: {min: 'too short', max: 'too long'}});` + * @class Validatable */ -exports.Validatable = Validatable; - function Validatable() { } @@ -29,19 +28,16 @@ function Validatable() { * * Default error message "can't be blank" * - * @example presence of title + * For example, validate presence of title * ``` * Post.validatesPresenceOf('title'); * ``` - * @example with custom message + *Example with custom message * ``` - * Post.validatesPresenceOf('title', {message: 'Can not be blank'}); + * Post.validatesPresenceOf('title', {message: 'Cannot be blank'}); * ``` * - * @sync - * - * @nocode - * @see helper/validatePresence + * @param */ Validatable.validatesPresenceOf = getConfigurator('presence'); @@ -54,14 +50,14 @@ Validatable.validatesPresenceOf = getConfigurator('presence'); * - max: too long * - is: length is wrong * - * @example length validations + * Example: length validations * ``` * User.validatesLengthOf('password', {min: 7}); * User.validatesLengthOf('email', {max: 100}); * User.validatesLengthOf('state', {is: 2}); * User.validatesLengthOf('nick', {min: 3, max: 15}); * ``` - * @example length validations with custom error messages + * Example: length validations with custom error messages * ``` * User.validatesLengthOf('password', {min: 7, message: {min: 'too weak'}}); * User.validatesLengthOf('state', {is: 2, message: {is: 'is not valid state name'}}); @@ -76,7 +72,7 @@ Validatable.validatesLengthOf = getConfigurator('length'); /** * Validate numericality. * - * @example + * Example * ``` * User.validatesNumericalityOf('age', { message: { number: '...' }}); * User.validatesNumericalityOf('age', {int: true, message: { int: '...' }}); @@ -96,7 +92,7 @@ Validatable.validatesNumericalityOf = getConfigurator('numericality'); /** * Validate inclusion in set * - * @example + * Example: * ``` * User.validatesInclusionOf('gender', {in: ['male', 'female']}); * User.validatesInclusionOf('role', { @@ -115,7 +111,7 @@ Validatable.validatesInclusionOf = getConfigurator('inclusion'); /** * Validate exclusion * - * @example `Company.validatesExclusionOf('domain', {in: ['www', 'admin']});` + * Example: `Company.validatesExclusionOf('domain', {in: ['www', 'admin']});` * * Default error message: is reserved * @@ -332,7 +328,7 @@ function getConfigurator(name, opts) { * @param {Function} callback called with (valid) * @return {Boolean} true if no async validation configured and all passed * - * @example ExpressJS controller: render user if valid, show flash otherwise + * Example: ExpressJS controller: render user if valid, show flash otherwise * ``` * user.isValid(function (valid) { * if (valid) res.render({user: user});