diff --git a/packages/-ember-data/node-tests/fixtures/expected.js b/packages/-ember-data/node-tests/fixtures/expected.js index c54f8aa8e25..2819b4c1edb 100644 --- a/packages/-ember-data/node-tests/fixtures/expected.js +++ b/packages/-ember-data/node-tests/fixtures/expected.js @@ -148,6 +148,7 @@ module.exports = { '(public) @ember-data/adapter MinimumAdapterInterface#shouldReloadAll [OPTIONAL]', '(public) @ember-data/adapter MinimumAdapterInterface#shouldReloadRecord [OPTIONAL]', '(public) @ember-data/adapter MinimumAdapterInterface#updateRecord', + '(public) @ember-data/adapter MinimumAdapterInterface#destroy [OPTIONAL]', '(public) @ember-data/adapter RESTAdapter#coalesceFindRequests', '(public) @ember-data/adapter RESTAdapter#createRecord', '(public) @ember-data/adapter RESTAdapter#deleteRecord', diff --git a/packages/-ember-data/tests/integration/store/adapter-for-test.js b/packages/-ember-data/tests/integration/store/adapter-for-test.js index e2210fbbfe3..12e2babe3e9 100644 --- a/packages/-ember-data/tests/integration/store/adapter-for-test.js +++ b/packages/-ember-data/tests/integration/store/adapter-for-test.js @@ -1,4 +1,5 @@ import { assign } from '@ember/polyfills'; +import { run } from '@ember/runloop'; import { module, test } from 'qunit'; @@ -278,4 +279,31 @@ module('integration/store - adapterFor', function(hooks) { ); assert.ok(jsonApiAdapter === adapter, 'We fell back to the -json-api adapter instance for the per-type adapter'); }); + + test('adapters are destroyed', async function(assert) { + let { owner } = this; + let didInstantiate = false; + let didDestroy = false; + + class AppAdapter extends TestAdapter { + didInit() { + didInstantiate = true; + } + + destroy() { + didDestroy = true; + } + } + + owner.register('adapter:application', AppAdapter); + + let adapter = store.adapterFor('application'); + + assert.ok(adapter instanceof AppAdapter, 'precond - We found the correct adapter'); + assert.ok(didInstantiate, 'precond - We instantiated the adapter'); + + run(store, 'destroy'); + + assert.ok(didDestroy, 'adapter was destroyed'); + }); }); diff --git a/packages/-ember-data/tests/integration/store/serializer-for-test.js b/packages/-ember-data/tests/integration/store/serializer-for-test.js index 21d5d21c444..dfd1a92d112 100644 --- a/packages/-ember-data/tests/integration/store/serializer-for-test.js +++ b/packages/-ember-data/tests/integration/store/serializer-for-test.js @@ -1,4 +1,5 @@ import { assign } from '@ember/polyfills'; +import { run } from '@ember/runloop'; import { module, test } from 'qunit'; @@ -454,4 +455,31 @@ module('integration/store - serializerFor', function(hooks) { ); } ); + + test('serializers are destroyed', async function(assert) { + let { owner } = this; + let didInstantiate = false; + let didDestroy = false; + + class AppSerializer extends TestSerializer { + didInit() { + didInstantiate = true; + } + + destroy() { + didDestroy = true; + } + } + + owner.register('serializer:application', AppSerializer); + + let serializer = store.serializerFor('application'); + + assert.ok(serializer instanceof AppSerializer, 'precond - We found the correct serializer'); + assert.ok(didInstantiate, 'precond - We instantiated the serializer'); + + run(store, 'destroy'); + + assert.ok(didDestroy, 'serializer was destroyed'); + }); }); diff --git a/packages/store/addon/-private/system/core-store.ts b/packages/store/addon/-private/system/core-store.ts index 992f6e25de3..7e34f400dbd 100644 --- a/packages/store/addon/-private/system/core-store.ts +++ b/packages/store/addon/-private/system/core-store.ts @@ -3487,14 +3487,29 @@ abstract class CoreStore extends Service { } } + destroy() { + // enqueue destruction of any adapters/serializers we have created + for (let adapterName in this._adapterCache) { + let adapter = this._adapterCache[adapterName]; + if (typeof adapter.destroy === 'function') { + adapter.destroy(); + } + } + + for (let serializerName in this._serializerCache) { + let serializer = this._serializerCache[serializerName]; + if (typeof serializer.destroy === 'function') { + serializer.destroy(); + } + } + + return super.destroy(); + } + willDestroy() { super.willDestroy(); this.recordArrayManager.destroy(); - // Check if we need to null this out - // this._adapterCache = null; - // this._serializerCache = null; - identifierCacheFor(this).destroy(); this.unloadAll(); diff --git a/packages/store/addon/-private/ts-interfaces/minimum-adapter-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-adapter-interface.ts index f03b6529d58..eb91d0e20f0 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-adapter-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-adapter-interface.ts @@ -699,6 +699,18 @@ interface Adapter { * @return {boolean} true if the a new request for all records of the type in SnapshotRecordArray should be made in the background, false otherwise */ shouldBackgroundReloadAll?(store: Store, snapshotArray: SnapshotRecordArray): boolean; + + /** + * In some situations the adapter may need to perform cleanup when destroyed, + * that cleanup can be done in `destroy`. + * + * If not implemented, the store does not inform the adapter of destruction. + * + * @method destroy [OPTIONAL] + * @public + * @optional + */ + destroy?(): void; } export default Adapter;