From d2b04f465117575d4f36b57e0cb8667c0ede81cf Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Fri, 9 Dec 2016 10:44:44 -0800 Subject: [PATCH] es6 RecordArrayManager --- addon/-private/system/model/internal-model.js | 10 ++- addon/-private/system/record-array-manager.js | 63 ++++++++++--------- .../adapter-populated-record-array.js | 5 +- .../system/record-arrays/record-array.js | 4 +- addon/-private/system/store.js | 4 +- tests/dummy/app/routes/query/route.js | 2 - tests/unit/record-arrays/record-array-test.js | 29 ++++++--- 7 files changed, 70 insertions(+), 47 deletions(-) diff --git a/addon/-private/system/model/internal-model.js b/addon/-private/system/model/internal-model.js index df96f1868ea..5bc9e621528 100644 --- a/addon/-private/system/model/internal-model.js +++ b/addon/-private/system/model/internal-model.js @@ -5,6 +5,7 @@ import Relationships from "ember-data/-private/system/relationships/state/create import Snapshot from "ember-data/-private/system/snapshot"; import EmptyObject from "ember-data/-private/system/empty-object"; import isEnabled from 'ember-data/-private/features'; +import OrderedSet from "ember-data/-private/system/ordered-set"; import { getOwner @@ -114,7 +115,6 @@ export default class InternalModel { this.modelName = modelName; this.dataHasInitialized = false; this._loadingPromise = null; - this._recordArrays = undefined; this._record = null; this.currentState = RootState.empty; this.isReloading = false; @@ -126,6 +126,7 @@ export default class InternalModel { // caches for lazy getters this._modelClass = null; this.__deferredTriggers = null; + this.__recordArrays = null; this._references = null; this._recordReference = null; this.__inFlightAttributes = null; @@ -149,6 +150,13 @@ export default class InternalModel { return this._recordReference; } + get _recordArrays() { + if (this.__recordArrays === null) { + this.__recordArrays = OrderedSet.create(); + } + return this.__recordArrays; + } + get references() { if (this._references === null) { this._references = new EmptyObject(); diff --git a/addon/-private/system/record-array-manager.js b/addon/-private/system/record-array-manager.js index b80759d7993..79fc0c02445 100644 --- a/addon/-private/system/record-array-manager.js +++ b/addon/-private/system/record-array-manager.js @@ -8,7 +8,6 @@ import { FilteredRecordArray, AdapterPopulatedRecordArray } from "ember-data/-private/system/record-arrays"; -import OrderedSet from "ember-data/-private/system/ordered-set"; import { assert } from 'ember-data/-private/debug'; const { @@ -65,11 +64,13 @@ const { @class RecordArrayManager @namespace DS @private - @extends Ember.Object */ -export default Ember.Object.extend({ - init() { +export default class RecordArrayManager { + constructor(options) { heimdall.increment(create); + this.store = options.store; + this.isDestroying = false; + this.isDestroyed = false; this.filteredRecordArrays = MapWithDefault.create({ defaultValue() { return []; } }); @@ -84,20 +85,20 @@ export default Ember.Object.extend({ this.changedRecords = []; this.loadedRecords = []; this._adapterPopulatedRecordArrays = []; - }, + } recordDidChange(internalModel) { heimdall.increment(recordDidChange); if (this.changedRecords.push(internalModel) !== 1) { return; } emberRun.schedule('actions', this, this.updateRecordArrays); - }, + } recordArraysForRecord(internalModel) { heimdall.increment(recordArraysForRecord); - internalModel._recordArrays = internalModel._recordArrays || OrderedSet.create(); + return internalModel._recordArrays; - }, + } /** This method is invoked whenever data is loaded into the store by the @@ -127,18 +128,18 @@ export default Ember.Object.extend({ } updated.length = 0; - }, + } _recordWasDeleted(internalModel) { heimdall.increment(_recordWasDeleted); - let recordArrays = internalModel._recordArrays; + let recordArrays = internalModel.__recordArrays; if (!recordArrays) { return; } recordArrays.forEach(array => array._removeInternalModels([internalModel])); - internalModel._recordArrays = null; - }, + internalModel.__recordArrays = null; + } _recordWasChanged(internalModel) { heimdall.increment(_recordWasChanged); @@ -149,7 +150,7 @@ export default Ember.Object.extend({ filter = get(array, 'filterFunction'); this.updateFilterRecordArray(array, filter, modelName, internalModel); }); - }, + } //Need to update live arrays on loading recordWasLoaded(internalModel) { @@ -157,7 +158,7 @@ export default Ember.Object.extend({ if (this.loadedRecords.push(internalModel) !== 1) { return; } emberRun.schedule('actions', this, this._flushLoadedRecords); - }, + } _flushLoadedRecords() { heimdall.increment(_flushLoadedRecords); @@ -183,7 +184,7 @@ export default Ember.Object.extend({ } this.loadedRecords.length = 0; - }, + } /** Update an individual filter. @@ -204,7 +205,7 @@ export default Ember.Object.extend({ recordArrays.delete(array); array._removeInternalModels([internalModel]); } - }, + } _addInternalModelToRecordArray(array, internalModel) { heimdall.increment(_addRecordToRecordArray); @@ -213,7 +214,7 @@ export default Ember.Object.extend({ array._pushInternalModels([internalModel]); recordArrays.add(array); } - }, + } syncLiveRecordArray(array, modelName) { assert(`recordArrayManger.syncLiveRecordArray expects modelName not modelClass as the second param`, typeof modelName === 'string'); @@ -232,7 +233,7 @@ export default Ember.Object.extend({ } this.populateLiveRecordArray(array, modelName); - }, + } populateLiveRecordArray(array, modelName) { assert(`recordArrayManger.populateLiveRecordArray expects modelName not modelClass as the second param`, typeof modelName === 'string'); @@ -248,7 +249,7 @@ export default Ember.Object.extend({ this._addInternalModelToRecordArray(array, record); } } - }, + } /** This method is invoked if the `filterFunction` property is @@ -276,7 +277,7 @@ export default Ember.Object.extend({ this.updateFilterRecordArray(array, filter, modelName, record); } } - }, + } /** Get the `DS.RecordArray` for a modelName, which contains all loaded records of @@ -291,7 +292,7 @@ export default Ember.Object.extend({ heimdall.increment(liveRecordArrayFor); return this.liveRecordArrays.get(modelName); - }, + } /** Create a `DS.RecordArray` for a modelName. @@ -310,7 +311,7 @@ export default Ember.Object.extend({ isLoaded: true, manager: this }); - }, + } /** Create a `DS.FilteredRecordArray` for a modelName and register it for updates. @@ -337,7 +338,7 @@ export default Ember.Object.extend({ this.registerFilteredRecordArray(array, modelName, filter); return array; - }, + } /** Create a `DS.AdapterPopulatedRecordArray` for a modelName with given query. @@ -362,7 +363,7 @@ export default Ember.Object.extend({ this._adapterPopulatedRecordArrays.push(array); return array; - }, + } /** Register a RecordArray for a given modelName to be backed by @@ -383,7 +384,7 @@ export default Ember.Object.extend({ recordArrays.push(array); this.updateFilter(array, modelName, filter); - }, + } /** Unregister a RecordArray. @@ -414,16 +415,20 @@ export default Ember.Object.extend({ } } } - }, + } willDestroy() { - this._super(...arguments); - this.filteredRecordArrays.forEach(value => flatten(value).forEach(destroy)); this.liveRecordArrays.forEach(destroy); this._adapterPopulatedRecordArrays.forEach(destroy); + this.isDestroyed = true; } -}); + + destroy() { + this.isDestroying = true; + Ember.run.schedule('actions', this, this.willDestroy); + } +} function destroy(entry) { entry.destroy(); diff --git a/addon/-private/system/record-arrays/adapter-populated-record-array.js b/addon/-private/system/record-arrays/adapter-populated-record-array.js index 24653246f2c..cbb76b7cd3e 100644 --- a/addon/-private/system/record-arrays/adapter-populated-record-array.js +++ b/addon/-private/system/record-arrays/adapter-populated-record-array.js @@ -87,7 +87,10 @@ export default RecordArray.extend({ links: cloneNull(payload.links) }); - internalModels.forEach(internalModel => this.manager.recordArraysForRecord(internalModel).add(this)); + for (let i = 0, l = internalModels.length; i < l; i++) { + let internalModel = internalModels[i]; + this.manager.recordArraysForRecord(internalModel).add(this) + } // TODO: should triggering didLoad event be the last action of the runLoop? Ember.run.once(this, 'trigger', 'didLoad'); diff --git a/addon/-private/system/record-arrays/record-array.js b/addon/-private/system/record-arrays/record-array.js index 66182beeac7..a077fcdbfa3 100644 --- a/addon/-private/system/record-arrays/record-array.js +++ b/addon/-private/system/record-arrays/record-array.js @@ -202,7 +202,7 @@ export default Ember.ArrayProxy.extend(Ember.Evented, { _dissociateFromOwnRecords() { this.get('content').forEach(internalModel => { - let recordArrays = internalModel._recordArrays; + let recordArrays = internalModel.__recordArrays; if (recordArrays) { recordArrays.delete(this); @@ -215,7 +215,7 @@ export default Ember.ArrayProxy.extend(Ember.Evented, { @private */ _unregisterFromManager() { - get(this, 'manager').unregisterRecordArray(this); + this.manager.unregisterRecordArray(this); }, willDestroy() { diff --git a/addon/-private/system/store.js b/addon/-private/system/store.js index 945ef6ed36b..2dfa8acbf85 100644 --- a/addon/-private/system/store.js +++ b/addon/-private/system/store.js @@ -209,9 +209,7 @@ Store = Service.extend({ this._super(...arguments); this._backburner = new Backburner(['normalizeRelationships', 'syncRelationships', 'finished']); // internal bookkeeping; not observable - this.recordArrayManager = RecordArrayManager.create({ - store: this - }); + this.recordArrayManager = new RecordArrayManager({ store: this }); this._identityMap = new IdentityMap(); this._pendingSave = []; this._instanceCache = new ContainerInstanceCache(getOwner(this), this); diff --git a/tests/dummy/app/routes/query/route.js b/tests/dummy/app/routes/query/route.js index c6db6f4f49d..2bbafccb4c7 100644 --- a/tests/dummy/app/routes/query/route.js +++ b/tests/dummy/app/routes/query/route.js @@ -24,8 +24,6 @@ export default Route.extend({ let modelName = params.modelName; delete params.modelName; - heimdall.enableTimelineFeatures(); - let token = heimdall.start('ember-data'); return this.get('store').query(modelName, params) .then((records) => { diff --git a/tests/unit/record-arrays/record-array-test.js b/tests/unit/record-arrays/record-array-test.js index 87af38604ab..5a36a4b5986 100644 --- a/tests/unit/record-arrays/record-array-test.js +++ b/tests/unit/record-arrays/record-array-test.js @@ -192,14 +192,25 @@ test('#_removeInternalModels', function(assert) { assert.deepEqual(content, [], 'now contains no models'); }); +class FakeInternalModel { + constructor(record) { + this._record = record; + this.__recordArrays = null; + } + + get _recordArrays() { + return this.__recordArrays; + } + + getRecord() { return this._record; } + + createSnapshot() { + return this._record; + } +} + function internalModelFor(record) { - return { - _recordArrays: undefined, - getRecord() { return record; }, - createSnapshot() { - return record; - } - }; + return new FakeInternalModel(record); } test('#save', function(assert) { @@ -237,7 +248,7 @@ test('#destroy', function(assert) { let internalModel1 = internalModelFor(model1); // TODO: this will be removed once we fix ownership related memory leaks. - internalModel1._recordArrays = { + internalModel1.__recordArrays = { delete(array) { didDissociatieFromOwnRecords++; assert.equal(array, recordArray); @@ -308,7 +319,7 @@ test('#destroy', function(assert) { let internalModel1 = internalModelFor(model1); // TODO: this will be removed once we fix ownership related memory leaks. - internalModel1._recordArrays = { + internalModel1.__recordArrays = { delete(array) { didDissociatieFromOwnRecords++; assert.equal(array, recordArray);