From 102324b2804960fd6016caab0a8f5ba93eb43913 Mon Sep 17 00:00:00 2001 From: Jonathon Storer Date: Wed, 7 Feb 2018 22:12:16 -0600 Subject: [PATCH 1/7] wip --- .node-version | 2 +- package-lock.json | 323 ++++++++++++++++++++++++++++++++++++++++ package.json | 6 +- specs/belongsTo.spec.js | 5 +- 4 files changed, 329 insertions(+), 7 deletions(-) create mode 100644 package-lock.json diff --git a/.node-version b/.node-version index c666a7d..ae9a76b 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -v0.10.32 +8.0.0 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..63c76b1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,323 @@ +{ + "name": "mongoose-relation", + "version": "0.5.16", + "lockfileVersion": 1, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "mocha": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.0.tgz", + "integrity": "sha512-ukB2dF+u4aeJjc6IGtPNnJXfeby5d4ZqySlIBT0OEyva/DrMjVm5HkQxKnHDLKEfEQBsEnwTg9HHhtPHJdTd8w==", + "dev": true, + "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 + } + } + }, + "mongodb": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.2.tgz", + "integrity": "sha512-E50FmpSQchZAimn2uPIegoNoH9UQYR1yiGHtQPmmg8/Ekc97w6owHoqaBoz+assnd9V5LxMzmQ/VEWMsQMgZhQ==", + "dev": true + }, + "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 + }, + "mongoose": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.0.3.tgz", + "integrity": "sha512-y4NlLzZaQe5vJHjcEjHLKK6utjs7sVEPN971+d1vVJJGrmA+zeeFA1MEmC1J0ujD34eOSghnExXJPwCrxHHZCw==", + "dev": true, + "dependencies": { + "async": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", + "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", + "dev": true + } + } + }, + "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, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 26fa187..17bb0bb 100644 --- a/package.json +++ b/package.json @@ -24,16 +24,16 @@ }, "main": "./index.js", "engines": { - "node": ">= 0.2.0" + "node": ">= 8" }, "dependencies": { - "mongoose": ">=3.0.0", "async": "^0.9.0", "i": "^0.3.2" }, "devDependencies": { + "mongoose": ">=3.0.0", "mocha": "latest", "should": "latest", - "node-uuid": "^1.4.3" + "node-uuid": "latest" } } diff --git a/specs/belongsTo.spec.js b/specs/belongsTo.spec.js index 5448350..5580baf 100644 --- a/specs/belongsTo.spec.js +++ b/specs/belongsTo.spec.js @@ -2,11 +2,10 @@ require('./spec_helper'); var mongoose = require('mongoose'), should = require('should'), - uuid = require('node-uuid'), - schema; + uuid = require('node-uuid'); describe('belongsTo', function() { - subject = null; + let subject = null; before(function() { var partSchema = new mongoose.Schema({}); From 335416289e063710aefb539c41ae5ec3a77926cb Mon Sep 17 00:00:00 2001 From: Jonathon Storer Date: Thu, 8 Feb 2018 08:09:21 -0600 Subject: [PATCH 2/7] wip --- .node-version | 2 +- lib/belongsTo.js | 64 +- lib/hasAndBelongsToMany.js | 620 +++++++++--------- lib/hasMany.js | 292 ++++----- lib/index.js | 35 +- lib/utils.js | 10 +- package-lock.json | 150 ++++- package.json | 4 +- specs/belongsTo.spec.js | 18 +- specs/hasAndBelongsToMany.spec.js | 86 +-- specs/hasMany.spec.js | 38 +- specs/index.js | 28 + .../relationship/hasAndBelongsToMany.spec.js | 2 +- specs/spec_helper.js | 22 - 14 files changed, 742 insertions(+), 629 deletions(-) create mode 100644 specs/index.js delete mode 100644 specs/spec_helper.js diff --git a/.node-version b/.node-version index ae9a76b..486db33 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -8.0.0 +8.9.1 diff --git a/lib/belongsTo.js b/lib/belongsTo.js index f4b6ace..7f193ee 100644 --- a/lib/belongsTo.js +++ b/lib/belongsTo.js @@ -1,40 +1,42 @@ -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 - }; +'use strict'; - if (options.polymorphic) { - idCast.polymorphic = true; +module.exports = function (mongoose, i) { - var typeCast = { - polymorphic: true, - type: String, - required: idCast.required, - enum: options.enum + 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) { + this.populate(associationName, (err, model) => { + this[associationName].__touch(next); + }); + }); + }; }; }; diff --git a/lib/hasAndBelongsToMany.js b/lib/hasAndBelongsToMany.js index 706e40d..439f60d 100644 --- a/lib/hasAndBelongsToMany.js +++ b/lib/hasAndBelongsToMany.js @@ -1,372 +1,368 @@ -var mongoose = require('mongoose') - , ObjectId = mongoose.Schema.ObjectId - , MongooseArray = mongoose.Types.Array - , i = require('i')() - , utils = require('./utils') - , merge = utils.merge; +module.exports = function (mongoose, i, utils) { + return function hasAndBelongsToMany (schema, model, options) { + this.type = 'habtm'; -module.exports = function hasAndBelongsToMany (schema, model, options) { - this.type = 'habtm'; + this.schema = schema; + this.model = model; + this.options = options || {}; - this.schema = schema; - this.model = model; - this.options = options || {}; + this.pathName = this.options.through || i.pluralize(this.model.toLowerCase()); - this.pathName = this.options.through || i.pluralize(this.model.toLowerCase()); + let path = {}; + path[this.pathName] = [{ type: mongoose.Schema.ObjectId, index: true, ref: this.model }]; + this.schema.add(path); - var path = {}; - path[this.pathName] = [{ type: ObjectId, index: true, ref: this.model }]; - this.schema.add(path); + this.schema.paths[this.pathName].options[this.type] = this.model; + this.schema.paths[this.pathName].options.relationshipType = this.type; + this.schema.paths[this.pathName].options.relationshipModel = this.model; - this.schema.paths[this.pathName].options[this.type] = this.model; - this.schema.paths[this.pathName].options.relationshipType = this.type; - this.schema.paths[this.pathName].options.relationshipModel = this.model; - - if (this.options.dependent) { - this.schema.paths[this.pathName].options.dependent = this.options.dependent; - } + if (this.options.dependent) { + this.schema.paths[this.pathName].options.dependent = this.options.dependent; + } - var setChild = this.options.hasOwnProperty('setChild') ? this.options.setChild : true; - this.schema.paths[this.pathName].options.setChild = setChild; + let setChild = this.options.hasOwnProperty('setChild') ? this.options.setChild : true; + this.schema.paths[this.pathName].options.setChild = setChild; - if (!this.schema.paths[this.pathName].options.setChild) { - if (this.schema.paths[this.pathName].options.dependent == 'nullify') { - throw new Error("dependent cannot be set to 'nullify' while setChild is false"); - } + if (!this.schema.paths[this.pathName].options.setChild) { + if (this.schema.paths[this.pathName].options.dependent == 'nullify') { + throw new Error("dependent cannot be set to 'nullify' while setChild is false"); + } - if (this.schema.paths[this.pathName].options.dependent == 'destroy') { - throw new Error("dependent cannot be set to 'destroy' while setChild is false"); - } + if (this.schema.paths[this.pathName].options.dependent == 'destroy') { + throw new Error("dependent cannot be set to 'destroy' while setChild is false"); + } + }; }; -}; -/* Builds the instance of the child element -* -* @param {Object|Array} objs -* @return {Document|Array} -* @api public -*/ + /* Builds the instance of the child element + * + * @param {Object|Array} objs + * @return {Document|Array} + * @api public + */ + + // mongoose > 3.9.x support + let MongooseArray = mongoose.Types.Array; + let base = MongooseArray.mixin || MongooseArray.prototype; + + base.build = function (objs) { + let childModelName = this._schema.options.relationshipModel; + + let buildOne = function(obj){ + let childModel = mongoose.model(obj.__t || childModelName) + , child = new childModel(obj); + + this._parent[this._path].push(child); + + if (!!this._schema.options.setChild) { + + // start remove me asap + // needed for this._childToParent.name + model = mongoose.model(this._schema.options.relationshipModel); + for (let path in model.schema.paths) { + options = model.schema.paths[path].options; + ref = (options.relationshipModel || options.ref); + if(ref == this._parent.constructor.modelName){ + options.name = path; + this._childToParent = options + } + } + // end remove me asap -// mongoose > 3.9.x support -var base = MongooseArray.mixin || MongooseArray.prototype; + child[this._childToParent.name].push(this._parent); + } -base.build = function (objs) { - var childModelName = this._schema.options.relationshipModel; + return child; + }.bind(this); - var buildOne = function(obj){ - var childModel = mongoose.model(obj.__t || childModelName) - , child = new childModel(obj); + if (Array.isArray(objs)) { + return objs.map(buildOne); + } else { + return buildOne(objs); + } + }; - this._parent[this._path].push(child); + /* Create a child document and add it to the parent `Array` + * + * @param {Object|Array} objs [object(s) to create] + * @param {Functions} callback [passed: (err, parent, created children)] + * @api public + */ - if (!!this._schema.options.setChild) { + base.create = function (objs, callback) { + objs = this.build(objs); - // start remove me asap - // needed for this._childToParent.name - model = mongoose.model(this._schema.options.relationshipModel); - for (var path in model.schema.paths) { - options = model.schema.paths[path].options; - ref = (options.relationshipModel || options.ref); - if(ref == this._parent.constructor.modelName){ - options.name = path; - this._childToParent = options + let complete = function(err, docs){ + this._parent.save(function(err){ + callback(err, this._parent, docs); + }.bind(this)); + }.bind(this); + + let validForCreate = function(doc){ + if (!!this._schema.options.setChild) { + + // start remove me asap + // needed for this._childToParent.name + model = mongoose.model(this._schema.options.relationshipModel); + + for (let path in model.schema.paths) { + options = model.schema.paths[path].options; + ref = (options.relationshipModel || options.ref); + if(ref == this._parent.constructor.modelName){ + options.name = path; + this._childToParent = options + } } - } - // end remove me asap - child[this._childToParent.name].push(this._parent); - } + this._allowed_discriminators = [ model.modelName ].concat(Object.keys(model.discriminators || {})); + let childIsAllowed = function (child) { + return !!~this._allowed_discriminators.indexOf(child.constructor.modelName); + }.bind(this); - return child; - }.bind(this); + // end remove me asap - if (Array.isArray(objs)) { - return objs.map(buildOne); - } else { - return buildOne(objs); - } -}; - -/* Create a child document and add it to the parent `Array` -* -* @param {Object|Array} objs [object(s) to create] -* @param {Functions} callback [passed: (err, parent, created children)] -* @api public -*/ - -base.create = function (objs, callback) { - objs = this.build(objs); + return !!this._childToParent && childIsAllowed(doc); + } else { + return true + } + }.bind(this); - var complete = function(err, docs){ - this._parent.save(function(err){ - callback(err, this._parent, docs); - }.bind(this)); - }.bind(this); + let createOne = function(doc, done){ + if (!validForCreate(doc)) + return done(new Error('Parent model not referenced anywhere in the Child Schema')); + doc.save(done); + }; - var validForCreate = function(doc){ - if (!!this._schema.options.setChild) { + if(Array.isArray(objs)){ + let count = objs.length, docs = []; - // start remove me asap - // needed for this._childToParent.name - model = mongoose.model(this._schema.options.relationshipModel); + objs.forEach(function(obj){ + createOne(obj, function(err, doc){ + if (err) return complete(err); + docs.push(doc); + --count || complete(null, docs); + }.bind(this)); + }.bind(this)); + } + else { + createOne(objs, complete); + } + }; - for (var path in model.schema.paths) { - options = model.schema.paths[path].options; - ref = (options.relationshipModel || options.ref); - if(ref == this._parent.constructor.modelName){ - options.name = path; - this._childToParent = options - } + /* Append an already instantiated document saves it in the process. + * + * @param {Document} child + * @param {Function} callback + * @api public + */ + + base.append = function (child, callback) { + + // start remove me asap + // needed for this._childToParent.name + model = mongoose.model(this._schema.options.relationshipModel); + + for (let path in model.schema.paths) { + options = model.schema.paths[path].options; + ref = (options.relationshipModel || options.ref); + if(ref == this._parent.constructor.modelName){ + options.name = path; + this._childToParent = options } + } - this._allowed_discriminators = [ model.modelName ].concat(Object.keys(model.discriminators || {})); - var childIsAllowed = function (child) { - return !!~this._allowed_discriminators.indexOf(child.constructor.modelName); - }.bind(this); + this._allowed_discriminators = [ model.modelName ].concat(Object.keys(model.discriminators || {})); + let childIsAllowed = function (child) { + return !!~this._allowed_discriminators.indexOf(child.constructor.modelName); + }.bind(this); - // end remove me asap + // end remove me asap - return !!this._childToParent && childIsAllowed(doc); - } else { - return true + // TODO: abstract me + if(!childIsAllowed(child)) { + return throwErr('Wrong Model type'); } - }.bind(this); - - var createOne = function(doc, done){ - if (!validForCreate(doc)) - return done(new Error('Parent model not referenced anywhere in the Child Schema')); - doc.save(done); - }; - if(Array.isArray(objs)){ - var count = objs.length, docs = []; + if (!!this._schema.options.setChild) { + child[this._childToParent.name].push(this._parent._id); + } - objs.forEach(function(obj){ - createOne(obj, function(err, doc){ - if (err) return complete(err); - docs.push(doc); - --count || complete(null, docs); - }.bind(this)); - }.bind(this)); - } - else { - createOne(objs, complete); - } -}; + this._parent[this._path].push(child._id); -/* Append an already instantiated document saves it in the process. -* -* @param {Document} child -* @param {Function} callback -* @api public -*/ - -base.append = function (child, callback) { - - // start remove me asap - // needed for this._childToParent.name - model = mongoose.model(this._schema.options.relationshipModel); - - for (var path in model.schema.paths) { - options = model.schema.paths[path].options; - ref = (options.relationshipModel || options.ref); - if(ref == this._parent.constructor.modelName){ - options.name = path; - this._childToParent = options - } - } + callback && child.save(callback); + return child; + }; - this._allowed_discriminators = [ model.modelName ].concat(Object.keys(model.discriminators || {})); - var childIsAllowed = function (child) { - return !!~this._allowed_discriminators.indexOf(child.constructor.modelName); - }.bind(this); + /* Append many instantiated children documents + * + * @param {Array} children + * @param {Function} callback + * @api public + */ + base._concat = Array.prototype.concat; + base.concat = function (docs, callback) { + let throwErr = utils.throwErr(callback); + + if (!Array.isArray(docs)){ + return throwErr('First argument needs to be an Array'); + }; - // end remove me asap + let complete = function(err, docs) { + if(err){ return throwErr(err) } - // TODO: abstract me - if(!childIsAllowed(child)) { - return throwErr('Wrong Model type'); - } + let ids = docs.map(function (doc) { return doc._id }); + this._concat(ids); + this._markModified(); - if (!!this._schema.options.setChild) { - child[this._childToParent.name].push(this._parent._id); - } + callback(null, docs); + }.bind(this); - this._parent[this._path].push(child._id); + let count = docs.length; + let savedDocs = []; + docs.forEach(function(doc){ + this.append(doc); + doc.save(function(err, doc){ + if(err) return complete(err); - callback && child.save(callback); - return child; -}; + savedDocs.push(doc); + --count || complete(null, savedDocs); + }); + }.bind(this)); -/* Append many instantiated children documents -* -* @param {Array} children -* @param {Function} callback -* @api public -*/ -base._concat = Array.prototype.concat; -base.concat = function (docs, callback) { - var throwErr = utils.throwErr(callback); - - if (!Array.isArray(docs)){ - return throwErr('First argument needs to be an Array'); }; - var complete = function(err, docs) { - if(err){ return throwErr(err) } - - var ids = docs.map(function (doc) { return doc._id }); - this._concat(ids); - this._markModified(); + /* Find children documents + * + * *This is a copy of Model.find w/ added error throwing and such* + */ + base.find = function (conditions, fields, options, callback) { + // Copied from `Model.find` + if ('function' == typeof conditions) { + callback = conditions; + conditions = {}; + fields = null; + options = null; + } else if ('function' == typeof fields) { + callback = fields; + fields = null; + options = null; + } else if ('function' == typeof options) { + callback = options; + options = null; + } - callback(null, docs); - }.bind(this); + // start remove me asap + // needed for this._childToParent.name + model = mongoose.model(this._schema.options.relationshipModel); + for (let path in model.schema.paths) { + options = model.schema.paths[path].options; + ref = (options.relationshipModel || options.ref); + if(ref == this._parent.constructor.modelName){ + options.name = path; + this._childToParent = options + } + } + // end remove me asap - var count = docs.length; - var savedDocs = []; - docs.forEach(function(doc){ - this.append(doc); - doc.save(function(err, doc){ - if(err) return complete(err); + let childModel = mongoose.model(this._schema.options.relationshipModel); + childPath = this._childToParent; + safeConditions = {}, + throwErr = utils.throwErr(callback); - savedDocs.push(doc); - --count || complete(null, savedDocs); - }); - }.bind(this)); + utils.merge(safeConditions, conditions); -}; + if (!!this._schema.options.setChild) { + if (!childPath) { + return throwErr('Parent model not referenced anywhere in the Child Schema'); + } -/* Find children documents -* -* *This is a copy of Model.find w/ added error throwing and such* -*/ -base.find = function (conditions, fields, options, callback) { - // Copied from `Model.find` - if ('function' == typeof conditions) { - callback = conditions; - conditions = {}; - fields = null; - options = null; - } else if ('function' == typeof fields) { - callback = fields; - fields = null; - options = null; - } else if ('function' == typeof options) { - callback = options; - options = null; - } - - // start remove me asap - // needed for this._childToParent.name - model = mongoose.model(this._schema.options.relationshipModel); - for (var path in model.schema.paths) { - options = model.schema.paths[path].options; - ref = (options.relationshipModel || options.ref); - if(ref == this._parent.constructor.modelName){ - options.name = path; - this._childToParent = options + let childConditions = {}; + childConditions[childPath.name] = this._parent._id; + utils.merge(safeConditions, childConditions); } - } - // end remove me asap - - var childModel = mongoose.model(this._schema.options.relationshipModel); - childPath = this._childToParent; - safeConditions = {}, - throwErr = utils.throwErr(callback); - merge(safeConditions, conditions); + utils.merge(safeConditions, { _id: { $in: this._parent[this._path] } }); - if (!!this._schema.options.setChild) { - if (!childPath) { - return throwErr('Parent model not referenced anywhere in the Child Schema'); - } + let query = childModel.find(safeConditions, options).select(fields); - var childConditions = {}; - childConditions[childPath.name] = this._parent._id; - merge(safeConditions, childConditions); - } + callback && query.exec(callback); + return query; + }; - merge(safeConditions, { _id: { $in: this._parent[this._path] } }); + /* Syntactic sugar to populate the array + * + * @param {Array} fields + * @param {Function} callback + * @return {Query} + * @api public + */ + base.populate = function (fields, callback) { + if ('function' == typeof fields) { + callback = fields; + fields = null; + } - var query = childModel.find(safeConditions, options).select(fields); + // TODO: do we really need to initialize a new doc? + return this._parent.constructor + .findById(this._parent._id) + .populate(this._path, fields) + .exec(callback); + }; - callback && query.exec(callback); - return query; -}; + /* Overrides MongooseArray.remove only for dependent:destroy relationships + * + * @param {ObjectId} id + * @param {Function} callback + * @return {ObjectId} + * @api public + */ + base._remove = base.remove; + base.remove = base.delete = function (id, callback) { + let parent = this._parent, + childModel = mongoose.model(this._schema.options.relationshipModel); + childPath = this._childToParent; + child = null, + throwErr = utils.throwErr(callback); + + if (id._id) { + let child = id; + id = child._id; + } -/* Syntactic sugar to populate the array -* -* @param {Array} fields -* @param {Function} callback -* @return {Query} -* @api public -*/ -base.populate = function (fields, callback) { - if ('function' == typeof fields) { - callback = fields; - fields = null; - } - - // TODO: do we really need to initialize a new doc? - return this._parent.constructor - .findById(this._parent._id) - .populate(this._path, fields) - .exec(callback); -}; + // TODO: should a callback be required? + if (!callback) { + callback = function (err) { + if (err) { + throw err; + } + }; + } -/* Overrides MongooseArray.remove only for dependent:destroy relationships -* -* @param {ObjectId} id -* @param {Function} callback -* @return {ObjectId} -* @api public -*/ -base._remove = base.remove; -base.remove = base.delete = function (id, callback) { - var parent = this._parent, - childModel = mongoose.model(this._schema.options.relationshipModel); - childPath = this._childToParent; - child = null, - throwErr = utils.throwErr(callback); - - if (id._id) { - var child = id; - id = child._id; - } - - // TODO: should a callback be required? - if (!callback) { - callback = function (err) { - if (err) { - throw err; - } + let hasOrFetchChild = function(done){ + if(child){ + done(null, child); + } else { + childModel.findById(id, done); + }; }; - } - var hasOrFetchChild = function(done){ - if(child){ - done(null, child); + // TODO: is this needed? + // I think this removing the id from the instance array + // however, it could be not needed + this._remove(id); + + // TODO: shold habtm support delete and destroy? + if (!!~['delete', 'destroy', 'nullify'].indexOf(this._schema.options.dependent)){ + hasOrFetchChild(function(err, child){ + if (err) { return throwErr(err) }; + child[childPath.name].remove(parent._id); + child.save(function(err, child){ + if (err){ return throwErr(err) }; + callback(null, parent); + }); + }); } else { - childModel.findById(id, done); - }; + callback(null, parent); + } }; - - // TODO: is this needed? - // I think this removing the id from the instance array - // however, it could be not needed - this._remove(id); - - // TODO: shold habtm support delete and destroy? - if (!!~['delete', 'destroy', 'nullify'].indexOf(this._schema.options.dependent)){ - hasOrFetchChild(function(err, child){ - if (err) { return throwErr(err) }; - child[childPath.name].remove(parent._id); - child.save(function(err, child){ - if (err){ return throwErr(err) }; - callback(null, parent); - }); - }); - } else { - callback(null, parent); - } }; diff --git a/lib/hasMany.js b/lib/hasMany.js index 250369f..3a395eb 100644 --- a/lib/hasMany.js +++ b/lib/hasMany.js @@ -1,181 +1,177 @@ -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 hasManyNullify (associationName, next) { - var Model = this[associationName].associationModel - , conditions = this[associationName].find()._conditions - , fieldsToUnset = this[associationName].find()._conditions - , options = { multi: true }; + }; - for (field in fieldsToUnset) { fieldsToUnset[field] = 1; } + function hasManyDelete (associationName, next) { + this[associationName].find().remove(next); + }; - Model.update(conditions, { $unset: fieldsToUnset }, options, next); -}; + function hasManyNullify (associationName, next) { + let Model = this[associationName].associationModel + , conditions = this[associationName].find()._conditions + , fieldsToUnset = this[associationName].find()._conditions + , options = { multi: true }; -var dependencyStrategy = { - destroy: hasManyDestroy, - delete: hasManyDelete, - nullify: hasManyNullify -}; + for (field in fieldsToUnset) { fieldsToUnset[field] = 1; } -function associate (object) { - object[this.foreignKey] = this.doc._id; - if (this.foreignKeyType) { - object[this.foreignKeyType] = this.modelName; - } - return object; -}; + Model.update(conditions, { $unset: fieldsToUnset }, options, next); + }; -function HasMany (doc, options) { - options = options || {}; - this._options = options; + let dependencyStrategy = { + destroy: hasManyDestroy, + delete: hasManyDelete, + nullify: hasManyNullify + }; - this.as = this._options.as; - this.inverse_of = this._options.inverse_of; + function associate (object) { + object[this.foreignKey] = this.doc._id; + if (this.foreignKeyType) { + object[this.foreignKeyType] = this.modelName; + } + return object; + }; - this.doc = doc; - this.modelName = this.doc.constructor.modelName; - this.Model = mongoose.model(this.modelName); + function HasMany (doc, options) { + options = options || {}; + this._options = options; - this.associationModelName = this._options.associationModelName; - this.associationModel = mongoose.model(this.associationModelName); + this.as = this._options.as; + this.inverse_of = this._options.inverse_of; - if (this.as) { - this.foreignKey = this.as; - this.foreignKeyType = this.as + '_type'; - } - else { - this.foreignKey = this.inverse_of || i.underscore(this.modelName); - } -} + this.doc = doc; + this.modelName = this.doc.constructor.modelName; + this.Model = mongoose.model(this.modelName); -HasMany.prototype.build = function(objects) { - var Model; + this.associationModelName = this._options.associationModelName; + this.associationModel = mongoose.model(this.associationModelName); - if (Array.isArray(objects)) { - return objects.map(function(object) { - return this.build(object); - }.bind(this)); + if (this.as) { + this.foreignKey = this.as; + this.foreignKeyType = this.as + '_type'; + } + else { + this.foreignKey = this.inverse_of || i.underscore(this.modelName); + } } - else { - return associate.bind(this)(new this.associationModel(objects || {})); - } -}; - -HasMany.prototype.create = function(objects, callback) { - if (Array.isArray(objects)) { - var docs = [], count = objects.length; - objects.forEach(function(object) { - this.create(object, function(err, doc) { - if (err) return callback(err); - docs.push(doc); - --count || callback(null, docs); - }); - }.bind(this)); - } - else { - this.build(objects).save(callback); + HasMany.prototype.build = function(objects) { + let Model; + + 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.find = function(conditions, fields, options, callback) { - if ('function' == typeof conditions) { - callback = conditions; - conditions = {}; - } - conditions = conditions || {}; - fields = fields || null; - options = options || null; - - associate.bind(this)(conditions); - return this.associationModel.find(conditions, fields, options, callback); -}; + HasMany.prototype.create = function(objects, callback) { + if (Array.isArray(objects)) { + let docs = [], count = objects.length; + + objects.forEach(function(object) { + this.create(object, function(err, doc) { + if (err) return callback(err); + docs.push(doc); + --count || callback(null, docs); + }); + }.bind(this)); + } + else { + this.build(objects).save(callback); + }; + }; -HasMany.prototype.findOne = function() { - var callback - , args = Array.prototype.slice.call(arguments); + HasMany.prototype.find = function(conditions, fields, options, callback) { + if ('function' == typeof conditions) { + callback = conditions; + conditions = {}; + } - if ('function' == typeof args[args.length - 1]) { - callback = args[args.length - 1]; - args.pop(); - } + conditions = conditions || {}; + fields = fields || null; + options = options || null; - return this.find.apply(this, args).findOne(callback); -}; + associate.bind(this)(conditions); + return this.associationModel.find(conditions, fields, options, callback); + }; -HasMany.prototype.concat = function(objects, callback) { - var docs = []; + HasMany.prototype.findOne = function() { + let callback + , args = Array.prototype.slice.call(arguments); - if (Array.isArray(objects)) { - var count = objects.length; + if ('function' == typeof args[args.length - 1]) { + callback = args[args.length - 1]; + args.pop(); + } - objects.forEach(function(object) { - this.concat(object, function(err, doc) { - if (err) return callback(err); - docs.push(doc); - --count || callback(null, docs); - }); - }.bind(this)); - } - else { - associate.bind(this)(objects).save(callback); + return this.find.apply(this, args).findOne(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); + HasMany.prototype.concat = function(objects, callback) { + let docs = []; + + if (Array.isArray(objects)) { + let count = objects.length; + + objects.forEach(function(object) { + this.concat(object, function(err, doc) { + if (err) return callback(err); + docs.push(doc); + --count || callback(null, docs); + }); + }.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); + }; }; -}; -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 (schema, associationName, options) { + options = options || {}; + options.associationName = associationName; + options.associationModelName = options.modelName || i.classify(associationName); - schema.virtual(associationName).get(function() { - return new HasMany(this, options); - }); + schema.virtual(associationName).get(function() { + return new HasMany(this, options); + }); - schema.methods.__touch = function(next) { - this.increment(); - this.save(next); - }; + schema.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]) { + schema.pre('remove', function(next) { + dependencyStrategy[options.dependent].call(this, associationName, next); + }); + }; }; + }; diff --git a/lib/index.js b/lib/index.js index bbe7755..d57aa31 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,29 +1,24 @@ -// mongo-relations - -module.exports = exports = function mongoRelations (mongoose) { - var Schema = mongoose.Schema - , belongsTo = require('./belongsTo') - , hasMany = require('./hasMany') - , hasAndBelongsToMany = require('./hasAndBelongsToMany'); - - /* Syntactic sugar to create the relationships - * - * @param {String} model [name of the model in the DB] - * @param {Object} options [through, dependent] - * @return {Schema} - * @api public - */ - Schema.prototype.belongsTo = function (model, options) { - belongsTo(this, model, options); - }; +'use strict'; + +let i = require('i')(); +let utils = require('./utils'); + +module.exports = function mongooseRelation (mongoose) { - Schema.prototype.hasMany = function (model, options) { + //let belongsTo = require('./belongsTo')(mongoose, i); + let hasMany = require('./hasMany')(mongoose, i); + let hasAndBelongsToMany = require('./hasAndBelongsToMany')(mongoose, i, utils); + + mongoose.Schema.prototype.belongsTo = require('./belongsTo')(mongoose, i); + + mongoose.Schema.prototype.hasMany = function (model, options) { hasMany(this, model, options); }; - Schema.prototype.habtm = function (model, options) { + mongoose.Schema.prototype.habtm = function (model, options) { new hasAndBelongsToMany(this, model, options); }; return mongoose; + }; 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 index 63c76b1..6abfb22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2,6 +2,7 @@ "name": "mongoose-relation", "version": "0.5.16", "lockfileVersion": 1, + "requires": true, "dependencies": { "async": { "version": "0.9.2", @@ -24,7 +25,11 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } }, "browser-stdout": { "version": "1.3.0", @@ -54,7 +59,10 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true + "dev": true, + "requires": { + "ms": "2.0.0" + } }, "diff": { "version": "3.3.1", @@ -78,7 +86,15 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true + "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", @@ -107,7 +123,11 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } }, "inherits": { "version": "2.0.3", @@ -137,7 +157,10 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } }, "minimist": { "version": "0.0.8", @@ -149,19 +172,37 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true + "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 + "dev": true, + "requires": { + "ms": "2.0.0" + } } } }, @@ -169,25 +210,48 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.2.tgz", "integrity": "sha512-E50FmpSQchZAimn2uPIegoNoH9UQYR1yiGHtQPmmg8/Ekc97w6owHoqaBoz+assnd9V5LxMzmQ/VEWMsQMgZhQ==", - "dev": true + "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 + "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 + "dev": true, + "requires": { + "lodash": "4.17.5" + } } } }, @@ -208,6 +272,12 @@ "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", @@ -233,7 +303,10 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true + "dev": true, + "requires": { + "wrappy": "1.0.2" + } }, "path-is-absolute": { "version": "1.0.1", @@ -251,7 +324,11 @@ "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 + "dev": true, + "requires": { + "resolve-from": "2.0.0", + "semver": "5.5.0" + } }, "resolve-from": { "version": "2.0.0", @@ -269,19 +346,33 @@ "version": "13.2.1", "resolved": "https://registry.npmjs.org/should/-/should-13.2.1.tgz", "integrity": "sha512-l+/NwEMO+DcstsHEwPHRHzC9j4UOE3VQwJGcMWSsD/vqpqHbnQ+1iSHy64Ihmmjx1uiRPD9pFadTSc3MJtXAgw==", - "dev": true + "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 + "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 + "dev": true, + "requires": { + "should-type": "1.4.0", + "should-type-adaptors": "1.1.0" + } }, "should-type": { "version": "1.4.0", @@ -293,7 +384,11 @@ "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 + "dev": true, + "requires": { + "should-type": "1.4.0", + "should-util": "1.0.0" + } }, "should-util": { "version": "1.0.0", @@ -307,17 +402,38 @@ "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 + "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 17bb0bb..24b4445 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,9 @@ }, "dependencies": { "async": "^0.9.0", - "i": "^0.3.2" + "i": "^0.3.2", + "why-is-node-running": "^1.2.3", + "wtfnode": "^0.5.7" }, "devDependencies": { "mongoose": ">=3.0.0", diff --git a/specs/belongsTo.spec.js b/specs/belongsTo.spec.js index 5580baf..02ce29a 100644 --- a/specs/belongsTo.spec.js +++ b/specs/belongsTo.spec.js @@ -1,14 +1,14 @@ -require('./spec_helper'); +'use strict'; -var mongoose = require('mongoose'), - should = require('should'), - uuid = require('node-uuid'); +let mongoose = require('./');; +let should = require('should'); +let uuid = require('node-uuid'); describe('belongsTo', function() { - let subject = null; + let subject, schema, partSchema, spareModel; before(function() { - var partSchema = new mongoose.Schema({}); + partSchema = new mongoose.Schema({}); partSchema.belongsTo('widget'); schema = mongoose.model('Part_' + uuid.v4(), partSchema).schema; subject = schema.paths.widget; @@ -67,7 +67,7 @@ describe('belongsTo', function() { describe('polymorphic', function() { before(function() { - var partSchema = new mongoose.Schema({}), + let partSchema = new mongoose.Schema({}), spareSchema = new mongoose.Schema({}); partSchema.belongsTo('assemblable', { polymorphic: true, required: true, enum: [ 'Bed', 'Dresser', 'Chair' ] }); spareSchema.belongsTo('assemblable', { polymorphic: true, }); @@ -129,7 +129,7 @@ describe('belongsTo', function() { }); describe('touch:true', function(done) { - var messageSchema, Message, message + let messageSchema, Message, message , mailboxSchema, Mailbox, mailbox; before(function(done) { @@ -151,7 +151,7 @@ describe('belongsTo', function() { }); it('touches the parent document before save', function(done) { - var oldVersion = mailbox.__v; + let oldVersion = mailbox.__v; message.save(function(err){ should.strictEqual(err, null); Mailbox.findById(mailbox._id, function(err, mailbox){ diff --git a/specs/hasAndBelongsToMany.spec.js b/specs/hasAndBelongsToMany.spec.js index 9b2e683..07c6686 100644 --- a/specs/hasAndBelongsToMany.spec.js +++ b/specs/hasAndBelongsToMany.spec.js @@ -1,6 +1,6 @@ -require('./spec_helper'); +require('./'); -var mongoose = require('mongoose'), +let mongoose = require('mongoose'), should = require('should'), TwitterUser = require('./support/twitterUserModel'), Pet = require('./support/petModel') @@ -36,7 +36,7 @@ describe('hasManyBelongsToMany', function() { }); it('test presence of added methods to the MongooseArray', function() { - var category = new Category(), + let category = new Category(), post = new TwitterPost(); category.posts.create.should.be.a.Function; @@ -60,10 +60,10 @@ describe('hasManyBelongsToMany', function() { describe('setChild true', function(){ it('instantiates one child document', function(){ - var category = new Category(), + let category = new Category(), post = { title: 'Easy relationships with mongoose-relationships' }; - var built = category.posts.build(post); + let built = category.posts.build(post); built.should.be.an.instanceof(TwitterPost); built.categories.should.containEql(category._id); @@ -73,14 +73,14 @@ describe('hasManyBelongsToMany', function() { }); it('instantiates many children documents', function(done) { - var category = new Category(), + let category = new Category(), posts = [{}, {}]; - var built = category.posts.build(posts); + let built = category.posts.build(posts); category.posts.should.have.length(2); - var count = category.posts.length; + let count = category.posts.length; built.forEach(function(post){ post.should.be.an.instanceof(TwitterPost); post.categories.should.containEql(category._id); @@ -90,7 +90,7 @@ describe('hasManyBelongsToMany', function() { }); it('appends an instantiated child document', function(done) { - var category = new Category(), + let category = new Category(), post = new TwitterPost(); category.posts.append(post, function(err, post){ @@ -104,13 +104,13 @@ describe('hasManyBelongsToMany', function() { }); it('concatenates many instantiated child documents', function(done) { - var category = new Category(), + let category = new Category(), posts = [new TwitterPost(), new TwitterPost()]; category.posts.concat(posts, function(err, posts){ should.strictEqual(err, null); - var count = posts.length; + let count = posts.length; posts.forEach(function(post){ post.categories.should.containEql(category._id); category.posts.should.containEql(post._id); @@ -120,7 +120,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates one child document', function(done) { - var category = new Category(), + let category = new Category(), post = { title: 'Easy relationships with mongoose-relationships' }; category.posts.create(post, function(err, category, post){ @@ -140,7 +140,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates many child documents', function(done){ - var category = new Category(); + let category = new Category(); posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ] @@ -151,7 +151,7 @@ describe('hasManyBelongsToMany', function() { posts.should.have.length(2); - var count = posts.length; + let count = posts.length; posts.forEach(function(post){ category.posts.should.containEql(post._id) post.should.be.an.instanceof(TwitterPost); @@ -162,12 +162,12 @@ describe('hasManyBelongsToMany', function() { }); it('finds children documents', function(done){ - var category = new Category(), + let category = new Category(), posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ] category.posts.create(posts, function(err, category, posts){ - var firstFind = category.posts.find({}) + let firstFind = category.posts.find({}) firstFind.should.be.an.instanceof(mongoose.Query); firstFind._conditions.should.have.property('_id'); @@ -184,7 +184,7 @@ describe('hasManyBelongsToMany', function() { post.categories.should.containEql(category._id); }); - var secondFind = firstFind.find({ title: 'Blog post #1' }, function(err, otherTwitterPosts){ + let secondFind = firstFind.find({ title: 'Blog post #1' }, function(err, otherTwitterPosts){ secondFind._conditions.title.should.equal('Blog post #1'); secondFind._conditions.should.have.property('_id'); @@ -198,12 +198,12 @@ describe('hasManyBelongsToMany', function() { }); it('deletes dependent', function(done){ - var category = new Category(), + let category = new Category(), posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ] category.posts.create(posts, function(err, category, posts){ - var post = posts[0]; + let post = posts[0]; category.posts.remove(post._id, function(err, category){ should.strictEqual(err, null); @@ -246,7 +246,7 @@ describe('hasManyBelongsToMany', function() { }); it('populations of path', function(done){ - var category = new Category(), + let category = new Category(), posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ]; @@ -256,7 +256,7 @@ describe('hasManyBelongsToMany', function() { should.strictEqual(err, null); // Syntactic sugar - var testSugar = function(){ + let testSugar = function(){ category.posts.populate(function(err, category){ should.strictEqual(err, null); @@ -268,7 +268,7 @@ describe('hasManyBelongsToMany', function() { }); }; - var count = populatedCategory.posts.length; + let count = populatedCategory.posts.length; populatedCategory.posts.forEach(function(post){ post.should.be.an.instanceof(TwitterPost); }); @@ -281,10 +281,10 @@ describe('hasManyBelongsToMany', function() { describe('setChild false', function(){ it('instantiates one child document', function(){ - var tweet = new Tweet(), + let tweet = new Tweet(), tag = { name: 'Easy' }; - var built = tweet.tags.build(tag); + let built = tweet.tags.build(tag); built.should.be.an.instanceof(Tag); tweet.tags.should.containEql(built._id); @@ -292,14 +292,14 @@ describe('hasManyBelongsToMany', function() { }); it('instantiates many children documents', function(done) { - var tweet = new Tweet(), + let tweet = new Tweet(), tags = [{}, {}]; - var built = tweet.tags.build(tags); + let built = tweet.tags.build(tags); tweet.tags.should.have.length(2); - var count = tweet.tags.length; + let count = tweet.tags.length; built.forEach(function(tag){ tag.should.be.an.instanceof(Tag); should(tag.tweets).eql(undefined); @@ -309,7 +309,7 @@ describe('hasManyBelongsToMany', function() { }); it('appends an instantiated child document', function(done) { - var tweet = new Tweet(), + let tweet = new Tweet(), tag = new Tag(); tweet.tags.append(tag, function(err, tag){ @@ -321,13 +321,13 @@ describe('hasManyBelongsToMany', function() { }); it('concats many instantiated child documents', function(done) { - var tweet = new Tweet(), + let tweet = new Tweet(), tags = [new Tag(), new Tag()]; tweet.tags.concat(tags, function(err, tags){ should.strictEqual(err, null); - var count = tags.length; + let count = tags.length; tags.forEach(function(tag){ should(tag.tweets).eql(undefined); tweet.tags.should.containEql(tag._id); @@ -337,7 +337,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates one child document', function(done) { - var tweet = new Tweet({ author: new TwitterUser() }), + let tweet = new Tweet({ author: new TwitterUser() }), tag = { name: 'Easy' }; tweet.tags.create(tag, function(err, tweet, tag){ @@ -356,7 +356,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates many child documents', function(done){ - var tweet = new Tweet({ author: new TwitterUser()}); + let tweet = new Tweet({ author: new TwitterUser()}); tags = [ { name: 'Blog tag #1' }, { name: 'Blog tag #2' } ] @@ -367,7 +367,7 @@ describe('hasManyBelongsToMany', function() { tags.should.have.length(2); - var count = tags.length; + let count = tags.length; tags.forEach(function(tag){ tweet.tags.should.containEql(tag._id) tag.should.be.an.instanceof(Tag); @@ -378,14 +378,14 @@ describe('hasManyBelongsToMany', function() { }); it('finds children documents', function(done){ - var tweet = new Tweet({ author: new TwitterUser()}), + let tweet = new Tweet({ author: new TwitterUser()}), tags = [ { name: 'Blog tag #1' }, { name: 'Blog tag #2' } ] tweet.tags.create(tags, function(err, tweet, tags){ should.strictEqual(err, null); - var find = tweet.tags.find({}) + let find = tweet.tags.find({}) find.should.be.an.instanceof(mongoose.Query); find._conditions.should.have.property('_id'); @@ -395,7 +395,7 @@ describe('hasManyBelongsToMany', function() { find.exec(function(err, newTags){ should.strictEqual(err, null); - var testFind = function(){ + let testFind = function(){ find.find({name: 'Blog tag #1'}, function(err, otherTags){ find._conditions.name.should.equal('Blog tag #1'); find._conditions.should.have.property('_id'); @@ -407,7 +407,7 @@ describe('hasManyBelongsToMany', function() { }); }; - var count = newTags.length; + let count = newTags.length; newTags.should.have.length(2); newTags.forEach(function(tag){ tweet.tags.should.containEql(tag._id) @@ -420,7 +420,7 @@ describe('hasManyBelongsToMany', function() { }); it('populations of path', function(done){ - var tweet = new Tweet({ author: new TwitterUser() }), + let tweet = new Tweet({ author: new TwitterUser() }), tags = [ { name: 'Blog tag #1' }, { name: 'Blog tag #2' } ]; @@ -430,11 +430,11 @@ describe('hasManyBelongsToMany', function() { should.strictEqual(err, null); // Syntactic sugar - var testSugar = function(){ + let testSugar = function(){ tweet.tags.populate(function(err, tweet){ should.strictEqual(err, null); - var count = tweet.tags.length; + let count = tweet.tags.length; tweet.tags.forEach(function(tag){ tag.should.be.an.instanceof(Tag); --count || done(); @@ -442,7 +442,7 @@ describe('hasManyBelongsToMany', function() { }); }; - var count = populatedTweet.tags.length; + let count = populatedTweet.tags.length; populatedTweet.tags.forEach(function(tag){ tag.should.be.an.instanceof(Tag); --count || testSugar(); @@ -455,7 +455,7 @@ describe('hasManyBelongsToMany', function() { }); describe('with descriminators', function(){ - var user, dog, fish; + let user, dog, fish; beforeEach(function(done){ user = new TwitterUser(); dog = new Dog({ name: 'Maddie', date_of_birth: new Date('12/24/2005'), breed: 'Border Collie Mix' }); @@ -537,7 +537,7 @@ describe('with descriminators', function(){ it('populates pets from the parent model with the correct type', function(done) { user.pets.populate(function(err, user){ should.strictEqual(err, null); - var foundFish, foundDog; + let foundFish, foundDog; user.pets.forEach(function(pet){ if(pet.id == fish.id){ foundFish = pet }; diff --git a/specs/hasMany.spec.js b/specs/hasMany.spec.js index ee4dfa0..d783216 100644 --- a/specs/hasMany.spec.js +++ b/specs/hasMany.spec.js @@ -1,12 +1,12 @@ -require('./spec_helper'); +require('./'); -var mongoose = require('mongoose') +let mongoose = require('mongoose') , async = require('async') , should = require('should') , uuid = require('node-uuid'); describe('hasMany without options', function(){ - var userSchema, User, user, widgetSchema, Widget, widget; + let userSchema, User, user, widgetSchema, Widget, widget; before(function(){ widgetSchema = mongoose.Schema({ name: String }); @@ -20,7 +20,7 @@ describe('hasMany without options', function(){ }); describe('schema', function(){ - it('has a virtual to represent the relationship', function(){ + it.only('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); @@ -100,7 +100,7 @@ describe('hasMany without options', function(){ }); describe('find', function(){ - var find; + let find; before(function(){ user = new User(); @@ -187,7 +187,7 @@ describe('hasMany without options', function(){ }); describe('findOne', function(){ - var find; + let find; before(function(done){ user = new User(); @@ -211,7 +211,7 @@ describe('hasMany without options', function(){ }); describe('concat', function(){ - var other_widget; + let other_widget; before(function(){ user = new User(); @@ -267,7 +267,7 @@ describe('hasMany without options', function(){ }); describe('push', function(){ - var otherWidget; + let otherWidget; before(function(){ user = new User({}); @@ -278,14 +278,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,7 +337,7 @@ 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; @@ -419,7 +419,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 +472,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 +481,7 @@ describe('hasMany polymorphic:true', function() { }); describe('hasMany inverse_of', function() { - var Reader, reader, Book; + let Reader, reader, Book; before(function(){ readerSchema = mongoose.Schema({ }); @@ -508,14 +508,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,7 +551,7 @@ 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(); @@ -592,7 +592,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 +607,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..43b303d --- /dev/null +++ b/specs/index.js @@ -0,0 +1,28 @@ +let wtfnode = require('wtfnode'); + +let mongoose = require('../')(require('mongoose')); + +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) { + wtfnode.dump(); + 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/relationship/hasAndBelongsToMany.spec.js b/specs/relationship/hasAndBelongsToMany.spec.js index a2bb92f..b8d5f5f 100644 --- a/specs/relationship/hasAndBelongsToMany.spec.js +++ b/specs/relationship/hasAndBelongsToMany.spec.js @@ -1,4 +1,4 @@ -require('../spec_helper'); +require('../'); var should = require('should') , Relationship = require('../../lib/relationship/hasAndBelongsToMany') diff --git a/specs/spec_helper.js b/specs/spec_helper.js deleted file mode 100644 index 7a460ab..0000000 --- a/specs/spec_helper.js +++ /dev/null @@ -1,22 +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); - }); - } -}); - -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); -}); From 708efde4c3112c2025d1800bfb2ba658312b22d5 Mon Sep 17 00:00:00 2001 From: Jonathon Storer Date: Thu, 8 Feb 2018 21:01:51 -0600 Subject: [PATCH 3/7] wip --- lib/hasAndBelongsToMany.js | 2 + lib/hasMany.js | 89 ++++++++-------- lib/index.js | 3 +- lib/polyfill.js | 5 + package.json | 6 +- specs/belongsTo.spec.js | 31 +++--- specs/hasAndBelongsToMany.spec.js | 6 +- specs/hasMany.spec.js | 169 +++++++++++++++++++++--------- specs/index.js | 9 +- 9 files changed, 198 insertions(+), 122 deletions(-) create mode 100644 lib/polyfill.js diff --git a/lib/hasAndBelongsToMany.js b/lib/hasAndBelongsToMany.js index 439f60d..744c002 100644 --- a/lib/hasAndBelongsToMany.js +++ b/lib/hasAndBelongsToMany.js @@ -1,4 +1,6 @@ module.exports = function (mongoose, i, utils) { + + return function hasAndBelongsToMany (schema, model, options) { this.type = 'habtm'; diff --git a/lib/hasMany.js b/lib/hasMany.js index 3a395eb..faee83f 100644 --- a/lib/hasMany.js +++ b/lib/hasMany.js @@ -65,34 +65,33 @@ module.exports = function (mongoose, i) { } } - HasMany.prototype.build = function(objects) { - let Model; - - 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.build = function(object) { + let doc = new this.associationModel(object || {}); + return associate.bind(this)(doc); }; - HasMany.prototype.create = function(objects, callback) { - if (Array.isArray(objects)) { - let docs = [], count = objects.length; + HasMany.prototype.create = function() { + let self = this; + let objects = Array.from(arguments); + let cb; - objects.forEach(function(object) { - this.create(object, function(err, doc) { - if (err) return callback(err); - docs.push(doc); - --count || callback(null, docs); - }); - }.bind(this)); + if ('function' == typeof objects[objects.length - 1]) { + cb = objects.pop(); } - else { - this.build(objects).save(callback); - }; + + return new Promise(function (resolve, reject) { + let promises = objects.map(function (object) { + return self.build(object).save(); + }); + + 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) { @@ -105,39 +104,43 @@ module.exports = function (mongoose, i) { fields = fields || null; options = options || null; - associate.bind(this)(conditions); + condition = associate.bind(this)(conditions); return this.associationModel.find(conditions, fields, options, callback); }; HasMany.prototype.findOne = function() { let callback - , args = Array.prototype.slice.call(arguments); + , args = Array.from(arguments); if ('function' == typeof args[args.length - 1]) { - callback = args[args.length - 1]; - args.pop(); + callback = args.pop(); } return this.find.apply(this, args).findOne(callback); }; - HasMany.prototype.concat = function(objects, callback) { - let docs = []; - - if (Array.isArray(objects)) { - let count = objects.length; + HasMany.prototype.concat = function() { + let self = this; + let objects = Array.from(arguments); + let cb; - objects.forEach(function(object) { - this.concat(object, function(err, doc) { - if (err) return callback(err); - docs.push(doc); - --count || callback(null, docs); - }); - }.bind(this)); + if ('function' == typeof objects[objects.length - 1]) { + cb = objects.pop(); } - else { - associate.bind(this)(objects).save(callback); - }; + + return new Promise(function (resolve, reject) { + let promises = objects.map(function (object) { + return associate.bind(self)(object).save(); + }); + + 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; diff --git a/lib/index.js b/lib/index.js index d57aa31..e43c115 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,11 +1,12 @@ 'use strict'; +require('./polyfill'); + let i = require('i')(); let utils = require('./utils'); module.exports = function mongooseRelation (mongoose) { - //let belongsTo = require('./belongsTo')(mongoose, i); let hasMany = require('./hasMany')(mongoose, i); let hasAndBelongsToMany = require('./hasAndBelongsToMany')(mongoose, i, utils); 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/package.json b/package.json index 24b4445..08dabfd 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ "wtfnode": "^0.5.7" }, "devDependencies": { + "mocha": "^5.0.0", "mongoose": ">=3.0.0", - "mocha": "latest", - "should": "latest", - "node-uuid": "latest" + "node-uuid": "latest", + "should": "latest" } } diff --git a/specs/belongsTo.spec.js b/specs/belongsTo.spec.js index 02ce29a..badfe40 100644 --- a/specs/belongsTo.spec.js +++ b/specs/belongsTo.spec.js @@ -128,36 +128,35 @@ describe('belongsTo', function() { }); }); - describe('touch:true', function(done) { + describe('touch:true', function() { let messageSchema, Message, message , mailboxSchema, Mailbox, mailbox; - before(function(done) { + before(function() { messageSchema = new mongoose.Schema({ }); messageSchema.belongsTo('mailbox', { touch: true }); Message = mongoose.model('Message', messageSchema); mailboxSchema = new mongoose.Schema({ }); mailboxSchema.hasMany('messages'); - Mailbox = mongoose.model('Mailbox', mailboxSchema); - mailbox = new Mailbox(); - mailbox.save(function(err){ - mailbox.messages.create({ }, function(err, msg){ - message = msg; - done(); - }); + + return Mailbox.create({}).then(function(_mailbox) { + mailbox = _mailbox; + return mailbox.messages.create({ }); + }).then(function (msg) { + message = msg; + }).catch(function (err) { + throw (err); }); }); - it('touches the parent document before save', function(done) { + it('touches the parent document before save', function() { let 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(); - }); + return message.save().then(function(){ + return Mailbox.findById(mailbox._id); + }).then(function(mailbox){ + should(mailbox.__v).not.eql(oldVersion); }); }); }); diff --git a/specs/hasAndBelongsToMany.spec.js b/specs/hasAndBelongsToMany.spec.js index 07c6686..d907c8d 100644 --- a/specs/hasAndBelongsToMany.spec.js +++ b/specs/hasAndBelongsToMany.spec.js @@ -17,13 +17,13 @@ describe('hasManyBelongsToMany', function() { it("cannot set 'dependent:nullify' and 'setChild:false'", function(){ (function(){ BookSchema.habtm('Page', { setChild: false, dependent: 'nullify' }); - }).should.throw("dependent cannot be set to 'nullify' while setChild is false") + }).should.throw(Error, /dependent cannot be set to 'nullify' while setChild is false/) }); it("cannot set 'dependent:destroy' and 'setChild:false'", function(){ (function(){ BookSchema.habtm('Page', { setChild: false, dependent: 'destroy' }); - }).should.throw("dependent cannot be set to 'destroy' while setChild is false") + }).should.throw(Error, /dependent cannot be set to 'destroy' while setChild is false/) }); }); @@ -39,7 +39,7 @@ describe('hasManyBelongsToMany', function() { let category = new Category(), post = new TwitterPost(); - category.posts.create.should.be.a.Function; + should(category.posts.create).be.a.Function; post.categories.create.should.be.a.Function; category.posts.find.should.be.a.Function; diff --git a/specs/hasMany.spec.js b/specs/hasMany.spec.js index d783216..e401747 100644 --- a/specs/hasMany.spec.js +++ b/specs/hasMany.spec.js @@ -20,7 +20,7 @@ describe('hasMany without options', function(){ }); describe('schema', function(){ - it.only('has a virtual to represent the relationship', 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); @@ -59,7 +59,7 @@ describe('hasMany without options', function(){ should(built.name).equal('Beam') }); - it('instantiates many children documents', function() { + it.skip('instantiates many children documents', function() { built = user.widgets.build([{}, {}]); built.forEach(function(widget){ @@ -85,18 +85,27 @@ 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(){ @@ -130,9 +139,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) { @@ -191,7 +258,7 @@ describe('hasMany without options', function(){ before(function(done){ user = new User(); - user.widgets.create([{ }, { }], done); + user.widgets.create({ }, { }, done); }); it('returns a findOne critera', function() { @@ -236,20 +303,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(); }); @@ -342,24 +407,25 @@ describe('hasMany dependent', function(){ , favoriteSchema, Favorite, favorite, favoriteEventCalled = false , repostSchema, Repost, repost, repostEventCalled = false; - before(function(done){ + //before(function(done){ + before(function(){ likeSchema = mongoose.Schema({}); likeSchema.belongsTo('post'); - likeSchema.pre('remove', function(next){ Like.emit('destroy-test-event', this); next(); }); + //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; }); + //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(); }); + //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; }); + //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(); }); + //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; }); + //Repost.once('destroy-test-event', function(){ repostEventCalled = true; }); postSchema = new mongoose.Schema({}); postSchema.hasMany('likes', { dependent: 'delete' }); @@ -367,16 +433,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[0]; - like = output.like[0]; - repost = output.repost[0]; - 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(); }); }); }); @@ -396,8 +462,8 @@ describe('hasMany dependent', function(){ 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(); }); }); @@ -555,21 +621,20 @@ describe('hasMany discriminated', function() { , 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); diff --git a/specs/index.js b/specs/index.js index 43b303d..50bd3b0 100644 --- a/specs/index.js +++ b/specs/index.js @@ -1,5 +1,3 @@ -let wtfnode = require('wtfnode'); - let mongoose = require('../')(require('mongoose')); let resetDb = function(next){ @@ -10,11 +8,14 @@ before(function(done){ if(mongoose.get('isConnected')){ resetDb(done); } else { - mongoose.connection.on('open', function(){ resetDb(done); }); } + mongoose.connection.on('open', function() { + resetDb(done); + }); + } }); after(function (done) { - wtfnode.dump(); + mongoose.disconnect(); done(); }); From a15de0ce436861766153d533c97963818807e514 Mon Sep 17 00:00:00 2001 From: Jonathon Storer Date: Thu, 8 Feb 2018 21:03:52 -0600 Subject: [PATCH 4/7] wip --- lib/hasAndBelongsToMany.js | 620 +++++++++++++++--------------- specs/hasAndBelongsToMany.spec.js | 92 ++--- 2 files changed, 357 insertions(+), 355 deletions(-) diff --git a/lib/hasAndBelongsToMany.js b/lib/hasAndBelongsToMany.js index 744c002..706e40d 100644 --- a/lib/hasAndBelongsToMany.js +++ b/lib/hasAndBelongsToMany.js @@ -1,370 +1,372 @@ -module.exports = function (mongoose, i, utils) { +var mongoose = require('mongoose') + , ObjectId = mongoose.Schema.ObjectId + , MongooseArray = mongoose.Types.Array + , i = require('i')() + , utils = require('./utils') + , merge = utils.merge; +module.exports = function hasAndBelongsToMany (schema, model, options) { + this.type = 'habtm'; - return function hasAndBelongsToMany (schema, model, options) { - this.type = 'habtm'; + this.schema = schema; + this.model = model; + this.options = options || {}; - this.schema = schema; - this.model = model; - this.options = options || {}; + this.pathName = this.options.through || i.pluralize(this.model.toLowerCase()); - this.pathName = this.options.through || i.pluralize(this.model.toLowerCase()); + var path = {}; + path[this.pathName] = [{ type: ObjectId, index: true, ref: this.model }]; + this.schema.add(path); - let path = {}; - path[this.pathName] = [{ type: mongoose.Schema.ObjectId, index: true, ref: this.model }]; - this.schema.add(path); + this.schema.paths[this.pathName].options[this.type] = this.model; + this.schema.paths[this.pathName].options.relationshipType = this.type; + this.schema.paths[this.pathName].options.relationshipModel = this.model; - this.schema.paths[this.pathName].options[this.type] = this.model; - this.schema.paths[this.pathName].options.relationshipType = this.type; - this.schema.paths[this.pathName].options.relationshipModel = this.model; + if (this.options.dependent) { + this.schema.paths[this.pathName].options.dependent = this.options.dependent; + } - if (this.options.dependent) { - this.schema.paths[this.pathName].options.dependent = this.options.dependent; - } - - let setChild = this.options.hasOwnProperty('setChild') ? this.options.setChild : true; - this.schema.paths[this.pathName].options.setChild = setChild; + var setChild = this.options.hasOwnProperty('setChild') ? this.options.setChild : true; + this.schema.paths[this.pathName].options.setChild = setChild; - if (!this.schema.paths[this.pathName].options.setChild) { - if (this.schema.paths[this.pathName].options.dependent == 'nullify') { - throw new Error("dependent cannot be set to 'nullify' while setChild is false"); - } + if (!this.schema.paths[this.pathName].options.setChild) { + if (this.schema.paths[this.pathName].options.dependent == 'nullify') { + throw new Error("dependent cannot be set to 'nullify' while setChild is false"); + } - if (this.schema.paths[this.pathName].options.dependent == 'destroy') { - throw new Error("dependent cannot be set to 'destroy' while setChild is false"); - } - }; + if (this.schema.paths[this.pathName].options.dependent == 'destroy') { + throw new Error("dependent cannot be set to 'destroy' while setChild is false"); + } }; +}; - /* Builds the instance of the child element - * - * @param {Object|Array} objs - * @return {Document|Array} - * @api public - */ - - // mongoose > 3.9.x support - let MongooseArray = mongoose.Types.Array; - let base = MongooseArray.mixin || MongooseArray.prototype; - - base.build = function (objs) { - let childModelName = this._schema.options.relationshipModel; - - let buildOne = function(obj){ - let childModel = mongoose.model(obj.__t || childModelName) - , child = new childModel(obj); - - this._parent[this._path].push(child); - - if (!!this._schema.options.setChild) { - - // start remove me asap - // needed for this._childToParent.name - model = mongoose.model(this._schema.options.relationshipModel); - for (let path in model.schema.paths) { - options = model.schema.paths[path].options; - ref = (options.relationshipModel || options.ref); - if(ref == this._parent.constructor.modelName){ - options.name = path; - this._childToParent = options - } - } - // end remove me asap +/* Builds the instance of the child element +* +* @param {Object|Array} objs +* @return {Document|Array} +* @api public +*/ - child[this._childToParent.name].push(this._parent); - } +// mongoose > 3.9.x support +var base = MongooseArray.mixin || MongooseArray.prototype; - return child; - }.bind(this); +base.build = function (objs) { + var childModelName = this._schema.options.relationshipModel; - if (Array.isArray(objs)) { - return objs.map(buildOne); - } else { - return buildOne(objs); - } - }; + var buildOne = function(obj){ + var childModel = mongoose.model(obj.__t || childModelName) + , child = new childModel(obj); - /* Create a child document and add it to the parent `Array` - * - * @param {Object|Array} objs [object(s) to create] - * @param {Functions} callback [passed: (err, parent, created children)] - * @api public - */ + this._parent[this._path].push(child); - base.create = function (objs, callback) { - objs = this.build(objs); + if (!!this._schema.options.setChild) { - let complete = function(err, docs){ - this._parent.save(function(err){ - callback(err, this._parent, docs); - }.bind(this)); - }.bind(this); - - let validForCreate = function(doc){ - if (!!this._schema.options.setChild) { - - // start remove me asap - // needed for this._childToParent.name - model = mongoose.model(this._schema.options.relationshipModel); - - for (let path in model.schema.paths) { - options = model.schema.paths[path].options; - ref = (options.relationshipModel || options.ref); - if(ref == this._parent.constructor.modelName){ - options.name = path; - this._childToParent = options - } + // start remove me asap + // needed for this._childToParent.name + model = mongoose.model(this._schema.options.relationshipModel); + for (var path in model.schema.paths) { + options = model.schema.paths[path].options; + ref = (options.relationshipModel || options.ref); + if(ref == this._parent.constructor.modelName){ + options.name = path; + this._childToParent = options } + } + // end remove me asap - this._allowed_discriminators = [ model.modelName ].concat(Object.keys(model.discriminators || {})); - let childIsAllowed = function (child) { - return !!~this._allowed_discriminators.indexOf(child.constructor.modelName); - }.bind(this); + child[this._childToParent.name].push(this._parent); + } - // end remove me asap + return child; + }.bind(this); - return !!this._childToParent && childIsAllowed(doc); - } else { - return true - } - }.bind(this); + if (Array.isArray(objs)) { + return objs.map(buildOne); + } else { + return buildOne(objs); + } +}; - let createOne = function(doc, done){ - if (!validForCreate(doc)) - return done(new Error('Parent model not referenced anywhere in the Child Schema')); - doc.save(done); - }; +/* Create a child document and add it to the parent `Array` +* +* @param {Object|Array} objs [object(s) to create] +* @param {Functions} callback [passed: (err, parent, created children)] +* @api public +*/ - if(Array.isArray(objs)){ - let count = objs.length, docs = []; +base.create = function (objs, callback) { + objs = this.build(objs); - objs.forEach(function(obj){ - createOne(obj, function(err, doc){ - if (err) return complete(err); - docs.push(doc); - --count || complete(null, docs); - }.bind(this)); - }.bind(this)); - } - else { - createOne(objs, complete); - } - }; + var complete = function(err, docs){ + this._parent.save(function(err){ + callback(err, this._parent, docs); + }.bind(this)); + }.bind(this); - /* Append an already instantiated document saves it in the process. - * - * @param {Document} child - * @param {Function} callback - * @api public - */ - - base.append = function (child, callback) { - - // start remove me asap - // needed for this._childToParent.name - model = mongoose.model(this._schema.options.relationshipModel); - - for (let path in model.schema.paths) { - options = model.schema.paths[path].options; - ref = (options.relationshipModel || options.ref); - if(ref == this._parent.constructor.modelName){ - options.name = path; - this._childToParent = options + var validForCreate = function(doc){ + if (!!this._schema.options.setChild) { + + // start remove me asap + // needed for this._childToParent.name + model = mongoose.model(this._schema.options.relationshipModel); + + for (var path in model.schema.paths) { + options = model.schema.paths[path].options; + ref = (options.relationshipModel || options.ref); + if(ref == this._parent.constructor.modelName){ + options.name = path; + this._childToParent = options + } } - } - this._allowed_discriminators = [ model.modelName ].concat(Object.keys(model.discriminators || {})); - let childIsAllowed = function (child) { - return !!~this._allowed_discriminators.indexOf(child.constructor.modelName); - }.bind(this); + this._allowed_discriminators = [ model.modelName ].concat(Object.keys(model.discriminators || {})); + var childIsAllowed = function (child) { + return !!~this._allowed_discriminators.indexOf(child.constructor.modelName); + }.bind(this); - // end remove me asap + // end remove me asap - // TODO: abstract me - if(!childIsAllowed(child)) { - return throwErr('Wrong Model type'); + return !!this._childToParent && childIsAllowed(doc); + } else { + return true } + }.bind(this); - if (!!this._schema.options.setChild) { - child[this._childToParent.name].push(this._parent._id); - } + var createOne = function(doc, done){ + if (!validForCreate(doc)) + return done(new Error('Parent model not referenced anywhere in the Child Schema')); + doc.save(done); + }; - this._parent[this._path].push(child._id); + if(Array.isArray(objs)){ + var count = objs.length, docs = []; - callback && child.save(callback); - return child; - }; + objs.forEach(function(obj){ + createOne(obj, function(err, doc){ + if (err) return complete(err); + docs.push(doc); + --count || complete(null, docs); + }.bind(this)); + }.bind(this)); + } + else { + createOne(objs, complete); + } +}; - /* Append many instantiated children documents - * - * @param {Array} children - * @param {Function} callback - * @api public - */ - base._concat = Array.prototype.concat; - base.concat = function (docs, callback) { - let throwErr = utils.throwErr(callback); - - if (!Array.isArray(docs)){ - return throwErr('First argument needs to be an Array'); - }; +/* Append an already instantiated document saves it in the process. +* +* @param {Document} child +* @param {Function} callback +* @api public +*/ + +base.append = function (child, callback) { + + // start remove me asap + // needed for this._childToParent.name + model = mongoose.model(this._schema.options.relationshipModel); + + for (var path in model.schema.paths) { + options = model.schema.paths[path].options; + ref = (options.relationshipModel || options.ref); + if(ref == this._parent.constructor.modelName){ + options.name = path; + this._childToParent = options + } + } - let complete = function(err, docs) { - if(err){ return throwErr(err) } + this._allowed_discriminators = [ model.modelName ].concat(Object.keys(model.discriminators || {})); + var childIsAllowed = function (child) { + return !!~this._allowed_discriminators.indexOf(child.constructor.modelName); + }.bind(this); - let ids = docs.map(function (doc) { return doc._id }); - this._concat(ids); - this._markModified(); + // end remove me asap - callback(null, docs); - }.bind(this); + // TODO: abstract me + if(!childIsAllowed(child)) { + return throwErr('Wrong Model type'); + } - let count = docs.length; - let savedDocs = []; - docs.forEach(function(doc){ - this.append(doc); - doc.save(function(err, doc){ - if(err) return complete(err); + if (!!this._schema.options.setChild) { + child[this._childToParent.name].push(this._parent._id); + } - savedDocs.push(doc); - --count || complete(null, savedDocs); - }); - }.bind(this)); + this._parent[this._path].push(child._id); + callback && child.save(callback); + return child; +}; + +/* Append many instantiated children documents +* +* @param {Array} children +* @param {Function} callback +* @api public +*/ +base._concat = Array.prototype.concat; +base.concat = function (docs, callback) { + var throwErr = utils.throwErr(callback); + + if (!Array.isArray(docs)){ + return throwErr('First argument needs to be an Array'); }; - /* Find children documents - * - * *This is a copy of Model.find w/ added error throwing and such* - */ - base.find = function (conditions, fields, options, callback) { - // Copied from `Model.find` - if ('function' == typeof conditions) { - callback = conditions; - conditions = {}; - fields = null; - options = null; - } else if ('function' == typeof fields) { - callback = fields; - fields = null; - options = null; - } else if ('function' == typeof options) { - callback = options; - options = null; - } + var complete = function(err, docs) { + if(err){ return throwErr(err) } - // start remove me asap - // needed for this._childToParent.name - model = mongoose.model(this._schema.options.relationshipModel); - for (let path in model.schema.paths) { - options = model.schema.paths[path].options; - ref = (options.relationshipModel || options.ref); - if(ref == this._parent.constructor.modelName){ - options.name = path; - this._childToParent = options - } - } - // end remove me asap + var ids = docs.map(function (doc) { return doc._id }); + this._concat(ids); + this._markModified(); - let childModel = mongoose.model(this._schema.options.relationshipModel); - childPath = this._childToParent; - safeConditions = {}, - throwErr = utils.throwErr(callback); + callback(null, docs); + }.bind(this); - utils.merge(safeConditions, conditions); + var count = docs.length; + var savedDocs = []; + docs.forEach(function(doc){ + this.append(doc); + doc.save(function(err, doc){ + if(err) return complete(err); - if (!!this._schema.options.setChild) { - if (!childPath) { - return throwErr('Parent model not referenced anywhere in the Child Schema'); - } + savedDocs.push(doc); + --count || complete(null, savedDocs); + }); + }.bind(this)); - let childConditions = {}; - childConditions[childPath.name] = this._parent._id; - utils.merge(safeConditions, childConditions); - } +}; - utils.merge(safeConditions, { _id: { $in: this._parent[this._path] } }); +/* Find children documents +* +* *This is a copy of Model.find w/ added error throwing and such* +*/ +base.find = function (conditions, fields, options, callback) { + // Copied from `Model.find` + if ('function' == typeof conditions) { + callback = conditions; + conditions = {}; + fields = null; + options = null; + } else if ('function' == typeof fields) { + callback = fields; + fields = null; + options = null; + } else if ('function' == typeof options) { + callback = options; + options = null; + } + + // start remove me asap + // needed for this._childToParent.name + model = mongoose.model(this._schema.options.relationshipModel); + for (var path in model.schema.paths) { + options = model.schema.paths[path].options; + ref = (options.relationshipModel || options.ref); + if(ref == this._parent.constructor.modelName){ + options.name = path; + this._childToParent = options + } + } + // end remove me asap - let query = childModel.find(safeConditions, options).select(fields); + var childModel = mongoose.model(this._schema.options.relationshipModel); + childPath = this._childToParent; + safeConditions = {}, + throwErr = utils.throwErr(callback); - callback && query.exec(callback); - return query; - }; + merge(safeConditions, conditions); - /* Syntactic sugar to populate the array - * - * @param {Array} fields - * @param {Function} callback - * @return {Query} - * @api public - */ - base.populate = function (fields, callback) { - if ('function' == typeof fields) { - callback = fields; - fields = null; + if (!!this._schema.options.setChild) { + if (!childPath) { + return throwErr('Parent model not referenced anywhere in the Child Schema'); } - // TODO: do we really need to initialize a new doc? - return this._parent.constructor - .findById(this._parent._id) - .populate(this._path, fields) - .exec(callback); - }; + var childConditions = {}; + childConditions[childPath.name] = this._parent._id; + merge(safeConditions, childConditions); + } - /* Overrides MongooseArray.remove only for dependent:destroy relationships - * - * @param {ObjectId} id - * @param {Function} callback - * @return {ObjectId} - * @api public - */ - base._remove = base.remove; - base.remove = base.delete = function (id, callback) { - let parent = this._parent, - childModel = mongoose.model(this._schema.options.relationshipModel); - childPath = this._childToParent; - child = null, - throwErr = utils.throwErr(callback); - - if (id._id) { - let child = id; - id = child._id; - } + merge(safeConditions, { _id: { $in: this._parent[this._path] } }); - // TODO: should a callback be required? - if (!callback) { - callback = function (err) { - if (err) { - throw err; - } - }; - } + var query = childModel.find(safeConditions, options).select(fields); + + callback && query.exec(callback); + return query; +}; - let hasOrFetchChild = function(done){ - if(child){ - done(null, child); - } else { - childModel.findById(id, done); - }; +/* Syntactic sugar to populate the array +* +* @param {Array} fields +* @param {Function} callback +* @return {Query} +* @api public +*/ +base.populate = function (fields, callback) { + if ('function' == typeof fields) { + callback = fields; + fields = null; + } + + // TODO: do we really need to initialize a new doc? + return this._parent.constructor + .findById(this._parent._id) + .populate(this._path, fields) + .exec(callback); +}; + +/* Overrides MongooseArray.remove only for dependent:destroy relationships +* +* @param {ObjectId} id +* @param {Function} callback +* @return {ObjectId} +* @api public +*/ +base._remove = base.remove; +base.remove = base.delete = function (id, callback) { + var parent = this._parent, + childModel = mongoose.model(this._schema.options.relationshipModel); + childPath = this._childToParent; + child = null, + throwErr = utils.throwErr(callback); + + if (id._id) { + var child = id; + id = child._id; + } + + // TODO: should a callback be required? + if (!callback) { + callback = function (err) { + if (err) { + throw err; + } }; + } - // TODO: is this needed? - // I think this removing the id from the instance array - // however, it could be not needed - this._remove(id); - - // TODO: shold habtm support delete and destroy? - if (!!~['delete', 'destroy', 'nullify'].indexOf(this._schema.options.dependent)){ - hasOrFetchChild(function(err, child){ - if (err) { return throwErr(err) }; - child[childPath.name].remove(parent._id); - child.save(function(err, child){ - if (err){ return throwErr(err) }; - callback(null, parent); - }); - }); + var hasOrFetchChild = function(done){ + if(child){ + done(null, child); } else { - callback(null, parent); - } + childModel.findById(id, done); + }; }; + + // TODO: is this needed? + // I think this removing the id from the instance array + // however, it could be not needed + this._remove(id); + + // TODO: shold habtm support delete and destroy? + if (!!~['delete', 'destroy', 'nullify'].indexOf(this._schema.options.dependent)){ + hasOrFetchChild(function(err, child){ + if (err) { return throwErr(err) }; + child[childPath.name].remove(parent._id); + child.save(function(err, child){ + if (err){ return throwErr(err) }; + callback(null, parent); + }); + }); + } else { + callback(null, parent); + } }; diff --git a/specs/hasAndBelongsToMany.spec.js b/specs/hasAndBelongsToMany.spec.js index d907c8d..9b2e683 100644 --- a/specs/hasAndBelongsToMany.spec.js +++ b/specs/hasAndBelongsToMany.spec.js @@ -1,6 +1,6 @@ -require('./'); +require('./spec_helper'); -let mongoose = require('mongoose'), +var mongoose = require('mongoose'), should = require('should'), TwitterUser = require('./support/twitterUserModel'), Pet = require('./support/petModel') @@ -17,13 +17,13 @@ describe('hasManyBelongsToMany', function() { it("cannot set 'dependent:nullify' and 'setChild:false'", function(){ (function(){ BookSchema.habtm('Page', { setChild: false, dependent: 'nullify' }); - }).should.throw(Error, /dependent cannot be set to 'nullify' while setChild is false/) + }).should.throw("dependent cannot be set to 'nullify' while setChild is false") }); it("cannot set 'dependent:destroy' and 'setChild:false'", function(){ (function(){ BookSchema.habtm('Page', { setChild: false, dependent: 'destroy' }); - }).should.throw(Error, /dependent cannot be set to 'destroy' while setChild is false/) + }).should.throw("dependent cannot be set to 'destroy' while setChild is false") }); }); @@ -36,10 +36,10 @@ describe('hasManyBelongsToMany', function() { }); it('test presence of added methods to the MongooseArray', function() { - let category = new Category(), + var category = new Category(), post = new TwitterPost(); - should(category.posts.create).be.a.Function; + category.posts.create.should.be.a.Function; post.categories.create.should.be.a.Function; category.posts.find.should.be.a.Function; @@ -60,10 +60,10 @@ describe('hasManyBelongsToMany', function() { describe('setChild true', function(){ it('instantiates one child document', function(){ - let category = new Category(), + var category = new Category(), post = { title: 'Easy relationships with mongoose-relationships' }; - let built = category.posts.build(post); + var built = category.posts.build(post); built.should.be.an.instanceof(TwitterPost); built.categories.should.containEql(category._id); @@ -73,14 +73,14 @@ describe('hasManyBelongsToMany', function() { }); it('instantiates many children documents', function(done) { - let category = new Category(), + var category = new Category(), posts = [{}, {}]; - let built = category.posts.build(posts); + var built = category.posts.build(posts); category.posts.should.have.length(2); - let count = category.posts.length; + var count = category.posts.length; built.forEach(function(post){ post.should.be.an.instanceof(TwitterPost); post.categories.should.containEql(category._id); @@ -90,7 +90,7 @@ describe('hasManyBelongsToMany', function() { }); it('appends an instantiated child document', function(done) { - let category = new Category(), + var category = new Category(), post = new TwitterPost(); category.posts.append(post, function(err, post){ @@ -104,13 +104,13 @@ describe('hasManyBelongsToMany', function() { }); it('concatenates many instantiated child documents', function(done) { - let category = new Category(), + var category = new Category(), posts = [new TwitterPost(), new TwitterPost()]; category.posts.concat(posts, function(err, posts){ should.strictEqual(err, null); - let count = posts.length; + var count = posts.length; posts.forEach(function(post){ post.categories.should.containEql(category._id); category.posts.should.containEql(post._id); @@ -120,7 +120,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates one child document', function(done) { - let category = new Category(), + var category = new Category(), post = { title: 'Easy relationships with mongoose-relationships' }; category.posts.create(post, function(err, category, post){ @@ -140,7 +140,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates many child documents', function(done){ - let category = new Category(); + var category = new Category(); posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ] @@ -151,7 +151,7 @@ describe('hasManyBelongsToMany', function() { posts.should.have.length(2); - let count = posts.length; + var count = posts.length; posts.forEach(function(post){ category.posts.should.containEql(post._id) post.should.be.an.instanceof(TwitterPost); @@ -162,12 +162,12 @@ describe('hasManyBelongsToMany', function() { }); it('finds children documents', function(done){ - let category = new Category(), + var category = new Category(), posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ] category.posts.create(posts, function(err, category, posts){ - let firstFind = category.posts.find({}) + var firstFind = category.posts.find({}) firstFind.should.be.an.instanceof(mongoose.Query); firstFind._conditions.should.have.property('_id'); @@ -184,7 +184,7 @@ describe('hasManyBelongsToMany', function() { post.categories.should.containEql(category._id); }); - let secondFind = firstFind.find({ title: 'Blog post #1' }, function(err, otherTwitterPosts){ + var secondFind = firstFind.find({ title: 'Blog post #1' }, function(err, otherTwitterPosts){ secondFind._conditions.title.should.equal('Blog post #1'); secondFind._conditions.should.have.property('_id'); @@ -198,12 +198,12 @@ describe('hasManyBelongsToMany', function() { }); it('deletes dependent', function(done){ - let category = new Category(), + var category = new Category(), posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ] category.posts.create(posts, function(err, category, posts){ - let post = posts[0]; + var post = posts[0]; category.posts.remove(post._id, function(err, category){ should.strictEqual(err, null); @@ -246,7 +246,7 @@ describe('hasManyBelongsToMany', function() { }); it('populations of path', function(done){ - let category = new Category(), + var category = new Category(), posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ]; @@ -256,7 +256,7 @@ describe('hasManyBelongsToMany', function() { should.strictEqual(err, null); // Syntactic sugar - let testSugar = function(){ + var testSugar = function(){ category.posts.populate(function(err, category){ should.strictEqual(err, null); @@ -268,7 +268,7 @@ describe('hasManyBelongsToMany', function() { }); }; - let count = populatedCategory.posts.length; + var count = populatedCategory.posts.length; populatedCategory.posts.forEach(function(post){ post.should.be.an.instanceof(TwitterPost); }); @@ -281,10 +281,10 @@ describe('hasManyBelongsToMany', function() { describe('setChild false', function(){ it('instantiates one child document', function(){ - let tweet = new Tweet(), + var tweet = new Tweet(), tag = { name: 'Easy' }; - let built = tweet.tags.build(tag); + var built = tweet.tags.build(tag); built.should.be.an.instanceof(Tag); tweet.tags.should.containEql(built._id); @@ -292,14 +292,14 @@ describe('hasManyBelongsToMany', function() { }); it('instantiates many children documents', function(done) { - let tweet = new Tweet(), + var tweet = new Tweet(), tags = [{}, {}]; - let built = tweet.tags.build(tags); + var built = tweet.tags.build(tags); tweet.tags.should.have.length(2); - let count = tweet.tags.length; + var count = tweet.tags.length; built.forEach(function(tag){ tag.should.be.an.instanceof(Tag); should(tag.tweets).eql(undefined); @@ -309,7 +309,7 @@ describe('hasManyBelongsToMany', function() { }); it('appends an instantiated child document', function(done) { - let tweet = new Tweet(), + var tweet = new Tweet(), tag = new Tag(); tweet.tags.append(tag, function(err, tag){ @@ -321,13 +321,13 @@ describe('hasManyBelongsToMany', function() { }); it('concats many instantiated child documents', function(done) { - let tweet = new Tweet(), + var tweet = new Tweet(), tags = [new Tag(), new Tag()]; tweet.tags.concat(tags, function(err, tags){ should.strictEqual(err, null); - let count = tags.length; + var count = tags.length; tags.forEach(function(tag){ should(tag.tweets).eql(undefined); tweet.tags.should.containEql(tag._id); @@ -337,7 +337,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates one child document', function(done) { - let tweet = new Tweet({ author: new TwitterUser() }), + var tweet = new Tweet({ author: new TwitterUser() }), tag = { name: 'Easy' }; tweet.tags.create(tag, function(err, tweet, tag){ @@ -356,7 +356,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates many child documents', function(done){ - let tweet = new Tweet({ author: new TwitterUser()}); + var tweet = new Tweet({ author: new TwitterUser()}); tags = [ { name: 'Blog tag #1' }, { name: 'Blog tag #2' } ] @@ -367,7 +367,7 @@ describe('hasManyBelongsToMany', function() { tags.should.have.length(2); - let count = tags.length; + var count = tags.length; tags.forEach(function(tag){ tweet.tags.should.containEql(tag._id) tag.should.be.an.instanceof(Tag); @@ -378,14 +378,14 @@ describe('hasManyBelongsToMany', function() { }); it('finds children documents', function(done){ - let tweet = new Tweet({ author: new TwitterUser()}), + var tweet = new Tweet({ author: new TwitterUser()}), tags = [ { name: 'Blog tag #1' }, { name: 'Blog tag #2' } ] tweet.tags.create(tags, function(err, tweet, tags){ should.strictEqual(err, null); - let find = tweet.tags.find({}) + var find = tweet.tags.find({}) find.should.be.an.instanceof(mongoose.Query); find._conditions.should.have.property('_id'); @@ -395,7 +395,7 @@ describe('hasManyBelongsToMany', function() { find.exec(function(err, newTags){ should.strictEqual(err, null); - let testFind = function(){ + var testFind = function(){ find.find({name: 'Blog tag #1'}, function(err, otherTags){ find._conditions.name.should.equal('Blog tag #1'); find._conditions.should.have.property('_id'); @@ -407,7 +407,7 @@ describe('hasManyBelongsToMany', function() { }); }; - let count = newTags.length; + var count = newTags.length; newTags.should.have.length(2); newTags.forEach(function(tag){ tweet.tags.should.containEql(tag._id) @@ -420,7 +420,7 @@ describe('hasManyBelongsToMany', function() { }); it('populations of path', function(done){ - let tweet = new Tweet({ author: new TwitterUser() }), + var tweet = new Tweet({ author: new TwitterUser() }), tags = [ { name: 'Blog tag #1' }, { name: 'Blog tag #2' } ]; @@ -430,11 +430,11 @@ describe('hasManyBelongsToMany', function() { should.strictEqual(err, null); // Syntactic sugar - let testSugar = function(){ + var testSugar = function(){ tweet.tags.populate(function(err, tweet){ should.strictEqual(err, null); - let count = tweet.tags.length; + var count = tweet.tags.length; tweet.tags.forEach(function(tag){ tag.should.be.an.instanceof(Tag); --count || done(); @@ -442,7 +442,7 @@ describe('hasManyBelongsToMany', function() { }); }; - let count = populatedTweet.tags.length; + var count = populatedTweet.tags.length; populatedTweet.tags.forEach(function(tag){ tag.should.be.an.instanceof(Tag); --count || testSugar(); @@ -455,7 +455,7 @@ describe('hasManyBelongsToMany', function() { }); describe('with descriminators', function(){ - let user, dog, fish; + var user, dog, fish; beforeEach(function(done){ user = new TwitterUser(); dog = new Dog({ name: 'Maddie', date_of_birth: new Date('12/24/2005'), breed: 'Border Collie Mix' }); @@ -537,7 +537,7 @@ describe('with descriminators', function(){ it('populates pets from the parent model with the correct type', function(done) { user.pets.populate(function(err, user){ should.strictEqual(err, null); - let foundFish, foundDog; + var foundFish, foundDog; user.pets.forEach(function(pet){ if(pet.id == fish.id){ foundFish = pet }; From 7068c77d6b3f295297fd96423f4424b03158680d Mon Sep 17 00:00:00 2001 From: Jonathon Storer Date: Thu, 8 Feb 2018 21:07:53 -0600 Subject: [PATCH 5/7] wip --- specs/hasMany.spec.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/specs/hasMany.spec.js b/specs/hasMany.spec.js index e401747..e72bc72 100644 --- a/specs/hasMany.spec.js +++ b/specs/hasMany.spec.js @@ -250,19 +250,16 @@ describe('hasMany without options', function(){ should(find).be.instanceOf(mongoose.Query); }); }); - }); describe('findOne', function(){ - let find; - before(function(done){ user = new User(); 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'); @@ -407,25 +404,18 @@ describe('hasMany dependent', function(){ , 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' }); @@ -454,10 +444,6 @@ describe('hasMany dependent', function(){ done(); }); }); - - it('does not run child middlewares', function(){ - should(likeEventCalled).be.false; - }); }); describe('destroy', function(){ @@ -467,10 +453,6 @@ describe('hasMany dependent', function(){ done(); }); }); - - it('runs child middlewares', function(){ - should(favoriteEventCalled).be.true; - }); }); describe('nullify', function(){ From 0d6c43fc34e026de81937aaf9ebe64be2750faae Mon Sep 17 00:00:00 2001 From: Jonathon Storer Date: Fri, 9 Feb 2018 05:34:57 -0600 Subject: [PATCH 6/7] wip --- lib/hasAndBelongsToMany.js | 2 + lib/index.js | 2 +- specs/hasAndBelongsToMany.spec.js | 94 ++++++++++++++++--------------- 3 files changed, 51 insertions(+), 47 deletions(-) diff --git a/lib/hasAndBelongsToMany.js b/lib/hasAndBelongsToMany.js index 706e40d..f711605 100644 --- a/lib/hasAndBelongsToMany.js +++ b/lib/hasAndBelongsToMany.js @@ -38,6 +38,8 @@ module.exports = function hasAndBelongsToMany (schema, model, options) { throw new Error("dependent cannot be set to 'destroy' while setChild is false"); } }; + console.log('--------'); + console.log(this.pathName, this.schema.paths[this.pathName]); }; /* Builds the instance of the child element diff --git a/lib/index.js b/lib/index.js index e43c115..d1d5247 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,7 +8,7 @@ let utils = require('./utils'); module.exports = function mongooseRelation (mongoose) { let hasMany = require('./hasMany')(mongoose, i); - let hasAndBelongsToMany = require('./hasAndBelongsToMany')(mongoose, i, utils); + let hasAndBelongsToMany = require('./hasAndBelongsToMany') mongoose.Schema.prototype.belongsTo = require('./belongsTo')(mongoose, i); diff --git a/specs/hasAndBelongsToMany.spec.js b/specs/hasAndBelongsToMany.spec.js index 9b2e683..4b2cc82 100644 --- a/specs/hasAndBelongsToMany.spec.js +++ b/specs/hasAndBelongsToMany.spec.js @@ -1,6 +1,6 @@ -require('./spec_helper'); +require('./'); -var mongoose = require('mongoose'), +let mongoose = require('mongoose'), should = require('should'), TwitterUser = require('./support/twitterUserModel'), Pet = require('./support/petModel') @@ -36,7 +36,7 @@ describe('hasManyBelongsToMany', function() { }); it('test presence of added methods to the MongooseArray', function() { - var category = new Category(), + let category = new Category(), post = new TwitterPost(); category.posts.create.should.be.a.Function; @@ -59,28 +59,30 @@ describe('hasManyBelongsToMany', function() { }); describe('setChild true', function(){ - it('instantiates one child document', function(){ - var category = new Category(), + it.only('instantiates one child document', function(){ + let category = new Category(), post = { title: 'Easy relationships with mongoose-relationships' }; - var built = category.posts.build(post); + let built = category.posts.build(post); built.should.be.an.instanceof(TwitterPost); + console.log(built); + console.log(built.categories); built.categories.should.containEql(category._id); - category.posts.should.containEql(built._id); + //category.posts.should.containEql(built._id); - category.posts.should.have.length(1); + //category.posts.should.have.length(1); }); it('instantiates many children documents', function(done) { - var category = new Category(), + let category = new Category(), posts = [{}, {}]; - var built = category.posts.build(posts); + let built = category.posts.build(posts); category.posts.should.have.length(2); - var count = category.posts.length; + let count = category.posts.length; built.forEach(function(post){ post.should.be.an.instanceof(TwitterPost); post.categories.should.containEql(category._id); @@ -90,7 +92,7 @@ describe('hasManyBelongsToMany', function() { }); it('appends an instantiated child document', function(done) { - var category = new Category(), + let category = new Category(), post = new TwitterPost(); category.posts.append(post, function(err, post){ @@ -104,13 +106,13 @@ describe('hasManyBelongsToMany', function() { }); it('concatenates many instantiated child documents', function(done) { - var category = new Category(), + let category = new Category(), posts = [new TwitterPost(), new TwitterPost()]; category.posts.concat(posts, function(err, posts){ should.strictEqual(err, null); - var count = posts.length; + let count = posts.length; posts.forEach(function(post){ post.categories.should.containEql(category._id); category.posts.should.containEql(post._id); @@ -120,7 +122,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates one child document', function(done) { - var category = new Category(), + let category = new Category(), post = { title: 'Easy relationships with mongoose-relationships' }; category.posts.create(post, function(err, category, post){ @@ -140,7 +142,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates many child documents', function(done){ - var category = new Category(); + let category = new Category(); posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ] @@ -151,7 +153,7 @@ describe('hasManyBelongsToMany', function() { posts.should.have.length(2); - var count = posts.length; + let count = posts.length; posts.forEach(function(post){ category.posts.should.containEql(post._id) post.should.be.an.instanceof(TwitterPost); @@ -162,12 +164,12 @@ describe('hasManyBelongsToMany', function() { }); it('finds children documents', function(done){ - var category = new Category(), + let category = new Category(), posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ] category.posts.create(posts, function(err, category, posts){ - var firstFind = category.posts.find({}) + let firstFind = category.posts.find({}) firstFind.should.be.an.instanceof(mongoose.Query); firstFind._conditions.should.have.property('_id'); @@ -184,7 +186,7 @@ describe('hasManyBelongsToMany', function() { post.categories.should.containEql(category._id); }); - var secondFind = firstFind.find({ title: 'Blog post #1' }, function(err, otherTwitterPosts){ + let secondFind = firstFind.find({ title: 'Blog post #1' }, function(err, otherTwitterPosts){ secondFind._conditions.title.should.equal('Blog post #1'); secondFind._conditions.should.have.property('_id'); @@ -198,12 +200,12 @@ describe('hasManyBelongsToMany', function() { }); it('deletes dependent', function(done){ - var category = new Category(), + let category = new Category(), posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ] category.posts.create(posts, function(err, category, posts){ - var post = posts[0]; + let post = posts[0]; category.posts.remove(post._id, function(err, category){ should.strictEqual(err, null); @@ -246,7 +248,7 @@ describe('hasManyBelongsToMany', function() { }); it('populations of path', function(done){ - var category = new Category(), + let category = new Category(), posts = [ { title: 'Blog post #1' }, { title: 'Blog post #2' } ]; @@ -256,7 +258,7 @@ describe('hasManyBelongsToMany', function() { should.strictEqual(err, null); // Syntactic sugar - var testSugar = function(){ + let testSugar = function(){ category.posts.populate(function(err, category){ should.strictEqual(err, null); @@ -268,7 +270,7 @@ describe('hasManyBelongsToMany', function() { }); }; - var count = populatedCategory.posts.length; + let count = populatedCategory.posts.length; populatedCategory.posts.forEach(function(post){ post.should.be.an.instanceof(TwitterPost); }); @@ -281,10 +283,10 @@ describe('hasManyBelongsToMany', function() { describe('setChild false', function(){ it('instantiates one child document', function(){ - var tweet = new Tweet(), + let tweet = new Tweet(), tag = { name: 'Easy' }; - var built = tweet.tags.build(tag); + let built = tweet.tags.build(tag); built.should.be.an.instanceof(Tag); tweet.tags.should.containEql(built._id); @@ -292,14 +294,14 @@ describe('hasManyBelongsToMany', function() { }); it('instantiates many children documents', function(done) { - var tweet = new Tweet(), + let tweet = new Tweet(), tags = [{}, {}]; - var built = tweet.tags.build(tags); + let built = tweet.tags.build(tags); tweet.tags.should.have.length(2); - var count = tweet.tags.length; + let count = tweet.tags.length; built.forEach(function(tag){ tag.should.be.an.instanceof(Tag); should(tag.tweets).eql(undefined); @@ -309,7 +311,7 @@ describe('hasManyBelongsToMany', function() { }); it('appends an instantiated child document', function(done) { - var tweet = new Tweet(), + let tweet = new Tweet(), tag = new Tag(); tweet.tags.append(tag, function(err, tag){ @@ -321,13 +323,13 @@ describe('hasManyBelongsToMany', function() { }); it('concats many instantiated child documents', function(done) { - var tweet = new Tweet(), + let tweet = new Tweet(), tags = [new Tag(), new Tag()]; tweet.tags.concat(tags, function(err, tags){ should.strictEqual(err, null); - var count = tags.length; + let count = tags.length; tags.forEach(function(tag){ should(tag.tweets).eql(undefined); tweet.tags.should.containEql(tag._id); @@ -337,7 +339,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates one child document', function(done) { - var tweet = new Tweet({ author: new TwitterUser() }), + let tweet = new Tweet({ author: new TwitterUser() }), tag = { name: 'Easy' }; tweet.tags.create(tag, function(err, tweet, tag){ @@ -356,7 +358,7 @@ describe('hasManyBelongsToMany', function() { }); it('creates many child documents', function(done){ - var tweet = new Tweet({ author: new TwitterUser()}); + let tweet = new Tweet({ author: new TwitterUser()}); tags = [ { name: 'Blog tag #1' }, { name: 'Blog tag #2' } ] @@ -367,7 +369,7 @@ describe('hasManyBelongsToMany', function() { tags.should.have.length(2); - var count = tags.length; + let count = tags.length; tags.forEach(function(tag){ tweet.tags.should.containEql(tag._id) tag.should.be.an.instanceof(Tag); @@ -378,14 +380,14 @@ describe('hasManyBelongsToMany', function() { }); it('finds children documents', function(done){ - var tweet = new Tweet({ author: new TwitterUser()}), + let tweet = new Tweet({ author: new TwitterUser()}), tags = [ { name: 'Blog tag #1' }, { name: 'Blog tag #2' } ] tweet.tags.create(tags, function(err, tweet, tags){ should.strictEqual(err, null); - var find = tweet.tags.find({}) + let find = tweet.tags.find({}) find.should.be.an.instanceof(mongoose.Query); find._conditions.should.have.property('_id'); @@ -395,7 +397,7 @@ describe('hasManyBelongsToMany', function() { find.exec(function(err, newTags){ should.strictEqual(err, null); - var testFind = function(){ + let testFind = function(){ find.find({name: 'Blog tag #1'}, function(err, otherTags){ find._conditions.name.should.equal('Blog tag #1'); find._conditions.should.have.property('_id'); @@ -407,7 +409,7 @@ describe('hasManyBelongsToMany', function() { }); }; - var count = newTags.length; + let count = newTags.length; newTags.should.have.length(2); newTags.forEach(function(tag){ tweet.tags.should.containEql(tag._id) @@ -420,7 +422,7 @@ describe('hasManyBelongsToMany', function() { }); it('populations of path', function(done){ - var tweet = new Tweet({ author: new TwitterUser() }), + let tweet = new Tweet({ author: new TwitterUser() }), tags = [ { name: 'Blog tag #1' }, { name: 'Blog tag #2' } ]; @@ -430,11 +432,11 @@ describe('hasManyBelongsToMany', function() { should.strictEqual(err, null); // Syntactic sugar - var testSugar = function(){ + let testSugar = function(){ tweet.tags.populate(function(err, tweet){ should.strictEqual(err, null); - var count = tweet.tags.length; + let count = tweet.tags.length; tweet.tags.forEach(function(tag){ tag.should.be.an.instanceof(Tag); --count || done(); @@ -442,7 +444,7 @@ describe('hasManyBelongsToMany', function() { }); }; - var count = populatedTweet.tags.length; + let count = populatedTweet.tags.length; populatedTweet.tags.forEach(function(tag){ tag.should.be.an.instanceof(Tag); --count || testSugar(); @@ -455,7 +457,7 @@ describe('hasManyBelongsToMany', function() { }); describe('with descriminators', function(){ - var user, dog, fish; + let user, dog, fish; beforeEach(function(done){ user = new TwitterUser(); dog = new Dog({ name: 'Maddie', date_of_birth: new Date('12/24/2005'), breed: 'Border Collie Mix' }); @@ -537,7 +539,7 @@ describe('with descriminators', function(){ it('populates pets from the parent model with the correct type', function(done) { user.pets.populate(function(err, user){ should.strictEqual(err, null); - var foundFish, foundDog; + let foundFish, foundDog; user.pets.forEach(function(pet){ if(pet.id == fish.id){ foundFish = pet }; From c2f1c20126851430d5c297e9bef9bf6b9a85a38d Mon Sep 17 00:00:00 2001 From: Jonathon Storer Date: Sun, 11 Feb 2018 02:51:01 -0500 Subject: [PATCH 7/7] wip --- lib/belongsTo.js | 1 + specs/belongsTo.spec.js | 138 ++++++++++++++------------- specs/models/account.js | 4 +- specs/models/benefit.js | 4 +- specs/models/benefit_bundle.js | 12 +++ specs/models/benefit_bundle_price.js | 14 +++ specs/models/email_identity.js | 10 ++ specs/models/identity.js | 11 +++ specs/models/index.js | 19 ++-- specs/models/membership.js | 4 +- specs/models/note.js | 15 +++ specs/models/phone_identity.js | 10 ++ specs/models/plan.js | 11 ++- specs/models/service_area.js | 13 +++ specs/models/social_identity.js | 13 +++ specs/models/subscription.js | 2 +- specs/models/user.js | 4 +- 17 files changed, 203 insertions(+), 82 deletions(-) create mode 100644 specs/models/benefit_bundle.js create mode 100644 specs/models/benefit_bundle_price.js create mode 100644 specs/models/email_identity.js create mode 100644 specs/models/identity.js create mode 100644 specs/models/note.js create mode 100644 specs/models/phone_identity.js create mode 100644 specs/models/service_area.js create mode 100644 specs/models/social_identity.js diff --git a/lib/belongsTo.js b/lib/belongsTo.js index c7a704d..bd11b2a 100644 --- a/lib/belongsTo.js +++ b/lib/belongsTo.js @@ -32,6 +32,7 @@ module.exports = function (mongoose, i) { 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/specs/belongsTo.spec.js b/specs/belongsTo.spec.js index a19bcd4..e6a5663 100644 --- a/specs/belongsTo.spec.js +++ b/specs/belongsTo.spec.js @@ -5,157 +5,161 @@ const should = require('should'); const uuid = require('node-uuid'); describe('belongsTo', function() { + let membershipSchema, Membership, + userSchema, User, + accountSchema, Account; + before(function() { - partSchema = new mongoose.Schema({}); - partSchema.belongsTo('widget'); - Part = mongoose.model('Part_' + uuid.v4(), partSchema); - schema = Part.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() { - let 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() { - let messageSchema, Message, message - , mailboxSchema, Mailbox, mailbox; + let membership, userVersion, accountVersion; before(function() { - messageSchema = new mongoose.Schema({ }); - messageSchema.belongsTo('mailbox', { touch: true }); - Message = mongoose.model('Message', messageSchema); - - mailboxSchema = new mongoose.Schema({ }); - mailboxSchema.hasMany('messages'); - Mailbox = mongoose.model('Mailbox', mailboxSchema); - - return Mailbox.create({}).then(function(_mailbox) { - mailbox = _mailbox; - return mailbox.messages.create({ }); - }).then(function (msg) { - message = msg; - }).catch(function (err) { - throw (err); + 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() { - let oldVersion = mailbox.__v; - return message.save().then(function(){ - return Mailbox.findById(mailbox._id); - }).then(function(mailbox){ - should(mailbox.__v).not.eql(oldVersion); + 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/models/account.js b/specs/models/account.js index 6d76679..b890313 100644 --- a/specs/models/account.js +++ b/specs/models/account.js @@ -6,9 +6,11 @@ module.exports = function (mongoose, uuid) { 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 index efba338..ad67539 100644 --- a/specs/models/benefit.js +++ b/specs/models/benefit.js @@ -3,9 +3,9 @@ module.exports = function (mongoose, uuid) { let benefitSchema = new mongoose.Schema({ name: { type: String } - }); + }, { timestamps: true }); - benefitSchema.hasAndBelongsToMany('plans', { inverseOf: 'benefits' }); + 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 index ac35c4c..35d05f1 100644 --- a/specs/models/index.js +++ b/specs/models/index.js @@ -1,9 +1,16 @@ let mongoose = require('mongoose'); let uuid = require('node-uuid'); -module.exports.User = require('./user')(mongoose); -module.exports.Membership = require('./membership')(mongoose); -module.exports.Account = require('./account')(mongoose, uuid); -module.exports.Subscription = require('./subscription')(mongoose); -module.exports.Plan = require('./plan')(mongoose); -module.exports.Benefit = require('./benefit')(mongoose); +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 index 323c3e6..082bfef 100644 --- a/specs/models/membership.js +++ b/specs/models/membership.js @@ -3,9 +3,9 @@ module.exports = function (mongoose) { let membershipSchema = new mongoose.Schema({ membership_type: { type: String } - }); + }, { timestamps: true }); - membershipSchema.belongsTo('user'); + 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 index d4707d5..550fd67 100644 --- a/specs/models/plan.js +++ b/specs/models/plan.js @@ -3,9 +3,16 @@ module.exports = function (mongoose, uuid) { let planSchema = new mongoose.Schema({ name: { type: String } - }); + }, { timestamps: true }); + - planSchema.hasAndBelongsToMany('benefits', { inverseOf: 'plans' }); + 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 index 50b4acd..893b001 100644 --- a/specs/models/subscription.js +++ b/specs/models/subscription.js @@ -3,7 +3,7 @@ module.exports = function (mongoose, uuid) { let subscriptionSchema = new mongoose.Schema({ status: { type: String } - }); + }, { timestamps: true }); subscriptionSchema.hasMany('plans'); diff --git a/specs/models/user.js b/specs/models/user.js index 8606398..16d2688 100644 --- a/specs/models/user.js +++ b/specs/models/user.js @@ -3,9 +3,11 @@ 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); };