Merge pull request #1395 from strongloop/fix/api-docs-mixins-validatable

Fix mixin and validatable docs
This commit is contained in:
Sakib Hasan 2017-06-06 14:46:21 -04:00 committed by GitHub
commit 3b45c76d0f
4 changed files with 661 additions and 95 deletions

View File

@ -10,7 +10,7 @@ var utils = require('./utils');
module.exports = ObserverMixin;
/**
* ObserverMixin class. Use to add observe/notifyObserversOf APIs to other
* ObserverMixin class. Use to add observe/notifyObserversOf APIs to other
* classes.
*
* @class ObserverMixin
@ -20,12 +20,30 @@ function ObserverMixin() {
/**
* Register an asynchronous observer for the given operation (event).
*
* Example:
*
* Registers a `before save` observer for a given model.
*
* ```javascript
* MyModel.observe('before save', function filterProperties(ctx, next) {
if (ctx.options && ctx.options.skipPropertyFilter) return next();
if (ctx.instance) {
FILTERED_PROPERTIES.forEach(function(p) {
ctx.instance.unsetAttribute(p);
});
} else {
FILTERED_PROPERTIES.forEach(function(p) {
delete ctx.data[p];
});
}
next();
});
* ```
*
* @param {String} operation The operation name.
* @callback {function} listener The listener function. It will be invoked with
* `this` set to the model constructor, e.g. `User`.
* @param {Object} context Operation-specific context.
* @param {function(Error=)} next The callback to call when the observer
* has finished.
* @end
*/
ObserverMixin.observe = function(operation, listener) {
@ -39,6 +57,16 @@ ObserverMixin.observe = function(operation, listener) {
/**
* Unregister an asynchronous observer for the given operation (event).
*
* Example:
*
* ```javascript
* MyModel.removeObserver('before save', function removedObserver(ctx, next) {
// some logic user want to apply to the removed observer...
next();
});
* ```
*
* @param {String} operation The operation name.
* @callback {function} listener The listener function.
* @end
@ -54,6 +82,15 @@ ObserverMixin.removeObserver = function(operation, listener) {
/**
* Unregister all asynchronous observers for the given operation (event).
*
* Example:
*
* Remove all observers connected to the `before save` operation.
*
* ```javascript
* MyModel.clearObservers('before save');
* ```
*
* @param {String} operation The operation name.
* @end
*/
@ -65,10 +102,29 @@ ObserverMixin.clearObservers = function(operation) {
/**
* Invoke all async observers for the given operation(s).
*
* Example:
*
* Notify all async observers for the `before save` operation.
*
* ```javascript
* var context = {
Model: Model,
instance: obj,
isNewInstance: true,
hookState: hookState,
options: options,
};
* Model.notifyObserversOf('before save', context, function(err) {
if (err) return cb(err);
// user can specify the logic after the observers have been notified
});
* ```
*
* @param {String|String[]} operation The operation name(s).
* @param {Object} context Operation-specific context.
* @param {function(Error=)} callback The callback to call when all observers
* has finished.
* @callback {function(Error=)} callback The callback to call when all observers
* have finished.
*/
ObserverMixin.notifyObserversOf = function(operation, context, callback) {
var self = this;
@ -123,19 +179,41 @@ ObserverMixin._notifyBaseObservers = function(operation, context, callback) {
};
/**
* Run the given function with before/after observers. It's done in three serial
* steps asynchronously:
* Run the given function with before/after observers.
*
* It's done in three serial asynchronous steps:
*
* - Notify the registered observers under 'before ' + operation
* - Execute the function
* - Notify the registered observers under 'after ' + operation
*
* If an error happens, it fails fast and calls the callback with err.
* If an error happens, it fails first and calls the callback with err.
*
* Example:
*
* ```javascript
* var context = {
Model: Model,
instance: obj,
isNewInstance: true,
hookState: hookState,
options: options,
};
* function work(done) {
process.nextTick(function() {
done(null, 1);
});
}
* Model.notifyObserversAround('execute', context, work, function(err) {
if (err) return cb(err);
// user can specify the logic after the observers have been notified
});
* ```
*
* @param {String} operation The operation name
* @param {Context} context The context object
* @param {Function} fn The task to be invoked as fn(done) or fn(context, done)
* @param {Function} callback The callback function
* @callback {Function} callback The callback function
* @returns {*}
*/
ObserverMixin.notifyObserversAround = function(operation, context, fn, callback) {

View File

@ -13,7 +13,7 @@ var RelationDefinition = relation.RelationDefinition;
module.exports = RelationMixin;
/**
* RelationMixin class. Use to define relationships between models.
* RelationMixin class. Use to define relationships between models.
*
* @class RelationMixin
*/
@ -21,7 +21,7 @@ function RelationMixin() {
}
/**
* Define a "one to many" relationship by specifying the model name
* Define a "one to many" relationship by specifying the model name.
*
* Examples:
* ```
@ -50,25 +50,33 @@ function RelationMixin() {
* // you can also call the Chapter.create method with the `chapters` property which will build a chapter
* // instance and save the it in the data source.
* book.chapters.create({name: 'Chapter 2'}, function(err, savedChapter) {
* // this callback is optional
* // this callback is optional
* });
*
* // Query chapters for the book
* book.chapters(function(err, chapters) { // all chapters with bookId = book.id
* book.chapters(function(err, chapters) {
* // all chapters with bookId = book.id
* console.log(chapters);
* });
*
* // Query chapters for the book with a filter
* book.chapters({where: {name: 'test'}, function(err, chapters) {
* // All chapters with bookId = book.id and name = 'test'
* console.log(chapters);
* });
* });
*```
* ```
*
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
* @options {Object} parameters Configuration parameters; see below.
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} foreignKey Property name of foreign key field.
* @property {Object} model Model object
* @property {String} polymorphic Define a polymorphic relation name.
* @property {String} through Name of the through model.
* @property {String} keyThrough Property name of the foreign key in the through model.
* @property {Object|Function} scope Explicitly define additional scopes.
* @property {Boolean} invert Specify if the relation is inverted.
* @property {Object} model The model object.
*/
RelationMixin.hasMany = function hasMany(modelTo, params) {
return RelationDefinition.hasMany(this, modelTo, params);
@ -123,8 +131,12 @@ RelationMixin.hasMany = function hasMany(modelTo, params) {
* @param {Class|String} modelTo Model object (or String name of model) to which you are creating the relationship.
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} primaryKey Property name of primary key field.
* @property {String} foreignKey Name of foreign key property.
*
* @property {Object|Function} scope Explicitly define additional scopes.
* @property {Object} properties Properties inherited from the parent object.
* @property {Object} options Property level options.
* @property {Boolean} options.invertProperties Specify if the properties should be inverted.
*/
RelationMixin.belongsTo = function(modelTo, params) {
return RelationDefinition.belongsTo(this, modelTo, params);
@ -132,6 +144,7 @@ RelationMixin.belongsTo = function(modelTo, params) {
/**
* A hasAndBelongsToMany relation creates a direct many-to-many connection with another model, with no intervening model.
*
* For example, if your application includes users and groups, with each group having many users and each user appearing
* in many groups, you could declare the models this way:
* ```
@ -155,28 +168,432 @@ RelationMixin.belongsTo = function(modelTo, params) {
* ```
*
* @param {String|Object} modelTo Model object (or String name of model) to which you are creating the relationship.
* the relation
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} foreignKey Property name of foreign key field.
* @property {Object} model Model object
* @property {String} throughTable The table name of the through model.
* @property {String} through Name of the through model.
* @property {String} polymorphic Define a polymorphic relation name.
* @property {Object|Function} scope Explicitly define additional scopes.
* @property {Object} model The model object.
*/
RelationMixin.hasAndBelongsToMany = function hasAndBelongsToMany(modelTo, params) {
return RelationDefinition.hasAndBelongsToMany(this, modelTo, params);
};
/**
* Define a "one to one" relationship by specifying the model name.
*
* Examples:
* ```
* Supplier.hasOne(Account, {as: 'account', foreignKey: 'supplierId'});
* ```
*
* If the target model doesnt have a foreign key property, LoopBack will add a property with the same name.
*
* The type of the property will be the same as the type of the target models id property.
*
* Please note the foreign key property is defined on the target model (in this example, Account).
*
* If you dont specify them, then LoopBack derives the relation name and foreign key as follows:
* - Relation name: Camel case of the model name, for example, for the supplier model the relation is supplier.
* - Foreign key: The relation name appended with Id, for example, for relation name supplier the default foreign key is supplierId.
*
* Build a new account for the supplier with the supplierId to be set to the id of the supplier.
* ```js
* var supplier = supplier.account.build(data);
* ```
*
* Create a new account for the supplier. If there is already an account, an error will be reported.
* ```js
* supplier.account.create(data, function(err, account) {
* ...
* });
* ```
*
* Find the supplier's account model.
* ```js
* supplier.account(function(err, account) {
* ...
* });
* ```
*
* Update the associated account.
* ```js
* supplier.account.update({balance: 100}, function(err, account) {
* ...
* });
* ```
*
* Remove the account for the supplier.
* ```js
* supplier.account.destroy(function(err) {
* ...
* });
* ```
*
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} primaryKey Property name of primary key field.
* @property {String} foreignKey Property name of foreign key field.
* @property {String} polymorphic Define a polymorphic relation name.
* @property {Object|Function} scope Explicitly define additional scopes.
* @property {Object} model The model object.
* @property {Object} properties Properties inherited from the parent object.
* @property {Function} methods Scoped methods for the given relation.
*/
RelationMixin.hasOne = function hasOne(modelTo, params) {
return RelationDefinition.hasOne(this, modelTo, params);
};
/**
* References one or more instances of the target model.
*
* For example, a Customer model references one or more instances of the Account model.
*
* Define the relation in the model definition:
*
* - Definition of Customer model:
* ```json
* {
"name": "Customer",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number"
}
},
"validations": [],
"relations": {
"accounts": {
"type": "referencesMany",
"model": "Account",
"foreignKey": "accountIds",
"options": {
"validate": true,
"forceId": false
}
}
},
"acls": [],
"methods": {}
}
* ```
*
* - Definition of Account model:
* ```json
* {
"name": "Account",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"name": {
"type": "string"
},
"balance": {
"type": "number"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
* ```
*
* On the bootscript, create a customer instance and for that customer instance reference many account instances.
*
* For example:
* ```javascript
* var Customer = app.models.Customer;
var accounts = [
{
name: 'Checking',
balance: 5000
},
{
name: 'Saving',
balance: 2000
}
];
Customer.create({name: 'Mary Smith'}, function(err, customer) {
console.log('Customer:', customer);
async.each(accounts, function(account, done) {
customer.accounts.create(account, done);
}, function(err) {
console.log('Customer with accounts:', customer);
customer.accounts(console.log);
cb(err);
});
});
* ```
*
* Sample referencesMany model data:
* ```javascript
* {
id: 1,
name: 'John Smith',
accounts: [
"saving-01", "checking-01",
]
}
* ```
*
* Supported helper methods:
* - customer.accounts()
* - customer.accounts.create()
* - customer.accounts.build()
* - customer.accounts.findById()
* - customer.accounts.destroy()
* - customer.accounts.updateById()
* - customer.accounts.exists()
* - customer.accounts.add()
* - customer.accounts.remove()
* - customer.accounts.at()
*
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {Any} default The default value.
* @property {Object} options Options to specify for the relationship.
* @property {Boolean} options.forceId Force generation of id for embedded items. Default is false.
* @property {Boolean} options.validate Denote if the embedded items should be validated. Default is true.
* @property {Boolean} options.persistent Denote if the embedded items should be persisted. Default is false.
* @property {Object|Function} scope Explicitly define additional scopes.
* @property {String} foreignKey Property name of foreign key field.
* @property {Object} properties Properties inherited from the parent object.
* @property {Function} methods Scoped methods for the given relation.
*/
RelationMixin.referencesMany = function referencesMany(modelTo, params) {
return RelationDefinition.referencesMany(this, modelTo, params);
};
/**
* Represent a model that embeds another model.
*
* For example, a Customer embeds one billingAddress from the Address model.
*
* - Define the relation in bootscript:
* ```js
* Customer.embedsOne(Address, {
* as: 'address', // default to the relation name - address
* property: 'billingAddress' // default to addressItem
* });
* ```
*
* OR, define the relation in the model definition:
*
* - Definition of Customer model:
* ```json
* {
"name": "Customer",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number"
}
},
"validations": [],
"relations": {
"address": {
"type": "embedsOne",
"model": "Address",
"property": "billingAddress",
"options": {
"validate": true,
"forceId": false
}
}
},
"acls": [],
"methods": {}
}
* ```
*
* - Definition of Address model:
* ```json
* {
"name": "Address",
"base": "Model",
"idInjection": true,
"properties": {
"street": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
},
"zipCode": {
"type": "string"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
* ```
*
* Sample embedded model data:
* ```javascript
* {
id: 1,
name: 'John Smith',
billingAddress: {
street: '123 Main St',
city: 'San Jose',
state: 'CA',
zipCode: '95124'
}
}
* ```
*
* Supported helper methods:
* - customer.address()
* - customer.address.build()
* - customer.address.create()
* - customer.address.update()
* - customer.address.destroy()
* - customer.address.value()
*
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} property Name of the property for the embedded item.
* @property {Any} default The default value.
* @property {Object} options Options to specify for the relationship.
* @property {Boolean} options.forceId Force generation of id for embedded items. Default is false.
* @property {Boolean} options.validate Denote if the embedded items should be validated. Default is true.
* @property {Boolean} options.persistent Denote if the embedded items should be persisted. Default is false.
* @property {Object|Function} scope Explicitly define additional scopes.
* @property {Object} properties Properties inherited from the parent object.
* @property {Function} methods Scoped methods for the given relation.
*/
RelationMixin.embedsOne = function embedsOne(modelTo, params) {
return RelationDefinition.embedsOne(this, modelTo, params);
};
/**
* Represent a model that can embed many instances of another model.
*
* For example, a Customer can have multiple email addresses and each email address is a complex object that contains label and address.
*
* Define the relation code in bootscript:
* ```javascript
Customer.embedsMany(EmailAddress, {
as: 'emails', // default to the relation name - emailAddresses
property: 'emailList' // default to emailAddressItems
});
* ```
*
* OR, define the relation in the model definition:
*
* - Definition of Customer model:
* ```json
* {
"name": "Customer",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number"
}
},
"validations": [],
"relations": {
"emails": {
"type": "embedsMany",
"model": "EmailAddress",
"property": "emailList",
"options": {
"validate": true,
"forceId": false
}
}
},
"acls": [],
"methods": {}
}
* ```
*
* - Definition of EmailAddress model:
* ```json
* {
"name": "EmailAddress",
"base": "Model",
"idInjection": true,
"properties": {
"label": {
"type": "string"
},
"address": {
"type": "string"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
* ```
*
* Sample embedded model data:
* ```javascript
* {
id: 1,
name: 'John Smith',
emails: [{
label: 'work',
address: 'john@xyz.com'
}, {
label: 'home',
address: 'john@gmail.com'
}]
}
* ```
*
* Supported helper methods:
* - customer.emails()
* - customer.emails.create()
* - customer.emails.build()
* - customer.emails.findById()
* - customer.emails.destroyById()
* - customer.emails.updateById()
* - customer.emails.exists()
* - customer.emails.add()
* - customer.emails.remove()
* - customer.emails.at()
* - customer.emails.value()
*
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} property Name of the property for the embedded item.
* @property {Any} default The default value.
* @property {Object} options Options to specify for the relationship.
* @property {Boolean} options.forceId Force generation of id for embedded items. Default is false.
* @property {Boolean} options.validate Denote if the embedded items should be validated. Default is true.
* @property {Boolean} options.persistent Denote if the embedded items should be persisted. Default is false.
* @property {String} polymorphic Define a polymorphic relation name.
* @property {Object|Function} scope Explicitly define additional scopes.
* @property {Object} properties Properties inherited from the parent object.
* @property {Function} methods Scoped methods for the given relation.
*/
RelationMixin.embedsMany = function embedsMany(modelTo, params) {
return RelationDefinition.embedsMany(this, modelTo, params);
};

View File

@ -24,35 +24,9 @@ function TransactionMixin() {
}
/**
* Begin a new transaction
* @param {Object|String} [options] Options can be one of the forms:
* - Object: {isolationLevel: '...', timeout: 1000}
* - String: isolationLevel
* Begin a new transaction.
*
* Valid values of `isolationLevel` are:
*
* - Transaction.READ_COMMITTED = 'READ COMMITTED'; // default
* - Transaction.READ_UNCOMMITTED = 'READ UNCOMMITTED';
* - Transaction.SERIALIZABLE = 'SERIALIZABLE';
* - Transaction.REPEATABLE_READ = 'REPEATABLE READ';
*
* @param {Function} cb Callback function. It calls back with (err, transaction).
* To pass the transaction context to one of the CRUD methods, use the `options`
* argument with `transaction` property, for example,
*
* ```js
*
* MyModel.beginTransaction('READ COMMITTED', function(err, tx) {
* MyModel.create({x: 1, y: 'a'}, {transaction: tx}, function(err, inst) {
* MyModel.find({x: 1}, {transaction: tx}, function(err, results) {
* // ...
* tx.commit(function(err) {...});
* });
* });
* });
* ```
*
* The transaction can be committed or rolled back. If timeout happens, the
* A transaction can be committed or rolled back. If timeout happens, the
* transaction will be rolled back. Please note a transaction is typically
* associated with a pooled connection. Committing or rolling back a transaction
* will release the connection back to the pool.
@ -65,6 +39,36 @@ function TransactionMixin() {
* source/connector instance. CRUD methods will not join the current transaction
* if its model is not attached the same data source.
*
* Example:
*
* To pass the transaction context to one of the CRUD methods, use the `options`
* argument with `transaction` property, for example,
*
* ```js
* MyModel.beginTransaction('READ COMMITTED', function(err, tx) {
* MyModel.create({x: 1, y: 'a'}, {transaction: tx}, function(err, inst) {
* MyModel.find({x: 1}, {transaction: tx}, function(err, results) {
* // ...
* tx.commit(function(err) {...});
* });
* });
* });
* ```
*
* @param {Object|String} options Options to be passed upon transaction.
*
* Can be one of the forms:
* - Object: {isolationLevel: '...', timeout: 1000}
* - String: isolationLevel
*
* Valid values of `isolationLevel` are:
*
* - Transaction.READ_COMMITTED = 'READ COMMITTED'; // default
* - Transaction.READ_UNCOMMITTED = 'READ UNCOMMITTED';
* - Transaction.SERIALIZABLE = 'SERIALIZABLE';
* - Transaction.REPEATABLE_READ = 'REPEATABLE READ';
* @callback {Function} cb Callback function.
* @returns {Promise|undefined} Returns a callback promise.
*/
TransactionMixin.beginTransaction = function(options, cb) {
cb = cb || utils.createPromiseCallback();
@ -107,9 +111,22 @@ TransactionMixin.beginTransaction = function(options, cb) {
if (Transaction) {
jutil.mixin(Transaction.prototype, ObserverMixin);
/**
* Commit a transaction and release it back to the pool
* @param {Function} cb Callback function
* @returns {Promise|undefined}
* Commit a transaction and release it back to the pool.
*
* Example:
*
* ```js
* MyModel.beginTransaction('READ COMMITTED', function(err, tx) {
* // some crud operation of your choice
* tx.commit(function(err) {
* // release the connection pool upon committing
* tx.close(err);
* });
* });
* ```
*
* @callback {Function} cb Callback function.
* @returns {Promise|undefined} Returns a callback promise.
*/
Transaction.prototype.commit = function(cb) {
var self = this;
@ -141,9 +158,22 @@ if (Transaction) {
};
/**
* Rollback a transaction and release it back to the pool
* @param {Function} cb Callback function
* @returns {Promise|undefined}
* Rollback a transaction and release it back to the pool.
*
* Example:
*
* ```js
* MyModel.beginTransaction('READ COMMITTED', function(err, tx) {
* // some crud operation of your choice
* tx.rollback(function(err) {
* // release the connection pool upon committing
* tx.close(err);
* });
* });
* ```
*
* @callback {Function} cb Callback function.
* @returns {Promise|undefined} Returns a callback promise.
*/
Transaction.prototype.rollback = function(cb) {
var self = this;

View File

@ -32,6 +32,7 @@ function Validatable() {
/**
* Validate presence of one or more specified properties.
*
* Requires a model to include a property to be considered valid; fails when validated field is blank.
*
* For example, validate presence of title
@ -48,34 +49,43 @@ function Validatable() {
* ```
*
* @param {String} propertyName One or more property names.
* @options {Object} errMsg Optional custom error message. Default is "can't be blank"
* @options {Object} options Configuration parameters; see below.
* @property {String} message Error message to use instead of default.
* @property {String} if Validate only if `if` exists.
* @property {String} unless Validate only if `unless` exists.
*/
Validatable.validatesPresenceOf = getConfigurator('presence');
/**
* Validate absence of one or more specified properties.
* A model should not include a property to be considered valid; fails when validated field not blank.
*
* A model should not include a property to be considered valid; fails when validated field is not blank.
*
* For example, validate absence of reserved
* ```
* Post.validatesAbsenceOf('reserved', { unless: 'special' });
* ```
*
* @param {String} propertyName One or more property names.
* @options {Object} errMsg Optional custom error message. Default is "can't be set"
* @options {Object} options Configuration parameters; see below.
* @property {String} message Error message to use instead of default.
* @property {String} if Validate only if `if` exists.
* @property {String} unless Validate only if `unless` exists.
*/
Validatable.validatesAbsenceOf = getConfigurator('absence');
/**
* Validate length. Require a property length to be within a specified range.
* Three kinds of validations: min, max, is.
* Validate length.
*
* Require a property length to be within a specified range.
*
* There are three kinds of validations: min, max, is.
*
* Default error messages:
*
* - min: too short
* - max: too long
* - is: length is wrong
* - is: length is wrong
*
* Example: length validations
* ```
@ -89,36 +99,42 @@ Validatable.validatesAbsenceOf = getConfigurator('absence');
* User.validatesLengthOf('password', {min: 7, message: {min: 'too weak'}});
* User.validatesLengthOf('state', {is: 2, message: {is: 'is not valid state name'}});
* ```
*
* @param {String} propertyName Property name to validate.
* @options {Object} Options See below.
* @options {Object} options Configuration parameters; see below.
* @property {Number} is Value that property must equal to validate.
* @property {Number} min Value that property must be less than to be valid.
* @property {Number} max Value that property must be less than to be valid.
* @property {Object} message Optional Object with string properties for custom error message for each validation: is, min, or max
* @property {Object} message Optional object with string properties for custom error message for each validation: is, min, or max.
*/
Validatable.validatesLengthOf = getConfigurator('length');
/**
* Validate numericality. Requires a value for property to be either an integer or number.
* Validate numericality.
*
* Requires a value for property to be either an integer or number.
*
* Example
* ```
* User.validatesNumericalityOf('age', { message: { number: '...' }});
* User.validatesNumericalityOf('age', {int: true, message: { int: '...' }});
* User.validatesNumericalityOf('age', { message: { number: 'is not a number' }});
* User.validatesNumericalityOf('age', {int: true, message: { int: 'is not an integer' }});
* ```
*
* @param {String} propertyName Property name to validate.
* @options {Object} Options See below.
* @options {Object} options Configuration parameters; see below.
* @property {Boolean} int If true, then property must be an integer to be valid.
* @property {Object} message Optional object with string properties for 'int' for integer validation. Default error messages:
*
* @property {Boolean} allowBlank Allow property to be blank.
* @property {Boolean} allowNull Allow property to be null.
* @property {Object} message Optional object with string properties for 'int' for integer validation. Default error messages:
* - number: is not a number
* - int: is not an integer
*/
Validatable.validatesNumericalityOf = getConfigurator('numericality');
/**
* Validate inclusion in set. Require a value for property to be in the specified array.
* Validate inclusion in set.
*
* Require a value for property to be in the specified array.
*
* Example:
* ```
@ -129,8 +145,8 @@ Validatable.validatesNumericalityOf = getConfigurator('numericality');
* ```
*
* @param {String} propertyName Property name to validate.
* @options {Object} Options See below
* @property {Array} inArray Property must match one of the values in the array to be valid.
* @options {Object} options Configuration parameters; see below.
* @property {Array} in Property must match one of the values in the array to be valid.
* @property {String} message Optional error message if property is not valid.
* Default error message: "is not included in the list".
* @property {Boolean} allowNull Whether null values are allowed.
@ -138,28 +154,31 @@ Validatable.validatesNumericalityOf = getConfigurator('numericality');
Validatable.validatesInclusionOf = getConfigurator('inclusion');
/**
* Validate exclusion. Require a property value not be in the specified array.
* Validate exclusion in a set.
*
* Require a property value not be in the specified array.
*
* Example: `Company.validatesExclusionOf('domain', {in: ['www', 'admin']});`
*
* @param {String} propertyName Property name to validate.
* @options {Object} Options
* @options {Object} options Configuration parameters; see below.
* @property {Array} in Property must not match any of the values in the array to be valid.
* @property {String} message Optional error message if property is not valid. Default error message: "is reserved".
* @property {String} message Optional error message if property is not valid. Default error message: "is reserved".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validatesExclusionOf = getConfigurator('exclusion');
/**
* Validate format. Require a model to include a property that matches the given format.
* Validate format.
*
* Require a model to include a property that matches the given format. Example:
* `User.validatesFormatOf('name', {with: /\w+/});`
* Require a model to include a property that matches the given format.
*
* Example: `User.validatesFormatOf('name', {with: /\w+/});`
*
* @param {String} propertyName Property name to validate.
* @options {Object} Options
* @options {Object} options Configuration parameters; see below.
* @property {RegExp} with Regular expression to validate format.
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validatesFormatOf = getConfigurator('format');
@ -168,7 +187,7 @@ Validatable.validatesFormatOf = getConfigurator('format');
* Validate using custom validation function.
*
* Example:
*
*```javascript
* User.validate('name', customValidator, {message: 'Bad name'});
* function customValidator(err) {
* if (this.name === 'bad') err();
@ -177,11 +196,12 @@ Validatable.validatesFormatOf = getConfigurator('format');
* user.isValid(); // true
* user.name = 'bad';
* user.isValid(); // false
* ```
*
* @param {String} propertyName Property name to validate.
* @param {Function} validatorFn Custom validation function.
* @options {Object} Options See below.
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
* @options {Object} options Configuration parameters; see below.
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validate = getConfigurator('custom');
@ -189,7 +209,6 @@ Validatable.validate = getConfigurator('custom');
/**
* Validate using custom asynchronous validation function.
*
*
* Example:
*```js
* User.validateAsync('name', customValidator, {message: 'Bad name'});
@ -209,17 +228,19 @@ Validatable.validate = getConfigurator('custom');
* user.isValid(function (isValid) {
* isValid; // false
* })
*```
* ```
*
* @param {String} propertyName Property name to validate.
* @param {Function} validatorFn Custom validation function.
* @options {Object} Options See below
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
* @options {Object} options Configuration parameters; see below.
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validateAsync = getConfigurator('custom', {async: true});
/**
* Validate uniqueness. Ensure the value for property is unique in the collection of models.
* Validate uniqueness of the value for a property in the collection of models.
*
* Not available for all connectors. Currently supported with these connectors:
* - In Memory
* - Oracle
@ -233,18 +254,33 @@ Validatable.validateAsync = getConfigurator('custom', {async: true});
* // The login must be unique within each Site.
* SiteUser.validateUniquenessOf('login', { scopedTo: ['siteId'] });
* ```
*
* @param {String} propertyName Property name to validate.
* @options {Object} Options See below.
* @options {Object} options Configuration parameters; see below.
* @property {RegExp} with Regular expression to validate format.
* @property {Array.<String>} scopedTo List of properties defining the scope.
* @property {String} message Optional error message if property is not valid. Default error message: "is not unique".
* @property {String} message Optional error message if property is not valid. Default error message: "is not unique".
* @property {Boolean} allowNull Whether null values are allowed.
* @property {String} ignoreCase Make the validation case insensitive
* @property {String} ignoreCase Make the validation case insensitive.
* @property {String} if Validate only if `if` exists.
* @property {String} unless Validate only if `unless` exists.
*/
Validatable.validatesUniquenessOf = getConfigurator('uniqueness', {async: true});
/**
* Validate if a value for a property is a Date.
*
* Example
* ```
* User.validatesDateOf('today', {message: 'today is not a date!'});
* ```
*
* @param {String} propertyName Property name to validate.
* @options {Object} options Configuration parameters; see below.
* @property {String} message Error message to use instead of default.
*/
Validatable.validatesDateOf = getConfigurator('date');
// implementation of validators
/*!
@ -438,15 +474,15 @@ function getConfigurator(name, opts) {
* NOTE: This method can be called as synchronous only when no asynchronous validation is
* configured. It's strongly recommended to run all validations as asyncronous.
*
* Example: ExpressJS controller: render user if valid, show flash otherwise
* ```
* Example: ExpressJS controller - render user if valid, show flash otherwise
* ```javascript
* user.isValid(function (valid) {
* if (valid) res.render({user: user});
* else res.flash('error', 'User is not valid'), console.log(user.errors), res.redirect('/users');
* });
* ```
* Another example:
* ```
* ```javascript
* user.isValid(function (valid) {
* if (!valid) {
* console.log(user.errors);
@ -458,7 +494,9 @@ function getConfigurator(name, opts) {
* }
* });
* ```
* @param {Function} callback called with (valid)
* @callback {Function} callback Called with (valid).
* @param {Object} data Data to be validated.
* @param {Object} options Options to be specified upon validation.
* @returns {Boolean} True if no asynchronous validation is configured and all properties pass validation.
*/
Validatable.prototype.isValid = function(callback, data, options) {
@ -663,6 +701,7 @@ var defaultMessages = {
/**
* Checks if attribute is undefined or null. Calls err function with 'blank' or 'null'.
* See defaultMessages. You can affect this behaviour with conf.allowBlank and conf.allowNull.
* @private
* @param {String} attr Property name of attribute
* @param {Object} conf conf object for validator
* @param {Function} err
@ -807,6 +846,8 @@ function ErrorCodes(messages) {
* callback(err);
* }
* ```
*
* @private
*/
function ValidationError(obj) {
if (!(this instanceof ValidationError)) return new ValidationError(obj);