Skip to content

Commit

Permalink
Merge pull request sequelize#2240 from overlookmotel/hooks-options
Browse files Browse the repository at this point in the history
Options passed to hooks
  • Loading branch information
mickhansen committed Sep 20, 2014
2 parents 87f1c86 + b5f31a8 commit 03f49f6
Show file tree
Hide file tree
Showing 8 changed files with 1,661 additions and 3,322 deletions.
9 changes: 7 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
- [FEATURE] Added `scope` to hasMany association definitions, provides default values to association setters/finders [#2268](https://github.com/sequelize/sequelize/pull/2268)
- [FEATURE] We now support transactions that automatically commit/rollback based on the result of the promise chain returned to the callback.
- [BUG] Only try to create indexes which don't already exist. Closes [#2162](https://github.com/sequelize/sequelize/issues/2162)
- [FEATURE] Hooks are passed options
- [FEATURE] Hooks need not return a result - undefined return is interpreted as a resolved promise
- [FEATURE] Added `find()` hooks

#### Backwards compatability changes
- The `fieldName` property, used in associations with a foreign key object `(A.hasMany(B, { foreignKey: { ... }})`, has been renamed to `name` to avoid confusion with `field`.
- The naming of the join table entry for N:M association getters is now singular (like includes)
- Signature of hooks has changed to pass options to all hooks
- Results returned by hooks are ignored - changes to results by hooks should be made by reference

# v2.0.0-dev13
We are working our way to the first 2.0.0 release candidate.
Expand All @@ -18,9 +23,9 @@ We are working our way to the first 2.0.0 release candidate.
- [FEATURE] Added support for passing an `indexes` array in options to `sequelize.define`. [#1485](https://github.com/sequelize/sequelize/issues/1485). See API reference for details.
- [FEATURE/INTERNALS] Standardized the output from `QueryInterface.showIndex`.
- [FEATURE] Include deleted rows in find [#2083](https://github.com/sequelize/sequelize/pull/2083)
- [FEATURE] Make addSingular and addPlural for n:m assocations (fx `addUser` and `addUsers` now both accept an array or an instance.
- [FEATURE] Make addSingular and addPlural for n:m associations (fx `addUser` and `addUsers` now both accept an array or an instance.
- [BUG] Hid `dottie.transform` on raw queries behind a flag (`nest`) [#2064](https://github.com/sequelize/sequelize/pull/2064)
- [BUG] Fixed problems with transcation parameter being removed / not passed on in associations [#1789](https://github.com/sequelize/sequelize/issues/1789) and [#1968](https://github.com/sequelize/sequelize/issues/1968)
- [BUG] Fixed problems with transaction parameter being removed / not passed on in associations [#1789](https://github.com/sequelize/sequelize/issues/1789) and [#1968](https://github.com/sequelize/sequelize/issues/1968)
- [BUG] Fix problem with minConnections. [#2048](https://github.com/sequelize/sequelize/issues/2048)
- [BUG] Fix default scope being overwritten [#2087](https://github.com/sequelize/sequelize/issues/2087)
- [BUG] Fixed updatedAt timestamp not being set in bulk create when validate = true. [#1962](https://github.com/sequelize/sequelize/issues/1962)
Expand Down
183 changes: 109 additions & 74 deletions lib/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,31 @@ var Utils = require('./utils')
* @mixin Hooks
*/
var Hooks = module.exports = function() {};
var hookTypes = {
beforeValidate: {params: 2},
afterValidate: {params: 2},
beforeCreate: {params: 2},
afterCreate: {params: 2},
beforeDestroy: {params: 2},
afterDestroy: {params: 2},
beforeUpdate: {params: 2},
afterUpdate: {params: 2},
beforeBulkCreate: {params: 2},
afterBulkCreate: {params: 2},
beforeBulkDestroy: {params: 1},
afterBulkDestroy: {params: 1},
beforeBulkUpdate: {params: 1},
afterBulkUpdate: {params: 1},
beforeFind: {params: 1},
beforeFindAfterExpandIncludeAll: {params: 1},
beforeFindAfterOptions: {params: 1},
afterFind: {params: 2}
};
var hookAliases = {
beforeDelete: 'beforeDestroy',
afterDelete: 'afterDestroy'
afterDelete: 'afterDestroy',
beforeBulkDelete: 'beforeBulkDestroy',
afterBulkDelete: 'afterBulkDestroy'
};

Hooks.replaceHookAliases = function(hooks) {
Expand All @@ -57,70 +79,39 @@ Hooks.replaceHookAliases = function(hooks) {
return hooks;
};

Hooks.runHooks = function() {
Hooks.runHooks = function(hooks) {
var self = this
, tick = 0
, hooks = arguments[0]
, lastIndex = arguments.length - 1
, fn = typeof arguments[lastIndex] === 'function' ? arguments[lastIndex] : null
, fnArgs = Array.prototype.slice.call(arguments, 1, fn ? lastIndex : arguments.length)
, resolveArgs = fnArgs;
, fn
, fnArgs = Array.prototype.slice.call(arguments, 1)
, hookType;

if (typeof fnArgs[fnArgs.length - 1] === 'function') {
fn = fnArgs.pop();
}

if (typeof hooks === 'string') {
hookType = hooks;
hooks = this.options.hooks[hooks] || [];
}

if (!Array.isArray(hooks)) {
hooks = hooks === undefined ? [] : [hooks];
}

var promise = new Promise(function(resolve, reject) {
if (hooks.length < 1) {
return resolve(fnArgs);
var promise = Promise.map(hooks, function(hook) {
if (typeof hook === 'object') {
hook = hook.fn;
}

var run = function(hook) {
if (!hook) {
return resolve(resolveArgs);
}

if (typeof hook === 'object') {
hook = hook.fn;
}

var maybePromise = hook.apply(self, fnArgs.concat(function() {
tick++;

if (!!arguments[0]) {
return reject(arguments[0]);
}
if (arguments.length) {
resolveArgs = Array.prototype.slice.call(arguments, 1);
}

return run(hooks[tick]);
}));

if (Utils.Promise.is(maybePromise)) {
maybePromise.spread(function() {
tick++;

if (arguments.length) {
resolveArgs = Array.prototype.slice.call(arguments);
}

return run(hooks[tick]);
}, reject);
}
};
if (hookType && hook.length > hookTypes[hookType].params) {
hook = Promise.promisify(hook, self);
}

run(hooks[tick]);
});
return hook.apply(self, fnArgs);
}, {concurrency: 1}).return();

if (fn) {
promise = promise.spread(function() {
fn.apply(self, [null].concat(Array.prototype.slice.apply(arguments)));
}, fn);
return promise.nodeify(fn);
}

return promise;
Expand All @@ -145,23 +136,19 @@ Hooks.addHook = function(hookType, name, fn) {
name = null;
}

var method = function() {
return fn.apply(this, Array.prototype.slice.call(arguments, 0, arguments.length - 1).concat(arguments[arguments.length - 1]));
};

// Aliases
hookType = hookAliases[hookType] || hookType;

// Just in case if we override the default DAOFactory.options
this.options.hooks[hookType] = this.options.hooks[hookType] || [];
this.options.hooks[hookType][this.options.hooks[hookType].length] = !!name ? {name: name, fn: method} : method;
this.options.hooks[hookType][this.options.hooks[hookType].length] = !!name ? {name: name, fn: fn} : fn;
return this;
};

/**
* A hook that is run before validation
* @param {String} name
* @param {Function} fn A callback function that is called with instance, callback(err)
* @param {Function} fn A callback function that is called with instance, options, callback(err)
*/
Hooks.beforeValidate = function(name, fn) {
return Hooks.addHook.call(this, 'beforeValidate', name, fn);
Expand All @@ -170,7 +157,7 @@ Hooks.beforeValidate = function(name, fn) {
/**
* A hook that is run after validation
* @param {String} name
* @param {Function} fn A callback function that is called with instance, callback(err)
* @param {Function} fn A callback function that is called with instance, options, callback(err)
*/
Hooks.afterValidate = function(name, fn) {
return Hooks.addHook.call(this, 'afterValidate', name, fn);
Expand All @@ -179,7 +166,7 @@ Hooks.afterValidate = function(name, fn) {
/**
* A hook that is run before creating a single instance
* @param {String} name
* @param {Function} fn A callback function that is called with attributes, callback(err)
* @param {Function} fn A callback function that is called with attributes, options, callback(err)
*/
Hooks.beforeCreate = function(name, fn) {
return Hooks.addHook.call(this, 'beforeCreate', name, fn);
Expand All @@ -188,7 +175,7 @@ Hooks.beforeCreate = function(name, fn) {
/**
* A hook that is run after creating a single instance
* @param {String} name
* @param {Function} fn A callback function that is called with attributes, callback(err)
* @param {Function} fn A callback function that is called with attributes, options, callback(err)
*/
Hooks.afterCreate = function(name, fn) {
return Hooks.addHook.call(this, 'afterCreate', name, fn);
Expand All @@ -197,37 +184,37 @@ Hooks.afterCreate = function(name, fn) {
/**
* A hook that is run before destroying a single instance
* @param {String} name
* @param {Function} fn A callback function that is called with instance, callback(err)
* @param {Function} fn A callback function that is called with instance, options, callback(err)
*
* @alias beforeDelete
*/
Hooks.beforeDestroy = function(name, fn) {
return Hooks.addHook.call(this, 'beforeDestroy', name, fn);
};

Hooks.beforeDelete = function(name, fn) {
return Hooks.addHook.call(this, 'beforeDelete', name, fn);
};

/**
* A hook that is run after destroying a single instance
* @param {String} name
* @param {Function} fn A callback function that is called with instance, callback(err)
* @param {Function} fn A callback function that is called with instance, options, callback(err)
*
* @alias afterDelete
*/
Hooks.afterDestroy = function(name, fn) {
return Hooks.addHook.call(this, 'afterDestroy', name, fn);
};

Hooks.beforeDelete = function(name, fn) {
return Hooks.addHook.call(this, 'beforeDelete', name, fn);
};

Hooks.afterDelete = function(name, fn) {
return Hooks.addHook.call(this, 'afterDelete', name, fn);
};

/**
* A hook that is run before updating a single instance
* @param {String} name
* @param {Function} fn A callback function that is called with instance, callback(err)
* @param {Function} fn A callback function that is called with instance, options, callback(err)
*/
Hooks.beforeUpdate = function(name, fn) {
return Hooks.addHook.call(this, 'beforeUpdate', name, fn);
Expand All @@ -236,7 +223,7 @@ Hooks.beforeUpdate = function(name, fn) {
/**
* A hook that is run after updating a single instance
* @param {String} name
* @param {Function} fn A callback function that is called with instance, callback(err)
* @param {Function} fn A callback function that is called with instance, options, callback(err)
*/
Hooks.afterUpdate = function(name, fn) {
return Hooks.addHook.call(this, 'afterUpdate', name, fn);
Expand All @@ -245,7 +232,7 @@ Hooks.afterUpdate = function(name, fn) {
/**
* A hook that is run before creating instances in bulk
* @param {String} name
* @param {Function} fn A callback function that is called with instances, fields, callback(err)
* @param {Function} fn A callback function that is called with instances, options, callback(err)
*/
Hooks.beforeBulkCreate = function(name, fn) {
return Hooks.addHook.call(this, 'beforeBulkCreate', name, fn);
Expand All @@ -254,34 +241,46 @@ Hooks.beforeBulkCreate = function(name, fn) {
/**
* A hook that is run after creating instances in bulk
* @param {String} name
* @param {Function} fn A callback function that is called with instances, fields, callback(err)
* @param {Function} fn A callback function that is called with instances, options, callback(err)
*/
Hooks.afterBulkCreate = function(name, fn) {
return Hooks.addHook.call(this, 'afterBulkCreate', name, fn);
};

/**
* A hook that is run before destroing instances in bulk
* A hook that is run before destroying instances in bulk
* @param {String} name
* @param {Function} fn A callback function that is called with where, callback(err)
* @param {Function} fn A callback function that is called with options, callback(err)
*
* @alias beforeBulkDelete
*/
Hooks.beforeBulkDestroy = function(name, fn) {
return Hooks.addHook.call(this, 'beforeBulkDestroy', name, fn);
};

Hooks.beforeBulkDelete = function(name, fn) {
return Hooks.addHook.call(this, 'beforeBulkDelete', name, fn);
};

/**
* A hook that is run after destroying instances in bulk
* @param {String} name
* @param {Function} fn A callback function that is called with where, callback(err)
* @param {Function} fn A callback function that is called with options, callback(err)
*
* @alias afterBulkDelete
*/
Hooks.afterBulkDestroy = function(name, fn) {
return Hooks.addHook.call(this, 'afterBulkDestroy', name, fn);
};

Hooks.afterBulkDelete = function(name, fn) {
return Hooks.addHook.call(this, 'afterBulkDelete', name, fn);
};

/**
* A hook that is run after updating instances in bulk
* @param {String} name
* @param {Function} fn A callback function that is called with attribute, where, callback(err)
* @param {Function} fn A callback function that is called with options, callback(err)
*/
Hooks.beforeBulkUpdate = function(name, fn) {
return Hooks.addHook.call(this, 'beforeBulkUpdate', name, fn);
Expand All @@ -290,8 +289,44 @@ Hooks.beforeBulkUpdate = function(name, fn) {
/**
* A hook that is run after updating instances in bulk
* @param {String} name
* @param {Function} fn A callback function that is called with attribute, where, callback(err)
* @param {Function} fn A callback function that is called with options, callback(err)
*/
Hooks.afterBulkUpdate = function(name, fn) {
return Hooks.addHook.call(this, 'afterBulkUpdate', name, fn);
};

/**
* A hook that is run before a find (select) query
* @param {String} name
* @param {Function} fn A callback function that is called with options, callback(err)
*/
Hooks.beforeFind = function(name, fn) {
return Hooks.addHook.call(this, 'beforeFind', name, fn);
};

/**
* A hook that is run before a find (select) query, after any { include: {all: ...} } options are expanded
* @param {String} name
* @param {Function} fn A callback function that is called with options, callback(err)
*/
Hooks.beforeFindAfterExpandIncludeAll = function(name, fn) {
return Hooks.addHook.call(this, 'beforeFindAfterExpandIncludeAll', name, fn);
};

/**
* A hook that is run before a find (select) query, after all option parsing is complete
* @param {String} name
* @param {Function} fn A callback function that is called with options, callback(err)
*/
Hooks.beforeFindAfterOptions = function(name, fn) {
return Hooks.addHook.call(this, 'beforeFindAfterOptions', name, fn);
};

/**
* A hook that is run after a find (select) query
* @param {String} name
* @param {Function} fn A callback function that is called with instance(s), options, callback(err)
*/
Hooks.afterFind = function(name, fn) {
return Hooks.addHook.call(this, 'afterFind', name, fn);
};
4 changes: 2 additions & 2 deletions lib/instance-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,14 @@ InstanceValidator.prototype.validate = function() {
*/
InstanceValidator.prototype.hookValidate = function() {
var self = this;
return self.modelInstance.Model.runHooks('beforeValidate', self.modelInstance).then(function() {
return self.modelInstance.Model.runHooks('beforeValidate', self.modelInstance, self.options).then(function() {
return self.validate().then(function(error) {
if (error) {
throw error;
}
});
}).then(function() {
return self.modelInstance.Model.runHooks('afterValidate', self.modelInstance);
return self.modelInstance.Model.runHooks('afterValidate', self.modelInstance, self.options);
}).return(self.modelInstance);
};

Expand Down
Loading

0 comments on commit 03f49f6

Please sign in to comment.