'use strict';
|
|
|
|
var PromiseProvider = require('../../promise_provider');
|
|
var VersionError = require('../../error').VersionError;
|
|
|
|
module.exports = applyHooks;
|
|
|
|
/*!
|
|
* Register hooks for this model
|
|
*
|
|
* @param {Model} model
|
|
* @param {Schema} schema
|
|
*/
|
|
|
|
function applyHooks(model, schema) {
|
|
var q = schema && schema.callQueue;
|
|
var toWrapEl;
|
|
var len;
|
|
var i;
|
|
var j;
|
|
var pointCut;
|
|
var keys;
|
|
var newName;
|
|
|
|
model.$appliedHooks = true;
|
|
for (i = 0; i < schema.childSchemas.length; ++i) {
|
|
var childModel = schema.childSchemas[i].model;
|
|
if (childModel.$appliedHooks) {
|
|
continue;
|
|
}
|
|
applyHooks(childModel, schema.childSchemas[i].schema);
|
|
if (childModel.discriminators != null) {
|
|
keys = Object.keys(childModel.discriminators);
|
|
for (j = 0; j < keys.length; ++j) {
|
|
applyHooks(childModel.discriminators[keys[j]],
|
|
childModel.discriminators[keys[j]].schema);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!q.length) {
|
|
return;
|
|
}
|
|
|
|
// we are only interested in 'pre' hooks, and group by point-cut
|
|
var toWrap = { post: [] };
|
|
var pair;
|
|
|
|
for (i = 0; i < q.length; ++i) {
|
|
pair = q[i];
|
|
if (pair[0] !== 'pre' && pair[0] !== 'post' && pair[0] !== 'on') {
|
|
continue;
|
|
}
|
|
var args = [].slice.call(pair[1]);
|
|
pointCut = pair[0] === 'on' ? 'post' : args[0];
|
|
if (!(pointCut in toWrap)) {
|
|
toWrap[pointCut] = {post: [], pre: []};
|
|
}
|
|
if (pair[0] === 'post') {
|
|
toWrap[pointCut].post.push(args);
|
|
} else if (pair[0] === 'on') {
|
|
toWrap[pointCut].push(args);
|
|
} else {
|
|
toWrap[pointCut].pre.push(args);
|
|
}
|
|
}
|
|
|
|
// 'post' hooks are simpler
|
|
len = toWrap.post.length;
|
|
toWrap.post.forEach(function(args) {
|
|
model.on.apply(model, args);
|
|
});
|
|
delete toWrap.post;
|
|
|
|
// 'init' should be synchronous on subdocuments
|
|
if (toWrap.init && (model.$isSingleNested || model.$isArraySubdocument)) {
|
|
if (toWrap.init.pre) {
|
|
toWrap.init.pre.forEach(function(args) {
|
|
model.prototype.$pre.apply(model.prototype, args);
|
|
});
|
|
}
|
|
if (toWrap.init.post) {
|
|
toWrap.init.post.forEach(function(args) {
|
|
model.prototype.$post.apply(model.prototype, args);
|
|
});
|
|
}
|
|
delete toWrap.init;
|
|
}
|
|
if (toWrap.set) {
|
|
// Set hooks also need to be sync re: gh-3479
|
|
newName = '$__original_set';
|
|
model.prototype[newName] = model.prototype.set;
|
|
if (toWrap.set.pre) {
|
|
toWrap.set.pre.forEach(function(args) {
|
|
model.prototype.$pre.apply(model.prototype, args);
|
|
});
|
|
}
|
|
if (toWrap.set.post) {
|
|
toWrap.set.post.forEach(function(args) {
|
|
model.prototype.$post.apply(model.prototype, args);
|
|
});
|
|
}
|
|
delete toWrap.set;
|
|
}
|
|
|
|
toWrap.validate = toWrap.validate || { pre: [], post: [] };
|
|
|
|
keys = Object.keys(toWrap);
|
|
len = keys.length;
|
|
for (i = 0; i < len; ++i) {
|
|
pointCut = keys[i];
|
|
// this is so we can wrap everything into a promise;
|
|
newName = ('$__original_' + pointCut);
|
|
if (!model.prototype[pointCut]) {
|
|
continue;
|
|
}
|
|
if (model.prototype[pointCut].$isWrapped) {
|
|
continue;
|
|
}
|
|
if (!model.prototype[pointCut].$originalFunction) {
|
|
model.prototype[newName] = model.prototype[pointCut];
|
|
}
|
|
model.prototype[pointCut] = (function(_newName) {
|
|
return function wrappedPointCut() {
|
|
var Promise = PromiseProvider.get();
|
|
|
|
var _this = this;
|
|
var args = [].slice.call(arguments);
|
|
var lastArg = args.pop();
|
|
var fn;
|
|
var originalError = new Error();
|
|
var $results;
|
|
if (lastArg && typeof lastArg !== 'function') {
|
|
args.push(lastArg);
|
|
} else {
|
|
fn = lastArg;
|
|
}
|
|
|
|
var promise = new Promise.ES6(function(resolve, reject) {
|
|
args.push(function(error) {
|
|
if (error) {
|
|
// gh-2633: since VersionError is very generic, take the
|
|
// stack trace of the original save() function call rather
|
|
// than the async trace
|
|
if (error instanceof VersionError) {
|
|
error.stack = originalError.stack;
|
|
}
|
|
if (!fn) {
|
|
_this.$__handleReject(error);
|
|
}
|
|
reject(error);
|
|
return;
|
|
}
|
|
|
|
// There may be multiple results and promise libs other than
|
|
// mpromise don't support passing multiple values to `resolve()`
|
|
$results = Array.prototype.slice.call(arguments, 1);
|
|
resolve.apply(promise, $results);
|
|
});
|
|
|
|
_this[_newName].apply(_this, args);
|
|
});
|
|
if (fn) {
|
|
if (this.constructor.$wrapCallback) {
|
|
fn = this.constructor.$wrapCallback(fn);
|
|
}
|
|
promise.then(
|
|
function() {
|
|
process.nextTick(function() {
|
|
fn.apply(null, [null].concat($results));
|
|
});
|
|
},
|
|
function(error) {
|
|
process.nextTick(function() {
|
|
fn(error);
|
|
});
|
|
});
|
|
}
|
|
return promise;
|
|
};
|
|
})(newName);
|
|
model.prototype[pointCut].$originalFunction = newName;
|
|
model.prototype[pointCut].$isWrapped = true;
|
|
|
|
toWrapEl = toWrap[pointCut];
|
|
var _len = toWrapEl.pre.length;
|
|
for (j = 0; j < _len; ++j) {
|
|
args = toWrapEl.pre[j];
|
|
args[0] = newName;
|
|
model.prototype.$pre.apply(model.prototype, args);
|
|
}
|
|
|
|
_len = toWrapEl.post.length;
|
|
for (j = 0; j < _len; ++j) {
|
|
args = toWrapEl.post[j];
|
|
args[0] = newName;
|
|
model.prototype.$post.apply(model.prototype, args);
|
|
}
|
|
}
|
|
}
|