From e888b8cff9ebc3e21a0e630bd439036850b405e3 Mon Sep 17 00:00:00 2001 From: Fabien Franzen Date: Tue, 29 Jul 2014 22:59:44 +0200 Subject: [PATCH] Allow custom scopeMethods option (obj/fn) for relation scopes --- lib/relation-definition.js | 34 ++++++++++++++++++++++++++++------ test/relations.test.js | 28 ++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/lib/relation-definition.js b/lib/relation-definition.js index bbafb8d0..b763c06e 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -52,7 +52,25 @@ function normalizeType(type) { } } return null; -} +}; + +function extendScopeMethods(definition, scopeMethods, ext) { + var relationClass = RelationClasses[definition.type]; + if (definition.type === RelationTypes.hasMany && definition.modelThrough) { + relationClass = RelationClasses.hasManyThrough; + } + if (typeof ext === 'function') { + ext.call(definition, scopeMethods, relationClass); + } else if (typeof ext === 'object') { + for (var key in ext) { + scopeMethods[key] = function () { + var relation = new relationClass(definition, this); + return ext[key].apply(relation, arguments); + }; + } + } + return scopeMethods; +}; /** * Relation definition class. Use to define relationships between models. @@ -518,7 +536,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) { } return filter; - }, scopeMethods); + }, extendScopeMethods(definition, scopeMethods, params.scopeMethods)); }; @@ -1536,12 +1554,14 @@ RelationDefinition.embedsMany = function embedsMany(modelFrom, modelTo, params) scopeMethods.create = scopeMethod(definition, 'create'); scopeMethods.build = scopeMethod(definition, 'build'); + scopeMethods.related = scopeMethod(definition, 'related'); // bound to definition + // Mix the property and scoped methods into the prototype class var scopeDefinition = defineScope(modelFrom.prototype, modelTo, accessorName, function () { return {}; - }, scopeMethods); + }, extendScopeMethods(definition, scopeMethods, params.scopeMethods)); - scopeDefinition.related = scopeMethod(definition, 'related'); // bound to definition + scopeDefinition.related = scopeMethods.related; }; EmbedsMany.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) { @@ -1934,12 +1954,14 @@ RelationDefinition.referencesMany = function referencesMany(modelFrom, modelTo, scopeMethods.create = scopeMethod(definition, 'create'); scopeMethods.build = scopeMethod(definition, 'build'); + scopeMethods.related = scopeMethod(definition, 'related'); + // Mix the property and scoped methods into the prototype class var scopeDefinition = defineScope(modelFrom.prototype, modelTo, relationName, function () { return {}; - }, scopeMethods); + }, extendScopeMethods(definition, scopeMethods, params.scopeMethods)); - scopeDefinition.related = scopeMethod(definition, 'related'); // bound to definition + scopeDefinition.related = scopeMethods.related; // bound to definition }; ReferencesMany.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) { diff --git a/test/relations.test.js b/test/relations.test.js index 9319a2f6..bcedc425 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -1858,7 +1858,16 @@ describe('relations', function () { }); it('can be declared', function (done) { - Category.referencesMany(Product); + Category.referencesMany(Product, { scopeMethods: { + reverse: function(cb) { + var modelInstance = this.modelInstance; + var fk = this.definition.keyFrom; + var ids = modelInstance[fk] || []; + modelInstance.updateAttribute(fk, ids.reverse(), function(err, inst) { + cb(err, inst[fk] || []); + }); + } + } }); db.automigrate(done); }); @@ -1887,7 +1896,7 @@ describe('relations', function () { }); }); - it('should not create duplicate record on scope', function (done) { + it('should not allow duplicate record on scope', function (done) { Category.findOne(function(err, cat) { cat.productIds = [product2.id, product2.id]; cat.save(function(err, p) { @@ -2035,14 +2044,25 @@ describe('relations', function () { }); }); + it('should allow custom scope methods - reverse', function(done) { + Category.findOne(function(err, cat) { + cat.products.reverse(function(err, ids) { + var expected = [product3.id, product2.id]; + ids.should.eql(expected); + cat.productIds.should.eql(expected); + done(); + }); + }) + }); + it('should include related items from scope', function(done) { Category.find({ include: 'products' }, function(err, categories) { categories.should.have.length(1); var cat = categories[0].toObject(); cat.name.should.equal('Category A'); cat.products.should.have.length(2); - cat.products[0].id.should.eql(product2.id); - cat.products[1].id.should.eql(product3.id); + cat.products[0].id.should.eql(product3.id); + cat.products[1].id.should.eql(product2.id); done(); }); });