From 7fe7bf9b9a8c911355842f8d5700bbb3986accd5 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 8 Aug 2014 09:01:24 -0700 Subject: [PATCH 1/5] Add scope definitions to the model class See https://github.com/strongloop/loopback/issues/454 --- lib/scope.js | 21 +++++++++++++-------- test/scope.test.js | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/scope.js b/lib/scope.js index 7fab605f..7ee30b3f 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -8,11 +8,12 @@ exports.defineScope = defineScope; exports.mergeQuery = mergeQuery; function ScopeDefinition(definition) { - this.sourceModel = definition.sourceModel; - this.targetModel = definition.targetModel || definition.sourceModel; + this.modelFrom = definition.modelFrom || definition.sourceModel; + this.modelTo = definition.modelTo || definition.targetModel; this.name = definition.name; this.params = definition.params; this.methods = definition.methods; + this.options = definition.options; } ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) { @@ -40,7 +41,7 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres || actualRefresh) { // It either doesn't hit the cache or refresh is required var params = mergeQuery(actualCond, scopeParams); - return this.targetModel.find(params, function (err, data) { + return this.modelTo.find(params, function (err, data) { if (!err && saveOnCache) { defineCachedRelations(self); self.__cachedRelations[name] = data; @@ -62,7 +63,7 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres * to return the query object * @param methods An object of methods keyed by the method name to be bound to the class */ -function defineScope(cls, targetClass, name, params, methods) { +function defineScope(cls, targetClass, name, params, methods, options) { // collect meta info about scope if (!cls._scopeMeta) { @@ -80,13 +81,17 @@ function defineScope(cls, targetClass, name, params, methods) { } var definition = new ScopeDefinition({ - sourceModel: cls, - targetModel: targetClass, + modelFrom: cls, + modelTo: targetClass, name: name, params: params, - methods: methods + methods: methods, + options: options || {} }); + cls.scopes = cls.scopes || {}; + cls.scopes[name] = definition; + // Define a property for the scope Object.defineProperty(cls, name, { enumerable: false, @@ -115,7 +120,7 @@ function defineScope(cls, targetClass, name, params, methods) { f._scope = typeof definition.params === 'function' ? definition.params.call(self) : definition.params; - f._targetClass = definition.targetModel.modelName; + f._targetClass = definition.modelTo.modelName; if (f._scope.collect) { f._targetClass = i8n.capitalize(f._scope.collect); } diff --git a/test/scope.test.js b/test/scope.test.js index ecaa8727..2060c80e 100644 --- a/test/scope.test.js +++ b/test/scope.test.js @@ -9,6 +9,14 @@ describe('scope', function () { db = getSchema(); Railway = db.define('Railway', { URID: {type: String, index: true} + }, { + scopes: { + highSpeed: { + where: { + highSpeed: true + } + } + } }); Station = db.define('Station', { USID: {type: String, index: true}, @@ -24,9 +32,15 @@ describe('scope', function () { Station.destroyAll(done); }); }); + + it('should define scope using options.scopes', function () { + Railway.scopes.should.have.property('highSpeed'); + Railway.highSpeed.should.be.function; + }); it('should define scope with query', function (done) { Station.scope('active', {where: {isActive: true}}); + Station.scopes.should.have.property('active'); Station.active.create(function (err, station) { should.not.exist(err); should.exist(station); From 3d5ec63c996bcb1369fdb757161f925ae0299f16 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 8 Aug 2014 09:25:49 -0700 Subject: [PATCH 2/5] Pass options into scope --- lib/relation-definition.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/relation-definition.js b/lib/relation-definition.js index bace4705..1171b998 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -553,7 +553,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) { } return filter; - }, scopeMethods); + }, scopeMethods, definition.options); }; @@ -1600,7 +1600,7 @@ RelationDefinition.embedsMany = function embedsMany(modelFrom, modelTo, params) // Mix the property and scoped methods into the prototype class var scopeDefinition = defineScope(modelFrom.prototype, modelTo, accessorName, function () { return {}; - }, scopeMethods); + }, scopeMethods, definition.options); scopeDefinition.related = scopeMethods.related; }; @@ -2014,7 +2014,7 @@ RelationDefinition.referencesMany = function referencesMany(modelFrom, modelTo, // Mix the property and scoped methods into the prototype class var scopeDefinition = defineScope(modelFrom.prototype, modelTo, relationName, function () { return {}; - }, scopeMethods); + }, scopeMethods, definition.options); scopeDefinition.related = scopeMethods.related; // bound to definition }; From 366ff3bf62819f1a3ca4a1b8334571140f601b6e Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 8 Aug 2014 09:30:16 -0700 Subject: [PATCH 3/5] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9920da04..d1ba358b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-datasource-juggler", - "version": "2.2.2", + "version": "2.3.0", "description": "LoopBack DataSoure Juggler", "keywords": [ "StrongLoop", From 950be998bc3d04efe5e1f7ab4b7d28245779f82b Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 8 Aug 2014 09:39:36 -0700 Subject: [PATCH 4/5] Fix the test case so that it works with other DBs --- test/relations.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/relations.test.js b/test/relations.test.js index 6f827760..d1a2003a 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -284,17 +284,17 @@ describe('relations', function () { Address.create({name: 'z'}, function (err, address) { patient.address(address); patient.save(function() { - verify(physician); + verify(physician, address.id); }); }); }); }); - function verify(physician) { + function verify(physician, addressId) { physician.patients({include: 'address'}, function (err, ch) { should.not.exist(err); should.exist(ch); ch.should.have.lengthOf(1); - ch[0].addressId.should.equal(1); + ch[0].addressId.should.eql(addressId); var address = ch[0].address(); should.exist(address); address.should.be.an.instanceof(Address); From c53dc74d168788d8a1854ab6f9f9415bf139fd33 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 8 Aug 2014 15:52:30 -0700 Subject: [PATCH 5/5] Fix a name conflict in scope metadata --- lib/dao.js | 8 ++++++-- lib/datasource.js | 2 +- lib/scope.js | 18 ++++++++++++++---- package.json | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/dao.js b/lib/dao.js index 7f51ab90..4ed71b3e 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -1235,8 +1235,12 @@ var defineScope = require('./scope.js').defineScope; * @param {ModelClass} [targetClass] The model class for the query, default to * the declaring model */ -DataAccessObject.scope = function (name, query, targetClass) { - defineScope(this, targetClass || this, name, query); +DataAccessObject.scope = function (name, query, targetClass, methods, options) { + var cls = this; + if (options && options.isStatic === false) { + cls = cls.prototype; + } + defineScope(cls, targetClass || cls, name, query, methods, options); }; /* diff --git a/lib/datasource.js b/lib/datasource.js index d710daa5..b488ce72 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -378,7 +378,7 @@ function isModelDataSourceAttached(model) { DataSource.prototype.defineScopes = function (modelClass, scopes) { if(scopes) { for(var s in scopes) { - defineScope(modelClass, modelClass, s, scopes[s]); + defineScope(modelClass, modelClass, s, scopes[s], {}, scopes[s].options); } } }; diff --git a/lib/scope.js b/lib/scope.js index 7ee30b3f..0c5ea86c 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -8,8 +8,9 @@ exports.defineScope = defineScope; exports.mergeQuery = mergeQuery; function ScopeDefinition(definition) { - this.modelFrom = definition.modelFrom || definition.sourceModel; - this.modelTo = definition.modelTo || definition.targetModel; + this.isStatic = definition.isStatic; + this.modelFrom = definition.modelFrom; + this.modelTo = definition.modelTo || definition.modelFrom; this.name = definition.name; this.params = definition.params; this.methods = definition.methods; @@ -80,7 +81,11 @@ function defineScope(cls, targetClass, name, params, methods, options) { } } + options = options || {}; + // Check if the cls is the class itself or its prototype + var isStatic = (typeof cls === 'function') || options.isStatic || false; var definition = new ScopeDefinition({ + isStatic: isStatic, modelFrom: cls, modelTo: targetClass, name: name, @@ -89,8 +94,13 @@ function defineScope(cls, targetClass, name, params, methods, options) { options: options || {} }); - cls.scopes = cls.scopes || {}; - cls.scopes[name] = definition; + if(isStatic) { + cls.scopes = cls.scopes || {}; + cls.scopes[name] = definition; + } else { + cls.constructor.scopes = cls.constructor.scopes || {}; + cls.constructor.scopes[name] = definition; + } // Define a property for the scope Object.defineProperty(cls, name, { diff --git a/package.json b/package.json index d1ba358b..a4e4fc46 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-datasource-juggler", - "version": "2.3.0", + "version": "2.3.1", "description": "LoopBack DataSoure Juggler", "keywords": [ "StrongLoop",