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/relation-definition.js b/lib/relation-definition.js index aa42e086..2e8b1dc7 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -564,7 +564,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) { } return filter; - }, scopeMethods); + }, scopeMethods, definition.options); }; @@ -1579,7 +1579,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; }; @@ -1985,7 +1985,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 }; diff --git a/lib/scope.js b/lib/scope.js index 7fab605f..0c5ea86c 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -8,11 +8,13 @@ exports.defineScope = defineScope; exports.mergeQuery = mergeQuery; function ScopeDefinition(definition) { - this.sourceModel = definition.sourceModel; - this.targetModel = definition.targetModel || definition.sourceModel; + 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; + this.options = definition.options; } ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) { @@ -40,7 +42,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 +64,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) { @@ -79,14 +81,27 @@ function defineScope(cls, targetClass, name, params, methods) { } } + 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({ - sourceModel: cls, - targetModel: targetClass, + isStatic: isStatic, + modelFrom: cls, + modelTo: targetClass, name: name, params: params, - methods: methods + methods: methods, + options: options || {} }); + 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, { enumerable: false, @@ -115,7 +130,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/package.json b/package.json index 9920da04..a4e4fc46 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-datasource-juggler", - "version": "2.2.2", + "version": "2.3.1", "description": "LoopBack DataSoure Juggler", "keywords": [ "StrongLoop", 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); 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);