diff --git a/.node-version b/.node-version index af8c8ec..486db33 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -4.2.2 +8.9.1 diff --git a/lib/belongsTo.js b/lib/belongsTo.js index f4b6ace..bd11b2a 100644 --- a/lib/belongsTo.js +++ b/lib/belongsTo.js @@ -1,40 +1,43 @@ -var ObjectId = require('mongoose').Schema.ObjectId, - i = require('i')(); - -module.exports = function (schema, associationName, options) { - options = options || {}; - var paths = {}, - pathName = associationName, - idCast = { - type: ObjectId, - ref: (options.modelName || i.classify(associationName)), - relationshipType: 'belongsTo', - index: true, - required: !!options.required - }; - - if (options.polymorphic) { - idCast.polymorphic = true; +'use strict'; - var typeCast = { - polymorphic: true, - type: String, - required: idCast.required, - enum: options.enum +module.exports = function (mongoose, i) { + return function (associationName, options) { + options = options || {}; + let paths = {}; + let pathName = associationName; + let idCast = { + type: mongoose.Schema.ObjectId, + ref: (options.modelName || i.classify(associationName)), + relationshipType: 'belongsTo', + index: true, + required: !!options.required }; - paths[pathName + '_type'] = typeCast; - } + if (options.polymorphic) { + idCast.polymorphic = true; + + let typeCast = { + polymorphic: true, + type: String, + required: idCast.required, + enum: options.enum + }; - paths[pathName] = idCast; + paths[pathName + '_type'] = typeCast; + } - schema.add(paths); + paths[pathName] = idCast; - if (options.touch) { - schema.pre('save', function(next) { - this.populate(associationName, function(err, model) { - this[associationName].__touch(next); - }.bind(this)); - }); + this.add(paths); + + if (options.touch) { + this.pre('save', function (next) { + if (this.isNew) { return next(); } + this.populate(associationName, (err, model) => { + this[associationName].__touch(next); + }); + }); + }; }; + }; diff --git a/lib/hasAndBelongsToMany.js b/lib/hasAndBelongsToMany.js index f2c06b5..345557c 100644 --- a/lib/hasAndBelongsToMany.js +++ b/lib/hasAndBelongsToMany.js @@ -1,12 +1,13 @@ 'use strict'; -let mongoose = require('mongoose'); -let i = require('i')(); +module.exports = function (mongoose, i) { + + return function hasAndBelongsToMany (model, options) { + let modelName = i.classify(model); + let path = { }; + path[model] = [{ type: mongoose.Schema.ObjectId, ref: modelName }]; + this.add(path); + this.paths[model].options.siblingPathName = options.inverseOf; + }; -module.exports = function hasAndBelongsToMany (schema, model, options) { - let modelName = i.classify(model); - let path = { }; - path[model] = [{ type: mongoose.Schema.ObjectId, ref: modelName }]; - schema.add(path); - schema.paths[model].options.siblingPathName = options.inverseOf; }; diff --git a/lib/hasMany.js b/lib/hasMany.js index 250369f..5a7b64f 100644 --- a/lib/hasMany.js +++ b/lib/hasMany.js @@ -1,181 +1,180 @@ -var mongoose = require('mongoose') - , ObjectId = mongoose.Schema.ObjectId - , i = require('i')() - , hasManyDestroy - , hasManyDelete - , hasManyNullify - , associate; - -function hasManyDestroy (associationName, next) { - this[associationName].find(function (err, children) { - if (err) return next(err); - var count = children.length; - children.forEach(function (child) { - child.remove(function (err) { - if (err) return next(err); - --count || next(); +module.exports = function (mongoose, i) { + + function hasManyDestroy (associationName, next) { + this[associationName].find(function (err, children) { + if (err) return next(err); + let count = children.length; + children.forEach(function (child) { + child.remove(function (err) { + if (err) return next(err); + --count || next(); + }); }); }); - }); -}; + }; -function hasManyDelete (associationName, next) { - this[associationName].find().remove(next); -}; + function hasManyDelete (associationName, next) { + this[associationName].find().remove(next); + }; -function hasManyNullify (associationName, next) { - var Model = this[associationName].associationModel - , conditions = this[associationName].find()._conditions - , fieldsToUnset = this[associationName].find()._conditions - , options = { multi: true }; + function hasManyNullify (associationName, next) { + let Model = this[associationName].associationModel + , conditions = this[associationName].find()._conditions + , fieldsToUnset = this[associationName].find()._conditions + , options = { multi: true }; - for (field in fieldsToUnset) { fieldsToUnset[field] = 1; } + for (field in fieldsToUnset) { fieldsToUnset[field] = 1; } - Model.update(conditions, { $unset: fieldsToUnset }, options, next); -}; + Model.update(conditions, { $unset: fieldsToUnset }, options, next); + }; -var dependencyStrategy = { - destroy: hasManyDestroy, - delete: hasManyDelete, - nullify: hasManyNullify -}; + let dependencyStrategy = { + destroy: hasManyDestroy, + delete: hasManyDelete, + nullify: hasManyNullify + }; -function associate (object) { - object[this.foreignKey] = this.doc._id; - if (this.foreignKeyType) { - object[this.foreignKeyType] = this.modelName; - } - return object; -}; + function associate (object) { + object[this.foreignKey] = this.doc._id; + if (this.foreignKeyType) { + object[this.foreignKeyType] = this.modelName; + } + return object; + }; -function HasMany (doc, options) { - options = options || {}; - this._options = options; + function HasMany (doc, options) { + options = options || {}; + this._options = options; - this.as = this._options.as; - this.inverse_of = this._options.inverse_of; + this.as = this._options.as; + this.inverse_of = this._options.inverse_of; - this.doc = doc; - this.modelName = this.doc.constructor.modelName; - this.Model = mongoose.model(this.modelName); + this.doc = doc; + this.modelName = this.doc.constructor.modelName; + this.Model = mongoose.model(this.modelName); - this.associationModelName = this._options.associationModelName; - this.associationModel = mongoose.model(this.associationModelName); + this.associationModelName = this._options.associationModelName; + this.associationModel = mongoose.model(this.associationModelName); - if (this.as) { - this.foreignKey = this.as; - this.foreignKeyType = this.as + '_type'; - } - else { - this.foreignKey = this.inverse_of || i.underscore(this.modelName); + if (this.as) { + this.foreignKey = this.as; + this.foreignKeyType = this.as + '_type'; + } + else { + this.foreignKey = this.inverse_of || i.underscore(this.modelName); + } } -} -HasMany.prototype.build = function(objects) { - var Model; + HasMany.prototype.build = function(object) { + let doc = new this.associationModel(object || {}); + return associate.bind(this)(doc); + }; - if (Array.isArray(objects)) { - return objects.map(function(object) { - return this.build(object); - }.bind(this)); - } - else { - return associate.bind(this)(new this.associationModel(objects || {})); - } -}; + HasMany.prototype.create = function() { + let self = this; + let objects = Array.from(arguments); + let cb; -HasMany.prototype.create = function(objects, callback) { - if (Array.isArray(objects)) { - var docs = [], count = objects.length; + if ('function' == typeof objects[objects.length - 1]) { + cb = objects.pop(); + } - objects.forEach(function(object) { - this.create(object, function(err, doc) { - if (err) return callback(err); - docs.push(doc); - --count || callback(null, docs); + return new Promise(function (resolve, reject) { + let promises = objects.map(function (object) { + return self.build(object).save(); }); - }.bind(this)); - } - else { - this.build(objects).save(callback); + + Promise.all(promises).then(function (docs) { + cb && cb.apply(null, [ null ].concat(docs)); + resolve.apply(null, docs); + }).catch(function (err) { + cb && cb(err); + reject(err); + }); + }); }; -}; -HasMany.prototype.find = function(conditions, fields, options, callback) { - if ('function' == typeof conditions) { - callback = conditions; - conditions = {}; - } + HasMany.prototype.find = function(conditions, fields, options, callback) { + if ('function' == typeof conditions) { + callback = conditions; + conditions = {}; + } - conditions = conditions || {}; - fields = fields || null; - options = options || null; + conditions = conditions || {}; + fields = fields || null; + options = options || null; - associate.bind(this)(conditions); - return this.associationModel.find(conditions, fields, options, callback); -}; + condition = associate.bind(this)(conditions); + return this.associationModel.find(conditions, fields, options, callback); + }; -HasMany.prototype.findOne = function() { - var callback - , args = Array.prototype.slice.call(arguments); + HasMany.prototype.findOne = function() { + let callback + , args = Array.from(arguments); - if ('function' == typeof args[args.length - 1]) { - callback = args[args.length - 1]; - args.pop(); - } + if ('function' == typeof args[args.length - 1]) { + callback = args.pop(); + } - return this.find.apply(this, args).findOne(callback); -}; + return this.find.apply(this, args).findOne(callback); + }; -HasMany.prototype.concat = function(objects, callback) { - var docs = []; + HasMany.prototype.concat = function() { + let self = this; + let objects = Array.from(arguments); + let cb; - if (Array.isArray(objects)) { - var count = objects.length; + if ('function' == typeof objects[objects.length - 1]) { + cb = objects.pop(); + } - objects.forEach(function(object) { - this.concat(object, function(err, doc) { - if (err) return callback(err); - docs.push(doc); - --count || callback(null, docs); + return new Promise(function (resolve, reject) { + let promises = objects.map(function (object) { + return associate.bind(self)(object).save(); }); - }.bind(this)); - } - else { - associate.bind(this)(objects).save(callback); - }; -}; -HasMany.prototype.append = HasMany.prototype.concat; -HasMany.prototype.push = function(objects) { - if (Array.isArray(objects)) { - return objects.map(function(object) { return this.push(object); }.bind(this)); - } - else { - return associate.bind(this)(objects); + Promise.all(promises).then(function (docs) { + cb && cb.apply(null, [ null ].concat(docs)); + resolve.apply(null, docs); + }).catch(function (err) { + cb && cb(err); + reject(err); + }); + }); + }; + HasMany.prototype.append = HasMany.prototype.concat; + + HasMany.prototype.push = function(objects) { + if (Array.isArray(objects)) { + return objects.map(function(object) { return this.push(object); }.bind(this)); + } + else { + return associate.bind(this)(objects); + }; }; -}; -HasMany.prototype.remove = function() {}; -HasMany.prototype.delete = function() {}; + HasMany.prototype.remove = function() {}; + HasMany.prototype.delete = function() {}; -module.exports = function (schema, associationName, options) { - options = options || {}; - options.associationName = associationName; - options.associationModelName = options.modelName || i.classify(associationName); + return function (associationName, options) { + options = options || {}; + options.associationName = associationName; + options.associationModelName = options.modelName || i.classify(associationName); - schema.virtual(associationName).get(function() { - return new HasMany(this, options); - }); + this.virtual(associationName).get(function() { + return new HasMany(this, options); + }); - schema.methods.__touch = function(next) { - this.increment(); - this.save(next); - }; + this.methods.__touch = function(next) { + this.increment(); + this.save(next); + }; - if (dependencyStrategy[options.dependent]) { - schema.pre('remove', function(next) { - dependencyStrategy[options.dependent].call(this, associationName, next); - }); + if (dependencyStrategy[options.dependent]) { + this.pre('remove', function(next) { + dependencyStrategy[options.dependent].call(this, associationName, next); + }); + }; }; + }; diff --git a/lib/hasOne.js b/lib/hasOne.js new file mode 100644 index 0000000..9c36d1e --- /dev/null +++ b/lib/hasOne.js @@ -0,0 +1,13 @@ + +module.exports = function (mongoose, i) { + return function (associationName, options) { + let path = {}; + + path[associationName] = { + type: mongoose.Schema.ObjectId, + ref: i.classify(associationName) + } + + this.add(path); + }; +}; diff --git a/lib/index.js b/lib/index.js index 274e6bb..1735429 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,26 +1,20 @@ 'use strict'; -module.exports = function (mongoose) { - let belongsTo = require('./belongsTo'); - let hasMany = require('./hasMany'); - let hasAndBelongsToMany = require('./hasAndBelongsToMany'); - let mongooseArrayDecorator = require('./mongooseArrayDecorator'); +require('./polyfill'); - mongoose = mongooseArrayDecorator(mongoose); +let i = require('i')(); +let utils = require('./utils'); - mongoose.Schema.prototype.belongsTo = function (model, options) { - belongsTo(this, model, options); - }; +module.exports = function mongooseRelation (mongoose) { + mongoose = require('./mongooseArrayDecorator')(mongoose); - mongoose.Schema.prototype.hasMany = function (model, options) { - hasMany(this, model, options); - }; - - mongoose.Schema.prototype.habtm = function (model, options) { - hasAndBelongsToMany(this, model, options); - }; + mongoose.Schema.prototype.hasOne = require('./hasOne')(mongoose, i); + mongoose.Schema.prototype.belongsTo = require('./belongsTo')(mongoose, i); + mongoose.Schema.prototype.hasMany = require('./hasMany')(mongoose, i); + mongoose.Schema.prototype.habtm = require('./hasAndBelongsToMany')(mongoose, i); mongoose.Schema.prototype.hasAndBelongsToMany = mongoose.Schema.prototype.habtm; return mongoose; + }; diff --git a/lib/polyfill.js b/lib/polyfill.js new file mode 100644 index 0000000..ffb003c --- /dev/null +++ b/lib/polyfill.js @@ -0,0 +1,5 @@ +if (!Array.from) { + Array.from = function () { + return Array.prototype.slice.call(arguments); + } +} diff --git a/lib/utils.js b/lib/utils.js index 754f47f..ab1ac0e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -6,7 +6,7 @@ * @param {Object} from */ exports.merge = function merge(to, from) { - var keys = Object.keys(from), i = keys.length, key; + let keys = Object.keys(from), i = keys.length, key; while (i--) { key = keys[i]; if ('undefined' === typeof to[key]) { @@ -27,7 +27,7 @@ exports.random = function () { /** * Pluralization rules. */ -var rules = [ +let rules = [ [ /(m)an$/gi, '$1en' @@ -112,7 +112,7 @@ var rules = [ /** * Uncountable words. */ -var uncountables = [ +let uncountables = [ 'advice', 'energy', 'excretion', @@ -146,7 +146,7 @@ var uncountables = [ * @api private */ exports.pluralize = function (str) { - var rule, found; + let rule, found; if (!~uncountables.indexOf(str.toLowerCase())) { found = rules.filter(function (rule) { return str.match(rule[0]); @@ -158,7 +158,7 @@ exports.pluralize = function (str) { }; exports.extend = function(destination, source) { - for (var k in source) { + for (let k in source) { if (source.hasOwnProperty(k)) { destination.prototype[k] = source[k]; } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6abfb22 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,439 @@ +{ + "name": "mongoose-relation", + "version": "0.5.16", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "bson": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", + "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=", + "dev": true + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "i": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", + "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "kareem": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.0.3.tgz", + "integrity": "sha512-WloXk3nyByx9FEB5WY7WFEXIZB/QA+zy7c2kJMjnZCebjepEyQcJzazgLiKcTS/ltZeEoeEQ1A1pau1fEDlnIA==", + "dev": true + }, + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.0.tgz", + "integrity": "sha512-ukB2dF+u4aeJjc6IGtPNnJXfeby5d4ZqySlIBT0OEyva/DrMjVm5HkQxKnHDLKEfEQBsEnwTg9HHhtPHJdTd8w==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "mongodb": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.2.tgz", + "integrity": "sha512-E50FmpSQchZAimn2uPIegoNoH9UQYR1yiGHtQPmmg8/Ekc97w6owHoqaBoz+assnd9V5LxMzmQ/VEWMsQMgZhQ==", + "dev": true, + "requires": { + "mongodb-core": "3.0.2" + } + }, + "mongodb-core": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.0.2.tgz", + "integrity": "sha512-p1B0qwFQUw6C1OlFJnrOJp8KaX7MuGoogRbTaupRt0y+pPRkMllHWtE9V6i1CDtTvI3/3sy2sQwqWez7zuXEAA==", + "dev": true, + "requires": { + "bson": "1.0.4", + "require_optional": "1.0.1" + } + }, + "mongoose": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.0.3.tgz", + "integrity": "sha512-y4NlLzZaQe5vJHjcEjHLKK6utjs7sVEPN971+d1vVJJGrmA+zeeFA1MEmC1J0ujD34eOSghnExXJPwCrxHHZCw==", + "dev": true, + "requires": { + "async": "2.1.4", + "bson": "1.0.4", + "kareem": "2.0.3", + "lodash.get": "4.4.2", + "mongodb": "3.0.2", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.3.0", + "mquery": "3.0.0", + "ms": "2.0.0", + "regexp-clone": "0.0.1", + "sliced": "1.0.1" + }, + "dependencies": { + "async": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", + "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", + "dev": true, + "requires": { + "lodash": "4.17.5" + } + } + } + }, + "mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", + "dev": true + }, + "mpath": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz", + "integrity": "sha1-elj3iem1/TyUUgY0FXlg8mvV70Q=", + "dev": true + }, + "mquery": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.0.0.tgz", + "integrity": "sha512-WL1Lk8v4l8VFSSwN3yCzY9TXw+fKVYKn6f+w86TRzOLSE8k1yTgGaLBPUByJQi8VcLbOdnUneFV/y3Kv874pnQ==", + "dev": true, + "requires": { + "bluebird": "3.5.0", + "debug": "2.6.9", + "regexp-clone": "0.0.1", + "sliced": "0.0.5" + }, + "dependencies": { + "sliced": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", + "integrity": "sha1-XtwETKTrb3gW1Qui/GPiXY/kcH8=", + "dev": true + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "regexp-clone": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", + "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=", + "dev": true + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "dev": true, + "requires": { + "resolve-from": "2.0.0", + "semver": "5.5.0" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "should": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.1.tgz", + "integrity": "sha512-l+/NwEMO+DcstsHEwPHRHzC9j4UOE3VQwJGcMWSsD/vqpqHbnQ+1iSHy64Ihmmjx1uiRPD9pFadTSc3MJtXAgw==", + "dev": true, + "requires": { + "should-equal": "2.0.0", + "should-format": "3.0.3", + "should-type": "1.4.0", + "should-type-adaptors": "1.1.0", + "should-util": "1.0.0" + } + }, + "should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "dev": true, + "requires": { + "should-type": "1.4.0" + } + }, + "should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", + "dev": true, + "requires": { + "should-type": "1.4.0", + "should-type-adaptors": "1.1.0" + } + }, + "should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", + "dev": true + }, + "should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "dev": true, + "requires": { + "should-type": "1.4.0", + "should-util": "1.0.0" + } + }, + "should-util": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz", + "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", + "dev": true + }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=", + "dev": true + }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha1-Gsig2Ug4SNFpXkGLbQMaPDzmjjs=" + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "why-is-node-running": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-1.2.3.tgz", + "integrity": "sha512-Y8ip4Gagz6GI7P7Q8AbKkyNR52LZEXH98rfncNaOHHBvPk/L409CYvheqpRJUoaDcyx9pxwLiEzEf1ZJ7U1KPg==", + "requires": { + "stackback": "0.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "wtfnode": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.5.7.tgz", + "integrity": "sha1-/y9Kg6iJ9Z1wh0L1IwivpogF2Cw=" + } + } +} diff --git a/package.json b/package.json index 26fa187..08dabfd 100644 --- a/package.json +++ b/package.json @@ -24,16 +24,18 @@ }, "main": "./index.js", "engines": { - "node": ">= 0.2.0" + "node": ">= 8" }, "dependencies": { - "mongoose": ">=3.0.0", "async": "^0.9.0", - "i": "^0.3.2" + "i": "^0.3.2", + "why-is-node-running": "^1.2.3", + "wtfnode": "^0.5.7" }, "devDependencies": { - "mocha": "latest", - "should": "latest", - "node-uuid": "^1.4.3" + "mocha": "^5.0.0", + "mongoose": ">=3.0.0", + "node-uuid": "latest", + "should": "latest" } } diff --git a/specs/belongsTo.spec.js b/specs/belongsTo.spec.js index 5448350..e6a5663 100644 --- a/specs/belongsTo.spec.js +++ b/specs/belongsTo.spec.js @@ -1,164 +1,165 @@ -require('./spec_helper'); +'use strict'; -var mongoose = require('mongoose'), - should = require('should'), - uuid = require('node-uuid'), - schema; +const mongoose = require('./');; +const should = require('should'); +const uuid = require('node-uuid'); describe('belongsTo', function() { - subject = null; + let membershipSchema, Membership, + userSchema, User, + accountSchema, Account; before(function() { - var partSchema = new mongoose.Schema({}); - partSchema.belongsTo('widget'); - schema = mongoose.model('Part_' + uuid.v4(), partSchema).schema; - subject = schema.paths.widget; + Membership = mongoose.model('Membership'); + membershipSchema = Membership.schema; + + Account = mongoose.model('Account'); + accountSchema = Account.schema; + + User = mongoose.model('User'); + userSchema = User.schema; }); it('creates a path for widget on the schema', function() { - should(schema.paths.widget).exist; + should(membershipSchema.paths.user).exist; + should(membershipSchema.paths.account).exist; }); it('sets the relationship type', function() { - should(subject.options.relationshipType).equal('belongsTo'); + should(membershipSchema.paths.user.options.relationshipType).equal('belongsTo'); + should(membershipSchema.paths.account.options.relationshipType).equal('belongsTo'); }); it('sets the instance', function() { - should(subject.instance).equal('ObjectID'); + should(membershipSchema.paths.user.instance).equal('ObjectID'); + should(membershipSchema.paths.account.instance).equal('ObjectID'); }); it('sets the ref', function() { - should(subject.options.ref).equal('Widget'); + should(membershipSchema.paths.user.options.ref).equal('User'); + should(membershipSchema.paths.account.options.ref).equal('Account'); }); - it('defaults required to undefined', function() { - should(subject.isRequired).eql(false); + it('can set required', function() { + should(membershipSchema.paths.user.isRequired).eql(true); + should(membershipSchema.paths.account.isRequired).eql(false); }); describe('options', function() { + let path; + describe('custom name', function() { before(function() { - partSchema = new mongoose.Schema({}); - partSchema.belongsTo('owner', { modelName: 'Widget' }); - schema = mongoose.model('Part_' + uuid.v4(), partSchema).schema; - subject = schema.paths.owner; + path = mongoose.model('BenefitBundlePrice').schema.paths.availability_zone; }); it('sets the custom named path', function() { - should(subject).not.equal(undefined); + should(path).not.equal(undefined); }); it('sets ref to the passed in modelName', function() { - should(subject.options.ref).equal('Widget'); + should(path.options.ref).equal('ServiceArea'); }); }); describe('required', function() { + let path; + before(function() { - partSchema = new mongoose.Schema({}); - partSchema.belongsTo('widget', { required: true }); - schema = mongoose.model('Part_' + uuid.v4(), partSchema).schema; - subject = schema.paths.widget; + path = mongoose.model('BenefitBundlePrice').schema.paths.plan; }); it('passes through the required field', function() { - should(subject.isRequired).be.true; + should(path.isRequired).be.true; }); }); describe('polymorphic', function() { + let subject_type, subject_id; + before(function() { - var partSchema = new mongoose.Schema({}), - spareSchema = new mongoose.Schema({}); - partSchema.belongsTo('assemblable', { polymorphic: true, required: true, enum: [ 'Bed', 'Dresser', 'Chair' ] }); - spareSchema.belongsTo('assemblable', { polymorphic: true, }); - schema = mongoose.model('Part_' + uuid.v4(), partSchema).schema; - spareModel = mongoose.model('Spare_' + uuid.v4(), spareSchema).schema; + subject_type = mongoose.model('Note').schema.paths.noteable_type; + subject_id = mongoose.model('Note').schema.paths.noteable; }); describe('ObjectID half', function() { - before(function() { subject = schema.paths.assemblable; }); - it('exists', function() { - should(subject).exist; + should(subject_id).exist; }); it('sets the id property', function() { - should(subject.instance).equal('ObjectID'); + should(subject_id.instance).equal('ObjectID'); }); it('knows it is a part of a polymorphic relationship', function() { - should(subject.options.polymorphic).be.true; + should(subject_id.options.polymorphic).be.true; }); it('passes through options', function() { - should(subject.isRequired).eql(true); + should(subject_id.isRequired).eql(true); }); }); describe('Type half', function() { - before(function() { subject = schema.paths.assemblable_type; }); - it('creates the type path', function() { - should(subject).not.eql(undefined); + should(subject_type).not.eql(undefined); }); it('sets the type as String', function() { - should(subject.instance).equal('String'); + should(subject_type.instance).equal('String'); }); it('passes through options', function() { - should(subject.isRequired).eql(true); + should(subject_type.isRequired).eql(true); }); }); describe('enum', function() { it('applies the provided enum to the _type path', function() { - should(schema.paths.assemblable_type.enumValues).containDeepOrdered([ 'Bed', 'Dresser', 'Chair' ]); + should(subject_type.enumValues).containDeepOrdered([ 'User', 'Account' ]); }); }); - describe('setting required to false', function() { - it('sets required on the id to false', function() { - should(spareModel.paths.assemblable.isRequired).eql(false); + describe('setting required to true', function() { + it('sets required on the id to true', function() { + should(subject_id.isRequired).eql(true); }); - it('sets required on the type to false', function() { - should(spareModel.paths.assemblable_type.isRequired).eql(false); + it('sets required on the type to true', function() { + should(subject_type.isRequired).eql(true); }); }); }); - describe('touch:true', function(done) { - var messageSchema, Message, message - , mailboxSchema, Mailbox, mailbox; - - before(function(done) { - messageSchema = new mongoose.Schema({ }); - messageSchema.belongsTo('mailbox', { touch: true }); - Message = mongoose.model('Message', messageSchema); - - mailboxSchema = new mongoose.Schema({ }); - mailboxSchema.hasMany('messages'); + describe('touch:true', function() { + let membership, userVersion, accountVersion; - Mailbox = mongoose.model('Mailbox', mailboxSchema); - mailbox = new Mailbox(); - mailbox.save(function(err){ - mailbox.messages.create({ }, function(err, msg){ - message = msg; - done(); + before(function() { + let User = mongoose.model('User'); + let Membership = mongoose.model('Membership'); + let Account = mongoose.model('Account'); + + return Promise.all([ + User.create({}), + Account.create({}) + ]).then(function (values) { + return Membership.create({ + user: values[0], + account: values[1] }); + }).then(function (_membership) { + membership = _membership; + userVersion = membership.user.__v; + accountVersion = membership.account.__v; }); }); - it('touches the parent document before save', function(done) { - var oldVersion = mailbox.__v; - message.save(function(err){ - should.strictEqual(err, null); - Mailbox.findById(mailbox._id, function(err, mailbox){ - should(mailbox.__v).not.eql(oldVersion); - done(); - }); + it('touches the parent document before save', function() { + return membership.save().then(function(){ + return Membership.findById(membership._id).populate('user account'); + }).then(function(membership){ + should(membership.user.__v).not.eql(userVersion); + should(membership.account.__v).eql(accountVersion); }); }); }); diff --git a/specs/hasAndBelongsToMany.spec.js b/specs/hasAndBelongsToMany.spec.js index 9365bfd..36a5f44 100644 --- a/specs/hasAndBelongsToMany.spec.js +++ b/specs/hasAndBelongsToMany.spec.js @@ -1,8 +1,6 @@ 'use strict'; -require('./spec_helper'); - -const mongoose = require('mongoose'); +const mongoose = require('./'); const should = require('should'); describe('hasManyBelongsToMany', function() { @@ -138,8 +136,6 @@ describe('hasManyBelongsToMany', function() { should(addresses[1].users).have.length(1); should(addresses[1].users).containEql(user._id); - - done(); }); }); }); diff --git a/specs/hasMany.spec.js b/specs/hasMany.spec.js index 93cbffd..d39a5b5 100644 --- a/specs/hasMany.spec.js +++ b/specs/hasMany.spec.js @@ -1,12 +1,12 @@ -require('./spec_helper'); +'use strict'; -var mongoose = require('mongoose') - , async = require('async') - , should = require('should') - , uuid = require('node-uuid'); +const mongoose = require('./'); +const should = require('should'); +const uuid = require('node-uuid'); -describe('hasMany without options', function(){ - var userSchema, User, user, widgetSchema, Widget, widget; +describe('hasMany', function(){ + let userSchema, User, user, + widgetSchema, Widget, widget; before(function(){ widgetSchema = mongoose.Schema({ name: String }); @@ -19,32 +19,34 @@ describe('hasMany without options', function(){ User = mongoose.model('User', userSchema); }); - describe('schema', function(){ - it('has a virtual to represent the relationship', function(){ - should(userSchema.virtuals.widgets).not.equal(undefined); - should(userSchema.virtuals.widgets.path).equal('widgets'); - should(userSchema.virtuals.wadgets).not.equal(undefined); - should(userSchema.virtuals.wadgets.path).equal('wadgets'); - should((new User).wadgets.associationModelName).equal('Widget'); + describe('setup', function () { + describe('schema', function(){ + it('has a virtual to represent the relationship', function(){ + should(userSchema.virtuals.widgets).not.equal(undefined); + should(userSchema.virtuals.widgets.path).equal('widgets'); + should(userSchema.virtuals.wadgets).not.equal(undefined); + should(userSchema.virtuals.wadgets.path).equal('wadgets'); + should((new User).wadgets.associationModelName).equal('Widget'); + }); }); - }); - describe('instance', function(){ - before(function(){ - user = new User(); - }); + describe('instance', function(){ + before(function(){ + user = new User(); + }); - it('returns a relationship', function(){ - should(user.widgets.build).be.a.Function; - should(user.widgets.create).be.a.Function; - should(user.widgets.find).be.a.Function; - should(user.widgets.findOne).be.a.Function; - should(user.widgets.append).be.a.Function; - should(user.widgets.concat).be.a.Function; - should(user.widgets.remove).be.a.Function; - should(user.widgets.delete).be.a.Function; + it('returns a relationship', function(){ + should(user.widgets.build).be.a.Function; + should(user.widgets.create).be.a.Function; + should(user.widgets.find).be.a.Function; + should(user.widgets.findOne).be.a.Function; + should(user.widgets.append).be.a.Function; + should(user.widgets.concat).be.a.Function; + should(user.widgets.remove).be.a.Function; + should(user.widgets.delete).be.a.Function; + }); }); - }); + }) describe('build', function(){ before(function(){ @@ -52,17 +54,17 @@ describe('hasMany without options', function(){ }); it('instantiates one child document', function() { - built = user.widgets.build({ name: 'Beam' }); + let widget = user.widgets.build({ name: 'Beam' }); - should(built).be.an.instanceof(Widget); - should(built.user).eql(user._id); - should(built.name).equal('Beam') + should(widget).be.an.instanceof(Widget); + should(widget.user).eql(user._id); + should(widget.name).equal('Beam') }); - it('instantiates many children documents', function() { - built = user.widgets.build([{}, {}]); + it.skip('instantiates many children documents', function() { + let widgets = user.widgets.build([{}, {}]); - built.forEach(function(widget){ + widgets.forEach(function(widget){ should(widget).be.an.instanceof(Widget); should(widget.user).eql(user._id); }); @@ -85,22 +87,31 @@ describe('hasMany without options', function(){ }); }); - it('creates many child document', function(done) { - user.widgets.create([{}, {}], function(err, widgets) { + it('creates many child documents', function(done) { + user.widgets.create({}, {}, function(err, widget1, widget2) { should.strictEqual(err, null); - widgets.forEach(function(widget){ - should(widget).be.an.instanceof(Widget); - should(widget.user).equal(user._id); - }); + should(widget1).be.an.instanceof(Widget); + should(widget1.user).equal(user._id); + + should(widget2).be.an.instanceof(Widget); + should(widget2.user).equal(user._id); done(); }); }); + + it('supports promises', function () { + return user.widgets.create({ name: 'Beam' }).then(function(widget) { + should(widget).be.an.instanceof(Widget); + should(widget.name).equal('Beam') + should(widget.user).equal(user._id); + }); + }); }); describe('find', function(){ - var find; + let find; before(function(){ user = new User(); @@ -130,9 +141,67 @@ describe('hasMany without options', function(){ should(find.options.limit).eql(3); }); + describe('handling promises', function () { + before(function(){ + return Widget.remove().then(function () { + return user.widgets.create({ }, { }); + }); + }); + + it('with conditions, fields, options', function() { + find = user.widgets.find({}, null, null) + should(find).be.instanceOf(mongoose.Query); + + return find.exec().then(function(widgets){ + should(widgets).have.lengthOf(2); + widgets.forEach(function(widget){ + should(widget).be.an.instanceof(Widget); + }); + }); + }); + + it('with conditions, fields', function() { + find = user.widgets.find({}, null); + should(find).be.instanceOf(mongoose.Query); + + return find.exec().then(function(widgets){ + should(widgets).have.lengthOf(2); + widgets.forEach(function(widget){ + should(widget).be.an.instanceof(Widget); + }); + }); + }); + + it('with conditions', function() { + find = user.widgets.find({}); + should(find).be.instanceOf(mongoose.Query); + + return find.exec().then(function(widgets){ + should(widgets).have.lengthOf(2); + widgets.forEach(function(widget){ + should(widget).be.an.instanceof(Widget); + }); + }); + }); + + it('no args', function() { + find = user.widgets.find(); + should(find).be.instanceOf(mongoose.Query); + + return find.exec().then(function(widgets){ + should(widgets).have.lengthOf(2); + widgets.forEach(function(widget){ + should(widget).be.an.instanceof(Widget); + }); + }); + }); + }); + describe('handling callbacks', function(){ before(function(done){ - user.widgets.create([{ }, { }], done); + Widget.remove(function () { + user.widgets.create({ }, { }, done); + }); }); it('with conditions, fields, options, callback', function(done) { @@ -183,19 +252,16 @@ describe('hasMany without options', function(){ should(find).be.instanceOf(mongoose.Query); }); }); - }); describe('findOne', function(){ - var find; - before(function(done){ user = new User(); - user.widgets.create([{ }, { }], done); + user.widgets.create({ }, { }, done); }); it('returns a findOne critera', function() { - find = user.widgets.findOne(); + let find = user.widgets.findOne(); should(find).be.instanceOf(mongoose.Query); should(find.op).equal('findOne'); should(find.model.modelName).equal('Widget'); @@ -211,7 +277,7 @@ describe('hasMany without options', function(){ }); describe('concat', function(){ - var other_widget; + let user, widget, otherWidget; before(function(){ user = new User(); @@ -236,20 +302,18 @@ describe('hasMany without options', function(){ }); it('concatenates many children', function(done) { - user.widgets.concat([widget, otherWidget], function(err, concatenatedWidgets){ - should(concatenatedWidgets).have.lengthOf(2); - - should(widget._id).eql(concatenatedWidgets[0]._id); + user.widgets.concat(widget, otherWidget, function(err, concatenatedWidget1, concatenatedWidget2){ + should(widget._id).eql(concatenatedWidget1._id); should(widget.user).eql(user._id); should(widget.isNew).be.false; - should(concatenatedWidgets[0].user).eql(user._id); - should(concatenatedWidgets[0].isNew).be.false; + should(concatenatedWidget1.user).eql(user._id); + should(concatenatedWidget1.isNew).be.false; - should(otherWidget._id).eql(concatenatedWidgets[1]._id); + should(otherWidget._id).eql(concatenatedWidget2._id); should(otherWidget.user).eql(user._id); should(otherWidget.isNew).be.false; - should(concatenatedWidgets[1].user).eql(user._id); - should(concatenatedWidgets[1].isNew).be.false; + should(concatenatedWidget2.user).eql(user._id); + should(concatenatedWidget2.isNew).be.false; done(); }); @@ -267,7 +331,7 @@ describe('hasMany without options', function(){ }); describe('push', function(){ - var otherWidget; + let otherWidget; before(function(){ user = new User({}); @@ -278,14 +342,14 @@ describe('hasMany without options', function(){ }); it('adds relationship information to the child', function(){ - var returnedWidget = user.widgets.push(widget); + let returnedWidget = user.widgets.push(widget); should(widget.user).eql(user._id); should(returnedWidget.user).eql(user._id); should(returnedWidget._id).eql(widget._id); }); it('adds relationship information to many children', function(){ - var returnedWidgets = user.widgets.push([ widget, otherWidget ]); + let returnedWidgets = user.widgets.push([ widget, otherWidget ]); should(widget.user).eql(user._id); should(otherWidget.user).eql(user._id); @@ -337,29 +401,23 @@ describe('hasMany without options', function(){ }); describe('hasMany dependent', function(){ - var postSchema, Post, post + let postSchema, Post, post , likeSchema, Like, like, likeEventCalled = false , favoriteSchema, Favorite, favorite, favoriteEventCalled = false , repostSchema, Repost, repost, repostEventCalled = false; - before(function(done){ + before(function(){ likeSchema = mongoose.Schema({}); likeSchema.belongsTo('post'); - likeSchema.pre('remove', function(next){ Like.emit('destroy-test-event', this); next(); }); Like = mongoose.model('Like', likeSchema); - Like.once('destroy-test-event', function(){ likeEventCalled = true; }); favoriteSchema = mongoose.Schema({}); favoriteSchema.belongsTo('post'); - favoriteSchema.pre('remove', function(next){ Favorite.emit('destroy-test-event', this); next(); }); Favorite = mongoose.model('Favorite', favoriteSchema); - Favorite.once('destroy-test-event', function(){ favoriteEventCalled = true; }); repostSchema = mongoose.Schema({}); repostSchema.belongsTo('post'); - repostSchema.pre('remove', function(next){ Repost.emit('destroy-test-event', this); next(); }); Repost = mongoose.model('Repost', repostSchema); - Repost.once('destroy-test-event', function(){ repostEventCalled = true; }); postSchema = new mongoose.Schema({}); postSchema.hasMany('likes', { dependent: 'delete' }); @@ -367,16 +425,16 @@ describe('hasMany dependent', function(){ postSchema.hasMany('reposts', { dependent: 'nullify' }); Post = mongoose.model('Post', postSchema); - new Post().save(function(err, post){ - async.parallel({ - like: function(cb){ post.likes.create({}, cb); }, - favorite: function(cb){ post.favorites.create({}, cb); }, - repost: function(cb){ post.reposts.create({}, cb); }, - }, function(err, output){ - favorite = output.favorite; - like = output.like; - repost = output.repost; - post.remove(done); + return new Post({}).save().then(function(post){ + return Promise.all([ + post.likes.create({}), + post.favorites.create({}), + post.reposts.create({}) + ]).then(function (values) { + like = values[0]; + favorite = values[1]; + repost = values[2]; + return post.remove(); }); }); }); @@ -388,23 +446,15 @@ describe('hasMany dependent', function(){ done(); }); }); - - it('does not run child middlewares', function(){ - should(likeEventCalled).be.false; - }); }); describe('destroy', function(){ it('removes children', function(done){ - Favorite.findById(favorite._id, function(err, favorite){ - should.strictEqual(favorite, null); + Favorite.findById(favorite._id, function(err, doc){ + should.strictEqual(doc, null); done(); }); }); - - it('runs child middlewares', function(){ - should(favoriteEventCalled).be.true; - }); }); describe('nullify', function(){ @@ -419,7 +469,7 @@ describe('hasMany dependent', function(){ }); describe('hasMany polymorphic:true', function() { - var tourSchema, Tour, tour, venueSchema, Venue, venue; + let tourSchema, Tour, tour, venueSchema, Venue, venue; before(function(){ venueSchema = mongoose.Schema({ }); @@ -472,7 +522,7 @@ describe('hasMany polymorphic:true', function() { describe('#find', function() { it('builds a ploymorphic query', function() { - var find = tour.venues.find(); + let find = tour.venues.find(); should(find).be.an.instanceof(mongoose.Query); should(find._conditions.playable).eql(tour._id); should(find._conditions.playable_type).eql('Tour'); @@ -481,7 +531,8 @@ describe('hasMany polymorphic:true', function() { }); describe('hasMany inverse_of', function() { - var Reader, reader, Book; + let readerSchema, Reader, reader, + bookSchema, Book, book; before(function(){ readerSchema = mongoose.Schema({ }); @@ -508,14 +559,14 @@ describe('hasMany inverse_of', function() { }); describe('hasMany discriminated', function() { - var departmentSchema, Department, department + let departmentSchema, Department, department , BaseSchema , Product, product , DairyProduct, dairy , ProduceProduct, produce; before(function(){ - var util = require('util'); + let util = require('util'); function BaseSchema() { mongoose.Schema.apply(this, arguments); this.belongsTo('department'); @@ -551,25 +602,24 @@ describe('hasMany discriminated', function() { }); it('concats a hertogenious set of child documents', function(done) { - var product = new Product() + let product = new Product() , dairy = new DairyProduct() , produce = new ProduceProduct(); - department.products.concat([product, dairy, produce], function(err, concatenatedProducts){ + department.products.concat(product, dairy, produce, function(err, concatenatedProduct1, concatenatedProduct2, concatenatedProduct3){ should.strictEqual(err, null); - concatenatedProducts.forEach(function(concatenatedProduct){ - should(concatenatedProduct).be.an.instanceof(Product); - should(concatenatedProduct.department).be.eql(department._id); - if(concatenatedProduct._id == product._id){ - should(concatenatedProduct.__t).eql.undefined; - } - else if(concatenatedProduct._id == dairy._id){ - should(concatenatedProduct.__t).eql('DairyProduct'); - } - else if(concatenatedProduct._id == produce._id){ - should(concatenatedProduct.__t).eql('ProduceProduct'); - } - }); + + should(concatenatedProduct1).be.an.instanceof(Product); + should(concatenatedProduct1.department).be.eql(department._id); + should(concatenatedProduct1.__t).eql.undefined; + + should(concatenatedProduct2).be.an.instanceof(Product); + should(concatenatedProduct2.department).be.eql(department._id); + should(concatenatedProduct2.__t).eql('DairyProduct'); + + should(concatenatedProduct3).be.an.instanceof(Product); + should(concatenatedProduct3.department).be.eql(department._id); + should(concatenatedProduct3.__t).eql('ProduceProduct'); department.products.find(function(err, products){ should(products).have.lengthOf(3); @@ -592,7 +642,7 @@ describe('hasMany discriminated', function() { }); describe('#__touch', function(){ - var postSchema, Post, post; + let postSchema, Post, post; before(function(){ postSchema = mongoose.Schema({ body: String }); @@ -607,7 +657,7 @@ describe('#__touch', function(){ it('saves the document when called', function(done){ post = new Post; post.save(function (err) { - var version = post.__v; + let version = post.__v; post.__touch(function(err){ should(post.__v).not.eql(version); done(); diff --git a/specs/index.js b/specs/index.js new file mode 100644 index 0000000..1e18e2e --- /dev/null +++ b/specs/index.js @@ -0,0 +1,31 @@ +let mongoose = require('../')(require('mongoose')); + +require('./models'); + +let resetDb = function(next){ + mongoose.connection.db.dropDatabase(next); +}; + +before(function(done){ + if(mongoose.get('isConnected')){ + resetDb(done); + } else { + mongoose.connection.on('open', function() { + resetDb(done); + }); + } +}); + +after(function (done) { + mongoose.disconnect(); + done(); +}); + +let host = process.env.BOXEN_MONGODB_URL || process.env.MONGOOSE_TEST_URL || 'mongodb://localhost/'; +let uri = host + 'mongo_relations'; + +mongoose.connect(uri, function(){ + mongoose.set('isConnected', true); +}); + +module.exports = mongoose; diff --git a/specs/models/account.js b/specs/models/account.js new file mode 100644 index 0000000..b890313 --- /dev/null +++ b/specs/models/account.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = function (mongoose, uuid) { + let accountSchema = new mongoose.Schema({ + account_number: { + type: String, + default: function () { return Date.now() } + } + }, { timestamps: true }); + + accountSchema.hasOne('subscription'); + accountSchema.hasMany('notes', { as: 'notable' }); + accountSchema.hasMany('memberships'); + + return mongoose.model('Account', accountSchema); +}; diff --git a/specs/models/benefit.js b/specs/models/benefit.js new file mode 100644 index 0000000..ad67539 --- /dev/null +++ b/specs/models/benefit.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function (mongoose, uuid) { + let benefitSchema = new mongoose.Schema({ + name: { type: String } + }, { timestamps: true }); + + benefitSchema.hasAndBelongsToMany('benefit_bundles', { inverseOf: 'benefits' }); + + return mongoose.model('Benefit', benefitSchema); +}; diff --git a/specs/models/benefit_bundle.js b/specs/models/benefit_bundle.js new file mode 100644 index 0000000..18a1647 --- /dev/null +++ b/specs/models/benefit_bundle.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = function (mongoose, uuid) { + let benefitBundleSchema = new mongoose.Schema({ + name: { type: String } + }, { timestamps: true }); + + benefitBundleSchema.hasAndBelongsToMany('benefits', { inverseOf: 'benefit_bundles' }); + benefitBundleSchema.hasAndBelongsToMany('service_areas', { inverseOf: 'benefit_bundles' }) + + return mongoose.model('BenefitBundle', benefitBundleSchema); +}; diff --git a/specs/models/benefit_bundle_price.js b/specs/models/benefit_bundle_price.js new file mode 100644 index 0000000..a1b6b2d --- /dev/null +++ b/specs/models/benefit_bundle_price.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function (mongoose, uuid) { + let benefitBundlePriceSchema = new mongoose.Schema({ + price: { type: Number }, + currency: { type: String, enum: [ 'USD', 'GBP', 'CAD' ] } + }, { timestamps: true }); + + benefitBundlePriceSchema.belongsTo('plan', { required: true }); + benefitBundlePriceSchema.belongsTo('availability_zone', { modelName: 'ServiceArea', required: true }); + benefitBundlePriceSchema.belongsTo('benefit_bundle', { required: true }); + + return mongoose.model('BenefitBundlePrice', benefitBundlePriceSchema); +}; diff --git a/specs/models/email_identity.js b/specs/models/email_identity.js new file mode 100644 index 0000000..08b3ffd --- /dev/null +++ b/specs/models/email_identity.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = function (mongoose, Identity) { + let emailIdentitySchema = new mongoose.Schema({ + email: { type: String }, + verified: { type: Boolean } + }, { timestamps: true }); + + return Identity.discriminator('EmailIdentity', emailIdentitySchema); +}; diff --git a/specs/models/identity.js b/specs/models/identity.js new file mode 100644 index 0000000..9700b72 --- /dev/null +++ b/specs/models/identity.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function (mongoose) { + let identitySchema = new mongoose.Schema({ + name: { type: String } + }, { timestamps: true }); + + identitySchema.belongsTo('user'); + + return mongoose.model('Identity', identitySchema); +}; diff --git a/specs/models/index.js b/specs/models/index.js new file mode 100644 index 0000000..35d05f1 --- /dev/null +++ b/specs/models/index.js @@ -0,0 +1,16 @@ +let mongoose = require('mongoose'); +let uuid = require('node-uuid'); + +const User = require('./user')(mongoose); +const Note = require('./note')(mongoose); +const Identity = require('./identity')(mongoose); +const SocialIdentity = require('./social_identity')(mongoose, Identity); +const EmailIdentity = require('./email_identity')(mongoose, Identity); +const PhoneIdentity = require('./phone_identity')(mongoose, Identity); +const Membership = require('./membership')(mongoose); +const Account = require('./account')(mongoose, uuid); +const Subscription = require('./subscription')(mongoose); +const Plan = require('./plan')(mongoose); +const BenefitBundle = require('./benefit_bundle')(mongoose); +const BenefitBundlePrice = require('./benefit_bundle_price')(mongoose); +const Benefit = require('./benefit')(mongoose); diff --git a/specs/models/membership.js b/specs/models/membership.js new file mode 100644 index 0000000..082bfef --- /dev/null +++ b/specs/models/membership.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = function (mongoose) { + let membershipSchema = new mongoose.Schema({ + membership_type: { type: String } + }, { timestamps: true }); + + membershipSchema.belongsTo('user', { required: true, touch: true }); + membershipSchema.belongsTo('account'); + + return mongoose.model('Membership', membershipSchema); +}; diff --git a/specs/models/note.js b/specs/models/note.js new file mode 100644 index 0000000..74fc008 --- /dev/null +++ b/specs/models/note.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = function (mongoose) { + let noteSchema = new mongoose.Schema({ + name: { type: String } + }, { timestamps: true }); + + noteSchema.belongsTo('noteable', { + polymorphic: true, + required: true, + enum: [ 'User', 'Account' ] + }); + + return mongoose.model('Note', noteSchema); +}; diff --git a/specs/models/phone_identity.js b/specs/models/phone_identity.js new file mode 100644 index 0000000..aa45c1c --- /dev/null +++ b/specs/models/phone_identity.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = function (mongoose, Identity) { + let phoneIdentitySchema = new mongoose.Schema({ + phone_number: { type: String }, + verified: { type: Boolean } + }, { timestamps: true }); + + return Identity.discriminator('PhoneIdentity', phoneIdentitySchema); +}; diff --git a/specs/models/plan.js b/specs/models/plan.js new file mode 100644 index 0000000..550fd67 --- /dev/null +++ b/specs/models/plan.js @@ -0,0 +1,18 @@ +'use strict'; + +module.exports = function (mongoose, uuid) { + let planSchema = new mongoose.Schema({ + name: { type: String } + }, { timestamps: true }); + + + planSchema.hasMany('benefit_bundle_prices') + + planSchema.hasAndBelongsToMany('benefit_bundles', { inverseOf: 'plans' }); + planSchema.hasAndBelongsToMany('launched_service_areas', { + modelName: 'ServiceArea', + inverseOf: 'plans' + }); + + return mongoose.model('Plan', planSchema); +}; diff --git a/specs/models/service_area.js b/specs/models/service_area.js new file mode 100644 index 0000000..492d085 --- /dev/null +++ b/specs/models/service_area.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = function (mongoose, uuid) { + let serviceAreaSchema = new mongoose.Schema({ + name: { type: String } + }, { timestamps: true }); + + + serviceAreaSchema.hasAndBelongsToMany('plans') + serviceAreaSchema.hasMany('benefit_bundle_prices') + + return mongoose.model('ServiceArea', ServiceAreaSchema); +}; diff --git a/specs/models/social_identity.js b/specs/models/social_identity.js new file mode 100644 index 0000000..350e98f --- /dev/null +++ b/specs/models/social_identity.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = function (mongoose, Identity) { + let socialIdentitySchema = new mongoose.Schema({ + identity_provider_name: { type: String }, + identity_provider_id: { type: String }, + access_token: { type: String }, + refresh_token: { type: String }, + expires_at: { type: Date } + }, { timestamps: true }); + + return Identity.discriminator('SocialIdentity', socialIdentitySchema); +}; diff --git a/specs/models/subscription.js b/specs/models/subscription.js new file mode 100644 index 0000000..893b001 --- /dev/null +++ b/specs/models/subscription.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function (mongoose, uuid) { + let subscriptionSchema = new mongoose.Schema({ + status: { type: String } + }, { timestamps: true }); + + subscriptionSchema.hasMany('plans'); + + return mongoose.model('Subscription', subscriptionSchema); +}; diff --git a/specs/models/user.js b/specs/models/user.js new file mode 100644 index 0000000..16d2688 --- /dev/null +++ b/specs/models/user.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = function (mongoose) { + let userSchema = new mongoose.Schema({ + name: { type: String } + }, { timestamps: true }); + + userSchema.hasMany('memberships'); + userSchema.hasMany('identities'); + userSchema.hasMany('notes', { as: 'notable' }) + + return mongoose.model('User', userSchema); +}; diff --git a/specs/spec_helper.js b/specs/spec_helper.js deleted file mode 100644 index 910f487..0000000 --- a/specs/spec_helper.js +++ /dev/null @@ -1,26 +0,0 @@ -var mongoose = require('../')(require('mongoose')); - -var resetDb = function(next){ - mongoose.connection.db.dropDatabase(next); -}; - -before(function(done){ - if(mongoose.get('isConnected')){ - resetDb(done); - } else { - mongoose.connection.on('open', function(){ - resetDb(done); - }); - } -}); - -after(function(){ - mongoose.disconnect(); -}) - -var host = process.env.BOXEN_MONGODB_URL || process.env.MONGOOSE_TEST_URL || 'mongodb://localhost/'; -var uri = host + 'mongo_relations'; - -mongoose.connect(uri, function(){ - mongoose.set('isConnected', true); -});