From 141e81c29f18fc0658a6116802f2f9c18d07a350 Mon Sep 17 00:00:00 2001 From: Kyle Herock Date: Sun, 30 Oct 2016 03:11:58 -0400 Subject: [PATCH 1/2] feat(schema): added loadClass for importing methods + virtuals from ES6 classes --- lib/schema.js | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/lib/schema.js b/lib/schema.js index 6e63bf77500..b1e68b479f4 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -1504,6 +1504,45 @@ Schema.prototype.remove = function(path) { } }; +/** + * Loads an ES6 class into a schema. Maps setters + getters, static methods, and instance methods to schema virtuals, statics, and methods. + * + * @param {Function} model + */ +Schema.prototype.loadClass = function(model) { + if (model === Object.prototype || model === Function.prototype) { + return this; + } + + // Add static methods + Object.getOwnPropertyNames(model).forEach(function(name) { + if (name.match(/^(length|name|prototype)$/)) { + return; + } + var method = Object.getOwnPropertyDescriptor(model, name); + if (typeof method.value === 'function') this.static(name, method.value); + }, this); + + // Add methods and virtuals + Object.getOwnPropertyNames(model.prototype).forEach(function(name) { + if (name.match(/^(constructor)$/)) { + return; + } + var method = Object.getOwnPropertyDescriptor(model.prototype, name); + if (typeof method.value === 'function') { + this.method(name, method.value); + } + if (typeof method.get === 'function') { + this.virtual(name).get(method.get); + } + if (typeof method.set === 'function') { + this.virtual(name).set(method.set); + } + }, this); + + return (this.loadClass(Object.getPrototypeOf(model))); +}; + /*! * ignore */ From 27d2342456bf366706842e2510db9f1148f52322 Mon Sep 17 00:00:00 2001 From: Kyle Herock Date: Sun, 30 Oct 2016 04:26:51 -0400 Subject: [PATCH 2/2] feat(model): support passing an ES6 class to compile --- lib/index.js | 16 ++++++++++++---- lib/model.js | 27 ++++++++++++++++++--------- lib/schema.js | 24 ++++++++++++++---------- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/lib/index.js b/lib/index.js index 24b5b34bc62..97e8ff3ced9 100644 --- a/lib/index.js +++ b/lib/index.js @@ -309,14 +309,23 @@ Mongoose.prototype.disconnect.$hasSideEffects = true; * var collectionName = 'actor' * var M = mongoose.model('Actor', schema, collectionName) * - * @param {String} name model name + * @param {String|Function} name model name or class extending Model * @param {Schema} [schema] - * @param {String} [collection] name (optional, induced from model name) + * @param {String} [collection] name (optional, inferred from model name) * @param {Boolean} [skipInit] whether to skip initialization (defaults to false) * @api public */ Mongoose.prototype.model = function(name, schema, collection, skipInit) { + var model; + if (typeof name === 'function') { + model = name; + name = model.name; + if (!(model.prototype instanceof Model)) { + throw new mongoose.Error('The provided class ' + name + ' must extend Model'); + } + } + if (typeof schema === 'string') { collection = schema; schema = false; @@ -354,7 +363,6 @@ Mongoose.prototype.model = function(name, schema, collection, skipInit) { this._applyPlugins(schema); } - var model; var sub; // connection.model() may be passing a different schema for @@ -393,7 +401,7 @@ Mongoose.prototype.model = function(name, schema, collection, skipInit) { } var connection = options.connection || this.connection; - model = this.Model.compile(name, schema, collection, connection, this); + model = this.Model.compile(model || name, schema, collection, connection, this); if (!skipInit) { model.init(); diff --git a/lib/model.js b/lib/model.js index 85c38a01555..58e4c43738e 100644 --- a/lib/model.js +++ b/lib/model.js @@ -3190,7 +3190,7 @@ Model._getSchema = function _getSchema(path) { /*! * Compiler utility. * - * @param {String} name model name + * @param {String|Function} name model name or class extending Model * @param {Schema} schema * @param {String} collectionName * @param {Connection} connection @@ -3207,19 +3207,28 @@ Model.compile = function compile(name, schema, collectionName, connection, base) schema.add(o); } - // generate new class - function model(doc, fields, skipId) { - if (!(this instanceof model)) { - return new model(doc, fields, skipId); - } - Model.call(this, doc, fields, skipId); + var model; + if (typeof name === 'function' && name.prototype instanceof Model) { + model = name; + name = model.name; + schema.loadClass(model, true); + } else { + // generate new class + model = function model(doc, fields, skipId) { + if (!(this instanceof model)) { + return new model(doc, fields, skipId); + } + Model.call(this, doc, fields, skipId); + }; } model.hooks = schema.s.hooks.clone(); model.base = base; model.modelName = name; - model.__proto__ = Model; - model.prototype.__proto__ = Model.prototype; + if (!(model.prototype instanceof Model)) { + model.__proto__ = Model; + model.prototype.__proto__ = Model.prototype; + } model.model = Model.prototype.model; model.db = model.prototype.db = connection; model.discriminators = model.prototype.discriminators = undefined; diff --git a/lib/schema.js b/lib/schema.js index b1e68b479f4..251592ebc11 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -1509,19 +1509,21 @@ Schema.prototype.remove = function(path) { * * @param {Function} model */ -Schema.prototype.loadClass = function(model) { +Schema.prototype.loadClass = function(model, virtualsOnly) { if (model === Object.prototype || model === Function.prototype) { return this; } // Add static methods - Object.getOwnPropertyNames(model).forEach(function(name) { - if (name.match(/^(length|name|prototype)$/)) { - return; - } - var method = Object.getOwnPropertyDescriptor(model, name); - if (typeof method.value === 'function') this.static(name, method.value); - }, this); + if (!virtualsOnly) { + Object.getOwnPropertyNames(model).forEach(function(name) { + if (name.match(/^(length|name|prototype)$/)) { + return; + } + var method = Object.getOwnPropertyDescriptor(model, name); + if (typeof method.value === 'function') this.static(name, method.value); + }, this); + } // Add methods and virtuals Object.getOwnPropertyNames(model.prototype).forEach(function(name) { @@ -1529,8 +1531,10 @@ Schema.prototype.loadClass = function(model) { return; } var method = Object.getOwnPropertyDescriptor(model.prototype, name); - if (typeof method.value === 'function') { - this.method(name, method.value); + if (!virtualsOnly) { + if (typeof method.value === 'function') { + this.method(name, method.value); + } } if (typeof method.get === 'function') { this.virtual(name).get(method.get);