Skip to content

Commit

Permalink
Create relationship object that deals with all sorts of goodies
Browse files Browse the repository at this point in the history
  • Loading branch information
jperkelens committed Oct 26, 2013
1 parent 6c58ae8 commit 4259e72
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 69 deletions.
62 changes: 62 additions & 0 deletions lib/relationship.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
var _ = require('underscore')
, inflect = require('inflect')
, mongoose = require('mongoose')
, ObjectId = mongoose.Schema.ObjectId

_.str = require('underscore.string');
_.mixin(_.str.exports());

module.exports = function Relationship(info) {
var schema = info.schema
, inflection = info.rules.iHaveMany ? inflect.pluralize : inflect.singularize
, myPath = info.myPath || _.sprintf('_%s', inflection(info.modelName.toLowerCase()))

function addPath() {
var addition = {}
, pathOptions = { type: ObjectId, ref: info.modelName }

addition[myPath] = info.rules.iHaveMany ? [pathOptions] : pathOptions
schema.add(addition)
}

function updateDependents(itsPath, updateOp) {
return function(next) {
var update = { }
update[updateOp] = {}
update[updateOp][itsPath] = this._doc._id

var query = info.rules.iHaveMany
? {_id: { $in: this._doc[myPath] }}
: {_id: this._doc[myPath]}

info.instance.model(info.modelName).update(query, update, {multi: true}, next)
}
}

function removeDependents() {
return function(next) {
var query = info.rules.iHaveMany
? {_id: { $in: this._doc[myPath] }}
: {_id: this._doc[myPath]}

info.instance.model(info.modelName).find(query, function(err, docs) {
_.each(docs, function(d) {
d.remove()
})
})

next()
}
}


addPath()
this.enforceRelationshipWith = function(itsPath, options) {
schema.post('save', updateDependents(itsPath, info.rules.itHasMany ? '$addToSet' : '$set'))

schema.pre('remove', !info.rules.itHasMany && options && options.delete
? removeDependents()
: updateDependents(itsPath, info.rules.itHasMany ? '$pull' : '$unset' ))
}
}

98 changes: 35 additions & 63 deletions lib/schema_methods.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,49 @@
var _ = require('underscore')
, inflect = require('inflect')
, mongoose = require('mongoose')
, ObjectId = mongoose.Schema.ObjectId

_.str = require('underscore.string');
_.mixin(_.str.exports());
var Relationship = require('./relationship')

module.exports = function(schema, instance) {
function addPathTo(modelName, pathOptions) {
var addition = {}
addition[modelName] = pathOptions
schema.add(addition)
}

function updateDependents(modelName, fieldName, pathName, updateOp) {
return function(next) {
var update = { }
update[updateOp] = {}
update[updateOp][fieldName] = this._doc._id

var query = _.isArray(this._doc[pathName])
? {_id: { $in: this._doc[pathName] }}
: {_id: this._doc[pathName]}

instance.model(modelName).update(query, update, {multi: true}, next)
schema.parentOf = function(modelName, pathName) {
var relInfo = {
schema: schema,
instance: instance,
modelName: modelName,
myPath: pathName,
rules: { iHaveMany: true }
}
}

function removeDependents(modelName, pathName) {
return function(next) {
var query = _.isArray(this._doc[pathName])
? {_id: { $in: this._doc[pathName] }}
: {_id: this._doc[pathName]}

instance.model(modelName).find(query, function(err, docs) {
_.each(docs, function(d) {
d.remove()
})
})

next()
}
return new Relationship(relInfo)
}

function Relationship(modelName, myPath, rules) {
var pathDescription = { type: ObjectId, ref: modelName }
, inflection = rules.iHaveMany ? inflect.pluralize : inflect.singularize

myPath = myPath || _.sprintf('_%s', inflection(modelName.toLowerCase()))
addPathTo(myPath, rules.iHaveMany ? [pathDescription] : pathDescription)

this.enforceRelationshipWith = function(itsPath, options) {
schema.post('save', updateDependents(modelName,
itsPath,
myPath,
rules.itHasMany ? '$addToSet' : '$set'))

schema.pre('remove', options && options.delete
? removeDependents(modelName, myPath)
: updateDependents(modelName,
itsPath,
myPath,
rules.itHasMany ? '$pull' : '$unset' ))
schema.childOf = function(modelName, pathName) {
var relInfo = {
schema: schema,
instance: instance,
modelName: modelName,
myPath: pathName,
rules: { itHasMany: true }
}
return new Relationship(relInfo)
}

schema.parentOf = function(modelName, pathName) {
var rules = { iHaveMany: true }
return new Relationship(modelName, pathName, rules)
schema.hasA = function(modelName, pathName) {
var relInfo = {
schema: schema,
instance: instance,
modelName: modelName,
myPath: pathName,
rules: {}
}
return new Relationship(relInfo)
}

schema.childOf = function(modelName, pathName) {
var rules = { itHasMany: true }
return new Relationship(modelName, pathName, rules)
schema.hasMany = function(modelName, pathName) {
var relInfo = {
schema: schema,
instance: instance,
modelName: modelName,
myPath: pathName,
rules: { iHaveMany: true, itHasMany: true }
}
return new Relationship(relInfo)
}
}
14 changes: 8 additions & 6 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,12 +350,14 @@ describe('childOf', function() {
Click.create({ip: 'hello', _referrer: id}, function(err, click) {
Referrer.create({_id: id, _clicks: [click._doc._id, id2] }, function(err) {
click.remove(function(err) {
Referrer.findById(id, function(err, referrer) {
referrer._doc._clicks.length.should.eql(1)
referrer._doc._clicks.should.include(id2)
referrer._doc._clicks.should.not.include(click._id)
done()
})
setTimeout(function() {
Referrer.findById(id, function(err, referrer) {
referrer._doc._clicks.length.should.eql(1)
referrer._doc._clicks.should.include(id2)
referrer._doc._clicks.should.not.include(click._id)
done()
})
}, 100)
})
})
})
Expand Down

0 comments on commit 4259e72

Please sign in to comment.