diff --git a/shared/store/collection_store.js b/shared/store/collection_store.js index a56d3598..2c2c7dad 100644 --- a/shared/store/collection_store.js +++ b/shared/store/collection_store.js @@ -30,6 +30,23 @@ _.extend(CollectionStore.prototype, Super.prototype, { return cachedCollection; }, + clear: function(collectionName, params) { + if (!_.isUndefined(collectionName) && params) { + var key = this._getStoreKey(collectionName, params); + return Super.prototype.clear.call(this, key); + } else if (!_.isUndefined(collectionName) && !params) { + var cachedItems = this._getCachedItemsByCollection(collectionName), + self = this, + storeKey; + _.each(cachedItems, function (item) { + storeKey = self._getStoreKey(collectionName, item.value.params); + Super.prototype.clear.call(self, storeKey); + }); + } else { + return Super.prototype.clear.call(this, null); + } + }, + mergeParams: function(collectionName, params, callback) { this.modelUtils.getCollectionConstructor(collectionName, function(Collection) { var mergedParams = _.extend({}, Collection.prototype.defaultParams, params); @@ -37,6 +54,14 @@ _.extend(CollectionStore.prototype, Super.prototype, { }); }, + _getCachedItemsByCollection:function(collectionName) { + var prefix = this._formatKey(this.modelUtils.underscorize(collectionName)); + + return _.filter(this.cache, function(val, key) { + return startsWith(key, prefix); + }); + }, + _getStoreKeyForCollection: function(collection, params) { var collectionName = this.modelUtils.modelName(collection.constructor); @@ -57,3 +82,7 @@ function sortParams(params) { }); return sorted; } + +function startsWith(string, prefix) { + return string.slice(0, prefix.length) == prefix; +} \ No newline at end of file diff --git a/shared/store/model_store.js b/shared/store/model_store.js index 06badc3d..0760614e 100644 --- a/shared/store/model_store.js +++ b/shared/store/model_store.js @@ -32,6 +32,23 @@ _.extend(ModelStore.prototype, Super.prototype, { return Super.prototype.get.call(this, key); }, + clear: function(modelName, id) { + if (modelName && id) { + var key = this._getModelStoreKey(modelName, id); + return Super.prototype.clear.call(this, key); + } else if (modelName && !id) { + var cachedItems = this._getCachedItemsByModel(modelName), + self = this, + modelStoreKey; + _.each(cachedItems, function (item) { + modelStoreKey = self._getModelStoreKey(modelName, item.value.id); + Super.prototype.clear.call(self, modelStoreKey); + }); + } else { + return Super.prototype.clear.call(this, null); + } + }, + find: function(modelName, params) { var prefix = this._formatKey(this._keyPrefix(modelName)), keys = Object.keys(this.cache), @@ -52,6 +69,13 @@ _.extend(ModelStore.prototype, Super.prototype, { } }, + _getCachedItemsByModel:function(modelName) { + var prefix = this._formatKey(this._keyPrefix(modelName)); + return _.filter(this.cache, function(val, key) { + return startsWith(key, prefix); + }); + }, + _formatKey: function(key) { return Super.prototype._formatKey.call(this, "_ms:" + key); }, diff --git a/test/shared/store/collection_store.test.js b/test/shared/store/collection_store.test.js index 21199ca9..6ed5085c 100644 --- a/test/shared/store/collection_store.test.js +++ b/test/shared/store/collection_store.test.js @@ -149,4 +149,94 @@ describe('CollectionStore', function() { should.exist(results); results.should.be.equal(collection); }); + + context("there is data to be cleared", function() { + var collection, collection2, anotherCollection, models, models2, modelsAnother, + params, params2, resultsCollection; + + beforeEach(function() { + models = [ + { + foo: 'bar', + id: 1 + }, { + foo: 'bot', + id: 2 + } + ]; + models2 = [ + { + foo: 'bar', + id: 11 + }, { + foo: 'bot', + id: 12 + } + ]; + modelsAnother = [ + { + foo: 'bee', + id: 111 + }, { + foo: 'hum', + id: 112 + } + ]; + params = { + offset: 0 + }; + params2 = {offset: 10}; + collection = new BaseCollection(models, {params: params}); + collection2 = new BaseCollection(models2, {params: params2}); + + function AnotherCollection() { + AnotherCollection.super_.apply(this, arguments); + } + util.inherits(AnotherCollection, BaseCollection); + + anotherCollection = new AnotherCollection(modelsAnother, {params: params }); + }); + + it("should allow clearing out of the store by params", function() { + this.store.set(collection, params); + this.store.set(collection2, params2); + this.store.clear(collection.constructor.name, params); + resultsCollection = this.store.get(collection.constructor.name, params); + should.not.exist(resultsCollection); + resultsCollection = this.store.get(collection2.constructor.name, params2); + should.exist(resultsCollection); + }); + + it("should allow clearing a collection out of the store", function() { + this.store.set(collection, params); + this.store.set(collection2, params2); + this.store.set(anotherCollection, params); + + resultsCollection = this.store.get(collection.constructor.name, params); + should.exist(resultsCollection); + resultsCollection = this.store.get(collection2.constructor.name, params2); + should.exist(resultsCollection); + + this.store.clear(collection.constructor.name); + resultsCollection = this.store.get(collection.constructor.name, params); + should.not.exist(resultsCollection); + resultsCollection = this.store.get(collection2.constructor.name, params2); + should.not.exist(resultsCollection); + this.store.cache.should.not.be.empty; + }); + + + it("should allow clearing out the store", function() { + this.store.set(collection, params); + this.store.set(collection2, params2); + this.store.set(anotherCollection, params); + + this.store.clear(); + resultsCollection = this.store.get(collection.constructor.name, params); + should.not.exist(resultsCollection); + resultsCollection = this.store.get(collection2.constructor.name, params2); + should.not.exist(resultsCollection); + this.store.cache.should.be.empty; + }); + }); }); diff --git a/test/shared/store/model_store.test.js b/test/shared/store/model_store.test.js index acd228a5..8f21a816 100644 --- a/test/shared/store/model_store.test.js +++ b/test/shared/store/model_store.test.js @@ -14,11 +14,17 @@ function MyModel() { } util.inherits(MyModel, BaseModel); +function MyModel2() { + MyModel2.super_.apply(this, arguments); +} +util.inherits(MyModel2, BaseModel); + function App() {} addClassMapping.add(modelUtils.modelName(MyModel), MyModel); describe('ModelStore', function() { + var model, result; beforeEach(function() { this.app = new App({modelUtils: modelUtils}); this.store = new ModelStore({ @@ -47,7 +53,7 @@ describe('ModelStore', function() { }); it("should support custom idAttribute", function() { - var model, modelAttrs, result; + var modelAttrs; modelAttrs = { foo: 'bar', @@ -67,52 +73,123 @@ describe('ModelStore', function() { result.should.eql(model); }); - it("should support returning a model instance", function() { - var model, modelAttrs, resultModel; + context("there is a model with id", function () { + var defaultModelAttrs; - modelAttrs = { - foo: 'bar', - id: 1 - }; - model = new MyModel(modelAttrs, {app: this.app}); - this.store.set(model); - resultModel = this.store.get('my_model', 1, true); - resultModel.should.be.an.instanceOf(BaseModel); - resultModel.toJSON().should.eql(modelAttrs); - resultModel.app.should.eql(this.app); - resultModel.should.be.equal(model); - }); + beforeEach(function() { + defaultModelAttrs = { + foo: 'bar', + id: 1 + }; - describe('find', function(){ - function MySecondModel() { - MySecondModel.super_.apply(this, arguments); - } - util.inherits(MySecondModel, BaseModel); + model = new MyModel(defaultModelAttrs, { app: this.app }); + }); - addClassMapping.add(modelUtils.modelName(MySecondModel), MySecondModel); + it("should get and set the values for a model", function() { + var resultModel; - it('should find a model on custom attributes', function(){ - var model, modelAttrs, result; - modelAttrs = { - foo: 'bar', - id: 1 - }; - model = new MyModel(modelAttrs); this.store.set(model); - result = this.store.find('my_model', {foo: 'bar'}); - result.should.eql(model); + resultModel = this.store.get('my_model', defaultModelAttrs.id); + resultModel.should.eql(model); }); - it('should skip different models, even when they match the query', function(){ - var model, modelAttrs, result; - modelAttrs = { - foo: 'bar', - id: 1 - }; - model = new MySecondModel(modelAttrs); + it("should support returning a model instance", function() { + var resultModel; + this.store.set(model); - result = this.store.find('my_model', {foo: 'bar'}); - should.equal(result, undefined); + resultModel = this.store.get('my_model', defaultModelAttrs.id, true); + resultModel.should.be.an.instanceOf(BaseModel); + resultModel.toJSON().should.eql(defaultModelAttrs); + resultModel.app.should.eql(this.app); + resultModel.should.be.equal(model); + }); + + it("should be able to be cleared from the store by id", function() { + var resultModel; + + this.store.set(model); + resultModel = this.store.get('my_model', defaultModelAttrs.id, true); + should.exist(resultModel); + this.store.clear('my_model', defaultModelAttrs.id); + resultModel = this.store.get('my_model', defaultModelAttrs.id, true); + should.not.exist(resultModel); + }); + + describe('find', function(){ + function MySecondModel() { + MySecondModel.super_.apply(this, arguments); + } + util.inherits(MySecondModel, BaseModel); + + addClassMapping.add(modelUtils.modelName(MySecondModel), MySecondModel); + + it('should find a model on custom attributes', function(){ + this.store.set(model); + resultModel = this.store.find('my_model', {foo: 'bar'}); + resultModel.should.eql(model); + }); + + it('should skip different models, even when they match the query', function(){ + model = new MySecondModel(defaultModelAttrs); + this.store.set(model); + resultModel = this.store.find('my_model', {foo: 'bar'}); + should.equal(resultModel, undefined); + }); + }); + context("more than one model", function () { + var defaultModelAttrs2, model2; + + beforeEach(function() { + defaultModelAttrs2 = { + foo: 'bar2', + id: 2 + }; + model2 = new MyModel(defaultModelAttrs2, { app: this.app }); + }); + + it("all should be able to be cleared from the store", function() { + + this.store.set(model); + this.store.set(model2); + + resultModel = this.store.get('my_model', defaultModelAttrs.id, true); + should.exist(resultModel); + resultModel = this.store.get('my_model', defaultModelAttrs2.id, true); + should.exist(resultModel); + this.store.clear(); + resultModel = this.store.get('my_model', defaultModelAttrs.id, true); + should.not.exist(resultModel); + resultModel = this.store.get('my_model', defaultModelAttrs2.id, true); + should.not.exist(resultModel); + }); + }); + context("more than one type of model", function () { + var defaultModelAttrs2, model2; + + beforeEach(function() { + defaultModelAttrs2 = { + foo: 'bar2', + id: 2 + }; + model2 = new MyModel2(defaultModelAttrs2, { app: this.app }); + }); + + it("should be able to be clear one full model from the store", function() { + + this.store.set(model); + this.store.set(model2); + + resultModel = this.store.get('my_model', defaultModelAttrs.id, true); + should.exist(resultModel); + resultModel = this.store.get('my_model2', defaultModelAttrs2.id, true); + should.exist(resultModel); + + this.store.clear('my_model'); + resultModel = this.store.get('my_model', defaultModelAttrs.id, true); + should.not.exist(resultModel); + resultModel = this.store.get('my_model2', defaultModelAttrs2.id, true); + should.exist(resultModel); + }); }); }); });