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

Commit 2ce56b09 by Jan Aagaard Meier

Added docs for assocations

1 parent f34afaec
......@@ -32,7 +32,8 @@ module.exports = (function() {
self.instance[instancePrimaryKey]
),
new Utils.where(
through.rawAttributes[self.association.foreignIdentifier], {
through.rawAttributes[self.association.foreignIdentifier],
{
join: new Utils.literal([
self.QueryInterface.quoteTable(self.association.target.getTableName()),
self.QueryInterface.quoteIdentifier(foreignPrimaryKey)
......
......@@ -9,12 +9,11 @@ var Utils = require("./utils")
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
* @mixes {Hooks}
* @mixes {Assocations}
* @mixes http://nodejs.org/api/events.html#events_class_events_eventemitter
*/
var DAOFactory = function(name, attributes, options) {
this.options = Utils._.extend({
......@@ -329,7 +328,7 @@ module.exports = (function() {
/**
* Sync this DAOFactory to the DB, that is create the table.
* @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) {
options = Utils._.extend({}, this.options, options || {})
......@@ -360,6 +359,7 @@ module.exports = (function() {
* Drop the table represented by this Model
* @param {Object} [options]
* @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) {
return this.QueryInterface.dropTable(this.getTableName(), options)
......@@ -369,6 +369,14 @@ module.exports = (function() {
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) {
this.options.schema = schema
......@@ -385,6 +393,11 @@ module.exports = (function() {
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() {
return this.QueryGenerator.addSchema(this)
}
......@@ -581,16 +594,16 @@ module.exports = (function() {
* @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 {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 {Number} [options.limit]
* @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 {Boolean} [queryOptions.raw] Returns the results as raw JS objects instead of DAO instances
*
* @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) {
var hasJoin = false
......@@ -638,7 +651,7 @@ module.exports = (function() {
* @param {Object} [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) {
var hasJoin = false
......@@ -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
* })
* ```
* @param {Object} [findOptions]
* @param {Object} [queryOptions]
* @param {Object} [findOptions] See findAll
* @param {Object} [queryOptions] See Sequelize.query
*
* @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
......@@ -835,7 +848,7 @@ module.exports = (function() {
* Find the maximum value of field
*
* @param {String} field
* @param {Object} options
* @param {Object} [options] See aggregate
* @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.
......@@ -848,7 +861,7 @@ module.exports = (function() {
* Find the minimum value of field
*
* @param {String} field
* @param {Object} options
* @param {Object} [options] See aggregate
* @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.
......@@ -861,7 +874,7 @@ module.exports = (function() {
* Find the sun of field
*
* @param {String} field
* @param {Object} options
* @param {Object} [options] See aggregate
* @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.
......@@ -935,7 +948,7 @@ module.exports = (function() {
* @param {Array} [options.include] an array of include options - Used to build prefetched/included model instances
* @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) {
Utils.validateParameter(values, Object, { optional: true })
......@@ -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 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.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) {
var self = this
......@@ -1518,7 +1532,7 @@ module.exports = (function() {
/**
* 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) {
return this.QueryInterface.describeTable(this.tableName, schema || this.options.schema || undefined)
......
......@@ -13,6 +13,34 @@ var bindToProcess = function(fct) {
}
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) {
this.fct = bindToProcess(fct)
}
......@@ -62,13 +90,7 @@ module.exports = (function() {
};
/**
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.
* Shortcut methods (success, ok) for listening for success events.
*/
CustomEventEmitter.prototype.success =
CustomEventEmitter.prototype.ok =
......@@ -79,12 +101,6 @@ module.exports = (function() {
/**
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.fail =
......@@ -94,6 +110,9 @@ module.exports = (function() {
return this;
}
/**
Shortcut methods (done, complete) for listening for both success and error events.
*/
CustomEventEmitter.prototype.done =
CustomEventEmitter.prototype.complete =
function(fct) {
......@@ -107,16 +126,21 @@ module.exports = (function() {
return this
}
/*
* Attach a function that is called every time the function that created this emitter executes a query.
*/
CustomEventEmitter.prototype.sql = function(fct) {
this.on('sql', bindToProcess(fct))
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.
* @return {void}
* @param {EventEmitter} emitter The event emitter that should receive the events.
* @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) {
options = Utils._.extend({
......@@ -135,6 +159,13 @@ module.exports = (function() {
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) {
var self = this
......@@ -147,6 +178,13 @@ module.exports = (function() {
}).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) {
var self = this
......
var Utils = require(__dirname + "/utils")
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 self = this
......@@ -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) {
if (!!method) {
this.serials.push({ klass: emitterOrKlass, method: method, params: params, options: options })
......@@ -35,6 +49,10 @@ module.exports = (function() {
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() {
var self = this
this.eventEmitter = new Utils.CustomEventEmitter(function() {
......@@ -44,6 +62,12 @@ module.exports = (function() {
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) {
var self = this
, serialCopy = Utils._.clone(this.serials)
......
......@@ -15,29 +15,41 @@ var getTags = function(tags, tagName) {
});
}
// TODO multiple @see tags
var options = {
output: 'output.md',
formatter: function (docfile) {
docfile = markdox.defaultFormatter(docfile);
docfile.members = [];
docfile.javadoc.forEach(function(javadoc, index){
docfile.javadoc.forEach(function(javadoc){
// Find constructor tags
javadoc.isConstructor = getTag(javadoc.raw.tags, 'constructor') !== undefined;
javadoc.isMixin = getTag(javadoc.raw.tags, 'mixin') !== undefined;
javadoc.isProperty = getTag(javadoc.raw.tags, 'property') !== undefined
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 = []
javadoc.paramTags.forEach(function (paramTag) {
if (paramTag.name.indexOf('.') === -1) {
params.push(paramTag.name);
}
});
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
if (javadoc.deprecated) {
var deprecation = getTag(javadoc.raw.tags, 'deprecated')
......@@ -63,8 +75,8 @@ var options = {
collection[i].text = see.local
}
} else {
collection[i].external = true
see.external = true
collection[i] = see
}
})
......@@ -75,6 +87,9 @@ var options = {
javadoc.name = property.string
}
}
if (javadoc.isMixin) {
javadoc.name = getTag(javadoc.raw.tags, 'mixin').string;
}
if (!javadoc.isClass) {
if (!javadoc.isProperty) {
......@@ -91,8 +106,7 @@ var options = {
};
markdox.process(process.argv[2] || './lib/hooks.js', options, function(){
md = fs.readFileSync('output.md').toString();
fs.writeFileSync('out.html', ghm.parse(md));
// var md = fs.readFileSync('output.md').toString();
// fs.writeFileSync('out.html', ghm.parse(md));
});
......@@ -4,11 +4,15 @@
<? if (!comment.ignore) { ?>
<a name="<?= comment.name ?>" />
<? 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) { ?>
### <?= comment.name ?>(<?= comment.paramStr ?>)
#### `<?= comment.name ?>(<?= comment.paramStr ?>)`
<? } else { ?>
### <?= comment.name ?>
#### `<?= comment.name ?>`
<? } ?>
<? } ?>
......@@ -37,10 +41,6 @@
**Deprecated** <? if (typeof comment.deprecated === "string") { ?><?- comment.deprecated ?><? } ?>
<? } ?>
<? if (comment.author) { ?>
Author: <?- comment.author ?>
<? } ?>
<? if (comment.version) { ?>
Version: <?= comment.version ?>
<? } ?>
......@@ -49,7 +49,7 @@
<? comment.see.forEach(function (see) { -?>
<? if (see.url !== false) { -?>
<? if (see.external) { -?>
* <a href="<?= see.url ?>"><?= see.text ?></a>
* <a href="<?= see.url ?>"><?= see.url ?></a>
<? } else { -?>
* <a href="https://github.com/sequelize/sequelize/wiki/API-Reference-<?= see.url ?>"><?= see.text ?></a>
<? } -?>
......@@ -59,32 +59,26 @@
<? }) -?>
<? if (comment.paramTags.length > 0) { ?>
#### Params:
<table>
<thead>
<th>Name</th><th>Type</th><th>Description</th>
</thead>
<? comment.paramTags.forEach(function(paramTag) { ?>
<tr>
<td><?= paramTag.name ?></td>
<td><?= paramTag.joinedTypes ?></td>
<td><?= paramTag.description ?></td>
</tr>
##### Params:
| Name | Type | Description |
| ---- | ---- | ----------- |
<? comment.paramTags.forEach(function(paramTag) { -?>
| <?= paramTag.name ?> | <?= paramTag.joinedTypes ?> | <?= paramTag.description ?> |
<? }) ?>
</table>
<? } ?>
<? if (comment.returnTags.length > 0) { ?>
#### Return:
<? comment.returnTags.forEach(function(returnTag) { ?>
* **<?= returnTag.joinedTypes ?>** <?= returnTag.description ?>
<? }) ?>
__Returns:__ *<?= comment.returnTags[0].joinedTypes ?>* <?= comment.returnTags[0].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() ?>_
......
......@@ -27,6 +27,7 @@ if (dialect.match(/^postgres/)) {
done()
})
it('should be able to search within an array', function(done) {
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[];')
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!