diff --git a/app/assets/javascripts/discourse/models/store.js.es6 b/app/assets/javascripts/discourse/models/store.js.es6 index f0608b86b0c..6708ea8defe 100644 --- a/app/assets/javascripts/discourse/models/store.js.es6 +++ b/app/assets/javascripts/discourse/models/store.js.es6 @@ -8,6 +8,32 @@ function flushMap() { _identityMap = {}; } +function storeMap(type, id, obj) { + if (!id) { return; } + + _identityMap[type] = _identityMap[type] || {}; + _identityMap[type][id] = obj; +} + +function fromMap(type, id) { + const byType = _identityMap[type]; + if (byType) { return byType[id]; } +} + +function removeMap(type, id) { + const byType = _identityMap[type]; + if (byType) { delete byType[id]; } +} + +function findAndRemoveMap(type, id) { + const byType = _identityMap[type]; + if (byType) { + const result = byType[id]; + delete byType[id]; + return result; + } +} + flushMap(); export default Ember.Object.extend({ @@ -63,9 +89,8 @@ export default Ember.Object.extend({ update(type, id, attrs) { return this.adapterFor(type).update(this, type, id, attrs, function(result) { if (result && result[type] && result[type].id) { - const oldRecord = _identityMap[type][id]; - delete _identityMap[type][id]; - _identityMap[type][result[type].id] = oldRecord; + const oldRecord = findAndRemoveMap(type, id); + storeMap(type, result[type].id, oldRecord); } return result; }); @@ -78,8 +103,7 @@ export default Ember.Object.extend({ destroyRecord(type, record) { return this.adapterFor(type).destroyRecord(this, type, record).then(function(result) { - const forType = _identityMap[type]; - if (forType) { delete forType[record.get('id')]; } + removeMap(type, record.get('id')); return result; }); }, @@ -101,9 +125,7 @@ export default Ember.Object.extend({ const klass = this.container.lookupFactory('model:' + type) || RestModel; const model = klass.create(obj); - if (obj.id) { - _identityMap[type][obj.id] = model; - } + storeMap(type, obj.id, model); return model; }, @@ -118,11 +140,24 @@ export default Ember.Object.extend({ return Discourse.Category.findById(id); } + const pluralType = this.pluralize(subType); const collection = root[this.pluralize(subType)]; if (collection) { - const found = collection.findProperty('id', id); + const hashedProp = "__hashed_" + pluralType; + let hashedCollection = root[hashedProp]; + if (!hashedCollection) { + hashedCollection = {}; + collection.forEach(function(it) { + hashedCollection[it.id] = it; + }); + root[hashedProp] = hashedCollection; + } + + const found = hashedCollection[id]; if (found) { - return this._hydrate(subType, found, root); + const hydrated = this._hydrate(subType, found, root); + hashedCollection[id] = hydrated; + return hydrated; } } }, @@ -154,9 +189,7 @@ export default Ember.Object.extend({ this._hydrateEmbedded(obj, root); } - _identityMap[type] = _identityMap[type] || {}; - - const existing = _identityMap[type][obj.id]; + const existing = fromMap(type, obj.id); if (existing === obj) { return existing; } if (existing) { diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6 index 4de1461546d..b29a618e8c8 100644 --- a/test/javascripts/helpers/create-pretender.js.es6 +++ b/test/javascripts/helpers/create-pretender.js.es6 @@ -39,6 +39,13 @@ const _moreWidgets = [ {id: 224, name: 'Good Repellant'} ]; +const fruits = [{id: 1, name: 'apple', farmer_id: 1, category_id: 4}, + {id: 2, name: 'banana', farmer_id: 1, category_id: 3}, + {id: 3, name: 'grape', farmer_id: 2, category_id: 5}]; + +const farmers = [{id: 1, name: 'Old MacDonald'}, + {id: 2, name: 'Luke Skywalker'}]; + function loggedIn() { return !!Discourse.User.current(); } @@ -178,11 +185,13 @@ export default function() { }); this.get('/fruits/:id', function() { - return response({ - __rest_serializer: "1", - fruit: {id: 1, name: 'apple', farmer_id: 1}, - farmers: [{id: 1, name: 'Evil Trout'}] - }); + const fruit = fruits[0]; + + return response({ __rest_serializer: "1", fruit, farmers: [farmers[0]] }); + }); + + this.get('/fruits', function() { + return response({ __rest_serializer: "1", fruits, farmers }); }); this.get('/widgets/:widget_id', function(request) { diff --git a/test/javascripts/models/store-test.js.es6 b/test/javascripts/models/store-test.js.es6 index 13e2e8d0041..6411f141f4a 100644 --- a/test/javascripts/models/store-test.js.es6 +++ b/test/javascripts/models/store-test.js.es6 @@ -93,6 +93,15 @@ test('find embedded', function() { const store = createStore(); store.find('fruit', 1).then(function(f) { ok(f.get('farmer'), 'it has the embedded object'); + ok(f.get('category'), 'categories are found automatically'); }); }); +test('findAll embedded', function() { + const store = createStore(); + store.findAll('fruit').then(function(fruits) { + equal(fruits.objectAt(0).get('farmer.name'), 'Old MacDonald'); + equal(fruits.objectAt(0).get('farmer'), fruits.objectAt(1).get('farmer'), 'points at the same object'); + equal(fruits.objectAt(2).get('farmer.name'), 'Luke Skywalker'); + }); +});