123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- '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);
- }
- }
- }
|