不要怂,就是干,撸起袖子干!

Commit 2ce56b09 by Jan Aagaard Meier

Added docs for assocations

1 parent f34afaec
...@@ -32,7 +32,8 @@ module.exports = (function() { ...@@ -32,7 +32,8 @@ module.exports = (function() {
self.instance[instancePrimaryKey] self.instance[instancePrimaryKey]
), ),
new Utils.where( new Utils.where(
through.rawAttributes[self.association.foreignIdentifier], { through.rawAttributes[self.association.foreignIdentifier],
{
join: new Utils.literal([ join: new Utils.literal([
self.QueryInterface.quoteTable(self.association.target.getTableName()), self.QueryInterface.quoteTable(self.association.target.getTableName()),
self.QueryInterface.quoteIdentifier(foreignPrimaryKey) self.QueryInterface.quoteIdentifier(foreignPrimaryKey)
......
...@@ -3,9 +3,83 @@ var Utils = require("./../utils") ...@@ -3,9 +3,83 @@ var Utils = require("./../utils")
, HasMany = require("./has-many") , HasMany = require("./has-many")
, BelongsTo = require("./belongs-to") , BelongsTo = require("./belongs-to")
/* Defines Mixin for all models. */ /**
* Creating assocations in sequelize is done by calling one of the belongsTo / hasOne / hasMany functions
* on a model (the source), and prodiving another model as the first argument to the function (the target).
*
* * hasOne - adds a foreign key to target
* * belongsTo - add a foreign key to source
* * hasMany - adds a foreign key to target, unless you also specifiy that target hasMany source, in which case a junction table is created with sourceId and targetId
*
* Creating an association will add a foreign key constraint to the attributes. All associations use `CASCADE` on update and `SET NULL` on delete, except for n:m, which also uses `CASCADE` on delete.
*
* When creating associations, you can provide an alias, via the `as` option. This is usefull if the same model
* is associated twice, or you want your association to be called something other than the name of the target model.
* As an example, consider the case where users have many pictures, one of which is their profile picture. All pictures
* have a `userId`, but in addition the user model also has a `profilePictureId`, to be able to easily load the user's profile
* picture.
*
* ```js
* User.hasMany(Picture)
* User.belongsTo(Picture, { as: 'ProfilePicture', constraints: false })
*
* user.getPictures() // gets you all pictures
* user.getProfilePicture() // gets you only the profile picture
*
* User.findAll({
* where: ...,
* include: [
* { model: Picture }, // load all pictures
* { model: Picture, as: 'ProfilePicture' }, // load the profile picture. Notice that the spelling must be the exact same as the one in the association
* ]
* })
* ```
*
* When getting associated models, you can limit your query to only load some models. These queries are written in the same way as queries to `find`/`findAll`. To only get pictures in JPG, you can do:
*
* ```js
* user.getPictures({
* where: {
* format: 'jpg'
* }
* })
* ```
*
* In the above we specify that a user belongs to his profile picture. Conceptually, this might not make sense,
* but since we want to add the foreign key to the user model this is the way to do it.
* Note how we also specified `constraints: false` for profile picture. This is because we add a foreign key from
* user to picture (profilePictureId), and from picture to user (userId). If we were to add foreign keys to both, it would
* create a cyclic dependency, and sequelize would not know which table to create first, since user depends on picture, and picture
* depends on user. These kinds of problems are detected by sequelize before the models are synced to the database, and you will
* get an error along the lines of `Error: Cyclic dependency found. 'users' is dependent of itself`. If you encounter this,
* you should either disable some constraints, or rethink your associations completely.
*
* @mixin Associations
*/
var Mixin = module.exports = function(){} var Mixin = module.exports = function(){}
/**
* Creates an association between this (the source) and the provided target. The foreign key is added on the target.
*
* Example: `User.hasOne(Profile)`. This will add userId to the profile table.
*
* The following methods are injected on the source:
*
* * get[AS] - for example getProfile()
* * set[AS] - for example setProfile(instance, options). Options are passed to `target.save`
* * create[AS] - for example createProfile(value, options). Builds and saves a new instance of the associated model. Values and options are passed on to `target.create`
*
* All methods return an event emitter.
*
* @param {DAOFactory} target
* @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
* @param {string} [options.as] The alias of this model. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized version of target.name
* @param {string} [options.foreignKey] The name of the foreign key in the target table. Defaults to the name of source + primary key of source
* @param {string} [options.onDelete='SET NULL']
* @param {string} [options.onUpdate='CASCADE']
* @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
*/
Mixin.hasOne = function(associatedDAOFactory, options) { Mixin.hasOne = function(associatedDAOFactory, options) {
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option) // Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
options = options || {} options = options || {}
...@@ -23,6 +97,28 @@ Mixin.hasOne = function(associatedDAOFactory, options) { ...@@ -23,6 +97,28 @@ Mixin.hasOne = function(associatedDAOFactory, options) {
return this return this
} }
/**
* Creates an association between this (the source) and the provided target. The foreign key is added on the source.
*
* Example: `Profile.belongsTo(User)`. This will add userId to the profile table.
*
* The following methods are injected on the source:
*
* * get[AS] - for example getUser()
* * set[AS] - for example setUser(instance, options). Options are passed to this.save
* * create[AS] - for example createUser(value, options). Builds and saves a new instance of the associated model. Values and options are passed on to target.create
*
* All methods return an event emitter.
*
* @param {DAOFactory} target
* @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
* @param {string} [options.as] The alias of this model. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized version of target.name
* @param {string} [options.foreignKey] The name of the foreign key in the source table. Defaults to the name of target + primary key of target
* @param {string} [options.onDelete='SET NULL']
* @param {string} [options.onUpdate='CASCADE']
* @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
*/
Mixin.belongsTo = function(associatedDAOFactory, options) { Mixin.belongsTo = function(associatedDAOFactory, options) {
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option) // Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
options = options || {} options = options || {}
...@@ -40,6 +136,73 @@ Mixin.belongsTo = function(associatedDAOFactory, options) { ...@@ -40,6 +136,73 @@ Mixin.belongsTo = function(associatedDAOFactory, options) {
return this return this
} }
/**
* Create an association that is either 1:m or n:m.
*
* ```js
* // Create a 1:m association between user and project
* User.hasMany(Project)
* ```
* ```js
* // Create a n:m association between user and project
* User.hasMany(Project)
* Project.hasMany(User)
* ```
* By default, the name of the join table will be source+target, so in this case projectsusers. This can be overridden by providing either a string or a DAOFactory as `through` in the options.
* The following methods are injected on the source:
*
* * get[AS] - for example getPictures().
* * set[AS] - for example setPictures(instances, defaultAttributes|options). Update the associations. All currently associated models that are not in instances will be removed.
* * add[AS] - for example addPicture(instance, defaultAttributes|options). Add another association.
* * create[AS] - for example createPicture(values, options). Build and save a new association.
* * remove[AS] - for example removePicture(instance). Remove a single association
* * has[AS] - for example hasPicture(instance). Is source associated to this target?
* * has[AS] [plural] - for example hasPictures(instances). Is source associated to all these targets?
*
* All methods return an event emitter.
*
* If you use a through model with custom attributes, these attributes can be set when adding / setting new associations in two ways. Consider users and projects from before
* with a join table that stores whether the project has been started yet:
* ```js
* var UserProjects = sequelize.define('userprojects', {
* started: Sequelize.BOOLEAN
* })
* User.hasMany(Project, { through: UserProjects })
* Project.hasMany(User, { through: UserProjects })
* ```
* ```js
* jan.addProject(homework, { started: false }) // The homework project is not started yet
* jan.setProjects([makedinner, doshopping], { started: true}) // Both shopping and dinner has been started
* ```
*
* If you want to set several target instances, but with different attributes you have to set the attributes on the instance, using a property with the name of the through model:
*
* ```js
* p1.userprojects {
* started: true
* }
* user.setProjects([p1, p2], {started: false}) // The default value is false, but p1 overrides that.
* ```
*
* Similarily, when fetching through a join table with custom attributes, these attributes will be available as an object with the name of the through model.
* ```js
* user.getProjects().success(function (projects) {
* var p1 = projects[0]
* p1.userprojects.started // Is this project started yet?
* })
* ```
*
* @param {DAOFactory} target
* @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
* @param {DAOFactory|string} [options.through] The name of the table that is used to join source and target in n:m associations. Can also be a sequelize model if you want to define the junction table yourself and add extra attributes to it.
* @param {string} [options.as] The alias of this model. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized version of target.name
* @param {string} [options.foreignKey] The name of the foreign key in the source table. Defaults to the name of target + primary key of target
* @param {string} [options.onDelete='SET NULL|CASCADE'] Cascade if this is a n:m, and set null if it is a 1:m
* @param {string} [options.onUpdate='CASCADE']
* @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
*/
Mixin.hasMany = function(associatedDAOFactory, options) { Mixin.hasMany = function(associatedDAOFactory, options) {
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option) // Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
options = options || {} options = options || {}
......
...@@ -9,12 +9,11 @@ var Utils = require("./utils") ...@@ -9,12 +9,11 @@ var Utils = require("./utils")
module.exports = (function() { module.exports = (function() {
/** /**
* A DAOFactory represents a table in the database. Sometimes you might also see it refererred to as model, or simply as factory. This class should _not_ be instantiated directly, It is created using `sequelize.define`, and already created models can be loaded using `sequelize.import` * A DAOFactory represents a table in the database. Sometimes you might also see it refererred to as model, or simply as factory. This class should _not_ be instantiated directly, it is created using `sequelize.define`, and already created models can be loaded using `sequelize.import`
* *
* @class DAOFactory * @class DAOFactory
* @mixes {Hooks} * @mixes {Hooks}
* @mixes {Assocations} * @mixes {Assocations}
* @mixes http://nodejs.org/api/events.html#events_class_events_eventemitter
*/ */
var DAOFactory = function(name, attributes, options) { var DAOFactory = function(name, attributes, options) {
this.options = Utils._.extend({ this.options = Utils._.extend({
...@@ -329,7 +328,7 @@ module.exports = (function() { ...@@ -329,7 +328,7 @@ module.exports = (function() {
/** /**
* Sync this DAOFactory to the DB, that is create the table. * Sync this DAOFactory to the DB, that is create the table.
* @see {Sequelize#sync} for options * @see {Sequelize#sync} for options
* @return {EventEmitter} * @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, the callback will be called with the model instance (this)
*/ */
DAOFactory.prototype.sync = function(options) { DAOFactory.prototype.sync = function(options) {
options = Utils._.extend({}, this.options, options || {}) options = Utils._.extend({}, this.options, options || {})
...@@ -360,6 +359,7 @@ module.exports = (function() { ...@@ -360,6 +359,7 @@ module.exports = (function() {
* Drop the table represented by this Model * Drop the table represented by this Model
* @param {Object} [options] * @param {Object} [options]
* @param {Boolean} [options.cascade=false] Also drop all objects depending on this table, such as views. Only works in postgres * @param {Boolean} [options.cascade=false] Also drop all objects depending on this table, such as views. Only works in postgres
* @return {EventEmitter} Fires `success`, `error` and `sql`.
*/ */
DAOFactory.prototype.drop = function(options) { DAOFactory.prototype.drop = function(options) {
return this.QueryInterface.dropTable(this.getTableName(), options) return this.QueryInterface.dropTable(this.getTableName(), options)
...@@ -369,6 +369,14 @@ module.exports = (function() { ...@@ -369,6 +369,14 @@ module.exports = (function() {
return this.QueryInterface.dropSchema(schema) return this.QueryInterface.dropSchema(schema)
} }
/**
* Apply a schema to this model. For postgres, this will actually place the schema in front of the table name - `"schema"."tableName"`, while the schema will be prepended to the table name for mysql and sqlite - `'schema.tablename'`.
*
* @param {String} schema The name of the schema
* @param {Object} [options]
* @param {String} [options.schemaDelimiter='.'] The character(s) that separates the schema name from the table name
* @return this
*/
DAOFactory.prototype.schema = function(schema, options) { DAOFactory.prototype.schema = function(schema, options) {
this.options.schema = schema this.options.schema = schema
...@@ -385,6 +393,11 @@ module.exports = (function() { ...@@ -385,6 +393,11 @@ module.exports = (function() {
return this return this
} }
/**
* Get the tablename of the model, taking schema into account
* @return {String|object} The name as a string if the model has no schema, or an object with `tableName`, `schema` and `delimiter` properties
*/
DAOFactory.prototype.getTableName = function() { DAOFactory.prototype.getTableName = function() {
return this.QueryGenerator.addSchema(this) return this.QueryGenerator.addSchema(this)
} }
...@@ -581,16 +594,16 @@ module.exports = (function() { ...@@ -581,16 +594,16 @@ module.exports = (function() {
* @param {Object} [options] A hash of options to describe the scope of the search * @param {Object} [options] A hash of options to describe the scope of the search
* @param {Object} [options.where] A hash of attributes to describe your search. See above for examples. * @param {Object} [options.where] A hash of attributes to describe your search. See above for examples.
* @param {Array<String>} [options.attributes] A list of the attributes that you want to select * @param {Array<String>} [options.attributes] A list of the attributes that you want to select
* @param {Array<Object|DAOFactory>} [options.include] A list of associations to eagerly load. Supported is either { include: [ DaoFactory1, DaoFactory2, ...] } or { include: [ { model: DaoFactory1, as: 'Alias' } ] }. When using the object form, you can also specify `attributes`, `where` to limit the relations and their columns, and `include` to load further nested relations * @param {Array<Object|DAOFactory>} [options.include] A list of associations to eagerly load. Supported is either { include: [ DaoFactory1, DaoFactory2, ...] } or { include: [ { model: DaoFactory1, as: 'Alias' } ] }. If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in the as attribute when eager loading Y). When using the object form, you can also specify `attributes` to specify what columns to load, `where` to limit the relations, and `include` to load further nested relations
* @param {String|Array|Sequelize.fn} [options.order] Specifies an ordering. If a string is provided, it will be esacped. Using an array, you can provide several columns / functions to order by. Each element can be further wrapped in a two-element array. The first element is the column / function to order by, the second is the direction. For example: `order: [['name', 'DESC']]`. In this way the column will be escaped, but the direction will not. * @param {String|Array|Sequelize.fn} [options.order] Specifies an ordering. If a string is provided, it will be esacped. Using an array, you can provide several columns / functions to order by. Each element can be further wrapped in a two-element array. The first element is the column / function to order by, the second is the direction. For example: `order: [['name', 'DESC']]`. In this way the column will be escaped, but the direction will not.
* @param {Number} [options.limit] * @param {Number} [options.limit]
* @param {Number} [options.offset] * @param {Number} [options.offset]
* @param {Object} [queryOptions] set the query options, e.g. raw, specifying that you want raw data instead of built DAOs. See sequelize.query for options * @param {Object} [queryOptions] Set the query options, e.g. raw, specifying that you want raw data instead of built DAOs. See sequelize.query for options
* @param {Transaction} [queryOptions.transaction] * @param {Transaction} [queryOptions.transaction]
* @param {Boolean} [queryOptions.raw] Returns the results as raw JS objects instead of DAO instances * @param {Boolean} [queryOptions.raw] Returns the results as raw JS objects instead of DAO instances
* *
* @see {Sequelize#query} * @see {Sequelize#query}
* @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, an array of DAOs will be returned to the success listener * @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, an array of DAOs will be passed to the success listener
*/ */
DAOFactory.prototype.findAll = function(options, queryOptions) { DAOFactory.prototype.findAll = function(options, queryOptions) {
var hasJoin = false var hasJoin = false
...@@ -638,7 +651,7 @@ module.exports = (function() { ...@@ -638,7 +651,7 @@ module.exports = (function() {
* @param {Object} [queryOptions] * @param {Object} [queryOptions]
* *
* @see {DAOFactory#findAll} for an explanation of options and queryOptions * @see {DAOFactory#findAll} for an explanation of options and queryOptions
* @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, a DAO will be returned to the success listener * @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, a DAO will be passed to the success listener
*/ */
DAOFactory.prototype.find = function(options, queryOptions) { DAOFactory.prototype.find = function(options, queryOptions) {
var hasJoin = false var hasJoin = false
...@@ -793,8 +806,8 @@ module.exports = (function() { ...@@ -793,8 +806,8 @@ module.exports = (function() {
// result.rows will contain rows 13 through 24, while result.count will return the total number of rows that matched your query // result.rows will contain rows 13 through 24, while result.count will return the total number of rows that matched your query
* }) * })
* ``` * ```
* @param {Object} [findOptions] * @param {Object} [findOptions] See findAll
* @param {Object} [queryOptions] * @param {Object} [queryOptions] See Sequelize.query
* *
* @see {DAOFactory#findAll} for a specification of find and query options * @see {DAOFactory#findAll} for a specification of find and query options
* @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, an object containing rows and count will be returned * @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, an object containing rows and count will be returned
...@@ -835,7 +848,7 @@ module.exports = (function() { ...@@ -835,7 +848,7 @@ module.exports = (function() {
* Find the maximum value of field * Find the maximum value of field
* *
* @param {String} field * @param {String} field
* @param {Object} options * @param {Object} [options] See aggregate
* @see {DAOFactory#aggregate} for options * @see {DAOFactory#aggregate} for options
* *
* @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, the maximum value of field will be returned to the listener. * @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, the maximum value of field will be returned to the listener.
...@@ -848,7 +861,7 @@ module.exports = (function() { ...@@ -848,7 +861,7 @@ module.exports = (function() {
* Find the minimum value of field * Find the minimum value of field
* *
* @param {String} field * @param {String} field
* @param {Object} options * @param {Object} [options] See aggregate
* @see {DAOFactory#aggregate} for options * @see {DAOFactory#aggregate} for options
* *
* @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, the minimum value of field will be returned to the listener. * @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, the minimum value of field will be returned to the listener.
...@@ -861,7 +874,7 @@ module.exports = (function() { ...@@ -861,7 +874,7 @@ module.exports = (function() {
* Find the sun of field * Find the sun of field
* *
* @param {String} field * @param {String} field
* @param {Object} options * @param {Object} [options] See aggregate
* @see {DAOFactory#aggregate} for options * @see {DAOFactory#aggregate} for options
* *
* @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, the value of the summation over field will be returned to the listener. * @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, the value of the summation over field will be returned to the listener.
...@@ -935,7 +948,7 @@ module.exports = (function() { ...@@ -935,7 +948,7 @@ module.exports = (function() {
* @param {Array} [options.include] an array of include options - Used to build prefetched/included model instances * @param {Array} [options.include] an array of include options - Used to build prefetched/included model instances
* @param {Transaction} [options.transaction] * @param {Transaction} [options.transaction]
* *
* @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, the DAO will be return to the success listener * @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, the DAO will be passed to the success listener
*/ */
DAOFactory.prototype.create = function(values, fieldsOrOptions) { DAOFactory.prototype.create = function(values, fieldsOrOptions) {
Utils.validateParameter(values, Object, { optional: true }) Utils.validateParameter(values, Object, { optional: true })
...@@ -1374,15 +1387,16 @@ module.exports = (function() { ...@@ -1374,15 +1387,16 @@ module.exports = (function() {
} }
/** /**
* Update multiple instances * Update multiple instances that match the where options.
* *
* @param {Object} attrValueHash A hash of fields to change and their new values * @param {Object} attrValueHash A hash of fields to change and their new values
* @param {Object where Options to describe the scope of the search. Note that these options are not wrapped in a { where: ... } is in find / findAll calls etc. This is probably due to change in 2.0 * @param {Object where Options to describe the scope of the search. Note that these options are not wrapped in a { where: ... } is in find / findAll calls etc. This is probably due to change in 2.0
* @param {Object} options * @param {Object} [options]
* @param {Boolean} [options.validate=true] Should each row be subject to validation before it is inserted. The whole insert will fail if one row fails validation * @param {Boolean} [options.validate=true] Should each row be subject to validation before it is inserted. The whole insert will fail if one row fails validation
* @param {Boolean} [options.hooks=false] Run before / after bulkUpdate hooks? * @param {Boolean} [options.hooks=false] Run before / after bulkUpdate hooks?
* @deprecated The syntax is due for change, in order to make `where` more consistent with the rest of the API
* *
* @return {EventEmitter} A promise which fires `success`, `error` and `sql`. * @return {EventEmitter} Fires `success`, `error` and `sql`.
*/ */
DAOFactory.prototype.update = function(attrValueHash, where, options) { DAOFactory.prototype.update = function(attrValueHash, where, options) {
var self = this var self = this
...@@ -1518,7 +1532,7 @@ module.exports = (function() { ...@@ -1518,7 +1532,7 @@ module.exports = (function() {
/** /**
* Run a describe query on the table * Run a describe query on the table
* *
* @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, a hash of attributes and their types will be returned * @return {EventEmitter} Fires `success`, `error` and `sql`. Upon success, a hash of attributes and their types will be passed to the success callback.
*/ */
DAOFactory.prototype.describe = function(schema) { DAOFactory.prototype.describe = function(schema) {
return this.QueryInterface.describeTable(this.tableName, schema || this.options.schema || undefined) return this.QueryInterface.describeTable(this.tableName, schema || this.options.schema || undefined)
......
...@@ -11,7 +11,7 @@ module.exports = (function() { ...@@ -11,7 +11,7 @@ module.exports = (function() {
/** /**
* This class represents an single instance, a database row. You might see it referred to as both DAO and instance. * This class represents an single instance, a database row. You might see it referred to as both DAO and instance.
* *
* DAO instances operate with the concept of a `dataValues` property, which stores the actual values represented by this DAO. By default, the values from dataValues can also be accessed directly from the DAO, that is: * DAO instances operate with the concept of a `dataValues` property, which stores the actual values represented by the instance. By default, the values from dataValues can also be accessed directly from the DAO, that is:
* ```js * ```js
* instance.field * instance.field
* // is the same as * // is the same as
...@@ -21,7 +21,7 @@ module.exports = (function() { ...@@ -21,7 +21,7 @@ module.exports = (function() {
* ``` * ```
* However, if getters and/or setters are defined for `field` they will be invoked, instead of returning the value from `dataValues`. * However, if getters and/or setters are defined for `field` they will be invoked, instead of returning the value from `dataValues`.
* *
* @see {Sequelize#define} Sequelize#define for more information about getters and setters * @see {Sequelize#define} for more information about getters and setters
* @class DAO * @class DAO
*/ */
var DAO = function(values, options) { var DAO = function(values, options) {
...@@ -54,7 +54,7 @@ module.exports = (function() { ...@@ -54,7 +54,7 @@ module.exports = (function() {
* A reference to the sequelize instance * A reference to the sequelize instance
* @see {Sequelize} * @see {Sequelize}
* @property sequelize * @property sequelize
* @return the sequelize instance * @return {Sequelize}
*/ */
Object.defineProperty(DAO.prototype, 'sequelize', { Object.defineProperty(DAO.prototype, 'sequelize', {
get: function(){ return this.Model.daoFactoryManager.sequelize } get: function(){ return this.Model.daoFactoryManager.sequelize }
...@@ -82,7 +82,7 @@ module.exports = (function() { ...@@ -82,7 +82,7 @@ module.exports = (function() {
}) })
/** /**
* Get the values of this DAO. Proxies to this.get * Get the values of this DAO. Proxies to `this.get`
* @see {DAO#get} * @see {DAO#get}
* @property values * @property values
* @return {Object} * @return {Object}
...@@ -94,7 +94,7 @@ module.exports = (function() { ...@@ -94,7 +94,7 @@ module.exports = (function() {
}) })
/** /**
* A getter for this.changed() * A getter for `this.changed()`
* *
* @see {DAO#changed} * @see {DAO#changed}
* @property isDirty * @property isDirty
...@@ -109,7 +109,7 @@ module.exports = (function() { ...@@ -109,7 +109,7 @@ module.exports = (function() {
/** /**
* Get the values of the primary keys of this instance. * Get the values of the primary keys of this instance.
* @property primaryKeyValues * @property primaryKeyValues
* @return {Object} The values of thep primary keys for this DAO * @return {Object} The values of the primary keys for this DAO
*/ */
Object.defineProperty(DAO.prototype, 'primaryKeyValues', { Object.defineProperty(DAO.prototype, 'primaryKeyValues', {
get: function() { get: function() {
...@@ -161,6 +161,7 @@ module.exports = (function() { ...@@ -161,6 +161,7 @@ module.exports = (function() {
* If no key is given, returns all values of the instance. * If no key is given, returns all values of the instance.
* *
* If key is given and a field or virtual getter is present for the key it will call the getter with key - else it will return the value for key. * If key is given and a field or virtual getter is present for the key it will call the getter with key - else it will return the value for key.
*
* @param {String} [key] * @param {String} [key]
* @return {Object|any} If no key is given, all values will be returned as a hash. If key is given, the value of that column / virtual getter will be returned * @return {Object|any} If no key is given, all values will be returned as a hash. If key is given, the value of that column / virtual getter will be returned
*/ */
...@@ -193,17 +194,22 @@ module.exports = (function() { ...@@ -193,17 +194,22 @@ module.exports = (function() {
} }
/** /**
* If values is an object and raw is true and no includes are in the Instance options, set will set dataValues to values, or extend it if it exists. * Set is used to update values on the instance (the sequelize representation of the instance that is, remember that nothing will be persisted before you actually call `save`).
* Else values will be looped and a recursive set will be kalled with key and value from the hash. * In its most basic form set will update a value stored in the underlying `dataValues` object. However, if a custom setter function is defined for the key, that function
* If set is called with a key and a value and the key matches an include option, it will build the child include with the value. * will be called instead. To bypass the setter, you can pass `raw: true` in the options object.
* If raw is false and a field or virtual setter is present for the key, it will call the setter with (value, key) - else it will set instance value for key to value. *
* If set is called with an object, it will loop over the object, and call set recursively for each key, value pair. If you set raw to true, the underlying dataValues will either be
* set directly to the object passed, or used to extend dataValues, it dataValues already contain values.
*
* Set can also be used to build instances for associations, if you have values for those. TODO - mick should probably write something here about how includes in set works - perhaps also even some tests?
* *
* @see {DAOFactory#find} DAOFactory#find for more information about includes * @see {DAOFactory#find} for more information about includes
* @param {String|Object} key(s) * @param {String|Object} key
* @param {any} value * @param {any} value
* @param {Object} [options] * @param {Object} [options]
* @param {Boolean} [options.raw=false] If set to true, field and virtual setters will be ignored * @param {Boolean} [options.raw=false] If set to true, field and virtual setters will be ignored
* @param {Boolean} [options.reset] TODO ???? * @param {Boolean} [options.reset=false] Clear all previously set data values
* @param {Object} [options.include]
*/ */
DAO.prototype.set = function (key, value, options) { DAO.prototype.set = function (key, value, options) {
var values var values
...@@ -378,12 +384,12 @@ module.exports = (function() { ...@@ -378,12 +384,12 @@ module.exports = (function() {
/** /**
* Persist this instance to the database * Persist this instance to the database
* *
* @param {Array} [fields] An optional array of string, representing database columns. If fields is provided, only those columns will be saved. * @param {Array} [fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be saved.
* @param {Object} [options] * @param {Object} [options]
* @param {Object} [options.fields] An alternative way of setting which fields should be persisted * @param {Object} [options.fields] An alternative way of setting which fields should be persisted
* @param {Transaction} [options.transaction] * @param {Transaction} [options.transaction]
* @return {EventEmitter} *
* @fires error, success, sql * @return {EventEmitter} Fires `success`, `error` and `sql`. On success, the callback will be called with this instance. On validation error, the callback will be called with an Error instance. This error will have a property for each of the fields for which validation failed, with the error message for that field.
*/ */
DAO.prototype.save = function(fieldsOrOptions, options) { DAO.prototype.save = function(fieldsOrOptions, options) {
if (fieldsOrOptions instanceof Array) { if (fieldsOrOptions instanceof Array) {
...@@ -558,11 +564,11 @@ module.exports = (function() { ...@@ -558,11 +564,11 @@ module.exports = (function() {
/* /*
* Refresh the current instance in-place, i.e. update the object with current data from the DB and return the same object. * Refresh the current instance in-place, i.e. update the object with current data from the DB and return the same object.
* This is different from doing a `find(DAO.id)`, because that would create and return a new object. With this method, * This is different from doing a `find(DAO.id)`, because that would create and return a new instance. With this method,
* all references to the DAO are updated with the new data and no new objects are created. * all references to the DAO are updated with the new data and no new objects are created.
* *
* @see {DAO#find} * @see {DAOFactory#find}
* @param {Object} [options] Options that are passed on to DAO#find * @param {Object} [options] Options that are passed on to `DAOFactory.find`
* @return {EventEmitter} * @return {EventEmitter}
* @fires error, success, sql * @fires error, success, sql
*/ */
...@@ -589,21 +595,18 @@ module.exports = (function() { ...@@ -589,21 +595,18 @@ module.exports = (function() {
} }
/* /*
* Validate this dao's attribute values according to validation rules set in the dao definition. * Validate the attribute of this instance according to validation rules set in the model definition.
* *
* @param {Object} [options] Options that are passed to the validator * @param {Object} [options] Options that are passed to the validator
* @param {Array} [options.skip] An array of strings. All properties that are in this array will not be validated * @param {Array} [options.skip] An array of strings. All properties that are in this array will not be validated
* @return {Object|null} null if and only if validation successful; otherwise an object containing { field name : [error msgs] } entries. * @see {DAOValidator}
*
* @return {Object|null} null if and only if validation successful; otherwise an Error instance containing { field name : [error msgs] } entries.
*/ */
DAO.prototype.validate = function(options) { DAO.prototype.validate = function(options) {
return new DaoValidator(this, options).validate() return new DaoValidator(this, options).validate()
} }
/*
* Validate this dao's attribute values according to validation rules set in the dao definition.
*
* @return CustomEventEmitter with null if validation successful; otherwise an object containing { field name : [error msgs] } entries.
*/
DAO.prototype.hookValidate = function(object) { DAO.prototype.hookValidate = function(object) {
var validator = new DaoValidator(this, object) var validator = new DaoValidator(this, object)
...@@ -611,7 +614,7 @@ module.exports = (function() { ...@@ -611,7 +614,7 @@ module.exports = (function() {
} }
/** /**
* This is the same as calling setAttributes, then calling save * This is the same as calling `setAttributes`, then calling `save`.
* *
* @see {DAO#setAttributes} * @see {DAO#setAttributes}
* @see {DAO#save} * @see {DAO#save}
...@@ -631,24 +634,22 @@ module.exports = (function() { ...@@ -631,24 +634,22 @@ module.exports = (function() {
} }
/** /**
* Update multiple attributes at once. The values are updated by calling set * Update multiple attributes at once. The values are updated by calling set for each `key, value` pair of the updates object.
* *
* @see {DAO#set} * @see {DAO#set}
* @param {Object} updates A hash of values to update * @param {Object} updates A hash of values to update
* @return {EventEmitter}
* @fires error, success, sql
*/ */
DAO.prototype.setAttributes = function(updates) { DAO.prototype.setAttributes = function(updates) {
this.set(updates) this.set(updates)
} }
/** /**
* Destroy the column corresponding to this DAO object. Depending on your setting for paranoid, the row will either be completely deleted, or have its deletedAt timestamp set to the current timestamp. * Destroy the row corresponding to this instance. Depending on your setting for paranoid, the row will either be completely deleted, or have its deletedAt timestamp set to the current time.
* *
* @param {Object} [options={}] * @param {Object} [options={}]
* @param {Boolean} [options.force=false] If set to true, paranoid models will actually be deleted * @param {Boolean} [options.force=false] If set to true, paranoid models will actually be deleted
* @return {EventEmitter} *
* @fires error, success, sql * @return {EventEmitter} Fires `success`, `error` and `sql`.
*/ */
DAO.prototype.destroy = function(options) { DAO.prototype.destroy = function(options) {
options = options || {} options = options || {}
...@@ -698,9 +699,9 @@ module.exports = (function() { ...@@ -698,9 +699,9 @@ module.exports = (function() {
* query. To get the correct value after an increment into the DAO you should do a reload. * query. To get the correct value after an increment into the DAO you should do a reload.
* *
*```js *```js
* instance.increment('number') increment number by 1 * instance.increment('number') // increment number by 1
* instance.increment(['number', 'count'], { by: 2 }) increment number and count by 2 * instance.increment(['number', 'count'], { by: 2 }) // increment number and count by 2
* instance.increment({ answer: 42, tries: 1}, { by: 1 }) increment answer by 42, and tries by 1. `by` is ignore, since each column has its own value * instance.increment({ answer: 42, tries: 1}, { by: 1 }) // increment answer by 42, and tries by 1. `by` is ignore, since each column has its own value
* ``` * ```
* *
* @see {DAO#reload} * @see {DAO#reload}
...@@ -708,8 +709,8 @@ module.exports = (function() { ...@@ -708,8 +709,8 @@ module.exports = (function() {
* @param {Object} [options] * @param {Object} [options]
* @param {Integer} [options.by=1] The number to increment by * @param {Integer} [options.by=1] The number to increment by
* @param {Transaction} [options.transaction=null] * @param {Transaction} [options.transaction=null]
* @return {EventEmitter} *
* @fires error, success, sql * @return {EventEmitter} Fires `success`, `error` and `sql`.
*/ */
DAO.prototype.increment = function(fields, countOrOptions) { DAO.prototype.increment = function(fields, countOrOptions) {
Utils.validateParameter(countOrOptions, Object, { Utils.validateParameter(countOrOptions, Object, {
...@@ -758,9 +759,9 @@ module.exports = (function() { ...@@ -758,9 +759,9 @@ module.exports = (function() {
* query. To get the correct value after an decrement into the DAO you should do a reload. * query. To get the correct value after an decrement into the DAO you should do a reload.
* *
* ```js * ```js
* instance.decrement('number') decrement number by 1 * instance.decrement('number') // decrement number by 1
* instance.decrement(['number', 'count'], { by: 2 }) decrement number and count by 2 * instance.decrement(['number', 'count'], { by: 2 }) // decrement number and count by 2
* instance.decrement({ answer: 42, tries: 1}, { by: 1 }) decrement answer by 42, and tries by 1. `by` is ignore, since each column has its own value * instance.decrement({ answer: 42, tries: 1}, { by: 1 }) // decrement answer by 42, and tries by 1. `by` is ignore, since each column has its own value
* ``` * ```
* *
* @see {DAO#reload} * @see {DAO#reload}
...@@ -768,8 +769,8 @@ module.exports = (function() { ...@@ -768,8 +769,8 @@ module.exports = (function() {
* @param {Object} [options] * @param {Object} [options]
* @param {Integer} [options.by=1] The number to decrement by * @param {Integer} [options.by=1] The number to decrement by
* @param {Transaction} [options.transaction=null] * @param {Transaction} [options.transaction=null]
* @return {EventEmitter} *
* @fires error, success, sql * @return {EventEmitter} Fires `success`, `error` and `sql`.
*/ */
DAO.prototype.decrement = function (fields, countOrOptions) { DAO.prototype.decrement = function (fields, countOrOptions) {
Utils.validateParameter(countOrOptions, Object, { Utils.validateParameter(countOrOptions, Object, {
...@@ -838,10 +839,10 @@ module.exports = (function() { ...@@ -838,10 +839,10 @@ module.exports = (function() {
} }
/** /**
* Convert the DAO to a JSON representation. Proxies to `get` * Convert the instance to a JSON representation. Proxies to calling `get` with no keys. This means get all values gotten from the DB, and apply all custom getters.
* *
* @see {DAO#get} * @see {DAO#get}
* @return {Object} The properties of the DAO * @return {Object} The JSON representation of this instance
*/ */
DAO.prototype.toJSON = function() { DAO.prototype.toJSON = function() {
return this.get(); return this.get();
......
...@@ -13,6 +13,34 @@ var bindToProcess = function(fct) { ...@@ -13,6 +13,34 @@ var bindToProcess = function(fct) {
} }
module.exports = (function() { module.exports = (function() {
/**
* Sequelize's custom event emitter class, returned by all asynchronous calls.
* The emitter provides a lovely mix of eventemitter and promise methods.
*
* There are several different syntaxes for attach a listener to the emitter:
*
* ```js
* Model.find(...).on('success', function (dao) {
* // Using it as a regular node emitter
* })
*
* Model.find(...).success(function (dao) {
* // Using the shortcut methods
* })
*
* Model.find(...).done(function (err, dao) {
* // Using the done method, which is called both if the operation succeeds, and if it fails. On success, the err argument will be null
* })
*
* Model.find(...).then(function (dao) {
* // Using the emitter as a promise. The first function is the success handler, and the second is the error handler.
* }, function (err) {
*
* })
*
* ```
* @class EventEmitter
*/
var CustomEventEmitter = function(fct) { var CustomEventEmitter = function(fct) {
this.fct = bindToProcess(fct) this.fct = bindToProcess(fct)
} }
...@@ -62,14 +90,8 @@ module.exports = (function() { ...@@ -62,14 +90,8 @@ module.exports = (function() {
}; };
/** /**
Shortcut methods (success, ok) for listening for success events. * Shortcut methods (success, ok) for listening for success events.
*/
Params:
- fct: A function that gets executed once the *success* event was triggered.
Result:
The function returns the instance of the query.
*/
CustomEventEmitter.prototype.success = CustomEventEmitter.prototype.success =
CustomEventEmitter.prototype.ok = CustomEventEmitter.prototype.ok =
function(fct) { function(fct) {
...@@ -79,12 +101,6 @@ module.exports = (function() { ...@@ -79,12 +101,6 @@ module.exports = (function() {
/** /**
Shortcut methods (failure, fail, error) for listening for error events. Shortcut methods (failure, fail, error) for listening for error events.
Params:
- fct: A function that gets executed once the *error* event was triggered.
Result:
The function returns the instance of the query.
*/ */
CustomEventEmitter.prototype.failure = CustomEventEmitter.prototype.failure =
CustomEventEmitter.prototype.fail = CustomEventEmitter.prototype.fail =
...@@ -94,6 +110,9 @@ module.exports = (function() { ...@@ -94,6 +110,9 @@ module.exports = (function() {
return this; return this;
} }
/**
Shortcut methods (done, complete) for listening for both success and error events.
*/
CustomEventEmitter.prototype.done = CustomEventEmitter.prototype.done =
CustomEventEmitter.prototype.complete = CustomEventEmitter.prototype.complete =
function(fct) { function(fct) {
...@@ -107,16 +126,21 @@ module.exports = (function() { ...@@ -107,16 +126,21 @@ module.exports = (function() {
return this return this
} }
/*
* Attach a function that is called every time the function that created this emitter executes a query.
*/
CustomEventEmitter.prototype.sql = function(fct) { CustomEventEmitter.prototype.sql = function(fct) {
this.on('sql', bindToProcess(fct)) this.on('sql', bindToProcess(fct))
return this; return this;
} }
/** /**
* Proxy every event of this custom event emitter to another one. * Proxy every event of this event emitter to another one.
* *
* @param {CustomEventEmitter} emitter The event emitter that should receive the events. * @param {EventEmitter} emitter The event emitter that should receive the events.
* @return {void} * @param {Object} [options]
* @param {Array} [options.events] An array of the events to proxy. Defaults to sql, error and success
* @return this
*/ */
CustomEventEmitter.prototype.proxy = function(emitter, options) { CustomEventEmitter.prototype.proxy = function(emitter, options) {
options = Utils._.extend({ options = Utils._.extend({
...@@ -135,6 +159,13 @@ module.exports = (function() { ...@@ -135,6 +159,13 @@ module.exports = (function() {
return this return this
} }
/**
* Attach listeners to the emitter, promise style
*
* @param {Function} onFulfilled The function to call if the promise is fulfilled (if the emitter emits success). Note that this function will always only be called with one argument, as per the promises/A spec. For functions that emit multiple arguments (e.g. findOrCreate) see `spread`
* @param {Function} onRejected
* @return {Bluebird.Promise}
*/
CustomEventEmitter.prototype.then = function(onFulfilled, onRejected) { CustomEventEmitter.prototype.then = function(onFulfilled, onRejected) {
var self = this var self = this
...@@ -147,6 +178,13 @@ module.exports = (function() { ...@@ -147,6 +178,13 @@ module.exports = (function() {
}).then(onFulfilled, onRejected) }).then(onFulfilled, onRejected)
} }
/**
* Attach listeners to the emitter, promise style. This listener will recieve all arguments, as opposed to `then` which will only recieve the first argument
*
* @param {Function} onFulfilled The function to call if the promise is fulfilled (if the emitter emits success).
* @param {Function} onRejected
* @return {Bluebird.Promise}
*/
CustomEventEmitter.prototype.spread = function(onFulfilled, onRejected) { CustomEventEmitter.prototype.spread = function(onFulfilled, onRejected) {
var self = this var self = this
......
var Utils = require(__dirname + "/utils") var Utils = require(__dirname + "/utils")
module.exports = (function() { module.exports = (function() {
/**
* The sequelize query chainer allows you to run several queries, either in parallel or serially, and attach a callback that is called when all queries are done.
* @class QueryChainer
*/
var QueryChainer = function(emitters) { var QueryChainer = function(emitters) {
var self = this var self = this
...@@ -24,6 +28,16 @@ module.exports = (function() { ...@@ -24,6 +28,16 @@ module.exports = (function() {
}) })
} }
/**
* Add an query to the chainer. This can be done in two ways - either by invoking the method like you would normally, and then adding the returned emitter to the chainer, or by passing the
* class that you want to call a method on, the name of the method, and its parameters to the chainer. The second form might sound a bit cumbersome, but it is used when you want to run
* queries in serial.
* @param {EventEmitter|Any} emitterOrKlass
* @param {String} [method]
* @param {Object} [params]
* @param {Object} [options]
* @return this
*/
QueryChainer.prototype.add = function(emitterOrKlass, method, params, options) { QueryChainer.prototype.add = function(emitterOrKlass, method, params, options) {
if (!!method) { if (!!method) {
this.serials.push({ klass: emitterOrKlass, method: method, params: params, options: options }) this.serials.push({ klass: emitterOrKlass, method: method, params: params, options: options })
...@@ -35,6 +49,10 @@ module.exports = (function() { ...@@ -35,6 +49,10 @@ module.exports = (function() {
return this return this
} }
/**
* Run the query chainer. In reality, this means, wait for all the added emtiters to finish, since the queries began executing as soon as you invoked their methods.
* @return {EventEmitter}
*/
QueryChainer.prototype.run = function() { QueryChainer.prototype.run = function() {
var self = this var self = this
this.eventEmitter = new Utils.CustomEventEmitter(function() { this.eventEmitter = new Utils.CustomEventEmitter(function() {
...@@ -44,6 +62,12 @@ module.exports = (function() { ...@@ -44,6 +62,12 @@ module.exports = (function() {
return this.eventEmitter.run() return this.eventEmitter.run()
} }
/**
* Run the chainer serially, so that each query waits for the previous one to finish before it starts.
* @param {Object} [options]
* @param {Object} [options.skipOnError=false] If set to true, all pending emitters will be skipped if a previous emitter failed
* @return {EventEmitter}
*/
QueryChainer.prototype.runSerially = function(options) { QueryChainer.prototype.runSerially = function(options) {
var self = this var self = this
, serialCopy = Utils._.clone(this.serials) , serialCopy = Utils._.clone(this.serials)
......
...@@ -12,7 +12,13 @@ var url = require("url") ...@@ -12,7 +12,13 @@ var url = require("url")
, sequelizeErrors = require('./errors') , sequelizeErrors = require('./errors')
/** /**
* The main class * This is the main class, the entry point to sequelize. To use it, you just need to import sequelize:
*
* ```js
* var Sequelize = require('sequelize');
* ```
*
* In addition to sequelize, the connection library for the dialect you want to use should also be installed in your project. You don't need to import it however, as sequelize will take care of that.
* *
* @class Sequelize * @class Sequelize
*/ */
...@@ -43,25 +49,31 @@ module.exports = (function() { ...@@ -43,25 +49,31 @@ module.exports = (function() {
* @name Sequelize * @name Sequelize
* @constructor * @constructor
* *
* @param {String} database The name of the database * @param {String} database The name of the database
* @param {String} [username=null] The username which is used to authenticate against the database. * @param {String} [username=null] The username which is used to authenticate against the database.
* @param {String} [password=null] The password which is used to authenticate against the database. * @param {String} [password=null] The password which is used to authenticate against the database.
* @param {Object} [options={}] An object with options. * @param {Object} [options={}] An object with options.
* @param {String} [options.dialect='mysql'] The dialect of the relational database. * @param {String} [options.dialect='mysql'] The dialect you of the database you are connecting to. One of mysql, postgres, sqlite and mariadb
* @param {String} [options.dialectModulePath=null] If specified, load the dialect library from this path. * @param {String} [options.dialectModulePath=null] If specified, load the dialect library from this path. For example, if you want to use pg.js instead of pg when connecting to a pg database, you should specify 'pg.js' here
* @param {String} [options.host='localhost'] The host of the relational database. * @param {String} [options.host='localhost'] The host of the relational database.
* @param {Integer} [options.port=] The port of the relational database. * @param {Integer} [options.port=] The port of the relational database.
* @param {String} [options.protocol='tcp'] The protocol of the relational database. * @param {String} [options.protocol='tcp'] The protocol of the relational database.
* @param {Object} [options.define={}] Options, which shall be default for every model definition. See sequelize#define for options * @param {Object} [options.define={}] Default options for model definitions. See sequelize.define for options
* @param {Object} [options.query={}] I have absolutely no idea. * @param {Object} [options.query={}] Default options for sequelize.query
* @param {Object} [options.sync={}] Options, which shall be default for every `sync` call. * @param {Object} [options.sync={}] Default options for sequelize.sync
* @param {Function} [options.logging=console.log] A function that gets executed everytime Sequelize would log something. * @param {Function} [options.logging=console.log] A function that gets executed everytime Sequelize would log something.
* @param {Boolean} [options.omitNull=false] A flag that defines if null values should be passed to SQL queries or not. * @param {Boolean} [options.omitNull=false] A flag that defines if null values should be passed to SQL queries or not.
* @param {Boolean} [options.queue=true] I have absolutely no idea. * @param {Boolean} [options.queue=true] Queue queries, so that only maxConcurrentQueries number of queries are executing at once. If false, all queries will be executed immediately.
* @param {Boolean} [options.native=false] A flag that defines if native library shall be used or not. * @param {int} [options.maxConcurrentQueries=50] The maximum number of queries that should be executed at once if queue is true.
* @param {Boolean} [options.replication=false] I have absolutely no idea. * @param {Boolean} [options.native=false] A flag that defines if native library shall be used or not. Currently only has an effect for postgres
* @param {Object} [options.pool={}] Something. * @param {Boolean} [options.replication=false] Use read / write replication. To enable replication, pass an object, with two properties, read and write. Write should be an object (a single server for handling writes), and read an array of object (several servers to handle reads). Each read/write server can have the following properties: `host`, `port`, `username`, `password`, `database`
* @param {Boolean} [options.quoteIdentifiers=true] Set to `false` to make table names and attributes case-insensitive on Postgres and skip double quoting of them. * @param {Object} [options.pool={}] Should sequelize use a connection pool. Default is true
* @param {int} [options.pool.maxConnections]
* @param {int} [options.pool.minConnections]
* @param {int} [options.pool.maxIdleTime] The maximum time, in milliseconds, that a connection can be idle before being released
* @param {function} [options.pool.validateConnection] A function that validates a connection. Called with client. The default function checks that client is an object, and that its state is not disconnected
* @param {Boolean} [options.quoteIdentifiers=true] Set to `false` to make table names and attributes case-insensitive on Postgres and skip double quoting of them.
*/ */
/** /**
...@@ -153,21 +165,19 @@ module.exports = (function() { ...@@ -153,21 +165,19 @@ module.exports = (function() {
} }
/** /**
* A reference to Utils * A reference to sequelize utilities. Most users will not need to use these utils directly. However, you might want to use `Sequelize.Utils._`, which is a reference to the lodash library, if you don't already have it imported in your project.
* @property Utils * @property Utils
* @see {Utils} * @see {Utils}
*/ */
Sequelize.Utils = Utils Sequelize.Utils = Utils
/**
* An object of different query types. This is used when doing raw queries (sequlize.query). If no type is provided to .query, sequelize will try to guess the correct type based on your SQL. This might not always work if you query is formatted in a special way
* @property QueryTypes
* @see {Sequelize#query}
* @see {QueryTypes}
*/
Sequelize.QueryTypes = QueryTypes Sequelize.QueryTypes = QueryTypes
/**
* The class used to validate attributes of an instance before saving. Validations are carried out using validator.js, and you can use `Sequelize.DAOValidator.Validator` to access the validator.js object and extend it with custom validation functions.
* @property DAOValidator
* @see https://github.com/chriso/validator.js
*/
Sequelize.DAOValidator = DAOValidator Sequelize.DAOValidator = DAOValidator
Sequelize.DAOFactory = Sequelize.Model = DAOFactory Sequelize.DAOFactory = Sequelize.Model = DAOFactory
...@@ -191,16 +201,24 @@ module.exports = (function() { ...@@ -191,16 +201,24 @@ module.exports = (function() {
}) })
/** /**
* A reference to transaction. Use this to access isolationLevels when creating a transaction * A reference to the sequelize transaction class. Use this to access isolationLevels when creating a transaction
* @property Transaction
* @see {Transaction} * @see {Transaction}
* @see {Sequelize#transaction}
*/ */
Sequelize.prototype.Transaction = Transaction Sequelize.prototype.Transaction = Transaction
/** /**
* Expose Sequelize Error types * A general error class
* @property Error
*/ */
Sequelize.prototype.Error = Sequelize.Error = Sequelize.prototype.Error = Sequelize.Error =
sequelizeErrors.BaseError sequelizeErrors.BaseError
/**
* Emitted when a validation fails
* @property ValidationError
*/
Sequelize.prototype.ValidationError = Sequelize.ValidationError = Sequelize.prototype.ValidationError = Sequelize.ValidationError =
sequelizeErrors.ValidationError sequelizeErrors.ValidationError
...@@ -229,8 +247,9 @@ module.exports = (function() { ...@@ -229,8 +247,9 @@ module.exports = (function() {
/** /**
* Returns an instance (singleton) of Migrator. * Returns an instance (singleton) of Migrator.
* *
* @see {Migrator}
* @function getMigrator * @function getMigrator
* @param {Object} [options={}] Some options * @param {Object} [options={}] See Migrator for options
* @param {Boolean} [force=false] A flag that defines if the migrator should get instantiated or not. * @param {Boolean} [force=false] A flag that defines if the migrator should get instantiated or not.
* @return {Migrator} An instance of Migrator. * @return {Migrator} An instance of Migrator.
*/ */
...@@ -255,6 +274,14 @@ module.exports = (function() { ...@@ -255,6 +274,14 @@ module.exports = (function() {
* sequelize.define(..., { * sequelize.define(..., {
* columnA: { * columnA: {
* type: Sequelize.BOOLEAN, * type: Sequelize.BOOLEAN,
* validate: {
* is: ["[a-z]",'i'], // will only allow letters
* max: 23, // only allow values <= 23
* isIn: {
* args: [['en', 'zh']],
* msg: "Must be English or Chinese"
* }
* },
* // Other attributes here * // Other attributes here
* }, * },
* columnB: Sequelize.STRING, * columnB: Sequelize.STRING,
...@@ -262,11 +289,15 @@ module.exports = (function() { ...@@ -262,11 +289,15 @@ module.exports = (function() {
* }) * })
* ``` * ```
* *
* As shown above, column definitions can be either strings, a reference to one of the datatypes that are predefined on the Sequelize constructor, or an object that allows you to specify both the type of the column, and other attributes such as default values, foreign key constraints and custom setters and getters.
*
* For a list of possible data types, see http://sequelizejs.com/docs/latest/models#data-types * For a list of possible data types, see http://sequelizejs.com/docs/latest/models#data-types
* *
* For more about getters and setters, see http://sequelizejs.com/docs/latest/models#getters---setters * For more about getters and setters, see http://sequelizejs.com/docs/latest/models#getters---setters
* *
* For more about instance and class methods see http://sequelizejs.com/docs/latest/models#expansion-of-models * For more about instance and class methods, see http://sequelizejs.com/docs/latest/models#expansion-of-models
*
* For more about validation, see http://sequelizejs.com/docs/latest/models#validations
* *
* @see {DataTypes} * @see {DataTypes}
* @see {Hooks} * @see {Hooks}
...@@ -274,45 +305,45 @@ module.exports = (function() { ...@@ -274,45 +305,45 @@ module.exports = (function() {
* @param {Object} attributes An object, where each attribute is a column of the table. Each column can be either a DataType, a string or a type-description object, with the properties described below: * @param {Object} attributes An object, where each attribute is a column of the table. Each column can be either a DataType, a string or a type-description object, with the properties described below:
* @param {String|DataType|Object} attributes.column The description of a database column * @param {String|DataType|Object} attributes.column The description of a database column
* @param {String|DataType} attributes.column.type A string or a data type * @param {String|DataType} attributes.column.type A string or a data type
* @param {Boolean} [attributes.column.allowNull=true] If false, the column will have a NOT NULL constraint * @param {Boolean} [attributes.column.allowNull=true] If false, the column will have a NOT NULL constraint, and a not null validation will be run before an instance is saved.
* @param {Boolean} [attributes.column.defaultValue=null] A literal default value, or a function, see sequelize#fn * @param {Any} [attributes.column.defaultValue=null] A literal default value, a javascript function, or an SQL function (see `sequelize.fn`)
* @param {String|Boolean} [attributes.column.unique=false] If true, the column will get a unique constraint. If a string is provided, the column will be part of a composite unique index. If multiple columns have the same string, they will be part of the same unique index * @param {String|Boolean} [attributes.column.unique=false] If true, the column will get a unique constraint. If a string is provided, the column will be part of a composite unique index. If multiple columns have the same string, they will be part of the same unique index
* @param {Boolean} [attributes.column.primaryKey=false] * @param {Boolean} [attributes.column.primaryKey=false]
* @param {Boolean} [attributes.column.autoIncrement=false] * @param {Boolean} [attributes.column.autoIncrement=false]
* @param {String} [attributes.column.comment=null] * @param {String} [attributes.column.comment=null]
* @param {String|DAOFactory} [attributes.column.references] If this column references another table, provide it here as a DAOFactory, or a string * @param {String|DAOFactory} [attributes.column.references] If this column references another table, provide it here as a DAOFactory, or a string
* @param {String} [attributes.column.referencesKey='id'] The column of the foreign table that this column references * @param {String} [attributes.column.referencesKey='id'] The column of the foreign table that this column references
* @param {String} [attributes.column.onUpdate] What should happen when the referenced key is updated. One of CASCADE, RESTRICT or NO ACTION * @param {String} [attributes.column.onUpdate] What should happen when the referenced key is updated. One of CASCADE, RESTRICT, SET DEFAULT, SET NULL or NO ACTION
* @param {String} [attributes.column.onDelete] What should happen when the referenced key is deleted. One of CASCADE, RESTRICT or NO ACTION * @param {String} [attributes.column.onDelete] What should happen when the referenced key is deleted. One of CASCADE, RESTRICT, SET DEFAULT, SET NULL or NO ACTION
* @param {Function} [attributes.column.get] Provide a custom getter for this column. Use this.getDataValue(String) and this.setDataValue(String, Value) to manipulate the underlying values. * @param {Function} [attributes.column.get] Provide a custom getter for this column. Use `this.getDataValue(String)` to manipulate the underlying values.
* @param {Function} [attributes.column.set] Provide a custom setter for this column. Use this.getDataValue(String) and this.setDataValue(String, Value) to manipulate the underlying values. * @param {Function} [attributes.column.set] Provide a custom setter for this column. Use `this.setDataValue(String, Value)` to manipulate the underlying values.
* @param {Object} [attributes.validate] An object of validations to execute for this column every time the model is saved. Can be either the name of a validation provided by validator.js, a validation function provided by extending validator.js (see the `DAOValidator` property for more details), or a custom validation function. Custom validation functions are called with the value of the field, and can possibly take a second callback argument, to signal that they are asynchronous. If the validator is sync, it should throw in the case of a failed validation, it it is async, the callback should be called with the error text.
* @param {Object} [options] These options are merged with the options provided to the Sequelize constructor * @param {Object} [options] These options are merged with the default define options provided to the Sequelize constructor
* @param {Object} [options.defaultScope] Define the default search scope to use for this model. Scopes have the same form as the options passed to find / findAll * @param {Object} [options.defaultScope] Define the default search scope to use for this model. Scopes have the same form as the options passed to find / findAll
* @param {Object} [options.scopes] More scopes, defined in the same way as defaultScope above. See `DAOFactory.scope` for more information about how scopes are defined, and what you can do with them * @param {Object} [options.scopes] More scopes, defined in the same way as defaultScope above. See `DAOFactory.scope` for more information about how scopes are defined, and what you can do with them
* @param {Boolean} [options.omitNull] Don't persits null values. This means that all columns with null values will not be saved * @param {Boolean} [options.omitNull] Don't persits null values. This means that all columns with null values will not be saved
* @param {Boolean} [options.timestamps=true] Handle createdAt and updatedAt timestamps * @param {Boolean} [options.timestamps=true] Adds createdAt and updatedAt timestamps to the model.
* @param {Boolean} [options.paranoid=false] Calling destroy will not delete the model, but instead set a deletedAt timestamp if this is true. Needs timestamps=true to work * @param {Boolean} [options.paranoid=false] Calling `destroy` will not delete the model, but instead set a `deletedAt` timestamp if this is true. Needs `timestamps=true` to work
* @param {Boolean} [options.underscored=false] Converts all camelCased columns to underscored if true * @param {Boolean} [options.underscored=false] Converts all camelCased columns to underscored if true
* @param {Boolean} [options.freezeTableName=false] If freezeTableName is true, sequelize will not try to alter the DAO name to get the table name. Otherwise, the tablename will be pluralized * @param {Boolean} [options.freezeTableName=false] If freezeTableName is true, sequelize will not try to alter the DAO name to get the table name. Otherwise, the dao name will be pluralized
* @param {String|Boolean} [options.createdAt] Override the name of the createdAt column if a string is provided, or disable it if true. Timestamps must be true * @param {String|Boolean} [options.createdAt] Override the name of the createdAt column if a string is provided, or disable it if false. Timestamps must be true
* @param {String|Boolean} [options.updatedAt] Override the name of the updatedAt column if a string is provided, or disable it if true. Timestamps must be true * @param {String|Boolean} [options.updatedAt] Override the name of the updatedAt column if a string is provided, or disable it if false. Timestamps must be true
* @param {String|Boolean} [options.deletedAt] Override the name of the deletedAt column if a string is provided, or disable it if true. Timestamps must be true * @param {String|Boolean} [options.deletedAt] Override the name of the deletedAt column if a string is provided, or disable it if false. Timestamps must be true
* @param {String} [options.tableName] Defaults to pluralized DAO name * @param {String} [options.tableName] Defaults to pluralized DAO name, unless freezeTableName is true, in which case it uses DAO name verbatim
* @param {Object} [options.getterMethods] Provide getter functions that work like those defined per column. If you provide a getter method with the same name as a column, it will be used to access the value of that column. If you provide a name that does not match a column, this function will act as a virtual getter, that can fetch multiple other values * @param {Object} [options.getterMethods] Provide getter functions that work like those defined per column. If you provide a getter method with the same name as a column, it will be used to access the value of that column. If you provide a name that does not match a column, this function will act as a virtual getter, that can fetch multiple other values
* @param {Object} [options.setterMethods] Provide setter functions that work like those defined per column. If you provide a setter method with the same name as a column, it will be used to update the value of that column. If you provide a name that does not match a column, this function will act as a virtual setter, that can act on and set other values, but will not be persisted * @param {Object} [options.setterMethods] Provide setter functions that work like those defined per column. If you provide a setter method with the same name as a column, it will be used to update the value of that column. If you provide a name that does not match a column, this function will act as a virtual setter, that can act on and set other values, but will not be persisted
* @param {Object} [options.instanceMethods] Provide functions that are added to each instance (DAO) * @param {Object} [options.instanceMethods] Provide functions that are added to each instance (DAO)
* @param {Object} [options.classMethods] Provide functions that are added to the model (DAOFactory) * @param {Object} [options.classMethods] Provide functions that are added to the model (DAOFactory)
* @param {String} [options.schema='public'] * @param {String} [options.schema='public']
* @param {String} [options.engine] * @param {String} [options.engine]
* @param {Strng} [options.charset] * @param {String} [options.charset]
* @param {String} [options.comment] * @param {String} [options.comment]
* @param {String} [options.collate] * @param {String} [options.collate]
* @param {Object} [options.hooks] An object of hook function that are called before and after certain lifecycle events. The possible hooks are: beforeValidate, afterValidate, beforeBulkCreate, beforeBulkDestroy, beforeBulkUpdate, beforeCreate, beforeDestroy, beforeUpdate, afterCreate, afterDestroy, afterUpdate, afterBulkCreate, afterBulkDestory and afterBulkUpdate. See Hooks for more information about hook functions and their signatures. Each property can either be a function, or an array of function. * @param {Object} [options.hooks] An object of hook function that are called before and after certain lifecycle events. The possible hooks are: beforeValidate, afterValidate, beforeBulkCreate, beforeBulkDestroy, beforeBulkUpdate, beforeCreate, beforeDestroy, beforeUpdate, afterCreate, afterDestroy, afterUpdate, afterBulkCreate, afterBulkDestory and afterBulkUpdate. See Hooks for more information about hook functions and their signatures. Each property can either be a function, or an array of functions.
* @param {Object} [options.validate] An object of model wide validations. Validations have access to all model values via `this`. If the validator function takes an argument, it is asumed to be async, and is called with a callback that accepts an optional error.
* *
* @return {DaoFactory} * @return {DaoFactory}
* TODO validation
* TODO hooks
*/ */
Sequelize.prototype.define = function(daoName, attributes, options) { Sequelize.prototype.define = function(daoName, attributes, options) {
options = options || {} options = options || {}
...@@ -401,10 +432,10 @@ module.exports = (function() { ...@@ -401,10 +432,10 @@ module.exports = (function() {
} }
/** /**
* Checks whether a DAO with the given name is defined * Checks whether a model with the given name is defined
* *
* @param {String} daoName The name of a model defined with Sequelize.define * @param {String} daoName The name of a model defined with Sequelize.define
* @return {Boolean} Is a DAO with that name already defined? * @return {Boolean} Is a model with that name already defined?
*/ */
Sequelize.prototype.isDefined = function(daoName) { Sequelize.prototype.isDefined = function(daoName) {
var daos = this.daoFactoryManager.daos var daos = this.daoFactoryManager.daos
...@@ -412,9 +443,9 @@ module.exports = (function() { ...@@ -412,9 +443,9 @@ module.exports = (function() {
} }
/** /**
* Imports a DAO defined in another file * Imports a model defined in another file
* *
* Imported DAOs are cached, so multiple calls to import with the same path will not load the file multiple times * Imported models are cached, so multiple calls to import with the same path will not load the file multiple times
* *
* See https://github.com/sequelize/sequelize/blob/master/examples/using-multiple-model-files/Task.js for a short example of how to define your models in separate files so that they can be imported by sequelize.import * See https://github.com/sequelize/sequelize/blob/master/examples/using-multiple-model-files/Task.js for a short example of how to define your models in separate files so that they can be imported by sequelize.import
* @param {String} path The path to the file that holds the model you want to import. If the part is relative, it will be resolved relatively to the calling file * @param {String} path The path to the file that holds the model you want to import. If the part is relative, it will be resolved relatively to the calling file
...@@ -445,27 +476,20 @@ module.exports = (function() { ...@@ -445,27 +476,20 @@ module.exports = (function() {
/** /**
* Execute a query on the DB, with the posibility to bypass all the sequelize goodness. * Execute a query on the DB, with the posibility to bypass all the sequelize goodness.
* *
* If you do not provide other arguments than the SQL, raw will be assumed to the true, and sequelize will not try to do any formatting to the results of the query.
*
* @method query * @method query
* @param {String} sql * @param {String} sql
* @param {DAOFactory} [callee] If callee is provided, the selected data will be used to build an instance of the DAO represented by the factory. Equivalent to calling DAOFactory.build with the values provided by the query. * @param {DAOFactory} [callee] If callee is provided, the selected data will be used to build an instance of the DAO represented by the factory. Equivalent to calling DAOFactory.build with the values provided by the query.
* @param {Object} [options={}] Query options. * @param {Object} [options={}] Query options.
* @param {Boolean} [options.raw] If true, sequelize will not try to format the results of the query, or build an instance of a model from the result * @param {Boolean} [options.raw] If true, sequelize will not try to format the results of the query, or build an instance of a model from the result
* @param {String} [options.type] What is the type of this query (SELECT, UPDATE etc.). If the query starts with SELECT, the type will be assumed to be SELECT, otherwise no assumptions are made. Only used when raw is false.
* @param {Transaction} [options.transaction=null] The transaction that the query should be executed under * @param {Transaction} [options.transaction=null] The transaction that the query should be executed under
* @param {Object|Array} [replacements] Either an object of named parameter replacements in the format `:param` or an array of unnamed replacements to replace `?` * @param [String] [options.type='SELECT'] The type of query you are executing. The query type affects how results are formatted before they are passed back. If no type is provided sequelize will try to guess the right type based on the sql, and fall back to SELECT. The type is a string, but `Sequelize.QueryTypes` is provided is convenience shortcuts. Current options are SELECT, BULKUPDATE and BULKDELETE
* @param {Object|Array} [replacements] Either an object of named parameter replacements in the format `:param` or an array of unnamed replacements to replace `?` in your SQL.
* @return {EventEmitter} * @return {EventEmitter}
* *
* @see {DAOFactory#build} for more information about callee. * @see {DAOFactory#build} for more information about callee.
*/ */
/**
* Execute a raw query against the DB.
*
* @method query
* @param {String} sql
* @param {Object} [options={raw:true}] Query options. See above for a full set of options
* @return {EventEmitter}
*/
Sequelize.prototype.query = function(sql, callee, options, replacements) { Sequelize.prototype.query = function(sql, callee, options, replacements) {
if (arguments.length === 4) { if (arguments.length === 4) {
if (Array.isArray(replacements)) { if (Array.isArray(replacements)) {
...@@ -575,6 +599,13 @@ module.exports = (function() { ...@@ -575,6 +599,13 @@ module.exports = (function() {
return chainer.runSerially() return chainer.runSerially()
} }
/**
* Drop all tables defined through this sequelize instance. This is done by calling Model.drop on each model
* @see {DAO#drop} for options
*
* @param {object} options The options passed to each call to Model.drop
* @return {EventEmitter} Fires `success`, `error` and `sql`.
*/
Sequelize.prototype.drop = function(options) { Sequelize.prototype.drop = function(options) {
var self = this var self = this
...@@ -596,9 +627,10 @@ module.exports = (function() { ...@@ -596,9 +627,10 @@ module.exports = (function() {
/** /**
* Test the connetion by trying to authenticate * Test the connetion by trying to authenticate
* @return {EventEmitter}
* @fires success If authentication was successfull * @fires success If authentication was successfull
* @error 'Invalid credentials' if the authentication failed (even if the database did not respond at all....) * @error 'Invalid credentials' if the authentication failed (even if the database did not respond at all...)
* @alias validate
* @return {EventEmitter} Fires `success`, `error` and `sql`.
*/ */
Sequelize.prototype.authenticate = function() { Sequelize.prototype.authenticate = function() {
var self = this var self = this
...@@ -616,20 +648,18 @@ module.exports = (function() { ...@@ -616,20 +648,18 @@ module.exports = (function() {
}).run() }).run()
} }
/**
* Alias of authenticate
*
* @see {Sequelize#authenticate}
* @return {EventEmitter}
* @fires success If authentication was successfull
* @error 'Invalid credentials' if the authentication failed (even if the database did not respond at all....)
*/
Sequelize.prototype.validate = Sequelize.prototype.authenticate; Sequelize.prototype.validate = Sequelize.prototype.authenticate;
/** /**
* Creates a object representing a database function. This can be used in search queries, both in where and order parts, and as default values in column definitions. * Creates a object representing a database function. This can be used in search queries, both in where and order parts, and as default values in column definitions.
* If you want to refer to columns in your function, you should use sequelize.col, so that the columns are properly interpreted as columns and not a strings. * If you want to refer to columns in your function, you should use `sequelize.col`, so that the columns are properly interpreted as columns and not a strings.
*
* Convert a user's username to upper case
* ```js
* instance.updateAttributes({
* username: self.sequelize.fn('upper', self.sequelize.col('username'))
* })
* ```
* *
* @see {DAOFactory#find} * @see {DAOFactory#find}
* @see {DAOFactory#findAll} * @see {DAOFactory#findAll}
...@@ -639,14 +669,14 @@ module.exports = (function() { ...@@ -639,14 +669,14 @@ module.exports = (function() {
* @method fn * @method fn
* @param {String} fn The function you want to call * @param {String} fn The function you want to call
* @param {any} args All further arguments will be passed as arguments to the function * @param {any} args All further arguments will be passed as arguments to the function
* @return An instance of Sequelize.fn * @return Sequelize.fn An instance of Sequelize.fn
*/ */
Sequelize.fn = Sequelize.prototype.fn = function (fn) { Sequelize.fn = Sequelize.prototype.fn = function (fn) {
return new Utils.fn(fn, Array.prototype.slice.call(arguments, 1)) return new Utils.fn(fn, Array.prototype.slice.call(arguments, 1))
} }
/** /**
* Creates a object representing a column in the DB. This is usefull in sequelize.fn, since raw string arguments to that will be escaped. * Creates a object representing a column in the DB. This is often useful in conjunction with `sequelize.fn`, since raw string arguments to fn will be escaped.
* @see {Sequelize#fn} * @see {Sequelize#fn}
* *
* @method col * @method col
...@@ -675,17 +705,13 @@ module.exports = (function() { ...@@ -675,17 +705,13 @@ module.exports = (function() {
* *
* @method literal * @method literal
* @param {any} val * @param {any} val
* @alias asIs
* @return An instance of Sequelize.literal * @return An instance of Sequelize.literal
*/ */
Sequelize.literal = Sequelize.prototype.literal = function (val) { Sequelize.literal = Sequelize.prototype.literal = function (val) {
return new Utils.literal(val) return new Utils.literal(val)
} }
/**
* An alias of literal
* @method asIs
* @see {Sequelize#literal}
*/
Sequelize.asIs = Sequelize.prototype.asIs = function (val) { Sequelize.asIs = Sequelize.prototype.asIs = function (val) {
return new Utils.asIs(val) return new Utils.asIs(val)
} }
...@@ -714,8 +740,41 @@ module.exports = (function() { ...@@ -714,8 +740,41 @@ module.exports = (function() {
return new Utils.or(Array.prototype.slice.call(arguments)) return new Utils.or(Array.prototype.slice.call(arguments))
} }
/*
* A way of specifying attr = condition. Mostly used internally
* @see {DAOFactory#find}
*
* @param {string} attr The attribute
* @param {String|Object} condition The condition. Can be both a simply type, or a further condition (`.or`, `.and`, `.literal` etc.)
* @method where
* @alias condition
* @return An instance of Sequelize.where
*/
Sequelize.where = Sequelize.prototype.where = function() {
return new Utils.where(Array.prototype.slice.call(arguments))
}
Sequelize.condition = Sequelize.prototype.condition = function() {
return new Utils.condition(Array.prototype.slice.call(arguments))
}
/** /**
* Start a transaction. * Start a transaction. When using transactions, you should pass the transaction in the options argument in order for the query to happen under that transaction
*
* ```js
* sequelize.transaction(function(t) {
* User.find(..., { transaction: t}).success(function (user) {
* user.updateAttributes(..., { transaction: t}).success(function () {
* t.commit()
* })
* })
*
* // the commit / rollback will emit events which can be observed via:
* t.done(function() {
* })
* })
* ```
*
* @see {Transaction} * @see {Transaction}
* @param {Object} [options={}] * @param {Object} [options={}]
...@@ -726,14 +785,6 @@ module.exports = (function() { ...@@ -726,14 +785,6 @@ module.exports = (function() {
* @fires error If there is an uncaught error during the transaction * @fires error If there is an uncaught error during the transaction
* @fires success When the transaction has ended (either comitted or rolled back) * @fires success When the transaction has ended (either comitted or rolled back)
*/ */
Sequelize.where = Sequelize.prototype.where = function() {
return new Utils.where(Array.prototype.slice.call(arguments))
}
Sequelize.condition = Sequelize.prototype.condition = function() {
return new Utils.condition(Array.prototype.slice.call(arguments))
}
Sequelize.prototype.transaction = function(_options, _callback) { Sequelize.prototype.transaction = function(_options, _callback) {
var options = (typeof _options === 'function') ? {} : _options var options = (typeof _options === 'function') ? {} : _options
, callback = (typeof _options === 'function') ? _options : _callback , callback = (typeof _options === 'function') ? _options : _callback
......
...@@ -15,29 +15,41 @@ var getTags = function(tags, tagName) { ...@@ -15,29 +15,41 @@ var getTags = function(tags, tagName) {
}); });
} }
// TODO multiple @see tags
var options = { var options = {
output: 'output.md', output: 'output.md',
formatter: function (docfile) { formatter: function (docfile) {
docfile = markdox.defaultFormatter(docfile); docfile = markdox.defaultFormatter(docfile);
docfile.members = []; docfile.members = [];
docfile.javadoc.forEach(function(javadoc, index){ docfile.javadoc.forEach(function(javadoc){
// Find constructor tags // Find constructor tags
javadoc.isConstructor = getTag(javadoc.raw.tags, 'constructor') !== undefined; javadoc.isConstructor = getTag(javadoc.raw.tags, 'constructor') !== undefined;
javadoc.isMixin = getTag(javadoc.raw.tags, 'mixin') !== undefined; javadoc.isMixin = getTag(javadoc.raw.tags, 'mixin') !== undefined;
javadoc.isProperty = getTag(javadoc.raw.tags, 'property') !== undefined javadoc.isProperty = getTag(javadoc.raw.tags, 'property') !== undefined
javadoc.mixes = getTags(javadoc.raw.tags, 'mixes'); javadoc.mixes = getTags(javadoc.raw.tags, 'mixes');
// Only show params without a dot in them (dots means attributes of object, so no need to clutter the co) // Only show params without a dot in them (dots means attributes of object, so no need to clutter the signature too much)
var params = [] var params = []
javadoc.paramTags.forEach(function (paramTag) { javadoc.paramTags.forEach(function (paramTag) {
if (paramTag.name.indexOf('.') === -1) { if (paramTag.name.indexOf('.') === -1) {
params.push(paramTag.name); params.push(paramTag.name);
} }
}); });
javadoc.paramStr = params.join(', '); javadoc.paramStr = params.join(', ');
// Convert | to &#124; to be able to use github flavored md tables
if (javadoc.paramTags) {
javadoc.paramTags.forEach(function (paramTag) {
paramTag.joinedTypes = paramTag.joinedTypes.replace(/\|/g, '&#124;')
});
}
// Handle aliases
javadoc.aliases = getTags(javadoc.raw.tags, 'alias').map(function (a) {
return a.string
}).join(', ')
// Handle deprecation text // Handle deprecation text
if (javadoc.deprecated) { if (javadoc.deprecated) {
var deprecation = getTag(javadoc.raw.tags, 'deprecated') var deprecation = getTag(javadoc.raw.tags, 'deprecated')
...@@ -57,14 +69,14 @@ var options = { ...@@ -57,14 +69,14 @@ var options = {
_see[0] = _see[0].substring(1) _see[0] = _see[0].substring(1)
collection[i].url = _see[0] collection[i].url = _see[0]
collection[i].text = see.local.replace(/{|}/g, '') collection[i].text = see.local.replace(/{|}/g, '')
} else { } else {
collection[i].url = false collection[i].url = false
collection[i].text = see.local collection[i].text = see.local
} }
} else { } else {
collection[i].external = true see.external = true
collection[i] = see
} }
}) })
...@@ -75,6 +87,9 @@ var options = { ...@@ -75,6 +87,9 @@ var options = {
javadoc.name = property.string javadoc.name = property.string
} }
} }
if (javadoc.isMixin) {
javadoc.name = getTag(javadoc.raw.tags, 'mixin').string;
}
if (!javadoc.isClass) { if (!javadoc.isClass) {
if (!javadoc.isProperty) { if (!javadoc.isProperty) {
...@@ -91,8 +106,7 @@ var options = { ...@@ -91,8 +106,7 @@ var options = {
}; };
markdox.process(process.argv[2] || './lib/hooks.js', options, function(){ markdox.process(process.argv[2] || './lib/hooks.js', options, function(){
md = fs.readFileSync('output.md').toString(); // var md = fs.readFileSync('output.md').toString();
// fs.writeFileSync('out.html', ghm.parse(md));
fs.writeFileSync('out.html', ghm.parse(md));
}); });
...@@ -4,11 +4,15 @@ ...@@ -4,11 +4,15 @@
<? if (!comment.ignore) { ?> <? if (!comment.ignore) { ?>
<a name="<?= comment.name ?>" /> <a name="<?= comment.name ?>" />
<? if (comment.isConstructor) { ?> <? if (comment.isConstructor) { ?>
## new <?= comment.name ?>(<?= comment.paramStr ?>) ### `new <?= comment.name ?>(<?= comment.paramStr ?>)`
<? } else if (comment.isMixin) { ?>
### Mixin <?= comment.name ?>
<? } else if (comment.isClass) { ?>
### Class <?= comment.name ?>
<? } else if (comment.isMethod || comment.isFunction) { ?> <? } else if (comment.isMethod || comment.isFunction) { ?>
### <?= comment.name ?>(<?= comment.paramStr ?>) #### `<?= comment.name ?>(<?= comment.paramStr ?>)`
<? } else { ?> <? } else { ?>
### <?= comment.name ?> #### `<?= comment.name ?>`
<? } ?> <? } ?>
<? } ?> <? } ?>
...@@ -37,10 +41,6 @@ ...@@ -37,10 +41,6 @@
**Deprecated** <? if (typeof comment.deprecated === "string") { ?><?- comment.deprecated ?><? } ?> **Deprecated** <? if (typeof comment.deprecated === "string") { ?><?- comment.deprecated ?><? } ?>
<? } ?> <? } ?>
<? if (comment.author) { ?>
Author: <?- comment.author ?>
<? } ?>
<? if (comment.version) { ?> <? if (comment.version) { ?>
Version: <?= comment.version ?> Version: <?= comment.version ?>
<? } ?> <? } ?>
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
<? comment.see.forEach(function (see) { -?> <? comment.see.forEach(function (see) { -?>
<? if (see.url !== false) { -?> <? if (see.url !== false) { -?>
<? if (see.external) { -?> <? if (see.external) { -?>
* <a href="<?= see.url ?>"><?= see.text ?></a> * <a href="<?= see.url ?>"><?= see.url ?></a>
<? } else { -?> <? } else { -?>
* <a href="https://github.com/sequelize/sequelize/wiki/API-Reference-<?= see.url ?>"><?= see.text ?></a> * <a href="https://github.com/sequelize/sequelize/wiki/API-Reference-<?= see.url ?>"><?= see.text ?></a>
<? } -?> <? } -?>
...@@ -59,32 +59,26 @@ ...@@ -59,32 +59,26 @@
<? }) -?> <? }) -?>
<? if (comment.paramTags.length > 0) { ?> <? if (comment.paramTags.length > 0) { ?>
#### Params: ##### Params:
<table> | Name | Type | Description |
<thead> | ---- | ---- | ----------- |
<th>Name</th><th>Type</th><th>Description</th> <? comment.paramTags.forEach(function(paramTag) { -?>
</thead> | <?= paramTag.name ?> | <?= paramTag.joinedTypes ?> | <?= paramTag.description ?> |
<? comment.paramTags.forEach(function(paramTag) { ?> <? }) ?>
<tr>
<td><?= paramTag.name ?></td>
<td><?= paramTag.joinedTypes ?></td>
<td><?= paramTag.description ?></td>
</tr>
<? }) ?>
</table>
<? } ?> <? } ?>
<? if (comment.returnTags.length > 0) { ?> <? if (comment.returnTags.length > 0) { ?>
#### Return: __Returns:__ *<?= comment.returnTags[0].joinedTypes ?>* <?= comment.returnTags[0].description ?>
<? comment.returnTags.forEach(function(returnTag) { ?> <? } ?>
* **<?= returnTag.joinedTypes ?>** <?= returnTag.description ?>
<? }) ?> <? if (comment.aliases) { ?>
__Aliases:__ *<?= comment.aliases ?>*
<? } ?> <? } ?>
<? } ?> <? } ?>
------ ======
<? }) ?> <? }) ?>
_This document is automatically generated based on source code comments. Please do not edit it directly, as your changes will be ignored. Please write on [IRC](irc://irc.freenode.net/#sequelizejs), open an issue or a create a pull request if you feel something can be improved. For help on how to write source code documentation see [JSDoc](http://usejsdoc.org) and [markdox](https://github.com/cbou/markdox)_ _This document is automatically generated based on source code comments. Please do not edit it directly, as your changes will be ignored. Please write on <a href="irc://irc.freenode.net/#sequelizejs">IRC</a>, open an issue or a create a pull request if you feel something can be improved. For help on how to write source code documentation see [JSDoc](http://usejsdoc.org) and [markdox](https://github.com/cbou/markdox)_
_This documentation was automagically created on <?= new Date().toString() ?>_ _This documentation was automagically created on <?= new Date().toString() ?>_
......
...@@ -27,6 +27,7 @@ if (dialect.match(/^postgres/)) { ...@@ -27,6 +27,7 @@ if (dialect.match(/^postgres/)) {
done() done()
}) })
it('should be able to search within an array', function(done) { it('should be able to search within an array', function(done) {
this.User.all({where: {email: ['hello', 'world']}}).on('sql', function(sql) { this.User.all({where: {email: ['hello', 'world']}}).on('sql', function(sql) {
expect(sql).to.equal('SELECT * FROM "Users" WHERE "Users"."email" && ARRAY[\'hello\',\'world\']::TEXT[];') expect(sql).to.equal('SELECT * FROM "Users" WHERE "Users"."email" && ARRAY[\'hello\',\'world\']::TEXT[];')
...@@ -274,7 +275,7 @@ if (dialect.match(/^postgres/)) { ...@@ -274,7 +275,7 @@ if (dialect.match(/^postgres/)) {
.error(console.log) .error(console.log)
}) })
}) })
describe('[POSTGRES] Unquoted identifiers', function() { describe('[POSTGRES] Unquoted identifiers', function() {
it("can insert and select", function(done) { it("can insert and select", function(done) {
var self = this var self = this
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!