diff --git a/addon/-private/system/model/internal-model.js b/addon/-private/system/model/internal-model.js index 959e1865916..ec3a79b7d30 100644 --- a/addon/-private/system/model/internal-model.js +++ b/addon/-private/system/model/internal-model.js @@ -521,6 +521,11 @@ export default class InternalModel { this.cancelDestroy(); } this._checkForOrphanedInternalModels(); + if (this.isDestroyed || this.isDestroying) { return; } + + // just in-case we are not one of the orphaned, we should still + // still destroy outselves + this.destroy(); } _checkForOrphanedInternalModels() { diff --git a/addon/-private/system/store.js b/addon/-private/system/store.js index 60a6612b72d..8d71bc4fafb 100644 --- a/addon/-private/system/store.js +++ b/addon/-private/system/store.js @@ -1128,14 +1128,13 @@ Store = Service.extend({ let internalModel = this._internalModelsFor(modelName).get(trueId); if (!internalModel) { - internalModel = this._buildInternalModel(modelName, trueId); + return this._buildInternalModel(modelName, trueId); + } else if (internalModel.hasScheduledDestroy()) { + internalModel.destroySync(); + return this._buildInternalModel(modelName, trueId); } else { - // if we already have an internalModel, we need to ensure any async teardown is cancelled - // since we want it again. - internalModel.cancelDestroy(); + return internalModel; } - - return internalModel; }, _internalModelDidReceiveRelationshipData(modelName, id, relationshipData) { diff --git a/tests/integration/records/unload-test.js b/tests/integration/records/unload-test.js index df15382eda2..b0669c43baf 100644 --- a/tests/integration/records/unload-test.js +++ b/tests/integration/records/unload-test.js @@ -16,7 +16,8 @@ let env; let Person = DS.Model.extend({ name: attr('string'), cars: hasMany('car', { async: false }), - boats: hasMany('boat', { async: true }) + boats: hasMany('boat', { async: true }), + bike: belongsTo('boat', { async: false, inverse: null }) }); Person.reopenClass({ toString() { return 'Person'; } }); @@ -38,6 +39,11 @@ let Boat = DS.Model.extend({ }); Boat.toString = function() { return 'Boat'; }; +let Bike = DS.Model.extend({ + name: DS.attr() +}); +Bike.toString = function() { return 'Bike'; }; + module("integration/unload - Unloading Records", { beforeEach() { env = setupStore({ @@ -45,7 +51,8 @@ module("integration/unload - Unloading Records", { person: Person, car: Car, group: Group, - boat: Boat + boat: Boat, + bike: Bike }); }, @@ -517,16 +524,73 @@ test("after unloading a record, the record can be fetched again immediately", fu assert.equal(internalModel.currentState.stateName, 'root.loaded.saved', 'We are loaded initially'); // we test that we can sync call unloadRecord followed by findRecord - run(function() { + return run(() => { store.unloadRecord(record); assert.equal(record.isDestroying, true, 'the record is destroying'); assert.equal(internalModel.currentState.stateName, 'root.empty', 'We are unloaded after unloadRecord'); - store.findRecord('person', '1'); + return store.findRecord('person', '1').then(newRecord => { + assert.equal(internalModel.currentState.stateName, 'root.empty', 'the old internalModel is discarded'); + assert.equal(newRecord._internalModel.currentState.stateName, 'root.loaded.saved', 'We are loaded after findRecord'); + }); }); - assert.equal(internalModel.currentState.stateName, 'root.loaded.saved', 'We are loaded after findRecord'); }); +test("after unloading a record, the record can be fetched again immediately (with relationships)", function(assert) { + const store = env.store; + // stub findRecord + env.adapter.findRecord = () => { + return { + data: { + type: 'person', + id: '1', + attributes: { + name: 'Adam Sunderland' + } + } + }; + }; + + // populate initial record + let record = run(() => { + return store.push({ + data: { + type: 'person', + id: '1', + relationships: { + bike: { + data: { type: 'bike', id: '1' } + } + } + }, + + included: [ + { + id: '1', + type: 'bike', + attributes: { + name: 'mr bike' + } + } + ] + }); + }); + + const internalModel = record._internalModel; + assert.equal(internalModel.currentState.stateName, 'root.loaded.saved', 'We are loaded initially'); + + assert.equal(record.get('bike.name'), 'mr bike'); + + // we test that we can sync call unloadRecord followed by findRecord + return run(() => { + store.unloadRecord(record); + assert.equal(record.isDestroying, true, 'the record is destroying'); + assert.equal(internalModel.currentState.stateName, 'root.empty', 'We are unloaded after unloadRecord'); + return store.findRecord('person', '1').then(newRecord => { + assert.ok(newRecord.get('bike') === null, 'the newRecord should NOT have had a bike'); + }); + }); +}); test("after unloading a record, the record can be fetched again soon there after", function(assert) { const store = env.store;