diff --git a/lib/hasAndBelongsToMany.js b/lib/hasAndBelongsToMany.js index 6650fb2..fe01677 100644 --- a/lib/hasAndBelongsToMany.js +++ b/lib/hasAndBelongsToMany.js @@ -3,16 +3,17 @@ var mongoose = require('mongoose') , MongooseArray = mongoose.Types.Array , i = require('i')() , utils = require('./utils') - , merge = utils.merge; + , merge = utils.merge + , HasAndBelongsToMany + , associate; -module.exports = function hasAndBelongsToMany (schema, model, options) { +module.exports = function hasAndBelongsToMany (schema, associationName, options) { this.type = 'habtm'; - this.schema = schema; - this.model = model; this.options = options || {}; - this.pathName = this.options.through || i.pluralize(this.model.toLowerCase()); + this.pathName = associationName; + this.model = this.options.modelName || i.classify(associationName); var path = {}; path[this.pathName] = [{ type: ObjectId, index: true, ref: this.model }]; @@ -40,12 +41,11 @@ module.exports = function hasAndBelongsToMany (schema, model, options) { }; }; -/* 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 MongooseArray.prototype.build = function (objs) { var childModelName = this._schema.options.relationshipModel; @@ -84,12 +84,11 @@ MongooseArray.prototype.build = function (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 -*/ +// 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 MongooseArray.prototype.create = function (objs, callback) { objs = this.build(objs); @@ -151,12 +150,12 @@ MongooseArray.prototype.create = function (objs, callback) { } }; -/* Append an already instantiated document saves it in the process. -* -* @param {Document} child -* @param {Function} callback -* @api public -*/ +// Append an already instantiated document saves it in the process. +// +// @param {Document} child +// @param {Function} callback +// @api public + MongooseArray.prototype.append = function (child, callback) { // start remove me asap @@ -194,12 +193,12 @@ MongooseArray.prototype.append = function (child, callback) { return child; }; -/* Append many instantiated children documents -* -* @param {Array} children -* @param {Function} callback -* @api public -*/ +// Append many instantiated children documents +// +// @param {Array} children +// @param {Function} callback +// @api public + MongooseArray.prototype._concat = MongooseArray.prototype.concat; MongooseArray.prototype.concat = function (docs, callback) { var throwErr = utils.throwErr(callback); @@ -232,10 +231,10 @@ MongooseArray.prototype.concat = function (docs, callback) { }; -/* Find children documents -* -* *This is a copy of Model.find w/ added error throwing and such* -*/ +// Find children documents + +// *This is a copy of Model.find w/ added error throwing and such* + MongooseArray.prototype.find = function (conditions, fields, options, callback) { // Copied from `Model.find` if ('function' == typeof conditions) { @@ -290,13 +289,13 @@ MongooseArray.prototype.find = function (conditions, fields, options, callback) return query; }; -/* Syntactic sugar to populate the array -* -* @param {Array} fields -* @param {Function} callback -* @return {Query} -* @api public -*/ +// Syntactic sugar to populate the array + +// @param {Array} fields +// @param {Function} callback +// @return {Query} +// @api public + MongooseArray.prototype.populate = function (fields, callback) { if ('function' == typeof fields) { callback = fields; @@ -310,13 +309,13 @@ MongooseArray.prototype.populate = function (fields, callback) { .exec(callback); }; -/* Overrides MongooseArray.remove only for dependent:destroy relationships -* -* @param {ObjectId} id -* @param {Function} callback -* @return {ObjectId} -* @api public -*/ +// Overrides MongooseArray.remove only for dependent:destroy relationships + +// @param {ObjectId} id +// @param {Function} callback +// @return {ObjectId} +// @api public + MongooseArray.prototype._remove = MongooseArray.prototype.remove; MongooseArray.prototype.remove = MongooseArray.prototype.delete = function (id, callback) { var parent = this._parent, diff --git a/lib/hasMany.js b/lib/hasMany.js index a939ba4..8cd6ea6 100644 --- a/lib/hasMany.js +++ b/lib/hasMany.js @@ -49,8 +49,7 @@ function associate (objects) { }; function HasMany (doc, options) { - options = options || {}; - this._options = options; + this._options = options || {}; this.as = this._options.as; this.inverse_of = this.inverse_of; @@ -72,8 +71,6 @@ function HasMany (doc, options) { } HasMany.prototype.build = function(objects){ - var Model; - if(Array.isArray(objects)){ return objects.map(function(object){ return this.build(object); diff --git a/lib/index.js b/lib/index.js index bbe7755..6435396 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,17 +2,18 @@ module.exports = exports = function mongoRelations (mongoose) { var Schema = mongoose.Schema - , belongsTo = require('./belongsTo') - , hasMany = require('./hasMany') - , hasAndBelongsToMany = require('./hasAndBelongsToMany'); + , habtmArrayFactory = require('./types/HasAndBelongsToManyArray') + , belongsTo + , hasAndBelongsToMany + , hasMany; + + // register HasAndBelongsToMany Type + mongoose.Types.HasAndBelongsToManyArray = require('./types/HasAndBelongsToManyArray')(mongoose.Types.Array) + + belongsTo = require('./belongsTo'); + hasAndBelongsToMany = require('./hasAndBelongsToMany'); + hasMany = require('./hasMany'); - /* 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); }; @@ -22,8 +23,9 @@ module.exports = exports = function mongoRelations (mongoose) { }; Schema.prototype.habtm = function (model, options) { - new hasAndBelongsToMany(this, model, options); + hasAndBelongsToMany(this, model, options); }; + Schema.prototype.hasAndBelongsToMany = Schema.prototype.habtm; return mongoose; }; diff --git a/lib/types/HasAndBelongsToManyArray.js b/lib/types/HasAndBelongsToManyArray.js new file mode 100644 index 0000000..f256aef --- /dev/null +++ b/lib/types/HasAndBelongsToManyArray.js @@ -0,0 +1,11 @@ +module.exports = exports = function (MongooseArray) { + var HasAndBelongsToManyArray; + + function HasAndBelongsToManyArray ( ) { + MongooseArray.apply(this, arguments); + }; + HasAndBelongsToManyArray.prototype = Object.create(MongooseArray.prototype); + HasAndBelongsToManyArray.prototype.build = function(){}; + + return HasAndBelongsToManyArray; +}; diff --git a/specs/hasAndBelongsToMany.spec.js b/specs/hasAndBelongsToMany.spec.js index 9b2e683..4bf3724 100644 --- a/specs/hasAndBelongsToMany.spec.js +++ b/specs/hasAndBelongsToMany.spec.js @@ -1,16 +1,54 @@ require('./spec_helper'); -var mongoose = require('mongoose'), - should = require('should'), - TwitterUser = require('./support/twitterUserModel'), - Pet = require('./support/petModel') - Dog = require('./support/dogModel') - Fish = require('./support/fishModel') - TwitterPost = require('./support/twitterPostModel'), - Category = require('./support/categoryModel'), - Tweet = require('./support/tweetModel'), - Tag = require('./support/tagModel'), - BookSchema = new mongoose.Schema({}); +var mongoose = require('mongoose') + , should = require('should') + , TwitterUser = require('./support/twitterUserModel') + , Pet = require('./support/petModel') + , Dog = require('./support/dogModel') + , Fish = require('./support/fishModel') + , TwitterPost = require('./support/twitterPostModel') + , Category = require('./support/categoryModel') + , Tweet = require('./support/tweetModel') + , Tag = require('./support/tagModel') + , BookSchema = new mongoose.Schema({}); + +describe('hasManyBelongsToMany without options', function() { + var paintingSchema, Painting, painting + , colorSchema, Color, color; + + before(function(){ + paintingSchema = new mongoose.Schema({ title: String }); + paintingSchema.hasAndBelongsToMany('colors'); + Painting = mongoose.model('Painting', paintingSchema); + + colorSchema = new mongoose.Schema({ name: String }); + colorSchema.habtm('paintings'); + Color = mongoose.model('Color', colorSchema); + }); + + describe('#build', function(){ + it.only('initializes a new document with the appropriate association', function(){ + painting = new Painting({ title: 'Mona Lisa' }); + color = painting.colors.build({ name: 'Black' }); + + should(color.paintings, 'paintings should contain painting.id').containEql(painting._id); + should(painting.colors, 'colors should contain color.id').containEql(color._id); + }); + }); + + describe('#create', function(){ + it('initializes a new document with the appropriate association', function(done){ + painting = new Painting({ title: 'Mona Lisa' }); + painting.colors.create({ name: 'Black' }, function(err, _, color){ + should(color.paintings).containEql(painting._id); + should(color.isNew).be.false; + should(painting.colors).containEql(color._id); + + done(); + }); + }); + }); +}); describe('hasManyBelongsToMany', function() { describe('valid options', function() { diff --git a/specs/hasMany.spec.js b/specs/hasMany.spec.js index a4828db..7dfc437 100644 --- a/specs/hasMany.spec.js +++ b/specs/hasMany.spec.js @@ -1,9 +1,9 @@ require('./spec_helper'); var mongoose = require('mongoose') - , async = require('async') - , should = require('should') - , uuid = require('node-uuid'); + , async = require('async') + , should = require('should') + , uuid = require('node-uuid'); describe('hasMany without options', function(){ var userSchema, User, user, widgetSchema, Widget, widget; diff --git a/specs/relationship/hasAndBelongsToMany.spec.js b/specs/relationship/hasAndBelongsToMany.spec.js deleted file mode 100644 index a2bb92f..0000000 --- a/specs/relationship/hasAndBelongsToMany.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -require('../spec_helper'); - -var should = require('should') - , Relationship = require('../../lib/relationship/hasAndBelongsToMany') - , User = require('../support/userModel') - , Pet = require('../support/petModel') - , Dog = require('../support/dogModel') - , Fish = require('../support/fishModel') - , Post = require('../support/postModel') - , Category = require('../support/categoryModel'); - -describe('hasManyBelongsToMany', function() { - describe('constructor', function() { - var path, relationship, user; - - beforeEach(function(){ - user = new User(); - relationship = new Relationship(user.pets); - }); - - it("holds reference to it's path", function(){ - should(relationship._path).eql(user.pets); - }); - - it('caches allowed discriminators', function(){ - should(relationship._allowed_discriminators.sort()).eql(['Pet', 'Dog', 'Fish'].sort()); - }); - - it('knows the relationship of the child to the parent', function(){ - var definition = relationship._childToParent; - should(definition.name).eql('users'); - should(definition.relationshipModel).eql('User'); - should(definition.relationshipType).eql('habtm'); - }); - - it('caches a reference to the parent of the relationship', function(){ - should(relationship._parent.constructor.modelName).eql('User'); - should(relationship._parentModelName).eql('User'); - }); - - it('caches the name of the child base model', function(){ - should(relationship._childModelName).eql('Pet'); - }); - }); -}); diff --git a/specs/support/categoryModel.js b/specs/support/categoryModel.js index 0d80fa2..382c011 100644 --- a/specs/support/categoryModel.js +++ b/specs/support/categoryModel.js @@ -7,7 +7,7 @@ var CategorySchema = new mongoose.Schema({ CategorySchema.belongsTo('twitter_user', { through: 'editor' }); // should only delete the reference -CategorySchema.habtm('TwitterPost', { through: 'posts', dependent: 'delete' }); +CategorySchema.habtm('posts', { modelName: 'TwitterPost', dependent: 'delete' }); CategorySchema.hasMany('pets'); diff --git a/specs/support/petSchemaBase.js b/specs/support/petSchemaBase.js index 71a5edc..2e59e3c 100644 --- a/specs/support/petSchemaBase.js +++ b/specs/support/petSchemaBase.js @@ -15,7 +15,7 @@ function PetSchemaBase() { Schema.apply(this, arguments); - this.habtm('TwitterUser', { setParent: false }); + this.habtm('twitter_users', { setParent: false }); this.belongsTo('category'); } util.inherits(PetSchemaBase, Schema); diff --git a/specs/support/tweetModel.js b/specs/support/tweetModel.js index 6183555..231755b 100644 --- a/specs/support/tweetModel.js +++ b/specs/support/tweetModel.js @@ -3,6 +3,6 @@ var mongoose = require('mongoose'); var tweetSchema = new mongoose.Schema({ title: String, body: String }); tweetSchema.belongsTo('author', { modelName: 'TwitterUser', required: true }); -tweetSchema.habtm('Tag', { through: 'tags', setChild: false }) +tweetSchema.habtm('tags', { setChild: false }) module.exports = mongoose.model('Tweet', tweetSchema); diff --git a/specs/support/twitterPostModel.js b/specs/support/twitterPostModel.js index 3396b58..842835e 100644 --- a/specs/support/twitterPostModel.js +++ b/specs/support/twitterPostModel.js @@ -6,7 +6,6 @@ var twitterPostSchema = new mongoose.Schema({ twitterPostSchema.belongsTo('author', { modelName: 'TwitterUser' }); -// should not delete the reference -twitterPostSchema.habtm('Category'); +twitterPostSchema.habtm('categories'); module.exports = mongoose.model('TwitterPost', twitterPostSchema); diff --git a/specs/support/twitterUserModel.js b/specs/support/twitterUserModel.js index d7b9413..decd6bd 100644 --- a/specs/support/twitterUserModel.js +++ b/specs/support/twitterUserModel.js @@ -6,6 +6,6 @@ twitterUserSchema.hasMany('categories'); twitterUserSchema.hasMany('tags', { dependent: 'nullify' }); twitterUserSchema.hasMany('tweets', { dependent: 'delete', inverse_of: 'author' }); -twitterUserSchema.habtm('Pet', { setParent: false }) +twitterUserSchema.habtm('pets', { setParent: false }) module.exports = mongoose.model('TwitterUser', twitterUserSchema);