cascading-relations
Advanced tools
Comparing version 0.5.0 to 0.6.0
541
index.js
@@ -9,71 +9,73 @@ var Q, dot, mongoose; | ||
module.exports = { | ||
plugin: function(schema, options) { | ||
schema.virtual('_related').get(function() { | ||
return this.$__.related; | ||
}).set(function(val) { | ||
return this.$__.related = val; | ||
}); | ||
schema.set('toObject', { | ||
virtuals: true | ||
}); | ||
schema.set('toJSON', { | ||
virtuals: true | ||
}); | ||
schema.post('init', function(next) { | ||
var info, orig, path, val, _ref; | ||
if (this.$__.populated != null) { | ||
this._related = {}; | ||
_ref = this.$__.populated; | ||
for (path in _ref) { | ||
info = _ref[path]; | ||
val = info.value; | ||
orig = dot.get(this, path); | ||
dot.set(this, path, val); | ||
dot.set(this._related, path, orig); | ||
module.exports = function(schema, options) { | ||
schema.virtual('_related').get(function() { | ||
return this.$__.related; | ||
}).set(function(val) { | ||
return this.$__.related = val; | ||
}); | ||
schema.set('toObject', { | ||
virtuals: true | ||
}); | ||
schema.set('toJSON', { | ||
virtuals: true | ||
}); | ||
schema.post('init', function(next) { | ||
var info, orig, path, val, _ref; | ||
if (this.$__.populated != null) { | ||
this._related = {}; | ||
_ref = this.$__.populated; | ||
for (path in _ref) { | ||
info = _ref[path]; | ||
val = info.value; | ||
orig = dot.get(this, path); | ||
dot.set(this, path, val); | ||
dot.set(this._related, path, orig); | ||
} | ||
} | ||
return true; | ||
}); | ||
schema.methods.cascadeSave = function(callback, allow) { | ||
if (allow == null) { | ||
allow = null; | ||
} | ||
this.$__.cascadeSave = true; | ||
this.$__.cascadeSaveLimitRelations = allow; | ||
return this.save(callback); | ||
}; | ||
schema.methods.$__saveRelation = function(path, val) { | ||
var allowedRelation, deferred, key, newVal, promises, | ||
_this = this; | ||
deferred = Q.defer(); | ||
allowedRelation = function(rel) { | ||
var allowed, _i, _len, _ref; | ||
_ref = _this.$__.cascadeSaveLimitRelations; | ||
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
allowed = _ref[_i]; | ||
if (allowed.substr(0, rel.length) === rel) { | ||
return true; | ||
} | ||
} | ||
return true; | ||
}); | ||
schema.methods.cascadeSave = function() { | ||
this.$__.cascadeSave = true; | ||
return this.save.apply(this, arguments); | ||
return false; | ||
}; | ||
schema.methods.$__saveRelation = function(path, val) { | ||
var deferred, key, newVal, promises; | ||
deferred = Q.defer(); | ||
promises = []; | ||
if (this.schema.paths[path]) { | ||
if (this.schema.paths[path].instance === 'ObjectID' && (this.schema.paths[path].options.ref != null)) { | ||
promises.push(this.$__saveSingleRelationAtPath(path)); | ||
} else if (this.schema.paths[path].options.type instanceof Array && this.schema.paths[path].caster && this.schema.paths[path].caster.instance === 'ObjectID' && (this.schema.paths[path].caster.options.ref != null)) { | ||
promises.push(this.$__saveMultiRelationAtPath(path)); | ||
} | ||
} else if (typeof val === 'object') { | ||
for (key in val) { | ||
newVal = val[key]; | ||
promises.push(this.$__saveRelation(path + '.' + key, newVal)); | ||
} | ||
if (this.$__.cascadeSaveLimitRelations && !allowedRelation(path)) { | ||
deferred.resolve(); | ||
return deferred.promise; | ||
} | ||
promises = []; | ||
if (this.schema.paths[path]) { | ||
if (this.schema.paths[path].instance === 'ObjectID' && (this.schema.paths[path].options.ref != null)) { | ||
promises.push(this.$__saveSingleRelationAtPath(path)); | ||
} else if (this.schema.paths[path].options.type instanceof Array && this.schema.paths[path].caster && this.schema.paths[path].caster.instance === 'ObjectID' && (this.schema.paths[path].caster.options.ref != null)) { | ||
promises.push(this.$__saveMultiRelationAtPath(path)); | ||
} | ||
if (!promises.length) { | ||
deferred.resolve(); | ||
} else { | ||
Q.all(promises).then(function() { | ||
return deferred.resolve(); | ||
}, function(err) { | ||
return deferred.reject(err); | ||
}); | ||
} else if (typeof val === 'object') { | ||
for (key in val) { | ||
newVal = val[key]; | ||
promises.push(this.$__saveRelation(path + '.' + key, newVal)); | ||
} | ||
return deferred.promise; | ||
}; | ||
schema.methods.$__saveSingleRelationAtPath = function(path) { | ||
var data, deferred, ref, through, | ||
_this = this; | ||
deferred = Q.defer(); | ||
ref = this.schema.paths[path].options.ref; | ||
through = this.schema.paths[path].options.$through; | ||
data = dot.get(this.get('_related'), path); | ||
this.$__saveRelatedDoc(path, data, ref, through).then(function(res) { | ||
_this.$__.populateRelations[path] = res; | ||
_this.set(path, res._id); | ||
} | ||
if (!promises.length) { | ||
deferred.resolve(); | ||
} else { | ||
Q.all(promises).then(function() { | ||
return deferred.resolve(); | ||
@@ -83,95 +85,103 @@ }, function(err) { | ||
}); | ||
return deferred.promise; | ||
}; | ||
schema.methods.$__saveMultiRelationAtPath = function(path) { | ||
var data, deferred, doc, promises, ref, through, _i, _len, | ||
_this = this; | ||
deferred = Q.defer(); | ||
ref = this.schema.paths[path].caster.options.ref; | ||
through = this.schema.paths[path].caster.options.through; | ||
data = dot.get(this.get('_related'), path); | ||
promises = []; | ||
if (!(data instanceof Array)) { | ||
deferred.reject(new Error("Data for multi relation must be an array!")); | ||
} else { | ||
for (_i = 0, _len = data.length; _i < _len; _i++) { | ||
doc = data[_i]; | ||
promises.push(this.$__saveRelatedDoc(path, doc, ref, through)); | ||
} | ||
Q.all(promises).then(function(results) { | ||
var result, _j, _len1; | ||
_this.$__.populateRelations[path] = {}; | ||
for (_j = 0, _len1 = results.length; _j < _len1; _j++) { | ||
result = results[_j]; | ||
_this.$__.populateRelations[path][result._id.toString()] = result; | ||
} | ||
return deferred.resolve(); | ||
}); | ||
} | ||
return deferred.promise; | ||
}; | ||
schema.methods.$__saveSingleRelationAtPath = function(path) { | ||
var data, deferred, ref, through, | ||
_this = this; | ||
deferred = Q.defer(); | ||
ref = this.schema.paths[path].options.ref; | ||
through = this.schema.paths[path].options.$through; | ||
data = dot.get(this.get('_related'), path); | ||
this.$__saveRelatedDoc(path, data, ref, through).then(function(res) { | ||
_this.$__.populateRelations[path] = res; | ||
_this.set(path, res._id); | ||
return deferred.resolve(); | ||
}, function(err) { | ||
return deferred.reject(err); | ||
}); | ||
return deferred.promise; | ||
}; | ||
schema.methods.$__saveMultiRelationAtPath = function(path) { | ||
var data, deferred, doc, promises, ref, through, _i, _len, | ||
_this = this; | ||
deferred = Q.defer(); | ||
ref = this.schema.paths[path].caster.options.ref; | ||
through = this.schema.paths[path].caster.options.through; | ||
data = dot.get(this.get('_related'), path); | ||
promises = []; | ||
if (!(data instanceof Array)) { | ||
deferred.reject(new Error("Data for multi relation must be an array!")); | ||
} else { | ||
for (_i = 0, _len = data.length; _i < _len; _i++) { | ||
doc = data[_i]; | ||
promises.push(this.$__saveRelatedDoc(path, doc, ref, through)); | ||
} | ||
return deferred.promise; | ||
}; | ||
schema.methods.$__saveRelatedDoc = function(path, data, ref, through) { | ||
var d, deferred, isArray, method, modelClass, newMod, orig, | ||
_this = this; | ||
deferred = Q.defer(); | ||
if (through) { | ||
d = dot.get(data, through); | ||
if (d instanceof Array) { | ||
if (d.indexOf(this._id) < 0) { | ||
d.push(this._id); | ||
dot.set(data, through, d); | ||
} | ||
} else { | ||
dot.set(data, through, this._id); | ||
Q.all(promises).then(function(results) { | ||
var result, _j, _len1; | ||
_this.$__.populateRelations[path] = {}; | ||
for (_j = 0, _len1 = results.length; _j < _len1; _j++) { | ||
result = results[_j]; | ||
_this.$__.populateRelations[path][result._id.toString()] = result; | ||
} | ||
return deferred.resolve(); | ||
}); | ||
} | ||
return deferred.promise; | ||
}; | ||
schema.methods.$__saveRelatedDoc = function(path, data, ref, through) { | ||
var allowed, d, deferred, isArray, method, modelClass, newArr, newMod, orig, _i, _len, _ref, | ||
_this = this; | ||
deferred = Q.defer(); | ||
if (through) { | ||
d = dot.get(data, through); | ||
if (d instanceof Array) { | ||
if (d.indexOf(this._id) < 0) { | ||
d.push(this._id); | ||
dot.set(data, through, d); | ||
} | ||
} else { | ||
dot.set(data, through, this._id); | ||
} | ||
modelClass = mongoose.model(ref); | ||
orig = this.get(path); | ||
if (orig instanceof Array) { | ||
isArray = true; | ||
} | ||
modelClass = mongoose.model(ref); | ||
orig = this.get(path); | ||
if (orig instanceof Array) { | ||
isArray = true; | ||
} else { | ||
isArray = false; | ||
} | ||
if (data._id) { | ||
if (isArray) { | ||
orig.push(data._id); | ||
this.set(path, orig); | ||
} else { | ||
isArray = false; | ||
this.set(path, data._id); | ||
} | ||
if (data._id) { | ||
if (isArray) { | ||
orig.push(data._id); | ||
this.set(path, orig); | ||
} else { | ||
this.set(path, data._id); | ||
modelClass.findById(data._id, function(err, res) { | ||
var allowed, method, newArr, _i, _len, _ref; | ||
if (err) { | ||
return deferred.reject(err); | ||
} else if (!res) { | ||
return deferred.reject(new Error('Could not find ref {ref} with ID ', +data._id.toString())); | ||
} | ||
modelClass.findById(data._id, function(err, res) { | ||
var method; | ||
if (err) { | ||
return deferred.reject(err); | ||
} else if (!res) { | ||
return deferred.reject(new Error('Could not find ref {ref} with ID ', +data._id.toString())); | ||
delete data._id; | ||
res.set(data); | ||
newArr = null; | ||
if ((res.cascadeSave != null) && typeof res.cascadeSave === 'function') { | ||
method = 'cascadeSave'; | ||
if (_this.$__.cascadeSaveLimitRelations) { | ||
newArr = []; | ||
_ref = _this.$__.cascadeSaveLimitRelations; | ||
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
allowed = _ref[_i]; | ||
if (allowed.substr(0, path.length + 1) === (path + '.')) { | ||
newArr.push(allowed.substr(path.length + 1)); | ||
} | ||
} | ||
} | ||
delete data._id; | ||
res.set(data); | ||
if ((res.cascadeSave != null) && typeof res.cascadeSave === 'function') { | ||
method = 'cascadeSave'; | ||
} else { | ||
method = 'save'; | ||
} | ||
return res[method](function(err, res) { | ||
if (err) { | ||
return deferred.reject(err); | ||
} | ||
return deferred.resolve(res); | ||
}); | ||
}); | ||
} else { | ||
newMod = new modelClass(data); | ||
if (isArray) { | ||
orig.push(newMod._id); | ||
this.set(path, orig); | ||
} else { | ||
this.set(path, newMod._id); | ||
} | ||
if ((newMod.cascadeSave != null) && typeof newMod.cascadeSave === 'function') { | ||
method = 'cascadeSave'; | ||
} else { | ||
method = 'save'; | ||
} | ||
newMod[method](function(err, res) { | ||
return res[method](function(err, res) { | ||
if (err) { | ||
@@ -181,117 +191,146 @@ return deferred.reject(err); | ||
return deferred.resolve(res); | ||
}); | ||
}, newArr); | ||
}); | ||
} else { | ||
newMod = new modelClass(data); | ||
if (isArray) { | ||
orig.push(newMod._id); | ||
this.set(path, orig); | ||
} else { | ||
this.set(path, newMod._id); | ||
} | ||
return deferred.promise; | ||
}; | ||
schema.pre('save', function(next) { | ||
var path, promises, val, _ref; | ||
if (this.$__.cascadeSave) { | ||
this.$__.populateRelations = {}; | ||
if (this._related != null) { | ||
promises = []; | ||
_ref = this._related; | ||
for (path in _ref) { | ||
val = _ref[path]; | ||
promises.push(this.$__saveRelation(path, val)); | ||
if ((newMod.cascadeSave != null) && typeof newMod.cascadeSave === 'function') { | ||
method = 'cascadeSave'; | ||
if (this.$__.cascadeSaveLimitRelations) { | ||
newArr = []; | ||
_ref = this.$__.cascadeSaveLimitRelations; | ||
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
allowed = _ref[_i]; | ||
if (allowed.substr(0, path.length + 1) === (path + '.')) { | ||
newArr.push(allowed.substr(path.length + 1)); | ||
} | ||
} | ||
return Q.all(promises).then(function() { | ||
return next(); | ||
}, function(err) { | ||
return next(err); | ||
}); | ||
} else { | ||
return next(); | ||
} | ||
} else { | ||
method = 'save'; | ||
} | ||
newMod[method](function(err, res) { | ||
if (err) { | ||
return deferred.reject(err); | ||
} | ||
return deferred.resolve(res); | ||
}, newArr); | ||
} | ||
return deferred.promise; | ||
}; | ||
schema.pre('save', function(next) { | ||
var path, promises, val, _ref; | ||
if (this.$__.cascadeSave) { | ||
this.$__.populateRelations = {}; | ||
if (this._related != null) { | ||
promises = []; | ||
_ref = this._related; | ||
for (path in _ref) { | ||
val = _ref[path]; | ||
promises.push(this.$__saveRelation(path, val)); | ||
} | ||
return Q.all(promises).then(function() { | ||
return next(); | ||
}, function(err) { | ||
return next(err); | ||
}); | ||
} else { | ||
return next(); | ||
} | ||
}); | ||
schema.post('save', function(doc) { | ||
var curVal, id, newRelated, newVal, path, rels, _i, _len, _ref; | ||
if (this.$__.cascadeSave) { | ||
newRelated = {}; | ||
_ref = doc.$__.populateRelations; | ||
for (path in _ref) { | ||
rels = _ref[path]; | ||
curVal = this.get(path); | ||
if (curVal instanceof Array) { | ||
newVal = []; | ||
for (_i = 0, _len = curVal.length; _i < _len; _i++) { | ||
id = curVal[_i]; | ||
if (rels[id.toString()] != null) { | ||
newVal.push(rels[id.toString()]); | ||
} else { | ||
newVal.push(id); | ||
} | ||
} | ||
dot.set(newRelated, path, newVal); | ||
} else { | ||
if (rels._id === curVal) { | ||
dot.set(newRelated, path, rels); | ||
} else { | ||
return next(); | ||
} | ||
}); | ||
schema.post('save', function(doc) { | ||
var curVal, id, newRelated, newVal, path, rels, _i, _len, _ref; | ||
if (this.$__.cascadeSave) { | ||
newRelated = {}; | ||
_ref = doc.$__.populateRelations; | ||
for (path in _ref) { | ||
rels = _ref[path]; | ||
curVal = this.get(path); | ||
if (curVal instanceof Array) { | ||
newVal = []; | ||
for (_i = 0, _len = curVal.length; _i < _len; _i++) { | ||
id = curVal[_i]; | ||
if (rels[id.toString()] != null) { | ||
newVal.push(rels[id.toString()]); | ||
} else { | ||
dot.set(newRelated, path, curVal); | ||
newVal.push(id); | ||
} | ||
} | ||
dot.set(newRelated, path, newVal); | ||
} else { | ||
if (rels._id === curVal) { | ||
dot.set(newRelated, path, rels); | ||
} else { | ||
dot.set(newRelated, path, curVal); | ||
} | ||
} | ||
this.set('_related', newRelated); | ||
this.$__.cascadeSave = false; | ||
} | ||
return true; | ||
}); | ||
schema.methods.$__handleDeletion = function(path) { | ||
if (this.schema.paths[path].instance === 'ObjectID' && (this.schema.paths[path].options.ref != null)) { | ||
return this.$__handleDeletionAtSingleRelationPath(path); | ||
} else if (this.schema.paths[path].options.type instanceof Array && this.schema.paths[path].caster && this.schema.paths[path].caster.instance === 'ObjectID' && (this.schema.paths[path].caster.options.ref != null)) { | ||
return this.$__handleDeletionAtMultiRelationPath(path); | ||
} | ||
}; | ||
schema.methods.$__handleDeletionAtSingleRelationPath = function(path) { | ||
var cascade, ref, through; | ||
ref = this.schema.paths[path].options.ref; | ||
cascade = this.schema.paths[path].options.$cascadeDelete; | ||
through = this.schema.paths[path].options.$through; | ||
return this.$__handleDeletionOfDoc(ref, this.get(path), cascade, through); | ||
}; | ||
schema.methods.$__handleDeletionAtMultiRelationPath = function(path) { | ||
var cascade, data, id, ref, through, _i, _len, _results; | ||
ref = this.schema.paths[path].caster.options.ref; | ||
cascade = this.schema.paths[path].caster.options.$cascadeDelete; | ||
through = this.schema.paths[path].caster.options.$through; | ||
data = this.get(path); | ||
_results = []; | ||
for (_i = 0, _len = data.length; _i < _len; _i++) { | ||
id = data[_i]; | ||
_results.push(this.$__handleDeletionOfDoc(ref, id, cascade, through)); | ||
} | ||
return _results; | ||
}; | ||
schema.methods.$__handleDeletionOfDoc = function(ref, id, cascade, through) { | ||
var modelClass; | ||
modelClass = mongoose.model(ref); | ||
if (cascade) { | ||
return modelClass.findById(id, function(err, res) { | ||
if (res) { | ||
return res.remove(); | ||
} | ||
}); | ||
} else if (through) { | ||
return modelClass.findById(id, function(err, res) { | ||
if (res) { | ||
res.set(through, null); | ||
return res.save(); | ||
} | ||
}); | ||
} | ||
}; | ||
return schema.post('remove', function(doc) { | ||
var config, path, _ref, _results; | ||
_ref = this.schema.paths; | ||
_results = []; | ||
for (path in _ref) { | ||
config = _ref[path]; | ||
_results.push(this.$__handleDeletion(path)); | ||
} | ||
return _results; | ||
}); | ||
} | ||
this.set('_related', newRelated); | ||
this.$__.cascadeSave = false; | ||
} | ||
return true; | ||
}); | ||
schema.methods.$__handleDeletion = function(path) { | ||
if (this.schema.paths[path].instance === 'ObjectID' && (this.schema.paths[path].options.ref != null)) { | ||
return this.$__handleDeletionAtSingleRelationPath(path); | ||
} else if (this.schema.paths[path].options.type instanceof Array && this.schema.paths[path].caster && this.schema.paths[path].caster.instance === 'ObjectID' && (this.schema.paths[path].caster.options.ref != null)) { | ||
return this.$__handleDeletionAtMultiRelationPath(path); | ||
} | ||
}; | ||
schema.methods.$__handleDeletionAtSingleRelationPath = function(path) { | ||
var cascade, ref, through; | ||
ref = this.schema.paths[path].options.ref; | ||
cascade = this.schema.paths[path].options.$cascadeDelete; | ||
through = this.schema.paths[path].options.$through; | ||
return this.$__handleDeletionOfDoc(ref, this.get(path), cascade, through); | ||
}; | ||
schema.methods.$__handleDeletionAtMultiRelationPath = function(path) { | ||
var cascade, data, id, ref, through, _i, _len, _results; | ||
ref = this.schema.paths[path].caster.options.ref; | ||
cascade = this.schema.paths[path].caster.options.$cascadeDelete; | ||
through = this.schema.paths[path].caster.options.$through; | ||
data = this.get(path); | ||
_results = []; | ||
for (_i = 0, _len = data.length; _i < _len; _i++) { | ||
id = data[_i]; | ||
_results.push(this.$__handleDeletionOfDoc(ref, id, cascade, through)); | ||
} | ||
return _results; | ||
}; | ||
schema.methods.$__handleDeletionOfDoc = function(ref, id, cascade, through) { | ||
var modelClass; | ||
modelClass = mongoose.model(ref); | ||
if (cascade) { | ||
return modelClass.findById(id, function(err, res) { | ||
if (res) { | ||
return res.remove(); | ||
} | ||
}); | ||
} else if (through) { | ||
return modelClass.findById(id, function(err, res) { | ||
if (res) { | ||
res.set(through, null); | ||
return res.save(); | ||
} | ||
}); | ||
} | ||
}; | ||
return schema.post('remove', function(doc) { | ||
var config, path, _ref, _results; | ||
_ref = this.schema.paths; | ||
_results = []; | ||
for (path in _ref) { | ||
config = _ref[path]; | ||
_results.push(this.$__handleDeletion(path)); | ||
} | ||
return _results; | ||
}); | ||
}; |
{ | ||
"name": "cascading-relations", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -65,7 +65,7 @@ var barClass, barSchema, bazClass, bazSchema, fooClass, fooSchema, mongoose, relations, should; | ||
fooSchema.plugin(relations.plugin); | ||
fooSchema.plugin(relations); | ||
barSchema.plugin(relations.plugin); | ||
barSchema.plugin(relations); | ||
bazSchema.plugin(relations.plugin); | ||
bazSchema.plugin(relations); | ||
@@ -195,3 +195,3 @@ barClass = mongoose.model('Bar', barSchema); | ||
}); | ||
return it('should do normal save without cascading', function(done) { | ||
it('should do normal save without cascading', function(done) { | ||
var foo; | ||
@@ -211,2 +211,32 @@ foo = new fooClass({ | ||
}); | ||
return it('should do a save while limiting cascaded relations', function(done) { | ||
var foo; | ||
foo = new fooClass({ | ||
title: 'My Foo', | ||
_related: { | ||
_bar: { | ||
title: 'My Bar', | ||
_related: { | ||
_baz: { | ||
title: 'My Baz' | ||
} | ||
} | ||
}, | ||
multi: { | ||
_bar: { | ||
title: 'My Bar 2', | ||
_related: { | ||
_baz: { | ||
title: 'My Baz 2' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
return foo.cascadeSave(function(err, res) { | ||
should.not.exist(res._related._bar._baz); | ||
return done(); | ||
}, ['_bar', 'multi._bar', 'multi._bar._baz']); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
35632
610