initial
This commit is contained in:
+200
@@ -0,0 +1,200 @@
|
||||
'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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Register methods for this model
|
||||
*
|
||||
* @param {Model} model
|
||||
* @param {Schema} schema
|
||||
*/
|
||||
|
||||
module.exports = function applyMethods(model, schema) {
|
||||
function apply(method, schema) {
|
||||
Object.defineProperty(model.prototype, method, {
|
||||
get: function() {
|
||||
var h = {};
|
||||
for (var k in schema.methods[method]) {
|
||||
h[k] = schema.methods[method][k].bind(this);
|
||||
}
|
||||
return h;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
for (var method in schema.methods) {
|
||||
if (schema.tree.hasOwnProperty(method)) {
|
||||
throw new Error('You have a method and a property in your schema both ' +
|
||||
'named "' + method + '"');
|
||||
}
|
||||
if (typeof schema.methods[method] === 'function') {
|
||||
model.prototype[method] = schema.methods[method];
|
||||
} else {
|
||||
apply(method, schema);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively call `applyMethods()` on child schemas
|
||||
model.$appliedMethods = true;
|
||||
for (var i = 0; i < schema.childSchemas.length; ++i) {
|
||||
if (schema.childSchemas[i].model.$appliedMethods) {
|
||||
continue;
|
||||
}
|
||||
applyMethods(schema.childSchemas[i].model, schema.childSchemas[i].schema);
|
||||
}
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Register statics for this model
|
||||
* @param {Model} model
|
||||
* @param {Schema} schema
|
||||
*/
|
||||
module.exports = function applyStatics(model, schema) {
|
||||
for (var i in schema.statics) {
|
||||
model[i] = schema.statics[i];
|
||||
}
|
||||
};
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
'use strict';
|
||||
|
||||
var defineKey = require('../document/compile').defineKey;
|
||||
var utils = require('../../utils');
|
||||
|
||||
var CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
|
||||
toJSON: true,
|
||||
toObject: true,
|
||||
_id: true,
|
||||
id: true
|
||||
};
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function discriminator(model, name, schema) {
|
||||
if (!(schema && schema.instanceOfSchema)) {
|
||||
throw new Error('You must pass a valid discriminator Schema');
|
||||
}
|
||||
|
||||
if (model.base && model.base.options.applyPluginsToDiscriminators) {
|
||||
model.base._applyPlugins(schema);
|
||||
}
|
||||
|
||||
if (model.schema.discriminatorMapping &&
|
||||
!model.schema.discriminatorMapping.isRoot) {
|
||||
throw new Error('Discriminator "' + name +
|
||||
'" can only be a discriminator of the root model');
|
||||
}
|
||||
|
||||
var key = model.schema.options.discriminatorKey;
|
||||
|
||||
var baseSchemaAddition = {};
|
||||
baseSchemaAddition[key] = {
|
||||
default: void 0,
|
||||
select: true,
|
||||
set: function(newName) {
|
||||
if (newName === name) {
|
||||
return name;
|
||||
}
|
||||
throw new Error('Can\'t set discriminator key "' + key + '", "' +
|
||||
name + '" !== "' + newName + '"');
|
||||
},
|
||||
$skipDiscriminatorCheck: true
|
||||
};
|
||||
baseSchemaAddition[key][model.schema.options.typeKey] = String;
|
||||
model.schema.add(baseSchemaAddition);
|
||||
defineKey(key, null, model.prototype, null, [key], model.schema.options);
|
||||
|
||||
if (schema.path(key) && schema.path(key).options.$skipDiscriminatorCheck !== true) {
|
||||
throw new Error('Discriminator "' + name +
|
||||
'" cannot have field with name "' + key + '"');
|
||||
}
|
||||
|
||||
function merge(schema, baseSchema) {
|
||||
if (baseSchema.paths._id &&
|
||||
baseSchema.paths._id.options &&
|
||||
!baseSchema.paths._id.options.auto) {
|
||||
var originalSchema = schema;
|
||||
utils.merge(schema, originalSchema, { retainKeyOrder: true });
|
||||
delete schema.paths._id;
|
||||
delete schema.tree._id;
|
||||
}
|
||||
utils.merge(schema, baseSchema, {
|
||||
retainKeyOrder: true,
|
||||
omit: { discriminators: true }
|
||||
});
|
||||
|
||||
var obj = {};
|
||||
obj[key] = {
|
||||
default: name,
|
||||
select: true,
|
||||
set: function(newName) {
|
||||
if (newName === name) {
|
||||
return name;
|
||||
}
|
||||
throw new Error('Can\'t set discriminator key "' + key + '"');
|
||||
},
|
||||
$skipDiscriminatorCheck: true
|
||||
};
|
||||
obj[key][schema.options.typeKey] = String;
|
||||
schema.add(obj);
|
||||
schema.discriminatorMapping = {key: key, value: name, isRoot: false};
|
||||
|
||||
if (baseSchema.options.collection) {
|
||||
schema.options.collection = baseSchema.options.collection;
|
||||
}
|
||||
|
||||
var toJSON = schema.options.toJSON;
|
||||
var toObject = schema.options.toObject;
|
||||
var _id = schema.options._id;
|
||||
var id = schema.options.id;
|
||||
|
||||
var keys = Object.keys(schema.options);
|
||||
schema.options.discriminatorKey = baseSchema.options.discriminatorKey;
|
||||
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
var _key = keys[i];
|
||||
if (!CUSTOMIZABLE_DISCRIMINATOR_OPTIONS[_key]) {
|
||||
if (!utils.deepEqual(schema.options[_key], baseSchema.options[_key])) {
|
||||
throw new Error('Can\'t customize discriminator option ' + _key +
|
||||
' (can only modify ' +
|
||||
Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') +
|
||||
')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
schema.options = utils.clone(baseSchema.options);
|
||||
if (toJSON) schema.options.toJSON = toJSON;
|
||||
if (toObject) schema.options.toObject = toObject;
|
||||
if (typeof _id !== 'undefined') {
|
||||
schema.options._id = _id;
|
||||
}
|
||||
schema.options.id = id;
|
||||
schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks);
|
||||
|
||||
schema.plugins = Array.prototype.slice(baseSchema.plugins);
|
||||
schema.callQueue = baseSchema.callQueue.
|
||||
concat(schema.callQueue.slice(schema._defaultMiddleware.length));
|
||||
schema._requiredpaths = undefined; // reset just in case Schema#requiredPaths() was called on either schema
|
||||
}
|
||||
|
||||
// merges base schema into new discriminator schema and sets new type field.
|
||||
merge(schema, model.schema);
|
||||
|
||||
if (!model.discriminators) {
|
||||
model.discriminators = {};
|
||||
}
|
||||
|
||||
if (!model.schema.discriminatorMapping) {
|
||||
model.schema.discriminatorMapping = {key: key, value: null, isRoot: true};
|
||||
model.schema.discriminators = {};
|
||||
}
|
||||
|
||||
model.schema.discriminators[name] = schema;
|
||||
|
||||
if (model.discriminators[name]) {
|
||||
throw new Error('Discriminator with name "' + name + '" already exists');
|
||||
}
|
||||
|
||||
return schema;
|
||||
};
|
||||
Reference in New Issue
Block a user