From ecaa1caeb5b8125712c8bf55aff47d36dbb28567 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Wed, 27 Jul 2022 04:56:55 -0700 Subject: [PATCH 01/29] ill follow you into the dark --- .../integration/records/delete-record-test.js | 4 - packages/store/addon/-private/identity-map.ts | 54 -------- .../addon/-private/internal-model-factory.ts | 60 +++++---- .../addon/-private/internal-model-map.ts | 121 ------------------ .../addon/-private/record-array-manager.ts | 8 +- 5 files changed, 38 insertions(+), 209 deletions(-) delete mode 100644 packages/store/addon/-private/identity-map.ts delete mode 100644 packages/store/addon/-private/internal-model-map.ts diff --git a/packages/-ember-data/tests/integration/records/delete-record-test.js b/packages/-ember-data/tests/integration/records/delete-record-test.js index 2daac4b483e..bee1c617f2a 100644 --- a/packages/-ember-data/tests/integration/records/delete-record-test.js +++ b/packages/-ember-data/tests/integration/records/delete-record-test.js @@ -308,8 +308,6 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { assert.true(internalModel.isEmpty, 'We reached the correct persisted saved state'); assert.strictEqual(get(store.peekAll('person'), 'length'), 0, 'The new person should be removed from the store'); - // let cache = store._identityMap._map.person._models; - // assert.ok(cache.indexOf(internalModel) === -1, 'The internal model is removed from the cache'); assert.true(internalModel.isDestroyed, 'The internal model is destroyed'); @@ -344,8 +342,6 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { assert.true(internalModel.isEmpty, 'We reached the correct persisted saved state'); assert.strictEqual(get(store.peekAll('person'), 'length'), 0, 'The new person should be removed from the store'); - // let cache = store._identityMap._map.person._models; - // assert.ok(cache.indexOf(internalModel) === -1, 'The internal model is removed from the cache'); assert.true(internalModel.isDestroyed, 'The internal model is destroyed'); diff --git a/packages/store/addon/-private/identity-map.ts b/packages/store/addon/-private/identity-map.ts deleted file mode 100644 index d14472c0d7a..00000000000 --- a/packages/store/addon/-private/identity-map.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { ConfidentDict } from '@ember-data/types/q/utils'; - -import InternalModelMap from './internal-model-map'; - -/** - @module @ember-data/store -*/ - -/** - `IdentityMap` is a custom storage map for records by modelName - used by `Store`. - - @class IdentityMap - @internal - */ -export default class IdentityMap { - private _map: ConfidentDict = Object.create(null); - - /** - Retrieves the `InternalModelMap` for a given modelName, - creating one if one did not already exist. This is - similar to `getWithDefault` or `get` on a `MapWithDefault` - - @method retrieve - @internal - @param modelName a previously normalized modelName - @return {InternalModelMap} the InternalModelMap for the given modelName - */ - retrieve(modelName: string): InternalModelMap { - let map = this._map[modelName]; - - if (map === undefined) { - map = this._map[modelName] = new InternalModelMap(modelName); - } - - return map; - } - - /** - Clears the contents of all known `RecordMaps`, but does - not remove the InternalModelMap instances. - - @internal - */ - clear(): void { - let map = this._map; - let keys = Object.keys(map); - - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - map[key].clear(); - } - } -} diff --git a/packages/store/addon/-private/internal-model-factory.ts b/packages/store/addon/-private/internal-model-factory.ts index ade102b82e3..210e5d1de3f 100644 --- a/packages/store/addon/-private/internal-model-factory.ts +++ b/packages/store/addon/-private/internal-model-factory.ts @@ -9,11 +9,10 @@ import type { import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordData } from '@ember-data/types/q/record-data'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; +import { Dict } from '@ember-data/types/q/utils'; import type Store from './core-store'; import type { IdentifierCache } from './identifier-cache'; -import IdentityMap from './identity-map'; -import type InternalModelMap from './internal-model-map'; import InternalModel from './model/internal-model'; import constructResource from './utils/construct-resource'; import WeakCache from './weak-cache'; @@ -94,11 +93,14 @@ export function internalModelFactoryFor(store: Store): InternalModelFactory { * @internal */ export default class InternalModelFactory { - declare _identityMap: IdentityMap; declare identifierCache: IdentifierCache; declare store: Store; + declare cache: Map; + declare peekList: Dict>; constructor(store: Store) { + this.cache = new Map(); + this.peekList = Object.create(null); this.store = store; this.identifierCache = store.identifierCache; this.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => { @@ -112,9 +114,9 @@ export default class InternalModelFactory { let altIdentifier = identifier === intendedIdentifier ? matchedIdentifier : identifier; // check for duplicate InternalModel's - const map = this.modelMapFor(identifier.type); - let im = map.get(intendedIdentifier.lid); - let otherIm = map.get(altIdentifier.lid); + const cache = this.cache; + let im = cache.get(intendedIdentifier); + let otherIm = cache.get(altIdentifier); // we cannot merge internalModels when both have records // (this may not be strictly true, we could probably swap the internalModel the record points at) @@ -136,7 +138,8 @@ export default class InternalModelFactory { // remove otherIm from cache if (otherIm) { - map.remove(otherIm, altIdentifier.lid); + cache.delete(altIdentifier); + this.peekList[altIdentifier.type]?.delete(altIdentifier); } if (im === null && otherIm === null) { @@ -148,14 +151,17 @@ export default class InternalModelFactory { } else if ((im === null && otherIm !== null) || (im && !im.hasRecord && otherIm && otherIm.hasRecord)) { if (im) { // TODO check if we are retained in any async relationships - map.remove(im, intendedIdentifier.lid); + cache.delete(intendedIdentifier); + this.peekList[intendedIdentifier.type]?.delete(intendedIdentifier); // im.destroy(); } - im = otherIm; + im = otherIm!; // TODO do we need to notify the id change? im._id = intendedIdentifier.id; im.identifier = intendedIdentifier; - map.add(im, intendedIdentifier.lid); + cache.set(intendedIdentifier, im); + this.peekList[intendedIdentifier.type] = this.peekList[intendedIdentifier.type] || new Set(); + this.peekList[intendedIdentifier.type]!.add(intendedIdentifier); // just use im } else { @@ -174,7 +180,6 @@ export default class InternalModelFactory { return intendedIdentifier; }); - this._identityMap = new IdentityMap(); } /** @@ -225,7 +230,7 @@ export default class InternalModelFactory { * @private */ peek(identifier: StableRecordIdentifier): InternalModel | null { - return this.modelMapFor(identifier.type).get(identifier.lid); + return this.cache.get(identifier) || null; } getByResource(resource: ResourceIdentifierObject): InternalModel { @@ -286,7 +291,7 @@ export default class InternalModelFactory { peekById(type: string, id: string): InternalModel | null { const identifier = this.identifierCache.peekRecordIdentifier({ type, id }); - let internalModel = identifier ? this.modelMapFor(type).get(identifier.lid) : null; + let internalModel = identifier ? this.cache.get(identifier) || null : null; if (internalModel && internalModel.hasScheduledDestroy()) { // unloadRecord is async, if one attempts to unload + then sync create, @@ -329,31 +334,34 @@ export default class InternalModelFactory { // lookupFactory should really return an object that creates // instances with the injections applied let internalModel = new InternalModel(this.store, identifier); - - this.modelMapFor(resource.type).add(internalModel, identifier.lid); + this.cache.set(identifier, internalModel); + this.peekList[identifier.type] = this.peekList[identifier.type] || new Set(); + this.peekList[identifier.type]!.add(identifier); return internalModel; } remove(internalModel: InternalModel): void { - let recordMap = this.modelMapFor(internalModel.modelName); - let clientId = internalModel.identifier.lid; - - recordMap.remove(internalModel, clientId); - const { identifier } = internalModel; - this.identifierCache.forgetRecordIdentifier(identifier); - } + this.cache.delete(identifier); + this.peekList[identifier.type]!.delete(identifier); - modelMapFor(type: string): InternalModelMap { - return this._identityMap.retrieve(type); + this.identifierCache.forgetRecordIdentifier(identifier); } clear(type?: string) { if (type === undefined) { - this._identityMap.clear(); + let keys = Object.keys(this.peekList); + keys.forEach((key) => this.clear(key)); } else { - this.modelMapFor(type).clear(); + let identifiers = this.peekList[type]; + if (identifiers) { + identifiers.forEach((identifier) => { + let internalModel = this.peek(identifier); + internalModel!.unloadRecord(); + this.remove(internalModel!); + }); + } } } } diff --git a/packages/store/addon/-private/internal-model-map.ts b/packages/store/addon/-private/internal-model-map.ts deleted file mode 100644 index 74a98d32eba..00000000000 --- a/packages/store/addon/-private/internal-model-map.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { assert } from '@ember/debug'; - -import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; -import type { ConfidentDict } from '@ember-data/types/q/utils'; - -import InternalModel from './model/internal-model'; - -/** - @module @ember-data/store -*/ - -/** - `InternalModelMap` is a custom storage map for internalModels of a given modelName - used by `IdentityMap`. - - It was extracted from an implicit pojo based "internalModel map" and preserves - that interface while we work towards a more official API. - - @class InternalModelMap - @internal - */ -export default class InternalModelMap { - _idToModel: ConfidentDict = Object.create(null); - _models: InternalModel[] = []; - modelName: string; - - constructor(modelName: string) { - this.modelName = modelName; - } - - get(id: string): InternalModel | null { - return this._idToModel[id] || null; - } - - has(id: string): boolean { - return !!this._idToModel[id]; - } - - get length(): number { - return this._models.length; - } - - get recordIdentifiers(): StableRecordIdentifier[] { - return this._models.map((m) => m.identifier); - } - - set(id: string, internalModel: InternalModel): void { - assert(`You cannot index an internalModel by an empty id'`, typeof id === 'string' && id.length > 0); - assert( - `You cannot set an index for an internalModel to something other than an internalModel`, - internalModel instanceof InternalModel - ); - assert( - `You cannot set an index for an internalModel that is not in the InternalModelMap`, - this.contains(internalModel) - ); - assert( - `You cannot update the id index of an InternalModel once set. Attempted to update ${id}.`, - !this.has(id) || this.get(id) === internalModel - ); - - this._idToModel[id] = internalModel; - } - - add(internalModel: InternalModel, id: string | null): void { - assert( - `You cannot re-add an already present InternalModel to the InternalModelMap.`, - !this.contains(internalModel) - ); - - if (id) { - assert( - `Duplicate InternalModel for ${this.modelName}:${id} detected.`, - !this.has(id) || this.get(id) === internalModel - ); - - this._idToModel[id] = internalModel; - } - - this._models.push(internalModel); - } - - remove(internalModel: InternalModel, id: string): void { - delete this._idToModel[id]; - - let loc = this._models.indexOf(internalModel); - - if (loc !== -1) { - this._models.splice(loc, 1); - } - } - - contains(internalModel: InternalModel): boolean { - return this._models.indexOf(internalModel) !== -1; - } - - /** - An array of all models of this modelName - @property models - @internal - @type Array - */ - get models(): InternalModel[] { - return this._models; - } - - /** - Destroy all models in the map - - @internal - */ - clear(): void { - let internalModels = this._models; - this._models = []; - - for (let i = 0; i < internalModels.length; i++) { - let internalModel = internalModels[i]; - internalModel.unloadRecord(); - } - } -} diff --git a/packages/store/addon/-private/record-array-manager.ts b/packages/store/addon/-private/record-array-manager.ts index 44f01088ecc..56078d37a4b 100644 --- a/packages/store/addon/-private/record-array-manager.ts +++ b/packages/store/addon/-private/record-array-manager.ts @@ -8,7 +8,6 @@ import { set } from '@ember/object'; import { _backburner as emberBackburner } from '@ember/runloop'; import { DEBUG } from '@glimmer/env'; -// import isStableIdentifier from '../identifiers/is-stable-identifier'; import type { CollectionResourceDocument, Meta } from '@ember-data/types/q/ember-data-json-api'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { Dict } from '@ember-data/types/q/utils'; @@ -130,8 +129,8 @@ class RecordArrayManager { return; } let hasNoPotentialDeletions = pending.length === 0; - let map = internalModelFactoryFor(this.store).modelMapFor(modelName); - let hasNoInsertionsOrRemovals = map.length === array.length; + let listSize = internalModelFactoryFor(this.store).peekList[modelName]?.size; + let hasNoInsertionsOrRemovals = listSize === array.length; /* Ideally the recordArrayManager has knowledge of the changes to be applied to @@ -204,7 +203,8 @@ class RecordArrayManager { } _visibleIdentifiersByType(modelName: string) { - let all = internalModelFactoryFor(this.store).modelMapFor(modelName).recordIdentifiers; + const list = internalModelFactoryFor(this.store).peekList[modelName]; + let all = list ? [...list.values()] : []; let visible: StableRecordIdentifier[] = []; for (let i = 0; i < all.length; i++) { let identifier = all[i]; From 3e8cc10703cba0a0d2b3c1ff8cd894028d51be6a Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Wed, 27 Jul 2022 12:44:33 -0700 Subject: [PATCH 02/29] more sanity --- .eslintrc.js | 35 +++++++-------- ember-data-types/q/identifier.ts | 5 ++- .../q/minimum-adapter-interface.ts | 4 +- .../custom-class-model-test.ts | 2 +- .../adapter/addon/-private/build-url-mixin.ts | 4 +- .../-private/utils/serialize-into-hash.ts | 2 +- packages/adapter/addon/index.ts | 4 +- packages/adapter/addon/rest.ts | 6 +-- .../model/addon/-private/legacy-data-utils.ts | 2 +- packages/model/addon/-private/many-array.ts | 4 +- .../model/addon/-private/notify-changes.ts | 2 +- packages/model/addon/-private/record-state.ts | 4 +- .../addon/-private/references/belongs-to.ts | 4 +- .../addon/-private/references/has-many.ts | 4 +- .../-private/{ => caches}/identifier-cache.ts | 10 ++--- .../-private/{ => caches}/instance-cache.ts | 22 +++++----- .../{ => caches}/internal-model-factory.ts | 8 ++-- .../-private/{ => caches}/record-data-for.ts | 2 +- packages/store/addon/-private/index.ts | 26 +++++------ .../internal-model.ts | 4 +- .../record-reference.ts | 6 +-- .../schema-definition-service.ts | 4 +- .../shim-model-class.ts | 4 +- .../{ => managers}/record-array-manager.ts | 10 ++--- .../record-data-store-wrapper.ts | 8 ++-- .../record-notification-manager.ts | 4 +- .../-private/{ => network}/fetch-manager.ts | 12 +++--- .../addon/-private/{ => network}/finders.js | 4 +- .../-private/{ => network}/request-cache.ts | 0 .../{ => network}/snapshot-record-array.ts | 2 +- .../addon/-private/{ => network}/snapshot.ts | 4 +- .../-private/{ => proxies}/promise-proxies.ts | 0 .../{ => proxies}/promise-proxy-base.d.ts | 0 .../{ => proxies}/promise-proxy-base.js | 0 .../adapter-populated-record-array.ts | 10 ++--- .../-private/record-arrays/record-array.ts | 8 ++-- .../{core-store.ts => store-service.ts} | 43 +++++++++---------- .../addon/-private/{ => utils}/coerce-id.ts | 2 +- .../addon/-private/{ => utils}/common.js | 0 .../-private/utils/construct-resource.ts | 4 +- .../{ => utils}/identifer-debug-consts.ts | 0 .../{ => utils}/normalize-model-name.ts | 0 .../addon/-private/utils/promise-record.ts | 6 +-- .../{ => utils}/serializer-response.ts | 4 +- .../addon/-private/{ => utils}/weak-cache.ts | 0 tsconfig.json | 32 +++++++------- 46 files changed, 158 insertions(+), 163 deletions(-) rename packages/store/addon/-private/{ => caches}/identifier-cache.ts (98%) rename packages/store/addon/-private/{ => caches}/instance-cache.ts (95%) rename packages/store/addon/-private/{ => caches}/internal-model-factory.ts (98%) rename packages/store/addon/-private/{ => caches}/record-data-for.ts (97%) rename packages/store/addon/-private/{model => legacy-model-support}/internal-model.ts (99%) rename packages/store/addon/-private/{model => legacy-model-support}/record-reference.ts (96%) rename packages/store/addon/-private/{ => legacy-model-support}/schema-definition-service.ts (97%) rename packages/store/addon/-private/{model => legacy-model-support}/shim-model-class.ts (97%) rename packages/store/addon/-private/{ => managers}/record-array-manager.ts (97%) rename packages/store/addon/-private/{ => managers}/record-data-store-wrapper.ts (97%) rename packages/store/addon/-private/{ => managers}/record-notification-manager.ts (96%) rename packages/store/addon/-private/{ => network}/fetch-manager.ts (98%) rename packages/store/addon/-private/{ => network}/finders.js (96%) rename packages/store/addon/-private/{ => network}/request-cache.ts (100%) rename packages/store/addon/-private/{ => network}/snapshot-record-array.ts (98%) rename packages/store/addon/-private/{ => network}/snapshot.ts (99%) rename packages/store/addon/-private/{ => proxies}/promise-proxies.ts (100%) rename packages/store/addon/-private/{ => proxies}/promise-proxy-base.d.ts (100%) rename packages/store/addon/-private/{ => proxies}/promise-proxy-base.js (100%) rename packages/store/addon/-private/{core-store.ts => store-service.ts} (98%) rename packages/store/addon/-private/{ => utils}/coerce-id.ts (98%) rename packages/store/addon/-private/{ => utils}/common.js (100%) rename packages/store/addon/-private/{ => utils}/identifer-debug-consts.ts (100%) rename packages/store/addon/-private/{ => utils}/normalize-model-name.ts (100%) rename packages/store/addon/-private/{ => utils}/serializer-response.ts (95%) rename packages/store/addon/-private/{ => utils}/weak-cache.ts (100%) diff --git a/.eslintrc.js b/.eslintrc.js index 4d7f8046bc6..82b1c025c49 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -194,26 +194,23 @@ module.exports = { 'ember-data-types/q/fetch-manager.ts', 'ember-data-types/q/ember-data-json-api.ts', 'ember-data-types/q/ds-model.ts', - 'packages/store/addon/-private/record-data-store-wrapper.ts', - 'packages/store/addon/-private/internal-model-factory.ts', - 'packages/store/addon/-private/snapshot.ts', - 'packages/store/addon/-private/snapshot-record-array.ts', - 'packages/store/addon/-private/schema-definition-service.ts', - 'packages/store/addon/-private/request-cache.ts', - 'packages/store/addon/-private/references/reference.ts', - 'packages/store/addon/-private/references/record.ts', - 'packages/store/addon/-private/record-notification-manager.ts', - 'packages/store/addon/-private/record-data-for.ts', - 'packages/store/addon/-private/normalize-model-name.ts', - 'packages/store/addon/-private/model/shim-model-class.ts', - 'packages/store/addon/-private/model/internal-model.ts', - 'packages/store/addon/-private/internal-model-map.ts', - 'packages/store/addon/-private/identity-map.ts', - 'packages/store/addon/-private/fetch-manager.ts', - 'packages/store/addon/-private/core-store.ts', - 'packages/store/addon/-private/coerce-id.ts', + 'packages/store/addon/-private/managers/record-data-store-wrapper.ts', + 'packages/store/addon/-private/caches/internal-model-factory.ts', + 'packages/store/addon/-private/network/snapshot.ts', + 'packages/store/addon/-private/network/snapshot-record-array.ts', + 'packages/store/addon/-private/legacy-model-support/schema-definition-service.ts', + 'packages/store/addon/-private/network/request-cache.ts', + 'packages/store/addon/-private/legacy-model-support/record-reference.ts', + 'packages/store/addon/-private/managers/record-notification-manager.ts', + 'packages/store/addon/-private/caches/record-data-for.ts', + 'packages/store/addon/-private/utils/normalize-model-name.ts', + 'packages/store/addon/-private/legacy-model-support/shim-model-class.ts', + 'packages/store/addon/-private/legacy-model-support/internal-model.ts', + 'packages/store/addon/-private/network/fetch-manager.ts', + 'packages/store/addon/-private/store-service.ts', + 'packages/store/addon/-private/utils/coerce-id.ts', 'packages/store/addon/-private/index.ts', - 'packages/store/addon/-private/identifier-cache.ts', + 'packages/store/addon/-private/caches/identifier-cache.ts', 'packages/serializer/tests/dummy/app/routes/application/route.ts', 'packages/serializer/tests/dummy/app/router.ts', 'packages/serializer/tests/dummy/app/resolver.ts', diff --git a/ember-data-types/q/identifier.ts b/ember-data-types/q/identifier.ts index ca6b65efdfe..e16ab2c7d50 100644 --- a/ember-data-types/q/identifier.ts +++ b/ember-data-types/q/identifier.ts @@ -2,7 +2,10 @@ @module @ember-data/store */ -import { DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '@ember-data/store/-private/identifer-debug-consts'; +import { + DEBUG_CLIENT_ORIGINATED, + DEBUG_IDENTIFIER_BUCKET, +} from '@ember-data/store/-private/utils/identifer-debug-consts'; import type { ExistingResourceObject, ResourceIdentifierObject } from './ember-data-json-api'; diff --git a/ember-data-types/q/minimum-adapter-interface.ts b/ember-data-types/q/minimum-adapter-interface.ts index 81f52bc66e2..67e370ef0f6 100644 --- a/ember-data-types/q/minimum-adapter-interface.ts +++ b/ember-data-types/q/minimum-adapter-interface.ts @@ -1,7 +1,7 @@ import type Store from '@ember-data/store'; +import type Snapshot from '@ember-data/store/-private/network/snapshot'; +import type SnapshotRecordArray from '@ember-data/store/-private/network/snapshot-record-array'; import type AdapterPopulatedRecordArray from '@ember-data/store/-private/record-arrays/adapter-populated-record-array'; -import type Snapshot from '@ember-data/store/-private/snapshot'; -import type SnapshotRecordArray from '@ember-data/store/-private/snapshot-record-array'; import type { ModelSchema } from './ds-model'; import type { RelationshipSchema } from './record-data-schemas'; diff --git a/packages/-ember-data/tests/unit/custom-class-support/custom-class-model-test.ts b/packages/-ember-data/tests/unit/custom-class-support/custom-class-model-test.ts index 7afcfb5b65d..30df23e5a59 100644 --- a/packages/-ember-data/tests/unit/custom-class-support/custom-class-model-test.ts +++ b/packages/-ember-data/tests/unit/custom-class-support/custom-class-model-test.ts @@ -9,7 +9,7 @@ import JSONAPIAdapter from '@ember-data/adapter/json-api'; import JSONAPISerializer from '@ember-data/serializer/json-api'; import Store from '@ember-data/store'; import type { RecordDataStoreWrapper, Snapshot } from '@ember-data/store/-private'; -import type NotificationManager from '@ember-data/store/-private/record-notification-manager'; +import type NotificationManager from '@ember-data/store/-private/managers/record-notification-manager'; import type { RecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordDataRecordWrapper } from '@ember-data/types/q/record-data-record-wrapper'; import type { AttributesSchema, RelationshipsSchema } from '@ember-data/types/q/record-data-schemas'; diff --git a/packages/adapter/addon/-private/build-url-mixin.ts b/packages/adapter/addon/-private/build-url-mixin.ts index e7692e7fb99..d949bd6ecff 100644 --- a/packages/adapter/addon/-private/build-url-mixin.ts +++ b/packages/adapter/addon/-private/build-url-mixin.ts @@ -3,8 +3,8 @@ import { camelize } from '@ember/string'; import { pluralize } from 'ember-inflector'; -import type Snapshot from '@ember-data/store/-private/snapshot'; -import type SnapshotRecordArray from '@ember-data/store/-private/snapshot-record-array'; +import type Snapshot from '@ember-data/store/-private/network/snapshot'; +import type SnapshotRecordArray from '@ember-data/store/-private/network/snapshot-record-array'; import type { Dict } from '@ember-data/types/q/utils'; /** diff --git a/packages/adapter/addon/-private/utils/serialize-into-hash.ts b/packages/adapter/addon/-private/utils/serialize-into-hash.ts index e82b8339753..92cbfbdac6d 100644 --- a/packages/adapter/addon/-private/utils/serialize-into-hash.ts +++ b/packages/adapter/addon/-private/utils/serialize-into-hash.ts @@ -3,7 +3,7 @@ import { assert } from '@ember/debug'; import type { Snapshot } from 'ember-data/-private'; import type Store from '@ember-data/store'; -import type ShimModelClass from '@ember-data/store/-private/model/shim-model-class'; +import type ShimModelClass from '@ember-data/store/-private/legacy-model-support/shim-model-class'; import type { DSModelSchema } from '@ember-data/types/q/ds-model'; import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-serializer-interface'; diff --git a/packages/adapter/addon/index.ts b/packages/adapter/addon/index.ts index 76ed5357b5b..bf7e8a64b8e 100644 --- a/packages/adapter/addon/index.ts +++ b/packages/adapter/addon/index.ts @@ -143,8 +143,8 @@ import { Promise as RSVPPromise } from 'rsvp'; import type Store from '@ember-data/store'; import type { Snapshot } from '@ember-data/store/-private'; -import type ShimModelClass from '@ember-data/store/-private/model/shim-model-class'; -import type SnapshotRecordArray from '@ember-data/store/-private/snapshot-record-array'; +import type ShimModelClass from '@ember-data/store/-private/legacy-model-support/shim-model-class'; +import type SnapshotRecordArray from '@ember-data/store/-private/network/snapshot-record-array'; import type { AdapterPayload, MinimumAdapterInterface } from '@ember-data/types/q/minimum-adapter-interface'; import type { Dict } from '@ember-data/types/q/utils'; diff --git a/packages/adapter/addon/rest.ts b/packages/adapter/addon/rest.ts index 33e8cca63d8..d35e4241c99 100644 --- a/packages/adapter/addon/rest.ts +++ b/packages/adapter/addon/rest.ts @@ -10,9 +10,9 @@ import { DEBUG } from '@glimmer/env'; import { Promise as RSVPPromise } from 'rsvp'; import type Store from '@ember-data/store'; -import type ShimModelClass from '@ember-data/store/-private/model/shim-model-class'; -import type Snapshot from '@ember-data/store/-private/snapshot'; -import type SnapshotRecordArray from '@ember-data/store/-private/snapshot-record-array'; +import type ShimModelClass from '@ember-data/store/-private/legacy-model-support/shim-model-class'; +import type Snapshot from '@ember-data/store/-private/network/snapshot'; +import type SnapshotRecordArray from '@ember-data/store/-private/network/snapshot-record-array'; import type { AdapterPayload } from '@ember-data/types/q/minimum-adapter-interface'; import type { Dict } from '@ember-data/types/q/utils'; diff --git a/packages/model/addon/-private/legacy-data-utils.ts b/packages/model/addon/-private/legacy-data-utils.ts index f2191052546..480bc9aa427 100644 --- a/packages/model/addon/-private/legacy-data-utils.ts +++ b/packages/model/addon/-private/legacy-data-utils.ts @@ -2,7 +2,7 @@ import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; import type Store from '@ember-data/store'; -import type ShimModelClass from '@ember-data/store/-private/model/shim-model-class'; +import type ShimModelClass from '@ember-data/store/-private/legacy-model-support/shim-model-class'; import type { JsonApiDocument } from '@ember-data/types/q/ember-data-json-api'; import type { StableExistingRecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { AdapterPayload } from '@ember-data/types/q/minimum-adapter-interface'; diff --git a/packages/model/addon/-private/many-array.ts b/packages/model/addon/-private/many-array.ts index 3b1dc623cd2..6850021063e 100644 --- a/packages/model/addon/-private/many-array.ts +++ b/packages/model/addon/-private/many-array.ts @@ -10,8 +10,8 @@ import { all } from 'rsvp'; import type Store from '@ember-data/store'; import { PromiseArray, recordDataFor } from '@ember-data/store/-private'; -import type { CreateRecordProperties } from '@ember-data/store/-private/core-store'; -import type ShimModelClass from '@ember-data/store/-private/model/shim-model-class'; +import type ShimModelClass from '@ember-data/store/-private/legacy-model-support/shim-model-class'; +import type { CreateRecordProperties } from '@ember-data/store/-private/store-service'; import type { DSModelSchema } from '@ember-data/types/q/ds-model'; import type { Links, PaginationLinks } from '@ember-data/types/q/ember-data-json-api'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; diff --git a/packages/model/addon/-private/notify-changes.ts b/packages/model/addon/-private/notify-changes.ts index 109a6b94825..8044c8f89ab 100644 --- a/packages/model/addon/-private/notify-changes.ts +++ b/packages/model/addon/-private/notify-changes.ts @@ -1,7 +1,7 @@ import { cacheFor } from '@ember/object/internals'; import type Store from '@ember-data/store'; -import type { NotificationType } from '@ember-data/store/-private/record-notification-manager'; +import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type Model from './model'; diff --git a/packages/model/addon/-private/record-state.ts b/packages/model/addon/-private/record-state.ts index 2c520c0caa6..832908b774e 100644 --- a/packages/model/addon/-private/record-state.ts +++ b/packages/model/addon/-private/record-state.ts @@ -6,8 +6,8 @@ import { cached, tracked } from '@glimmer/tracking'; import type Store from '@ember-data/store'; import { storeFor } from '@ember-data/store'; import { recordIdentifierFor } from '@ember-data/store/-private'; -import type { NotificationType } from '@ember-data/store/-private/record-notification-manager'; -import type RequestCache from '@ember-data/store/-private/request-cache'; +import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager'; +import type RequestCache from '@ember-data/store/-private/network/request-cache'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordData } from '@ember-data/types/q/record-data'; diff --git a/packages/model/addon/-private/references/belongs-to.ts b/packages/model/addon/-private/references/belongs-to.ts index e2762b0587e..fd8d7a3bfc0 100644 --- a/packages/model/addon/-private/references/belongs-to.ts +++ b/packages/model/addon/-private/references/belongs-to.ts @@ -8,8 +8,8 @@ import type { BelongsToRelationship } from '@ember-data/record-data/-private'; import type Store from '@ember-data/store'; import { assertPolymorphicType } from '@ember-data/store/-debug'; import { recordIdentifierFor } from '@ember-data/store/-private'; -import type { NotificationType } from '@ember-data/store/-private/record-notification-manager'; -import type { DebugWeakCache } from '@ember-data/store/-private/weak-cache'; +import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager'; +import type { DebugWeakCache } from '@ember-data/store/-private/utils/weak-cache'; import type { LinkObject, Links, diff --git a/packages/model/addon/-private/references/has-many.ts b/packages/model/addon/-private/references/has-many.ts index 62833eb020b..1dc86ccefa9 100644 --- a/packages/model/addon/-private/references/has-many.ts +++ b/packages/model/addon/-private/references/has-many.ts @@ -11,8 +11,8 @@ import type { ManyRelationship } from '@ember-data/record-data/-private'; import type Store from '@ember-data/store'; import { recordIdentifierFor } from '@ember-data/store'; import { assertPolymorphicType } from '@ember-data/store/-debug'; -import type { NotificationType } from '@ember-data/store/-private/record-notification-manager'; -import type { DebugWeakCache } from '@ember-data/store/-private/weak-cache'; +import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager'; +import type { DebugWeakCache } from '@ember-data/store/-private/utils/weak-cache'; import type { CollectionResourceDocument, CollectionResourceRelationship, diff --git a/packages/store/addon/-private/identifier-cache.ts b/packages/store/addon/-private/caches/identifier-cache.ts similarity index 98% rename from packages/store/addon/-private/identifier-cache.ts rename to packages/store/addon/-private/caches/identifier-cache.ts index d5043a35757..ffdcefe6ba8 100644 --- a/packages/store/addon/-private/identifier-cache.ts +++ b/packages/store/addon/-private/caches/identifier-cache.ts @@ -18,11 +18,11 @@ import type { } from '@ember-data/types/q/identifier'; import type { ConfidentDict } from '@ember-data/types/q/utils'; -import coerceId from './coerce-id'; -import { DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from './identifer-debug-consts'; -import normalizeModelName from './normalize-model-name'; -import isNonEmptyString from './utils/is-non-empty-string'; -import WeakCache from './weak-cache'; +import coerceId from '../utils/coerce-id'; +import { DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '../utils/identifer-debug-consts'; +import isNonEmptyString from '../utils/is-non-empty-string'; +import normalizeModelName from '../utils/normalize-model-name'; +import WeakCache from '../utils/weak-cache'; const IDENTIFIERS = new WeakSet(); diff --git a/packages/store/addon/-private/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts similarity index 95% rename from packages/store/addon/-private/instance-cache.ts rename to packages/store/addon/-private/caches/instance-cache.ts index 9de6d9e8d88..8aa1e87fc4f 100644 --- a/packages/store/addon/-private/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -13,19 +13,19 @@ import type { RecordData } from '@ember-data/types/q/record-data'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; import type { FindOptions } from '@ember-data/types/q/store'; -import coerceId, { ensureStringId } from './coerce-id'; -import type { CreateRecordProperties } from './core-store'; -import type Store from './core-store'; -import { assertIdentifierHasId } from './core-store'; +import InternalModel from '../legacy-model-support/internal-model'; +import RecordReference from '../legacy-model-support/record-reference'; +import RecordDataStoreWrapper from '../managers/record-data-store-wrapper'; +import Snapshot from '../network/snapshot'; +import type { CreateRecordProperties } from '../store-service'; +import type Store from '../store-service'; +import { assertIdentifierHasId } from '../store-service'; +import coerceId, { ensureStringId } from '../utils/coerce-id'; +import constructResource from '../utils/construct-resource'; +import normalizeModelName from '../utils/normalize-model-name'; +import WeakCache from '../utils/weak-cache'; import { internalModelFactoryFor, setRecordIdentifier } from './internal-model-factory'; -import InternalModel from './model/internal-model'; -import RecordReference from './model/record-reference'; -import normalizeModelName from './normalize-model-name'; import recordDataFor, { setRecordDataFor } from './record-data-for'; -import RecordDataStoreWrapper from './record-data-store-wrapper'; -import Snapshot from './snapshot'; -import constructResource from './utils/construct-resource'; -import WeakCache from './weak-cache'; const RECORD_REFERENCES = new WeakCache(DEBUG ? 'reference' : ''); export const StoreMap = new WeakCache(DEBUG ? 'store' : ''); diff --git a/packages/store/addon/-private/internal-model-factory.ts b/packages/store/addon/-private/caches/internal-model-factory.ts similarity index 98% rename from packages/store/addon/-private/internal-model-factory.ts rename to packages/store/addon/-private/caches/internal-model-factory.ts index 210e5d1de3f..5828a1ba5be 100644 --- a/packages/store/addon/-private/internal-model-factory.ts +++ b/packages/store/addon/-private/caches/internal-model-factory.ts @@ -11,11 +11,11 @@ import type { RecordData } from '@ember-data/types/q/record-data'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; import { Dict } from '@ember-data/types/q/utils'; -import type Store from './core-store'; +import InternalModel from '../legacy-model-support/internal-model'; +import type Store from '../store-service'; +import constructResource from '../utils/construct-resource'; +import WeakCache from '../utils/weak-cache'; import type { IdentifierCache } from './identifier-cache'; -import InternalModel from './model/internal-model'; -import constructResource from './utils/construct-resource'; -import WeakCache from './weak-cache'; /** @module @ember-data/store diff --git a/packages/store/addon/-private/record-data-for.ts b/packages/store/addon/-private/caches/record-data-for.ts similarity index 97% rename from packages/store/addon/-private/record-data-for.ts rename to packages/store/addon/-private/caches/record-data-for.ts index 3a93d5495d6..c4f866490ee 100644 --- a/packages/store/addon/-private/record-data-for.ts +++ b/packages/store/addon/-private/caches/record-data-for.ts @@ -5,7 +5,7 @@ import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordData } from '@ember-data/types/q/record-data'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; -import WeakCache from './weak-cache'; +import WeakCache from '../utils/weak-cache'; /* * Returns the RecordData instance associated with a given diff --git a/packages/store/addon/-private/index.ts b/packages/store/addon/-private/index.ts index c012b97c5e0..da37ffbd472 100644 --- a/packages/store/addon/-private/index.ts +++ b/packages/store/addon/-private/index.ts @@ -6,19 +6,19 @@ import { assert, deprecate } from '@ember/debug'; import { DEPRECATE_HELPERS } from '@ember-data/private-build-infra/deprecations'; -import _normalize from './normalize-model-name'; +import _normalize from './utils/normalize-model-name'; -export { default as Store, storeFor } from './core-store'; +export { default as Store, storeFor } from './store-service'; -export { recordIdentifierFor } from './internal-model-factory'; +export { recordIdentifierFor } from './caches/internal-model-factory'; -export { default as Snapshot } from './snapshot'; +export { default as Snapshot } from './network/snapshot'; export { setIdentifierGenerationMethod, setIdentifierUpdateMethod, setIdentifierForgetMethod, setIdentifierResetMethod, -} from './identifier-cache'; +} from './caches/identifier-cache'; export function normalizeModelName(modelName: string) { if (DEPRECATE_HELPERS) { @@ -37,23 +37,23 @@ export function normalizeModelName(modelName: string) { assert(`normalizeModelName support has been removed`); } -export { default as coerceId } from './coerce-id'; +export { default as coerceId } from './utils/coerce-id'; // `ember-data-model-fragments` relies on `InternalModel` -export { default as InternalModel } from './model/internal-model'; +export { default as InternalModel } from './legacy-model-support/internal-model'; -export { PromiseArray, PromiseObject, deprecatedPromiseObject } from './promise-proxies'; +export { PromiseArray, PromiseObject, deprecatedPromiseObject } from './proxies/promise-proxies'; export { default as RecordArray } from './record-arrays/record-array'; export { default as AdapterPopulatedRecordArray } from './record-arrays/adapter-populated-record-array'; -export { default as RecordArrayManager } from './record-array-manager'; +export { default as RecordArrayManager } from './managers/record-array-manager'; // // Used by tests -export { default as SnapshotRecordArray } from './snapshot-record-array'; +export { default as SnapshotRecordArray } from './network/snapshot-record-array'; // New -export { default as recordDataFor, removeRecordDataFor } from './record-data-for'; -export { default as RecordDataStoreWrapper } from './record-data-store-wrapper'; +export { default as recordDataFor, removeRecordDataFor } from './caches/record-data-for'; +export { default as RecordDataStoreWrapper } from './managers/record-data-store-wrapper'; -export { default as WeakCache } from './weak-cache'; +export { default as WeakCache } from './utils/weak-cache'; diff --git a/packages/store/addon/-private/model/internal-model.ts b/packages/store/addon/-private/legacy-model-support/internal-model.ts similarity index 99% rename from packages/store/addon/-private/model/internal-model.ts rename to packages/store/addon/-private/legacy-model-support/internal-model.ts index 9ea227c42b1..f242fd5cd4b 100644 --- a/packages/store/addon/-private/model/internal-model.ts +++ b/packages/store/addon/-private/legacy-model-support/internal-model.ts @@ -10,8 +10,8 @@ import type { ChangedAttributesHash, RecordData } from '@ember-data/types/q/reco import type { JsonApiResource, JsonApiValidationError } from '@ember-data/types/q/record-data-json-api'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; -import type Store from '../core-store'; -import { internalModelFactoryFor } from '../internal-model-factory'; +import { internalModelFactoryFor } from '../caches/internal-model-factory'; +import type Store from '../store-service'; import type ShimModelClass from './shim-model-class'; /** diff --git a/packages/store/addon/-private/model/record-reference.ts b/packages/store/addon/-private/legacy-model-support/record-reference.ts similarity index 96% rename from packages/store/addon/-private/model/record-reference.ts rename to packages/store/addon/-private/legacy-model-support/record-reference.ts index 722c6e41580..322be177351 100644 --- a/packages/store/addon/-private/model/record-reference.ts +++ b/packages/store/addon/-private/legacy-model-support/record-reference.ts @@ -10,9 +10,9 @@ import type { SingleResourceDocument } from '@ember-data/types/q/ember-data-json import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; -import type Store from '../core-store'; -import type { NotificationType } from '../record-notification-manager'; -import { unsubscribe } from '../record-notification-manager'; +import type { NotificationType } from '../managers/record-notification-manager'; +import { unsubscribe } from '../managers/record-notification-manager'; +import type Store from '../store-service'; /** @module @ember-data/store diff --git a/packages/store/addon/-private/schema-definition-service.ts b/packages/store/addon/-private/legacy-model-support/schema-definition-service.ts similarity index 97% rename from packages/store/addon/-private/schema-definition-service.ts rename to packages/store/addon/-private/legacy-model-support/schema-definition-service.ts index 41add669a96..d462b0c0fce 100644 --- a/packages/store/addon/-private/schema-definition-service.ts +++ b/packages/store/addon/-private/legacy-model-support/schema-definition-service.ts @@ -10,8 +10,8 @@ import { DEPRECATE_STRING_ARG_SCHEMAS } from '@ember-data/private-build-infra/de import type { RecordIdentifier } from '@ember-data/types/q/identifier'; import type { AttributesSchema, RelationshipsSchema } from '@ember-data/types/q/record-data-schemas'; -import type Store from './core-store'; -import normalizeModelName from './normalize-model-name'; +import type Store from '../store-service'; +import normalizeModelName from '../utils/normalize-model-name'; type ModelForMixin = (store: Store, normalizedModelName: string) => Model | null; diff --git a/packages/store/addon/-private/model/shim-model-class.ts b/packages/store/addon/-private/legacy-model-support/shim-model-class.ts similarity index 97% rename from packages/store/addon/-private/model/shim-model-class.ts rename to packages/store/addon/-private/legacy-model-support/shim-model-class.ts index 475871c107f..c70500d10d6 100644 --- a/packages/store/addon/-private/model/shim-model-class.ts +++ b/packages/store/addon/-private/legacy-model-support/shim-model-class.ts @@ -4,8 +4,8 @@ import type { ModelSchema } from '@ember-data/types/q/ds-model'; import type { AttributeSchema, RelationshipSchema } from '@ember-data/types/q/record-data-schemas'; import type { Dict } from '@ember-data/types/q/utils'; -import type Store from '../core-store'; -import WeakCache from '../weak-cache'; +import type Store from '../store-service'; +import WeakCache from '../utils/weak-cache'; const AvailableShims = new WeakCache>(DEBUG ? 'schema-shims' : ''); AvailableShims._generator = () => { diff --git a/packages/store/addon/-private/record-array-manager.ts b/packages/store/addon/-private/managers/record-array-manager.ts similarity index 97% rename from packages/store/addon/-private/record-array-manager.ts rename to packages/store/addon/-private/managers/record-array-manager.ts index 56078d37a4b..ec673b0ef28 100644 --- a/packages/store/addon/-private/record-array-manager.ts +++ b/packages/store/addon/-private/managers/record-array-manager.ts @@ -12,11 +12,11 @@ import type { CollectionResourceDocument, Meta } from '@ember-data/types/q/ember import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { Dict } from '@ember-data/types/q/utils'; -import type Store from './core-store'; -import { internalModelFactoryFor } from './internal-model-factory'; -import AdapterPopulatedRecordArray from './record-arrays/adapter-populated-record-array'; -import RecordArray from './record-arrays/record-array'; -import WeakCache from './weak-cache'; +import { internalModelFactoryFor } from '../caches/internal-model-factory'; +import AdapterPopulatedRecordArray from '../record-arrays/adapter-populated-record-array'; +import RecordArray from '../record-arrays/record-array'; +import type Store from '../store-service'; +import WeakCache from '../utils/weak-cache'; const RecordArraysCache = new WeakCache>(DEBUG ? 'record-arrays' : ''); RecordArraysCache._generator = () => new Set(); diff --git a/packages/store/addon/-private/record-data-store-wrapper.ts b/packages/store/addon/-private/managers/record-data-store-wrapper.ts similarity index 97% rename from packages/store/addon/-private/record-data-store-wrapper.ts rename to packages/store/addon/-private/managers/record-data-store-wrapper.ts index 9f0be766b84..161728c5924 100644 --- a/packages/store/addon/-private/record-data-store-wrapper.ts +++ b/packages/store/addon/-private/managers/record-data-store-wrapper.ts @@ -11,10 +11,10 @@ import type { } from '@ember-data/types/q/record-data-schemas'; import type { RecordDataStoreWrapper as StoreWrapper } from '@ember-data/types/q/record-data-store-wrapper'; -import type Store from './core-store'; -import type { IdentifierCache } from './identifier-cache'; -import { internalModelFactoryFor } from './internal-model-factory'; -import constructResource from './utils/construct-resource'; +import type { IdentifierCache } from '../caches/identifier-cache'; +import { internalModelFactoryFor } from '../caches/internal-model-factory'; +import type Store from '../store-service'; +import constructResource from '../utils/construct-resource'; /** @module @ember-data/store diff --git a/packages/store/addon/-private/record-notification-manager.ts b/packages/store/addon/-private/managers/record-notification-manager.ts similarity index 96% rename from packages/store/addon/-private/record-notification-manager.ts rename to packages/store/addon/-private/managers/record-notification-manager.ts index e555d2247f1..095506aa4ff 100644 --- a/packages/store/addon/-private/record-notification-manager.ts +++ b/packages/store/addon/-private/managers/record-notification-manager.ts @@ -2,8 +2,8 @@ import { DEBUG } from '@glimmer/env'; import type { RecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier'; -import type Store from './core-store'; -import WeakCache from './weak-cache'; +import type Store from '../store-service'; +import WeakCache from '../utils/weak-cache'; type UnsubscribeToken = Object; diff --git a/packages/store/addon/-private/fetch-manager.ts b/packages/store/addon/-private/network/fetch-manager.ts similarity index 98% rename from packages/store/addon/-private/fetch-manager.ts rename to packages/store/addon/-private/network/fetch-manager.ts index e2695ac00ff..b581322cf13 100644 --- a/packages/store/addon/-private/fetch-manager.ts +++ b/packages/store/addon/-private/network/fetch-manager.ts @@ -19,14 +19,14 @@ import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-ser import type { FindOptions } from '@ember-data/types/q/store'; import type { Dict } from '@ember-data/types/q/utils'; -import coerceId from './coerce-id'; -import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from './common'; -import type Store from './core-store'; -import ShimModelClass from './model/shim-model-class'; +import ShimModelClass from '../legacy-model-support/shim-model-class'; +import type Store from '../store-service'; +import coerceId from '../utils/coerce-id'; +import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from '../utils/common'; +import { normalizeResponseHelper } from '../utils/serializer-response'; +import WeakCache from '../utils/weak-cache'; import RequestCache from './request-cache'; -import { normalizeResponseHelper } from './serializer-response'; import Snapshot from './snapshot'; -import WeakCache from './weak-cache'; function payloadIsNotBlank(adapterPayload): boolean { if (Array.isArray(adapterPayload)) { diff --git a/packages/store/addon/-private/finders.js b/packages/store/addon/-private/network/finders.js similarity index 96% rename from packages/store/addon/-private/finders.js rename to packages/store/addon/-private/network/finders.js index 1a4943b66ca..bb5f504a067 100644 --- a/packages/store/addon/-private/finders.js +++ b/packages/store/addon/-private/network/finders.js @@ -2,8 +2,8 @@ import { assert } from '@ember/debug'; import { Promise } from 'rsvp'; -import { guardDestroyedStore } from './common'; -import { normalizeResponseHelper } from './serializer-response'; +import { guardDestroyedStore } from '../utils/common'; +import { normalizeResponseHelper } from '../utils/serializer-response'; /** @module @ember-data/store diff --git a/packages/store/addon/-private/request-cache.ts b/packages/store/addon/-private/network/request-cache.ts similarity index 100% rename from packages/store/addon/-private/request-cache.ts rename to packages/store/addon/-private/network/request-cache.ts diff --git a/packages/store/addon/-private/snapshot-record-array.ts b/packages/store/addon/-private/network/snapshot-record-array.ts similarity index 98% rename from packages/store/addon/-private/snapshot-record-array.ts rename to packages/store/addon/-private/network/snapshot-record-array.ts index 2f48b463f83..e1441aea93c 100644 --- a/packages/store/addon/-private/snapshot-record-array.ts +++ b/packages/store/addon/-private/network/snapshot-record-array.ts @@ -9,7 +9,7 @@ import type { ModelSchema } from '@ember-data/types/q/ds-model'; import type { FindOptions } from '@ember-data/types/q/store'; import type { Dict } from '@ember-data/types/q/utils'; -import type RecordArray from './record-arrays/record-array'; +import type RecordArray from '../record-arrays/record-array'; import type Snapshot from './snapshot'; /** SnapshotRecordArray is not directly instantiable. diff --git a/packages/store/addon/-private/snapshot.ts b/packages/store/addon/-private/network/snapshot.ts similarity index 99% rename from packages/store/addon/-private/snapshot.ts rename to packages/store/addon/-private/network/snapshot.ts index 67813e09b86..2e21747c55b 100644 --- a/packages/store/addon/-private/snapshot.ts +++ b/packages/store/addon/-private/network/snapshot.ts @@ -23,8 +23,8 @@ import type { RecordInstance } from '@ember-data/types/q/record-instance'; import type { FindOptions } from '@ember-data/types/q/store'; import type { Dict } from '@ember-data/types/q/utils'; -import type Store from './core-store'; -import type InternalModel from './model/internal-model'; +import type InternalModel from '../legacy-model-support/internal-model'; +import type Store from '../store-service'; type RecordId = string | null; diff --git a/packages/store/addon/-private/promise-proxies.ts b/packages/store/addon/-private/proxies/promise-proxies.ts similarity index 100% rename from packages/store/addon/-private/promise-proxies.ts rename to packages/store/addon/-private/proxies/promise-proxies.ts diff --git a/packages/store/addon/-private/promise-proxy-base.d.ts b/packages/store/addon/-private/proxies/promise-proxy-base.d.ts similarity index 100% rename from packages/store/addon/-private/promise-proxy-base.d.ts rename to packages/store/addon/-private/proxies/promise-proxy-base.d.ts diff --git a/packages/store/addon/-private/promise-proxy-base.js b/packages/store/addon/-private/proxies/promise-proxy-base.js similarity index 100% rename from packages/store/addon/-private/promise-proxy-base.js rename to packages/store/addon/-private/proxies/promise-proxy-base.js diff --git a/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts b/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts index 39fd15b0bca..b47e5c38856 100644 --- a/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts +++ b/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts @@ -7,11 +7,11 @@ import type { RecordInstance } from '@ember-data/types/q/record-instance'; import type { FindOptions } from '@ember-data/types/q/store'; import type { Dict } from '@ember-data/types/q/utils'; -import type Store from '../core-store'; -import type { PromiseArray } from '../promise-proxies'; -import { promiseArray } from '../promise-proxies'; -import type RecordArrayManager from '../record-array-manager'; -import SnapshotRecordArray from '../snapshot-record-array'; +import type RecordArrayManager from '../managers/record-array-manager'; +import SnapshotRecordArray from '../network/snapshot-record-array'; +import type { PromiseArray } from '../proxies/promise-proxies'; +import { promiseArray } from '../proxies/promise-proxies'; +import type Store from '../store-service'; import RecordArray from './record-array'; export interface AdapterPopulatedRecordArrayCreateArgs { diff --git a/packages/store/addon/-private/record-arrays/record-array.ts b/packages/store/addon/-private/record-arrays/record-array.ts index b477d07e686..147f5b1563b 100644 --- a/packages/store/addon/-private/record-arrays/record-array.ts +++ b/packages/store/addon/-private/record-arrays/record-array.ts @@ -16,10 +16,10 @@ import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; import type { FindOptions } from '@ember-data/types/q/store'; -import type Store from '../core-store'; -import type { PromiseArray } from '../promise-proxies'; -import { promiseArray } from '../promise-proxies'; -import SnapshotRecordArray from '../snapshot-record-array'; +import SnapshotRecordArray from '../network/snapshot-record-array'; +import type { PromiseArray } from '../proxies/promise-proxies'; +import { promiseArray } from '../proxies/promise-proxies'; +import type Store from '../store-service'; function recordForIdentifier(store: Store, identifier: StableRecordIdentifier): RecordInstance { return store._instanceCache.getRecord(identifier); diff --git a/packages/store/addon/-private/core-store.ts b/packages/store/addon/-private/store-service.ts similarity index 98% rename from packages/store/addon/-private/core-store.ts rename to packages/store/addon/-private/store-service.ts index 9efb8d2f35b..39c39506bce 100644 --- a/packages/store/addon/-private/core-store.ts +++ b/packages/store/addon/-private/store-service.ts @@ -40,31 +40,31 @@ import type { FindOptions } from '@ember-data/types/q/store'; import type { Dict } from '@ember-data/types/q/utils'; import edBackburner from './backburner'; -import coerceId, { ensureStringId } from './coerce-id'; -import FetchManager, { SaveOp } from './fetch-manager'; -import { _findAll, _query, _queryRecord } from './finders'; -import { IdentifierCache } from './identifier-cache'; -import { InstanceCache, storeFor, StoreMap } from './instance-cache'; +import { IdentifierCache } from './caches/identifier-cache'; +import { InstanceCache, storeFor, StoreMap } from './caches/instance-cache'; import { internalModelFactoryFor, peekRecordIdentifier, recordIdentifierFor, setRecordIdentifier, -} from './internal-model-factory'; -import RecordReference from './model/record-reference'; -import type ShimModelClass from './model/shim-model-class'; -import { getShimClass } from './model/shim-model-class'; -import normalizeModelName from './normalize-model-name'; -import { PromiseArray, promiseArray, PromiseObject, promiseObject } from './promise-proxies'; -import RecordArrayManager from './record-array-manager'; +} from './caches/internal-model-factory'; +import { setRecordDataFor } from './caches/record-data-for'; +import RecordReference from './legacy-model-support/record-reference'; +import { DSModelSchemaDefinitionService, getModelFactory } from './legacy-model-support/schema-definition-service'; +import type ShimModelClass from './legacy-model-support/shim-model-class'; +import { getShimClass } from './legacy-model-support/shim-model-class'; +import RecordArrayManager from './managers/record-array-manager'; +import RecordDataStoreWrapper from './managers/record-data-store-wrapper'; +import NotificationManager from './managers/record-notification-manager'; +import FetchManager, { SaveOp } from './network/fetch-manager'; +import { _findAll, _query, _queryRecord } from './network/finders'; +import type RequestCache from './network/request-cache'; +import { PromiseArray, promiseArray, PromiseObject, promiseObject } from './proxies/promise-proxies'; import AdapterPopulatedRecordArray from './record-arrays/adapter-populated-record-array'; import RecordArray from './record-arrays/record-array'; -import { setRecordDataFor } from './record-data-for'; -import RecordDataStoreWrapper from './record-data-store-wrapper'; -import NotificationManager from './record-notification-manager'; -import type RequestCache from './request-cache'; -import { DSModelSchemaDefinitionService, getModelFactory } from './schema-definition-service'; +import coerceId, { ensureStringId } from './utils/coerce-id'; import constructResource from './utils/construct-resource'; +import normalizeModelName from './utils/normalize-model-name'; import promiseRecord from './utils/promise-record'; export { storeFor }; @@ -306,7 +306,6 @@ class Store extends Service { // ensure that `getOwner(this)` works inside a model instance setOwner(createOptions, getOwner(this)); delete createOptions.container; - // TODO this needs to not use the private property here to get modelFactoryCache so as to not break interop return getModelFactory(this, this._modelFactoryCache, modelName).create(createOptions); } assert(`You must implement the store's instantiateRecord hook for your custom model class.`); @@ -326,6 +325,8 @@ class Store extends Service { getSchemaDefinitionService(): SchemaDefinitionService { if (HAS_MODEL_PACKAGE && !this._schemaDefinitionService) { + // it is potentially a mistake for the RFC to have not enabled chaining these services, though highlander rule is nice. + // what ember-m3 did via private API to allow both worlds to interop would be much much harder using this. this._schemaDefinitionService = new DSModelSchemaDefinitionService(this); } assert( @@ -367,10 +368,6 @@ class Store extends Service { ); if (HAS_MODEL_PACKAGE) { let normalizedModelName = normalizeModelName(modelName); - // TODO this is safe only because - // apps would be horribly broken if the schema service were using DS_MODEL but not using DS_MODEL's schema service. - // it is potentially a mistake for the RFC to have not enabled chaining these services, though highlander rule is nice. - // what ember-m3 did via private API to allow both worlds to interop would be much much harder using this. let maybeFactory = getModelFactory(this, this._modelFactoryCache, normalizedModelName); // for factorFor factory/class split @@ -2233,7 +2230,7 @@ class Store extends Service { if (_RecordData === undefined) { _RecordData = ( importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private') - ).RecordData as RecordDataConstruct; + ).RecordData; } let identifier = this.identifierCache.getOrCreateRecordIdentifier({ diff --git a/packages/store/addon/-private/coerce-id.ts b/packages/store/addon/-private/utils/coerce-id.ts similarity index 98% rename from packages/store/addon/-private/coerce-id.ts rename to packages/store/addon/-private/utils/coerce-id.ts index fe421bc8d70..6aa9961536e 100644 --- a/packages/store/addon/-private/coerce-id.ts +++ b/packages/store/addon/-private/utils/coerce-id.ts @@ -35,7 +35,7 @@ export function ensureStringId(id: Coercable): string { throw new Error(`Expected id to be a string or number, received ${String(id)}`); } - return normalized!; + return normalized; } export default coerceId; diff --git a/packages/store/addon/-private/common.js b/packages/store/addon/-private/utils/common.js similarity index 100% rename from packages/store/addon/-private/common.js rename to packages/store/addon/-private/utils/common.js diff --git a/packages/store/addon/-private/utils/construct-resource.ts b/packages/store/addon/-private/utils/construct-resource.ts index 079a24d1602..12710992cea 100644 --- a/packages/store/addon/-private/utils/construct-resource.ts +++ b/packages/store/addon/-private/utils/construct-resource.ts @@ -5,8 +5,8 @@ import type { ResourceIdentifierObject, } from '@ember-data/types/q/ember-data-json-api'; -import coerceId from '../coerce-id'; -import { isStableIdentifier } from '../identifier-cache'; +import { isStableIdentifier } from '../caches/identifier-cache'; +import coerceId from './coerce-id'; import isNonEmptyString from './is-non-empty-string'; function constructResource(type: ResourceIdentifierObject): ResourceIdentifierObject; diff --git a/packages/store/addon/-private/identifer-debug-consts.ts b/packages/store/addon/-private/utils/identifer-debug-consts.ts similarity index 100% rename from packages/store/addon/-private/identifer-debug-consts.ts rename to packages/store/addon/-private/utils/identifer-debug-consts.ts diff --git a/packages/store/addon/-private/normalize-model-name.ts b/packages/store/addon/-private/utils/normalize-model-name.ts similarity index 100% rename from packages/store/addon/-private/normalize-model-name.ts rename to packages/store/addon/-private/utils/normalize-model-name.ts diff --git a/packages/store/addon/-private/utils/promise-record.ts b/packages/store/addon/-private/utils/promise-record.ts index 03c182fd1ca..2df603774a9 100644 --- a/packages/store/addon/-private/utils/promise-record.ts +++ b/packages/store/addon/-private/utils/promise-record.ts @@ -1,9 +1,9 @@ import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; -import type Store from '../core-store'; -import type { PromiseObject } from '../promise-proxies'; -import { promiseObject } from '../promise-proxies'; +import type { PromiseObject } from '../proxies/promise-proxies'; +import { promiseObject } from '../proxies/promise-proxies'; +import type Store from '../store-service'; export default function promiseRecord( store: Store, diff --git a/packages/store/addon/-private/serializer-response.ts b/packages/store/addon/-private/utils/serializer-response.ts similarity index 95% rename from packages/store/addon/-private/serializer-response.ts rename to packages/store/addon/-private/utils/serializer-response.ts index c95b2015d93..2e68e242c27 100644 --- a/packages/store/addon/-private/serializer-response.ts +++ b/packages/store/addon/-private/utils/serializer-response.ts @@ -5,8 +5,8 @@ import type { JsonApiDocument } from '@ember-data/types/q/ember-data-json-api'; import type { AdapterPayload } from '@ember-data/types/q/minimum-adapter-interface'; import type { MinimumSerializerInterface, RequestType } from '@ember-data/types/q/minimum-serializer-interface'; -import type Store from './core-store'; -import type ShimModelClass from './model/shim-model-class'; +import type ShimModelClass from '../legacy-model-support/shim-model-class'; +import type Store from '../store-service'; /** This is a helper method that validates a JSON API top-level document diff --git a/packages/store/addon/-private/weak-cache.ts b/packages/store/addon/-private/utils/weak-cache.ts similarity index 100% rename from packages/store/addon/-private/weak-cache.ts rename to packages/store/addon/-private/utils/weak-cache.ts diff --git a/tsconfig.json b/tsconfig.json index 1d210d578df..10951be73af 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -42,24 +42,22 @@ "ember-data-types/q/fetch-manager.ts", "ember-data-types/q/ember-data-json-api.ts", "ember-data-types/q/ds-model.ts", - "packages/store/addon/-private/record-data-store-wrapper.ts", - "packages/store/addon/-private/internal-model-factory.ts", - "packages/store/addon/-private/snapshot.ts", - "packages/store/addon/-private/snapshot-record-array.ts", - "packages/store/addon/-private/schema-definition-service.ts", - "packages/store/addon/-private/request-cache.ts", - "packages/store/addon/-private/record-notification-manager.ts", - "packages/store/addon/-private/record-data-for.ts", - "packages/store/addon/-private/normalize-model-name.ts", - "packages/store/addon/-private/model/shim-model-class.ts", - "packages/store/addon/-private/model/internal-model.ts", - "packages/store/addon/-private/internal-model-map.ts", - "packages/store/addon/-private/identity-map.ts", - "packages/store/addon/-private/fetch-manager.ts", - "packages/store/addon/-private/core-store.ts", - "packages/store/addon/-private/coerce-id.ts", + "packages/store/addon/-private/managers/record-data-store-wrapper.ts", + "packages/store/addon/-private/caches/internal-model-factory.ts", + "packages/store/addon/-private/network/snapshot.ts", + "packages/store/addon/-private/network/snapshot-record-array.ts", + "packages/store/addon/-private/legacy-model-support/schema-definition-service.ts", + "packages/store/addon/-private/network/request-cache.ts", + "packages/store/addon/-private/managers/record-notification-manager.ts", + "packages/store/addon/-private/caches/record-data-for.ts", + "packages/store/addon/-private/utils/normalize-model-name.ts", + "packages/store/addon/-private/legacy-model-support/shim-model-class.ts", + "packages/store/addon/-private/legacy-model-support/internal-model.ts", + "packages/store/addon/-private/network/fetch-manager.ts", + "packages/store/addon/-private/store-service.ts", + "packages/store/addon/-private/utils/coerce-id.ts", "packages/store/addon/-private/index.ts", - "packages/store/addon/-private/identifier-cache.ts", + "packages/store/addon/-private/caches/identifier-cache.ts", "packages/serializer/tests/dummy/app/routes/application/route.ts", "packages/serializer/tests/dummy/app/router.ts", "packages/serializer/tests/dummy/app/resolver.ts", From 1be582ed2d32199271abb8693c2c64613d133f15 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Wed, 27 Jul 2022 17:21:17 -0700 Subject: [PATCH 03/29] fix imports --- ember-data-types/q/ds-model.ts | 2 +- ember-data-types/q/minimum-serializer-interface.ts | 2 +- packages/adapter/addon/json-api.ts | 4 ++-- packages/model/addon/-private/legacy-relationships-support.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ember-data-types/q/ds-model.ts b/ember-data-types/q/ds-model.ts index 1915075a33f..27eeec4d295 100644 --- a/ember-data-types/q/ds-model.ts +++ b/ember-data-types/q/ds-model.ts @@ -2,7 +2,7 @@ import type EmberObject from '@ember/object'; import type { Errors } from '@ember-data/model/-private'; import type Store from '@ember-data/store'; -import type InternalModel from '@ember-data/store/-private/model/internal-model'; +import type InternalModel from '@ember-data/store/-private/legacy-model-support/internal-model'; import type { JsonApiValidationError } from './record-data-json-api'; import type { AttributeSchema, RelationshipSchema, RelationshipsSchema } from './record-data-schemas'; diff --git a/ember-data-types/q/minimum-serializer-interface.ts b/ember-data-types/q/minimum-serializer-interface.ts index 81ac89e2f14..022375e1800 100644 --- a/ember-data-types/q/minimum-serializer-interface.ts +++ b/ember-data-types/q/minimum-serializer-interface.ts @@ -1,7 +1,7 @@ import type { Object as JSONObject } from 'json-typescript'; import type Store from '@ember-data/store'; -import type Snapshot from '@ember-data/store/-private/snapshot'; +import type Snapshot from '@ember-data/store/-private/network/snapshot'; import type { ModelSchema } from './ds-model'; import type { JsonApiDocument, SingleResourceDocument } from './ember-data-json-api'; diff --git a/packages/adapter/addon/json-api.ts b/packages/adapter/addon/json-api.ts index 9ddcc2d1c61..8c3057b57ef 100644 --- a/packages/adapter/addon/json-api.ts +++ b/packages/adapter/addon/json-api.ts @@ -7,8 +7,8 @@ import { dasherize } from '@ember/string'; import { pluralize } from 'ember-inflector'; import type Store from '@ember-data/store'; -import type ShimModelClass from '@ember-data/store/-private/model/shim-model-class'; -import type Snapshot from '@ember-data/store/-private/snapshot'; +import type ShimModelClass from '@ember-data/store/-private/legacy-model-support/shim-model-class'; +import type Snapshot from '@ember-data/store/-private/network/snapshot'; import type { AdapterPayload } from '@ember-data/types/q/minimum-adapter-interface'; import { serializeIntoHash } from './-private'; diff --git a/packages/model/addon/-private/legacy-relationships-support.ts b/packages/model/addon/-private/legacy-relationships-support.ts index 672dbd66331..e3549748c92 100644 --- a/packages/model/addon/-private/legacy-relationships-support.ts +++ b/packages/model/addon/-private/legacy-relationships-support.ts @@ -15,7 +15,7 @@ import type { RelationshipState } from '@ember-data/record-data/-private/graph/- import type Store from '@ember-data/store'; import type { InternalModel } from '@ember-data/store/-private'; import { recordDataFor, recordIdentifierFor, storeFor } from '@ember-data/store/-private'; -import type { IdentifierCache } from '@ember-data/store/-private/identifier-cache'; +import type { IdentifierCache } from '@ember-data/store/-private/caches/identifier-cache'; import type { DSModel } from '@ember-data/types/q/ds-model'; import type { ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; From 506000aa5e93f1632876a6d64c9f1468fa49ef11 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Wed, 27 Jul 2022 18:19:38 -0700 Subject: [PATCH 04/29] get tests passing --- .../addon/-private/caches/internal-model-factory.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/store/addon/-private/caches/internal-model-factory.ts b/packages/store/addon/-private/caches/internal-model-factory.ts index 5828a1ba5be..40d4276707b 100644 --- a/packages/store/addon/-private/caches/internal-model-factory.ts +++ b/packages/store/addon/-private/caches/internal-model-factory.ts @@ -358,8 +358,15 @@ export default class InternalModelFactory { if (identifiers) { identifiers.forEach((identifier) => { let internalModel = this.peek(identifier); + + // TODO we rely on not removing the main cache + // and only removing the peekList cache apparently. + // we should figure out this duality and codify whatever + // signal it is actually trying to give us. + // this.cache.delete(identifier); + this.peekList[identifier.type]!.delete(identifier); internalModel!.unloadRecord(); - this.remove(internalModel!); + // TODO we don't remove the identifier, should we? }); } } From ee62785df12121228724201b086aeea5b8c94c06 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Wed, 27 Jul 2022 18:41:45 -0700 Subject: [PATCH 05/29] replace usage of internalModel.isHiddenFromRecordArrays --- .../addon/-private/caches/instance-cache.ts | 41 +++++++++++++++++++ .../legacy-model-support/internal-model.ts | 36 ---------------- .../-private/managers/record-array-manager.ts | 17 ++------ .../store/addon/-private/store-service.ts | 4 +- 4 files changed, 47 insertions(+), 51 deletions(-) diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index 8aa1e87fc4f..18b830e5065 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -348,6 +348,47 @@ export class InstanceCache { } } +export function isHiddenFromRecordArrays(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { + // During dematerialization we don't want to rematerialize the record. + // recordWasDeleted can cause other records to rematerialize because it + // removes the internal model from the array and Ember arrays will always + // `objectAt(0)` and `objectAt(len -1)` to check whether `firstObject` or + // `lastObject` have changed. When this happens we don't want those + // models to rematerialize their records. + + // eager checks to avoid instantiating record data if we are empty or loading + let recordData = cache.peek({ identifier, bucket: 'recordData' }); + if (!recordData) { + return true; + } + + // if isLoading return false + // if isDematerializing, destroyed, or has scheduled destroy return true + // TODO eliminate this internalModel need + const internalModel = cache.getInternalModel(identifier); + if (!internalModel.isEmpty || internalModel.isLoading) { + return false; + } + if (recordData.isDeletionCommitted?.() || (recordData.isNew?.() && recordData.isDeleted?.())) { + return true; + } else { + return false; + } +} + +function _recordDataIsFullDeleted(recordData: RecordData): boolean { + if (recordData.isDeletionCommitted?.() || (recordData.isNew?.() && recordData.isDeleted?.())) { + return true; + } else { + return false; + } +} + +export function recordDataIsFullyDeleted(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { + let recordData = cache.peek({ identifier, bucket: 'recordData' }); + return !recordData || _recordDataIsFullDeleted(recordData); +} + function assertRecordsPassedToHasMany(records: RecordInstance[]) { assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records)); assert( diff --git a/packages/store/addon/-private/legacy-model-support/internal-model.ts b/packages/store/addon/-private/legacy-model-support/internal-model.ts index f242fd5cd4b..ea62bdbe36f 100644 --- a/packages/store/addon/-private/legacy-model-support/internal-model.ts +++ b/packages/store/addon/-private/legacy-model-support/internal-model.ts @@ -114,42 +114,6 @@ export default class InternalModel { return this.store._instanceCache.getRecordData(this.identifier); } - isHiddenFromRecordArrays() { - // During dematerialization we don't want to rematerialize the record. - // recordWasDeleted can cause other records to rematerialize because it - // removes the internal model from the array and Ember arrays will always - // `objectAt(0)` and `objectAt(len -1)` to check whether `firstObject` or - // `lastObject` have changed. When this happens we don't want those - // models to rematerialize their records. - - // eager checks to avoid instantiating record data if we are empty or loading - if (this.isEmpty) { - return true; - } - - if (this.isLoading) { - return false; - } - - let isRecordFullyDeleted = this._isRecordFullyDeleted(); - return this._isDematerializing || this.hasScheduledDestroy() || this.isDestroyed || isRecordFullyDeleted; - } - - _isRecordFullyDeleted(): boolean { - if (this._recordData.isDeletionCommitted && this._recordData.isDeletionCommitted()) { - return true; - } else if ( - this._recordData.isNew && - this._recordData.isDeleted && - this._recordData.isNew() && - this._recordData.isDeleted() - ) { - return true; - } else { - return false; - } - } - isDeleted(): boolean { if (this._recordData.isDeleted) { return this._recordData.isDeleted(); diff --git a/packages/store/addon/-private/managers/record-array-manager.ts b/packages/store/addon/-private/managers/record-array-manager.ts index ec673b0ef28..b6463de6b21 100644 --- a/packages/store/addon/-private/managers/record-array-manager.ts +++ b/packages/store/addon/-private/managers/record-array-manager.ts @@ -12,6 +12,7 @@ import type { CollectionResourceDocument, Meta } from '@ember-data/types/q/ember import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { Dict } from '@ember-data/types/q/utils'; +import { isHiddenFromRecordArrays } from '../caches/instance-cache'; import { internalModelFactoryFor } from '../caches/internal-model-factory'; import AdapterPopulatedRecordArray from '../record-arrays/adapter-populated-record-array'; import RecordArray from '../record-arrays/record-array'; @@ -37,16 +38,6 @@ function getIdentifier(identifier: StableRecordIdentifier): StableRecordIdentifi return identifier; } -function shouldIncludeInRecordArrays(store: Store, identifier: StableRecordIdentifier): boolean { - const cache = internalModelFactoryFor(store); - const internalModel = cache.peek(identifier); - - if (internalModel === null) { - return false; - } - return !internalModel.isHiddenFromRecordArrays(); -} - /** @class RecordArrayManager @internal @@ -90,7 +81,7 @@ class RecordArrayManager { // recordArrayManager pendingForIdentifier.delete(i); // build up a set of models to ensure we have purged correctly; - let isIncluded = shouldIncludeInRecordArrays(this.store, i); + let isIncluded = !isHiddenFromRecordArrays(this.store._instanceCache, i); if (!isIncluded) { identifiersToRemove.push(i); } @@ -208,7 +199,7 @@ class RecordArrayManager { let visible: StableRecordIdentifier[] = []; for (let i = 0; i < all.length; i++) { let identifier = all[i]; - let shouldInclude = shouldIncludeInRecordArrays(this.store, identifier); + let shouldInclude = !isHiddenFromRecordArrays(this.store._instanceCache, identifier); if (shouldInclude) { visible.push(identifier); @@ -400,7 +391,7 @@ function updateLiveRecordArray(store: Store, recordArray: RecordArray, identifie for (let i = 0; i < identifiers.length; i++) { let identifier = identifiers[i]; - let shouldInclude = shouldIncludeInRecordArrays(store, identifier); + let shouldInclude = !isHiddenFromRecordArrays(store._instanceCache, identifier); let recordArrays = recordArraysForIdentifier(identifier); if (shouldInclude) { diff --git a/packages/store/addon/-private/store-service.ts b/packages/store/addon/-private/store-service.ts index 39c39506bce..a0482006245 100644 --- a/packages/store/addon/-private/store-service.ts +++ b/packages/store/addon/-private/store-service.ts @@ -41,7 +41,7 @@ import type { Dict } from '@ember-data/types/q/utils'; import edBackburner from './backburner'; import { IdentifierCache } from './caches/identifier-cache'; -import { InstanceCache, storeFor, StoreMap } from './caches/instance-cache'; +import { InstanceCache, recordDataIsFullyDeleted, storeFor, StoreMap } from './caches/instance-cache'; import { internalModelFactoryFor, peekRecordIdentifier, @@ -2127,7 +2127,7 @@ class Store extends Service { `Cannot initiate a save request for an unloaded record: ${identifier}`, !internalModel.isEmpty && !internalModel.isDestroyed ); - if (internalModel._isRecordFullyDeleted()) { + if (recordDataIsFullyDeleted(this._instanceCache, identifier)) { return resolve(record); } From 1b8c5dccad0fbccb640ea1c2f591e928f423f8b1 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sat, 30 Jul 2022 20:19:01 -0700 Subject: [PATCH 06/29] begin move of deleteRecord logic into the store --- .../legacy-model-support/internal-model.ts | 17 -------------- .../store/addon/-private/store-service.ts | 22 ++++++++++++++++--- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/store/addon/-private/legacy-model-support/internal-model.ts b/packages/store/addon/-private/legacy-model-support/internal-model.ts index ea62bdbe36f..d73820827df 100644 --- a/packages/store/addon/-private/legacy-model-support/internal-model.ts +++ b/packages/store/addon/-private/legacy-model-support/internal-model.ts @@ -185,23 +185,6 @@ export default class InternalModel { this.store.recordArrayManager.recordDidChange(this.identifier); } - deleteRecord() { - run(() => { - const backburner = this.store._backburner; - backburner.run(() => { - if (this._recordData.setIsDeleted) { - this._recordData.setIsDeleted(true); - } - - if (this.isNew()) { - // destroyRecord follows up deleteRecord with save(). This prevents an unecessary save for a new record - this._deletedRecordWasNew = true; - this.unloadRecord(); - } - }); - }); - } - /* Unload the record for this internal model. This will cause the record to be destroyed and freed up for garbage collection. It will also do a check diff --git a/packages/store/addon/-private/store-service.ts b/packages/store/addon/-private/store-service.ts index a0482006245..ca5c464ed30 100644 --- a/packages/store/addon/-private/store-service.ts +++ b/packages/store/addon/-private/store-service.ts @@ -3,7 +3,7 @@ */ import { getOwner, setOwner } from '@ember/application'; import { assert, deprecate } from '@ember/debug'; -import { _backburner as emberBackburner } from '@ember/runloop'; +import { _backburner as emberBackburner, run } from '@ember/runloop'; import type { Backburner } from '@ember/runloop/-private/backburner'; import Service from '@ember/service'; import { registerWaiter, unregisterWaiter } from '@ember/test'; @@ -492,12 +492,28 @@ class Store extends Service { if (DEBUG) { assertDestroyingStore(this, 'deleteRecord'); } + // TODO eliminate this interleaving + // it is unlikely we need both an outer join and the inner run + // of our own queue this._backburner.join(() => { let identifier = peekRecordIdentifier(record); if (identifier) { - let internalModel = internalModelFactoryFor(this).peek(identifier); + const internalModel = internalModelFactoryFor(this).peek(identifier); if (internalModel) { - internalModel.deleteRecord(); + run(() => { + const backburner = this._backburner; + backburner.run(() => { + if (internalModel._recordData.setIsDeleted) { + internalModel._recordData.setIsDeleted(true); + } + + if (internalModel.isNew()) { + // destroyRecord follows up deleteRecord with save(). This prevents an unecessary save for a new record + internalModel._deletedRecordWasNew = true; + internalModel.unloadRecord(); + } + }); + }); } } }); From abe608871ae4ba8d252cb563269919abe1392ff4 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 1 Aug 2022 15:28:01 -0700 Subject: [PATCH 07/29] trim trim trim --- .../q/relationship-record-data.ts | 2 +- packages/model/addon/-private/model.js | 17 +- .../addon/-private/references/belongs-to.ts | 2 +- .../addon/-private/references/has-many.ts | 2 +- .../record-data/addon/-private/record-data.ts | 20 +- .../addon/-private/caches/instance-cache.ts | 33 +- .../-private/caches/internal-model-factory.ts | 30 +- .../legacy-model-support/internal-model.ts | 323 ++++-------------- .../managers/record-data-store-wrapper.ts | 31 +- .../store/addon/-private/network/snapshot.ts | 9 +- .../store/addon/-private/store-service.ts | 47 ++- .../app/services/store.js | 4 +- .../app/services/store.js | 4 +- 13 files changed, 167 insertions(+), 357 deletions(-) diff --git a/ember-data-types/q/relationship-record-data.ts b/ember-data-types/q/relationship-record-data.ts index e5188b2d155..d8a46158cd5 100644 --- a/ember-data-types/q/relationship-record-data.ts +++ b/ember-data-types/q/relationship-record-data.ts @@ -16,7 +16,7 @@ export interface RelationshipRecordData extends RecordData { storeWrapper: RecordDataStoreWrapper; identifier: StableRecordIdentifier; id: string | null; - clientId: string | null; + lid: string | null; isEmpty(): boolean; getResourceIdentifier(): RecordIdentifier; getBelongsTo(key: string): DefaultSingleResourceRelationship; diff --git a/packages/model/addon/-private/model.js b/packages/model/addon/-private/model.js index 1f96be63551..f62773d6c31 100644 --- a/packages/model/addon/-private/model.js +++ b/packages/model/addon/-private/model.js @@ -23,6 +23,7 @@ import { } from '@ember-data/private-build-infra/deprecations'; import { recordIdentifierFor, storeFor } from '@ember-data/store'; import { coerceId, deprecatedPromiseObject, InternalModel, WeakCache } from '@ember-data/store/-private'; +import { recordDataFor } from '@ember-data/store/-private'; import Errors from './errors'; import { LegacySupport } from './legacy-relationships-support'; @@ -454,13 +455,11 @@ class Model extends EmberObject { */ @tagged get id() { - // the _internalModel guard exists, because some dev-only deprecation code + // this guard exists, because some dev-only deprecation code // (addListener via validatePropertyInjections) invokes toString before the // object is real. - if (DEBUG) { - if (!this._internalModel) { - return void 0; - } + if (DEBUG && !this._internalModel) { + return void 0; } return this._internalModel.id; } @@ -794,7 +793,7 @@ class Model extends EmberObject { and value is an [oldProp, newProp] array. */ changedAttributes() { - return this._internalModel.changedAttributes(); + return recordDataFor(this).changedAttributes(); } /** @@ -817,7 +816,8 @@ class Model extends EmberObject { */ rollbackAttributes() { const { currentState } = this; - this._internalModel.rollbackAttributes(); + recordDataFor(this).rollbackAttributes(); + record.errors.clear(); currentState.cleanErrorRequests(); } @@ -830,8 +830,9 @@ class Model extends EmberObject { return storeFor(this)._instanceCache.createSnapshot(recordIdentifierFor(this)); } + // TODO can we remove this now? toStringExtension() { - // the _internalModel guard exists, because some dev-only deprecation code + // this guard exists, because some dev-only deprecation code // (addListener via validatePropertyInjections) invokes toString before the // object is real. return this._internalModel && this._internalModel.id; diff --git a/packages/model/addon/-private/references/belongs-to.ts b/packages/model/addon/-private/references/belongs-to.ts index fd8d7a3bfc0..e1b4e4c971e 100644 --- a/packages/model/addon/-private/references/belongs-to.ts +++ b/packages/model/addon/-private/references/belongs-to.ts @@ -277,7 +277,7 @@ export default class BelongsToReference { } _resource() { - return this.store._instanceCache.recordDataFor(this.#identifier, false).getBelongsTo(this.key); + return this.store._instanceCache.recordDataFor(this.#identifier).getBelongsTo(this.key); } /** diff --git a/packages/model/addon/-private/references/has-many.ts b/packages/model/addon/-private/references/has-many.ts index 1dc86ccefa9..e57ba3d1622 100644 --- a/packages/model/addon/-private/references/has-many.ts +++ b/packages/model/addon/-private/references/has-many.ts @@ -131,7 +131,7 @@ export default class HasManyReference { } _resource() { - return this.store._instanceCache.recordDataFor(this.#identifier, false).getHasMany(this.key); + return this.store._instanceCache.recordDataFor(this.#identifier).getHasMany(this.key); } /** diff --git a/packages/record-data/addon/-private/record-data.ts b/packages/record-data/addon/-private/record-data.ts index df5d1daec31..3ca6bdd4ee9 100644 --- a/packages/record-data/addon/-private/record-data.ts +++ b/packages/record-data/addon/-private/record-data.ts @@ -45,7 +45,7 @@ const EMPTY_ITERATOR = { export default class RecordDataDefault implements RelationshipRecordData { declare _errors?: JsonApiValidationError[]; declare modelName: string; - declare clientId: string; + declare lid: string; declare identifier: StableRecordIdentifier; declare id: string | null; declare isDestroyed: boolean; @@ -61,7 +61,7 @@ export default class RecordDataDefault implements RelationshipRecordData { constructor(identifier: RecordIdentifier, storeWrapper: RecordDataStoreWrapper) { this.modelName = identifier.type; - this.clientId = identifier.lid; + this.lid = identifier.lid; this.id = identifier.id; this.identifier = identifier; this.storeWrapper = storeWrapper; @@ -129,7 +129,7 @@ export default class RecordDataDefault implements RelationshipRecordData { _clearErrors() { if (this._errors) { this._errors = undefined; - this.storeWrapper.notifyErrorsChange(this.modelName, this.id, this.clientId); + this.storeWrapper.notifyErrorsChange(this.modelName, this.id, this.lid); } } @@ -283,6 +283,10 @@ export default class RecordDataDefault implements RelationshipRecordData { this._clearErrors(); this.notifyStateChange(); + if (dirtyKeys && dirtyKeys.length) { + this._notifyAttributes(dirtyKeys); + } + return dirtyKeys; } @@ -301,7 +305,7 @@ export default class RecordDataDefault implements RelationshipRecordData { if (data) { if (data.id) { // didCommit provided an ID, notify the store of it - this.storeWrapper.setRecordId(this.modelName, data.id, this.clientId); + this.storeWrapper.setRecordId(this.modelName, data.id, this.lid); this.id = coerceId(data.id); } if (data.relationships) { @@ -324,7 +328,7 @@ export default class RecordDataDefault implements RelationshipRecordData { } notifyStateChange() { - this.storeWrapper.notifyStateChange(this.modelName, this.id, this.clientId); + this.storeWrapper.notifyStateChange(this.modelName, this.id, this.lid); } // get ResourceIdentifiers for "current state" @@ -377,7 +381,7 @@ export default class RecordDataDefault implements RelationshipRecordData { if (errors) { this._errors = errors; } - this.storeWrapper.notifyErrorsChange(this.modelName, this.id, this.clientId); + this.storeWrapper.notifyErrorsChange(this.modelName, this.id, this.lid); } getBelongsTo(key: string): DefaultSingleResourceRelationship { @@ -468,11 +472,11 @@ export default class RecordDataDefault implements RelationshipRecordData { destroy() { this.isDestroyed = true; - this.storeWrapper.disconnectRecord(this.modelName, this.id, this.clientId); + this.storeWrapper.disconnectRecord(this.modelName, this.id, this.lid); } isRecordInUse() { - return this.storeWrapper.isRecordInUse(this.modelName, this.id, this.clientId); + return this.storeWrapper.isRecordInUse(this.modelName, this.id, this.lid); } /* diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index 18b830e5065..8d2a722b421 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -250,7 +250,7 @@ export class InstanceCache { __recordDataFor(resource: RecordIdentifier) { const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource); - return this.recordDataFor(identifier, false); + return this.recordDataFor(identifier); } // TODO move this to InstanceCache @@ -274,8 +274,8 @@ export class InstanceCache { return internalModelFactoryFor(this.store).getByResource(resource); } - setRecordId(modelName: string, newId: string, clientId: string) { - internalModelFactoryFor(this.store).setRecordId(modelName, newId, clientId); + setRecordId(modelName: string, newId: string, lid: string) { + internalModelFactoryFor(this.store).setRecordId(modelName, newId, lid); } _load(data: ExistingResourceObject): StableExistingRecordIdentifier { @@ -319,7 +319,12 @@ export class InstanceCache { } } - internalModel.setupData(data); + if (internalModel.isNew()) { + this.store._notificationManager.notify(identifier, 'identity'); + } + + const hasRecord = this.#instances.record.has(identifier); + this.getRecordData(identifier).pushData(data, hasRecord); if (!isUpdate) { this.store.recordArrayManager.recordDidChange(identifier); @@ -328,8 +333,26 @@ export class InstanceCache { return identifier as StableExistingRecordIdentifier; } - recordDataFor(identifier: StableRecordIdentifier | { type: string }, isCreate: boolean): RecordData { + destroyRecord(identifier: StableRecordIdentifier) { + const record = this.#instances.record.get(identifier); + assert( + 'Cannot destroy record while it is still materialized', + !record || record.isDestroyed || record.isDestroying + ); + + const factory = internalModelFactoryFor(this.store); + + let internalModel = factory.peek(identifier); + if (internalModel) { + internalModel.isDestroying = true; + factory.remove(internalModel); + internalModel._isDestroyed = true; + } + } + + recordDataFor(identifier: StableRecordIdentifier | { type: string }, isCreate?: boolean): RecordData { let recordData: RecordData; + // TODO remove isCreate arg @deprecate if needed if (isCreate === true) { // TODO remove once InternalModel is no longer essential to internal state // and just build a new identifier directly diff --git a/packages/store/addon/-private/caches/internal-model-factory.ts b/packages/store/addon/-private/caches/internal-model-factory.ts index 40d4276707b..616264d1343 100644 --- a/packages/store/addon/-private/caches/internal-model-factory.ts +++ b/packages/store/addon/-private/caches/internal-model-factory.ts @@ -204,21 +204,7 @@ export default class InternalModelFactory { const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); const internalModel = this.peek(identifier); - if (internalModel) { - // unloadRecord is async, if one attempts to unload + then sync push, - // we must ensure the unload is canceled before continuing - // The createRecord path will take _existingInternalModelForId() - // which will call `destroySync` instead for this unload + then - // sync createRecord scenario. Once we have true client-side - // delete signaling, we should never call destroySync - if (internalModel.hasScheduledDestroy()) { - internalModel.cancelDestroy(); - } - - return internalModel; - } - - return this._build(identifier, false); + return internalModel || this._build(identifier, false); } /** @@ -291,19 +277,7 @@ export default class InternalModelFactory { peekById(type: string, id: string): InternalModel | null { const identifier = this.identifierCache.peekRecordIdentifier({ type, id }); - let internalModel = identifier ? this.cache.get(identifier) || null : null; - - if (internalModel && internalModel.hasScheduledDestroy()) { - // unloadRecord is async, if one attempts to unload + then sync create, - // we must ensure the unload is complete before starting the create - // The push path will take this.lookup() - // which will call `cancelDestroy` instead for this unload + then - // sync push scenario. Once we have true client-side - // delete signaling, we should never call destroySync - internalModel.destroySync(); - internalModel = null; - } - return internalModel; + return identifier ? this.cache.get(identifier) || null : null; } build(newResourceInfo: NewResourceInfo): InternalModel { diff --git a/packages/store/addon/-private/legacy-model-support/internal-model.ts b/packages/store/addon/-private/legacy-model-support/internal-model.ts index d73820827df..5cc42f051fd 100644 --- a/packages/store/addon/-private/legacy-model-support/internal-model.ts +++ b/packages/store/addon/-private/legacy-model-support/internal-model.ts @@ -1,14 +1,10 @@ import { assert } from '@ember/debug'; -import { _backburner as emberBackburner, cancel, run } from '@ember/runloop'; import { DEBUG } from '@glimmer/env'; -import { HAS_MODEL_PACKAGE } from '@ember-data/private-build-infra'; -import type { DSModel } from '@ember-data/types/q/ds-model'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-serializer-interface'; -import type { ChangedAttributesHash, RecordData } from '@ember-data/types/q/record-data'; +import type { RecordData } from '@ember-data/types/q/record-data'; import type { JsonApiResource, JsonApiValidationError } from '@ember-data/types/q/record-data-json-api'; -import type { RecordInstance } from '@ember-data/types/q/record-instance'; import { internalModelFactoryFor } from '../caches/internal-model-factory'; import type Store from '../store-service'; @@ -18,16 +14,6 @@ import type ShimModelClass from './shim-model-class'; @module @ember-data/store */ -function isDSModel(record: RecordInstance | null): record is DSModel { - return ( - HAS_MODEL_PACKAGE && - !!record && - 'constructor' in record && - 'isModel' in record.constructor && - record.constructor.isModel === true - ); -} - type AdapterErrors = Error & { errors?: unknown[]; isAdapterError?: true; code?: string }; type SerializerWithParseErrors = MinimumSerializerInterface & { extractErrors?(store: Store, modelClass: ShimModelClass, error: AdapterErrors, recordId: string | null): any; @@ -36,22 +22,13 @@ type SerializerWithParseErrors = MinimumSerializerInterface & { export default class InternalModel { declare _id: string | null; declare modelName: string; - declare clientId: string; declare hasRecordData: boolean; declare _isDestroyed: boolean; - declare isError: boolean; - declare _pendingRecordArrayManagerFlush: boolean; declare _isDematerializing: boolean; - declare _doNotDestroy: boolean; declare isDestroying: boolean; declare _isUpdatingId: boolean; - declare _deletedRecordWasNew: boolean; // Not typed yet - declare _scheduledDestroy: any; - declare _modelClass: any; - declare __recordArrays: any; - declare error: any; declare store: Store; declare identifier: StableRecordIdentifier; declare hasRecord: boolean; @@ -62,15 +39,11 @@ export default class InternalModel { this._id = identifier.id; this._isUpdatingId = false; this.modelName = identifier.type; - this.clientId = identifier.lid; this.hasRecord = false; this.hasRecordData = false; this._isDestroyed = false; - this._doNotDestroy = false; - this.isError = false; - this._pendingRecordArrayManagerFlush = false; // used by the recordArrayManager // During dematerialization we don't want to rematerialize the record. The // reason this might happen is that dematerialization removes records from @@ -78,15 +51,6 @@ export default class InternalModel { // `objectAt(len - 1)` to test whether or not `firstObject` or `lastObject` // have changed. this._isDematerializing = false; - this._scheduledDestroy = null; - - this.error = null; - - // caches for lazy getters - this._modelClass = null; - this.__recordArrays = null; - - this.error = null; } get id(): string | null { @@ -104,12 +68,49 @@ export default class InternalModel { } } - get modelClass() { - if (this.store.modelFor) { - return this._modelClass || (this._modelClass = this.store.modelFor(this.modelName)); + /* + * calling `InstanceCache.setRecordId` is necessary to update + * the cache index for this record if we have changed. + * + * However, since the store is not aware of whether the update + * is from us (via user set) or from a push of new data + * it will also call us so that we can notify and update state. + * + * When it does so it calls with `fromCache` so that we can + * short-circuit instead of cycling back. + * + * This differs from the short-circuit in the `_isUpdatingId` + * case in that the the cache can originate the call to setId, + * so on first entry we will still need to do our own update. + */ + setId(id: string | null, fromCache: boolean = false) { + if (this._isUpdatingId === true) { + return; } + this._isUpdatingId = true; + let didChange = id !== this._id; + this._id = id; + + if (didChange && id !== null) { + if (!fromCache) { + this.store._instanceCache.setRecordId(this.modelName, id, this.identifier.lid); + } + // internal set of ID to get it to RecordData from DS.Model + // if we are within create we may not have a recordData yet. + if (this.hasRecordData && this._recordData.__setId) { + this._recordData.__setId(id); + } + } + + if (didChange && this.hasRecord) { + this.store._notificationManager.notify(this.identifier, 'identity'); + } + this._isUpdatingId = false; } + + + // STATE we end up needing for various reasons get _recordData(): RecordData { return this.store._instanceCache.getRecordData(this.identifier); } @@ -161,28 +162,8 @@ export default class InternalModel { return !this.isEmpty; } - dematerializeRecord() { - this._isDematerializing = true; - - // TODO IGOR add a test that fails when this is missing, something that involves canceling a destroy - // and the destroy not happening, and then later on trying to destroy - this._doNotDestroy = false; - // this has to occur before the internal model is removed - // for legacy compat. - const { identifier } = this; - this.store._instanceCache.removeRecord(identifier); - - // move to an empty never-loaded state - // ensure any record notifications happen prior to us - // unseting the record but after we've triggered - // destroy - this.store._backburner.join(() => { - this._recordData.unloadRecord(); - }); - - this.hasRecord = false; // this must occur after relationship removal - this.error = null; - this.store.recordArrayManager.recordDidChange(this.identifier); + get isDestroyed(): boolean { + return this._isDestroyed; } /* @@ -211,67 +192,23 @@ export default class InternalModel { assert('You can only unload a record which is not inFlight. `' + this + '`'); } } - this.dematerializeRecord(); - if (this._scheduledDestroy === null) { - this._scheduledDestroy = emberBackburner.schedule('destroy', this, '_checkForOrphanedInternalModels'); - } - } - - hasScheduledDestroy() { - return !!this._scheduledDestroy; - } - - cancelDestroy() { - assert( - `You cannot cancel the destruction of an InternalModel once it has already been destroyed`, - !this.isDestroyed - ); - - this._doNotDestroy = true; - this._isDematerializing = false; - cancel(this._scheduledDestroy); - this._scheduledDestroy = null; - } - - // typically, we prefer to async destroy this lets us batch cleanup work. - // Unfortunately, some scenarios where that is not possible. Such as: - // - // ```js - // const record = store.findRecord(‘record’, 1); - // record.unloadRecord(); - // store.createRecord(‘record’, 1); - // ``` - // - // In those scenarios, we make that model's cleanup work, sync. - // - destroySync() { - if (this._isDematerializing) { - this.cancelDestroy(); - } - this._checkForOrphanedInternalModels(); - if (this.isDestroyed || this.isDestroying) { - return; - } + this._isDematerializing = true; - // just in-case we are not one of the orphaned, we should still - // still destroy ourselves - this.destroy(); - } + // this has to occur before the internal model is removed + // for legacy compat. + const { identifier } = this; + this.store._instanceCache.removeRecord(identifier); - _checkForOrphanedInternalModels() { - this._isDematerializing = false; - this._scheduledDestroy = null; - if (this.isDestroyed) { - return; - } - } + // move to an empty never-loaded state + // ensure any record notifications happen prior to us + // unseting the record but after we've triggered + // destroy + this.store._backburner.join(() => { + this._recordData.unloadRecord(); + }); - destroyFromRecordData() { - if (this._doNotDestroy) { - this._doNotDestroy = false; - return; - } - this.destroy(); + this.hasRecord = false; // this must occur after relationship removal + this.store.recordArrayManager.recordDidChange(this.identifier); } destroy() { @@ -286,100 +223,6 @@ export default class InternalModel { this._isDestroyed = true; } - setupData(data) { - if (this.isNew()) { - this.store._notificationManager.notify(this.identifier, 'identity'); - } - this._recordData.pushData(data, this.hasRecord); - } - - notifyAttributes(keys: string[]): void { - if (this.hasRecord) { - let manager = this.store._notificationManager; - let { identifier } = this; - - if (!keys || !keys.length) { - manager.notify(identifier, 'attributes'); - } else { - for (let i = 0; i < keys.length; i++) { - manager.notify(identifier, 'attributes', keys[i]); - } - } - } - } - - get isDestroyed(): boolean { - return this._isDestroyed; - } - - hasChangedAttributes(): boolean { - if (!this.hasRecordData) { - // no need to calculate changed attributes when calling `findRecord` - return false; - } - return this._recordData.hasChangedAttributes(); - } - - changedAttributes(): ChangedAttributesHash { - if (!this.hasRecordData) { - // no need to calculate changed attributes when calling `findRecord` - return {}; - } - return this._recordData.changedAttributes(); - } - - adapterWillCommit(): void { - this._recordData.willCommit(); - let record = this.store._instanceCache.peek({ identifier: this.identifier, bucket: 'record' }); - if (record && isDSModel(record)) { - record.errors.clear(); - } - } - - notifyHasManyChange(key: string) { - if (this.hasRecord) { - this.store._notificationManager.notify(this.identifier, 'relationships', key); - } - } - - notifyBelongsToChange(key: string) { - if (this.hasRecord) { - this.store._notificationManager.notify(this.identifier, 'relationships', key); - } - } - - notifyStateChange(key?: string) { - if (this.hasRecord) { - this.store._notificationManager.notify(this.identifier, 'state'); - } - if (!key || key === 'isDeletionCommitted') { - this.store.recordArrayManager.recordDidChange(this.identifier); - } - } - - rollbackAttributes() { - this.store._backburner.join(() => { - let dirtyKeys = this._recordData.rollbackAttributes(); - - let record = this.store._instanceCache.peek({ identifier: this.identifier, bucket: 'record' }); - if (record && isDSModel(record)) { - record.errors.clear(); - } - - if (this.hasRecord && dirtyKeys && dirtyKeys.length > 0) { - this.notifyAttributes(dirtyKeys); - } - }); - } - - removeFromInverseRelationships() { - if (this.hasRecordData) { - this.store._backburner.join(() => { - this._recordData.removeFromInverseRelationships(); - }); - } - } - /* When a find request is triggered on the store, the user can optionally pass in attributes and relationships to be preloaded. These are meant to behave as if they @@ -394,9 +237,10 @@ export default class InternalModel { preloadData(preload) { let jsonPayload: JsonApiResource = {}; //TODO(Igor) consider the polymorphic case + const modelClass = this.store.modelFor(this.identifier.type); Object.keys(preload).forEach((key) => { let preloadValue = preload[key]; - let relationshipMeta = this.modelClass.metaForProperty(key); + let relationshipMeta = modelClass.metaForProperty(key); if (relationshipMeta.isRelationship) { if (!jsonPayload.relationships) { jsonPayload.relationships = {}; @@ -413,14 +257,15 @@ export default class InternalModel { } _preloadRelationship(key, preloadValue) { - let relationshipMeta = this.modelClass.metaForProperty(key); - let modelClass = relationshipMeta.type; + const modelClass = this.store.modelFor(this.identifier.type); + const relationshipMeta = modelClass.metaForProperty(key); + const relatedModelClass = relationshipMeta.type; let data; if (relationshipMeta.kind === 'hasMany') { assert('You need to pass in an array to set a hasMany property on a record', Array.isArray(preloadValue)); - data = preloadValue.map((value) => this._convertPreloadRelationshipToJSON(value, modelClass)); + data = preloadValue.map((value) => this._convertPreloadRelationshipToJSON(value, relatedModelClass)); } else { - data = this._convertPreloadRelationshipToJSON(preloadValue, modelClass); + data = this._convertPreloadRelationshipToJSON(preloadValue, relatedModelClass); } return { data }; } @@ -439,46 +284,6 @@ export default class InternalModel { return { type: internalModel.modelName, id: internalModel.id }; } - /* - * calling `InstanceCache.setRecordId` is necessary to update - * the cache index for this record if we have changed. - * - * However, since the store is not aware of whether the update - * is from us (via user set) or from a push of new data - * it will also call us so that we can notify and update state. - * - * When it does so it calls with `fromCache` so that we can - * short-circuit instead of cycling back. - * - * This differs from the short-circuit in the `_isUpdatingId` - * case in that the the cache can originate the call to setId, - * so on first entry we will still need to do our own update. - */ - setId(id: string | null, fromCache: boolean = false) { - if (this._isUpdatingId === true) { - return; - } - this._isUpdatingId = true; - let didChange = id !== this._id; - this._id = id; - - if (didChange && id !== null) { - if (!fromCache) { - this.store._instanceCache.setRecordId(this.modelName, id, this.clientId); - } - // internal set of ID to get it to RecordData from DS.Model - // if we are within create we may not have a recordData yet. - if (this.hasRecordData && this._recordData.__setId) { - this._recordData.__setId(id); - } - } - - if (didChange && this.hasRecord) { - this.store._notificationManager.notify(this.identifier, 'identity'); - } - this._isUpdatingId = false; - } - // FOR USE DURING COMMIT PROCESS adapterDidInvalidate(error: Error & { errors?: JsonApiValidationError[]; isAdapterError?: true; code?: string }) { if (error && error.isAdapterError === true && error.code === 'InvalidError') { diff --git a/packages/store/addon/-private/managers/record-data-store-wrapper.ts b/packages/store/addon/-private/managers/record-data-store-wrapper.ts index 161728c5924..d79ec469410 100644 --- a/packages/store/addon/-private/managers/record-data-store-wrapper.ts +++ b/packages/store/addon/-private/managers/record-data-store-wrapper.ts @@ -12,7 +12,6 @@ import type { import type { RecordDataStoreWrapper as StoreWrapper } from '@ember-data/types/q/record-data-store-wrapper'; import type { IdentifierCache } from '../caches/identifier-cache'; -import { internalModelFactoryFor } from '../caches/internal-model-factory'; import type Store from '../store-service'; import constructResource from '../utils/construct-resource'; @@ -86,19 +85,11 @@ export default class RecordDataStoreWrapper implements StoreWrapper { let pending = this._pendingNotifies; this._pendingNotifies = new Map(); this._willNotify = false; - const factory = internalModelFactoryFor(this._store); pending.forEach((map, identifier) => { - const internalModel = factory.peek(identifier); - if (internalModel) { - map.forEach((kind, key) => { - if (kind === 'belongsTo') { - internalModel.notifyBelongsToChange(key); - } else { - internalModel.notifyHasManyChange(key); - } - }); - } + map.forEach((kind, key) => { + this._store._notificationManager.notify(identifier, 'relationships', key); + }); }); } @@ -152,11 +143,8 @@ export default class RecordDataStoreWrapper implements StoreWrapper { notifyPropertyChange(type: string, id: string | null, lid: string | null | undefined, key?: string): void { const resource = constructResource(type, id, lid); const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); - let internalModel = internalModelFactoryFor(this._store).peek(identifier); - if (internalModel) { - internalModel.notifyAttributes(key ? [key] : []); - } + this._store._notificationManager.notify(identifier, 'attributes', key); } notifyHasManyChange(type: string, id: string | null, lid: string, key: string): void; @@ -181,10 +169,10 @@ export default class RecordDataStoreWrapper implements StoreWrapper { notifyStateChange(type: string, id: string | null, lid: string | null, key?: string): void { const resource = constructResource(type, id, lid); const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); - let internalModel = internalModelFactoryFor(this._store).peek(identifier); - if (internalModel) { - internalModel.notifyStateChange(key); + this._store._notificationManager.notify(identifier, 'state'); + if (!key || key === 'isDeletionCommitted') { + this._store.recordArrayManager.recordDidChange(identifier); } } @@ -231,9 +219,6 @@ export default class RecordDataStoreWrapper implements StoreWrapper { graph.remove(identifier); } } - let internalModel = internalModelFactoryFor(this._store).peek(identifier); - if (internalModel) { - internalModel.destroyFromRecordData(); - } + this._store._instanceCache.destroyRecord(identifier); } } diff --git a/packages/store/addon/-private/network/snapshot.ts b/packages/store/addon/-private/network/snapshot.ts index 2e21747c55b..acd699461af 100644 --- a/packages/store/addon/-private/network/snapshot.ts +++ b/packages/store/addon/-private/network/snapshot.ts @@ -2,7 +2,6 @@ @module @ember-data/store */ import { assert, deprecate } from '@ember/debug'; -import { get } from '@ember/object'; import { importSync } from '@embroider/macros'; @@ -160,10 +159,12 @@ export default class Snapshot implements Snapshot { let attributes = (this.__attributes = Object.create(null)); let attrs = Object.keys(this._store.getSchemaDefinitionService().attributesDefinitionFor(this.identifier)); let recordData = this._store._instanceCache.getRecordData(this.identifier); + const modelClass = this._store.modelFor(this.identifier.type); + const isDSModel = schemaIsDSModel(modelClass); attrs.forEach((keyName) => { - if (schemaIsDSModel(this._internalModel.modelClass)) { + if (isDSModel) { // if the schema is for a DSModel then the instance is too - attributes[keyName] = get(record as DSModel, keyName); + attributes[keyName] = record[keyName]; } else { attributes[keyName] = recordData.getAttr(keyName); } @@ -566,7 +567,7 @@ if (DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS) { since: { available: '4.5.0', enabled: '4.5.0' }, } ); - return this._internalModel.modelClass; + return this._store.modelFor(this.identifier.type); }, }); } diff --git a/packages/store/addon/-private/store-service.ts b/packages/store/addon/-private/store-service.ts index ca5c464ed30..11f635383b1 100644 --- a/packages/store/addon/-private/store-service.ts +++ b/packages/store/addon/-private/store-service.ts @@ -289,6 +289,7 @@ class Store extends Service { let modelName = identifier.type; let store = this; + let recordData = this._instanceCache.recordDataFor(identifier); let internalModel = this._instanceCache._internalModelForResource(identifier); let createOptions: any = { _internalModel: internalModel, @@ -298,7 +299,7 @@ class Store extends Service { _secretInit: (record: RecordInstance): void => { setRecordIdentifier(record, identifier); StoreMap.set(record, store); - setRecordDataFor(record, internalModel._recordData); + setRecordDataFor(record, recordData); }, container: null, // necessary hack for setOwner? }; @@ -496,15 +497,16 @@ class Store extends Service { // it is unlikely we need both an outer join and the inner run // of our own queue this._backburner.join(() => { - let identifier = peekRecordIdentifier(record); + const identifier = peekRecordIdentifier(record); if (identifier) { const internalModel = internalModelFactoryFor(this).peek(identifier); if (internalModel) { run(() => { const backburner = this._backburner; backburner.run(() => { - if (internalModel._recordData.setIsDeleted) { - internalModel._recordData.setIsDeleted(true); + const recordData = this._instanceCache.peek({ identifier, bucket: 'recordData' }); + if (recordData?.setIsDeleted) { + recordData.setIsDeleted(true); } if (internalModel.isNew()) { @@ -2147,12 +2149,16 @@ class Store extends Service { return resolve(record); } - internalModel.adapterWillCommit(); + + const recordData = this._instanceCache.getRecordData(identifier); + recordData.willCommit(); + if (isDSModel(record)) { + record.errors.clear(); + } if (!options) { options = {}; } - let recordData = this._instanceCache.getRecordData(identifier); let operation: 'createRecord' | 'deleteRecord' | 'updateRecord' = 'updateRecord'; // TODO handle missing isNew @@ -2186,20 +2192,21 @@ class Store extends Service { let data = payload && payload.data; if (!data) { assert( - `Your ${internalModel.modelName} record was saved to the server, but the response does not have an id and no id has been set client side. Records must have ids. Please update the server response to provide an id in the response or generate the id on the client side either before saving the record or while normalizing the response.`, - internalModel.id + `Your ${identifier.type} record was saved to the server, but the response does not have an id and no id has been set client side. Records must have ids. Please update the server response to provide an id in the response or generate the id on the client side either before saving the record or while normalizing the response.`, + identifier.id ); } const cache = this.identifierCache; + let actualIdentifier = identifier; if (operation !== 'deleteRecord' && data) { - cache.updateRecordIdentifier(identifier, data); + actualIdentifier = cache.updateRecordIdentifier(identifier, data); } //We first make sure the primary data has been updated - //TODO try to move notification to the user to the end of the runloop - internalModel._recordData.didCommit(data); - this.recordArrayManager.recordDidChange(internalModel.identifier); + const recordData = this._instanceCache.getRecordData(actualIdentifier); + recordData.didCommit(data); + this.recordArrayManager.recordDidChange(actualIdentifier); if (payload && payload.included) { this._push({ data: null, included: payload.included }); @@ -2228,13 +2235,13 @@ class Store extends Service { * @public * @param modelName * @param id - * @param clientId + * @param lid * @param storeWrapper */ createRecordDataFor( modelName: string, id: string | null, - clientId: string, + lid: string, storeWrapper: RecordDataStoreWrapper ): RecordData { if (HAS_RECORD_DATA_PACKAGE) { @@ -2252,7 +2259,7 @@ class Store extends Service { let identifier = this.identifierCache.getOrCreateRecordIdentifier({ type: modelName, id, - lid: clientId, + lid, }); return new _RecordData(identifier, storeWrapper); } @@ -2528,3 +2535,13 @@ export function assertIdentifierHasId( ): asserts identifier is StableExistingRecordIdentifier { assert(`Attempted to schedule a fetch for a record without an id.`, identifier.id !== null); } + +function isDSModel(record: RecordInstance | null): record is DSModel { + return ( + HAS_MODEL_PACKAGE && + !!record && + 'constructor' in record && + 'isModel' in record.constructor && + record.constructor.isModel === true + ); +} diff --git a/packages/unpublished-adapter-encapsulation-test-app/app/services/store.js b/packages/unpublished-adapter-encapsulation-test-app/app/services/store.js index c61976949e5..33ced4b2c4b 100644 --- a/packages/unpublished-adapter-encapsulation-test-app/app/services/store.js +++ b/packages/unpublished-adapter-encapsulation-test-app/app/services/store.js @@ -2,11 +2,11 @@ import { RecordData } from '@ember-data/record-data/-private'; import Store from '@ember-data/store'; export default class DefaultStore extends Store { - createRecordDataFor(modelName, id, clientId, storeWrapper) { + createRecordDataFor(modelName, id, lid, storeWrapper) { let identifier = this.identifierCache.getOrCreateRecordIdentifier({ type: modelName, id, - lid: clientId, + lid, }); return new RecordData(identifier, storeWrapper); } diff --git a/packages/unpublished-serializer-encapsulation-test-app/app/services/store.js b/packages/unpublished-serializer-encapsulation-test-app/app/services/store.js index c61976949e5..33ced4b2c4b 100644 --- a/packages/unpublished-serializer-encapsulation-test-app/app/services/store.js +++ b/packages/unpublished-serializer-encapsulation-test-app/app/services/store.js @@ -2,11 +2,11 @@ import { RecordData } from '@ember-data/record-data/-private'; import Store from '@ember-data/store'; export default class DefaultStore extends Store { - createRecordDataFor(modelName, id, clientId, storeWrapper) { + createRecordDataFor(modelName, id, lid, storeWrapper) { let identifier = this.identifierCache.getOrCreateRecordIdentifier({ type: modelName, id, - lid: clientId, + lid, }); return new RecordData(identifier, storeWrapper); } From 19494057813cc6d55b5e9198e2f581389164c32d Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 1 Aug 2022 15:43:24 -0700 Subject: [PATCH 08/29] even more trim --- .../addon/-private/caches/instance-cache.ts | 65 ++++++++++++++++++- .../legacy-model-support/internal-model.ts | 63 +----------------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index 8d2a722b421..11163b2f764 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -24,8 +24,9 @@ import coerceId, { ensureStringId } from '../utils/coerce-id'; import constructResource from '../utils/construct-resource'; import normalizeModelName from '../utils/normalize-model-name'; import WeakCache from '../utils/weak-cache'; -import { internalModelFactoryFor, setRecordIdentifier } from './internal-model-factory'; +import { internalModelFactoryFor, recordIdentifierFor, setRecordIdentifier } from './internal-model-factory'; import recordDataFor, { setRecordDataFor } from './record-data-for'; +import { JsonApiResource } from '@ember-data/types/q/record-data-json-api'; const RECORD_REFERENCES = new WeakCache(DEBUG ? 'reference' : ''); export const StoreMap = new WeakCache(DEBUG ? 'store' : ''); @@ -133,7 +134,7 @@ export class InstanceCache { if (options.preload) { this.store._backburner.join(() => { - internalModel.preloadData(options.preload); + preloadData(this.store, identifier, options.preload); }); } @@ -449,3 +450,63 @@ function extractRecordDataFromRecord(recordOrPromiseRecord: PromiseProxyRecord | function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is PromiseProxyRecord { return !!record.then; } + + /* + When a find request is triggered on the store, the user can optionally pass in + attributes and relationships to be preloaded. These are meant to behave as if they + came back from the server, except the user obtained them out of band and is informing + the store of their existence. The most common use case is for supporting client side + nested URLs, such as `/posts/1/comments/2` so the user can do + `store.findRecord('comment', 2, { preload: { post: 1 } })` without having to fetch the post. + + Preloaded data can be attributes and relationships passed in either as IDs or as actual + models. + */ +function preloadData(store: Store, identifier: StableRecordIdentifier, preload) { + let jsonPayload: JsonApiResource = {}; + //TODO(Igor) consider the polymorphic case + const modelClass = store.modelFor(identifier.type); + Object.keys(preload).forEach((key) => { + let preloadValue = preload[key]; + let relationshipMeta = modelClass.metaForProperty(key); + if (relationshipMeta.isRelationship) { + if (!jsonPayload.relationships) { + jsonPayload.relationships = {}; + } + jsonPayload.relationships[key] = preloadRelationship(modelClass, key, preloadValue); + } else { + if (!jsonPayload.attributes) { + jsonPayload.attributes = {}; + } + jsonPayload.attributes[key] = preloadValue; + } + }); + store._instanceCache.recordDataFor(identifier).pushData(jsonPayload); +} + + +function preloadRelationship(schema, key: string, preloadValue) { + const relationshipMeta = schema.metaForProperty(key); + const relatedType = relationshipMeta.type; + let data; + if (relationshipMeta.kind === 'hasMany') { + assert('You need to pass in an array to set a hasMany property on a record', Array.isArray(preloadValue)); + data = preloadValue.map((value) => _convertPreloadRelationshipToJSON(value, relatedType)); + } else { + data = _convertPreloadRelationshipToJSON(preloadValue, relatedType); + } + return { data }; +} + +/* + findRecord('user', '1', { preload: { friends: ['1'] }}); + findRecord('user', '1', { preload: { friends: [record] }}); +*/ +function _convertPreloadRelationshipToJSON(value, type: string) { + if (typeof value === 'string' || typeof value === 'number') { + return { type, id: value }; + } + // TODO if not a record instance assert it's an identifier + // and allow identifiers to be used + return recordIdentifierFor(value); +} diff --git a/packages/store/addon/-private/legacy-model-support/internal-model.ts b/packages/store/addon/-private/legacy-model-support/internal-model.ts index 5cc42f051fd..5990eef7dc3 100644 --- a/packages/store/addon/-private/legacy-model-support/internal-model.ts +++ b/packages/store/addon/-private/legacy-model-support/internal-model.ts @@ -6,7 +6,7 @@ import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-ser import type { RecordData } from '@ember-data/types/q/record-data'; import type { JsonApiResource, JsonApiValidationError } from '@ember-data/types/q/record-data-json-api'; -import { internalModelFactoryFor } from '../caches/internal-model-factory'; +import { internalModelFactoryFor, recordIdentifierFor } from '../caches/internal-model-factory'; import type Store from '../store-service'; import type ShimModelClass from './shim-model-class'; @@ -223,67 +223,6 @@ export default class InternalModel { this._isDestroyed = true; } - /* - When a find request is triggered on the store, the user can optionally pass in - attributes and relationships to be preloaded. These are meant to behave as if they - came back from the server, except the user obtained them out of band and is informing - the store of their existence. The most common use case is for supporting client side - nested URLs, such as `/posts/1/comments/2` so the user can do - `store.findRecord('comment', 2, { preload: { post: 1 } })` without having to fetch the post. - - Preloaded data can be attributes and relationships passed in either as IDs or as actual - models. - */ - preloadData(preload) { - let jsonPayload: JsonApiResource = {}; - //TODO(Igor) consider the polymorphic case - const modelClass = this.store.modelFor(this.identifier.type); - Object.keys(preload).forEach((key) => { - let preloadValue = preload[key]; - let relationshipMeta = modelClass.metaForProperty(key); - if (relationshipMeta.isRelationship) { - if (!jsonPayload.relationships) { - jsonPayload.relationships = {}; - } - jsonPayload.relationships[key] = this._preloadRelationship(key, preloadValue); - } else { - if (!jsonPayload.attributes) { - jsonPayload.attributes = {}; - } - jsonPayload.attributes[key] = preloadValue; - } - }); - this._recordData.pushData(jsonPayload); - } - - _preloadRelationship(key, preloadValue) { - const modelClass = this.store.modelFor(this.identifier.type); - const relationshipMeta = modelClass.metaForProperty(key); - const relatedModelClass = relationshipMeta.type; - let data; - if (relationshipMeta.kind === 'hasMany') { - assert('You need to pass in an array to set a hasMany property on a record', Array.isArray(preloadValue)); - data = preloadValue.map((value) => this._convertPreloadRelationshipToJSON(value, relatedModelClass)); - } else { - data = this._convertPreloadRelationshipToJSON(preloadValue, relatedModelClass); - } - return { data }; - } - - _convertPreloadRelationshipToJSON(value, modelClass) { - if (typeof value === 'string' || typeof value === 'number') { - return { type: modelClass, id: value }; - } - let internalModel; - if (value._internalModel) { - internalModel = value._internalModel; - } else { - internalModel = value; - } - // TODO IGOR DAVID assert if no id is present - return { type: internalModel.modelName, id: internalModel.id }; - } - // FOR USE DURING COMMIT PROCESS adapterDidInvalidate(error: Error & { errors?: JsonApiValidationError[]; isAdapterError?: true; code?: string }) { if (error && error.isAdapterError === true && error.code === 'InvalidError') { From b7283408dd668d735482d700428a9275c0a0c105 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Thu, 4 Aug 2022 22:05:49 -0700 Subject: [PATCH 09/29] still a lot to do, but starting to take shape --- .../node-tests/fixtures/expected.js | 1 - packages/model/addon/-private/attr.js | 4 +- packages/model/addon/-private/belongs-to.js | 4 +- packages/model/addon/-private/has-many.js | 4 +- .../model/addon/-private/legacy-data-fetch.js | 16 +- .../-private/legacy-relationships-support.ts | 49 +-- packages/model/addon/-private/many-array.ts | 11 +- packages/model/addon/-private/model.js | 47 +-- .../addon/-private/references/belongs-to.ts | 2 +- .../addon/-private/references/has-many.ts | 6 +- .../record-data/addon/-private/graph/index.ts | 4 +- .../record-data/addon/-private/record-data.ts | 21 +- .../addon/-private/caches/instance-cache.ts | 276 +++++++++++--- .../-private/caches/internal-model-factory.ts | 348 ------------------ .../legacy-model-support/internal-model.ts | 235 +----------- .../-private/managers/record-array-manager.ts | 5 +- .../managers/record-data-store-wrapper.ts | 22 +- .../addon/-private/network/fetch-manager.ts | 18 +- .../store/addon/-private/network/snapshot.ts | 33 +- .../store/addon/-private/store-service.ts | 162 +++++--- 20 files changed, 451 insertions(+), 817 deletions(-) delete mode 100644 packages/store/addon/-private/caches/internal-model-factory.ts diff --git a/packages/-ember-data/node-tests/fixtures/expected.js b/packages/-ember-data/node-tests/fixtures/expected.js index 688d966e8b9..cdd46eaad7d 100644 --- a/packages/-ember-data/node-tests/fixtures/expected.js +++ b/packages/-ember-data/node-tests/fixtures/expected.js @@ -39,7 +39,6 @@ module.exports = { '(private) @ember-data/debug InspectorDataAdapter#watchTypeIfUnseen', '(private) @ember-data/model Model#_createSnapshot', '(private) @ember-data/model Model#_debugInfo', - '(private) @ember-data/model Model#_internalModel', '(private) @ember-data/model Model#_notifyProperties', '(private) @ember-data/model Model#create', '(private) @ember-data/model Model#currentState', diff --git a/packages/model/addon/-private/attr.js b/packages/model/addon/-private/attr.js index acfeb3ce57f..ec53d9e57db 100644 --- a/packages/model/addon/-private/attr.js +++ b/packages/model/addon/-private/attr.js @@ -130,7 +130,7 @@ function attr(type, options) { return computed({ get(key) { if (DEBUG) { - if (['_internalModel', 'currentState'].indexOf(key) !== -1) { + if (['currentState'].indexOf(key) !== -1) { throw new Error( `'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your attr on ${this.constructor.toString()}` ); @@ -145,7 +145,7 @@ function attr(type, options) { }, set(key, value) { if (DEBUG) { - if (['_internalModel', 'currentState'].indexOf(key) !== -1) { + if (['currentState'].indexOf(key) !== -1) { throw new Error( `'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your attr on ${this.constructor.toString()}` ); diff --git a/packages/model/addon/-private/belongs-to.js b/packages/model/addon/-private/belongs-to.js index c501ad3cd89..06491f59f46 100644 --- a/packages/model/addon/-private/belongs-to.js +++ b/packages/model/addon/-private/belongs-to.js @@ -146,7 +146,7 @@ function belongsTo(modelName, options) { const support = LEGACY_SUPPORT.lookup(this); if (DEBUG) { - if (['_internalModel', 'recordData', 'currentState'].indexOf(key) !== -1) { + if (['currentState'].indexOf(key) !== -1) { throw new Error( `'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your belongsTo on ${this.constructor.toString()}` ); @@ -177,7 +177,7 @@ function belongsTo(modelName, options) { set(key, value) { const support = LEGACY_SUPPORT.lookup(this); if (DEBUG) { - if (['_internalModel', 'recordData', 'currentState'].indexOf(key) !== -1) { + if (['currentState'].indexOf(key) !== -1) { throw new Error( `'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your belongsTo on ${this.constructor.toString()}` ); diff --git a/packages/model/addon/-private/has-many.js b/packages/model/addon/-private/has-many.js index 8b2ef4a1580..8277b1e226b 100644 --- a/packages/model/addon/-private/has-many.js +++ b/packages/model/addon/-private/has-many.js @@ -183,7 +183,7 @@ function hasMany(type, options) { return computed({ get(key) { if (DEBUG) { - if (['_internalModel', 'currentState'].indexOf(key) !== -1) { + if (['currentState'].indexOf(key) !== -1) { throw new Error( `'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your hasMany on ${this.constructor.toString()}` ); @@ -193,7 +193,7 @@ function hasMany(type, options) { }, set(key, records) { if (DEBUG) { - if (['_internalModel', 'currentState'].indexOf(key) !== -1) { + if (['currentState'].indexOf(key) !== -1) { throw new Error( `'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your hasMany on ${this.constructor.toString()}` ); diff --git a/packages/model/addon/-private/legacy-data-fetch.js b/packages/model/addon/-private/legacy-data-fetch.js index f9eea8f6c45..f47ac0e28ff 100644 --- a/packages/model/addon/-private/legacy-data-fetch.js +++ b/packages/model/addon/-private/legacy-data-fetch.js @@ -8,6 +8,7 @@ import { DEPRECATE_RSVP_PROMISE } from '@ember-data/private-build-infra/deprecat import { iterateData, normalizeResponseHelper } from './legacy-data-utils'; export function _findHasMany(adapter, store, identifier, link, relationship, options) { + const record = store._instanceCache.getRecord(identifier); const snapshot = store._instanceCache.createSnapshot(identifier, options); let modelClass = store.modelFor(relationship.type); let useLink = !link || typeof link === 'string'; @@ -18,7 +19,7 @@ export function _findHasMany(adapter, store, identifier, link, relationship, opt promise = guardDestroyedStore(promise, store, label); promise = promise.then( (adapterPayload) => { - if (!_objectIsAlive(store._instanceCache.getInternalModel(identifier))) { + if (!_objectIsAlive(record)) { if (DEPRECATE_RSVP_PROMISE) { deprecate( `A Promise for fetching ${relationship.type} did not resolve by the time your model was destroyed. This will error in a future release.`, @@ -57,13 +58,14 @@ export function _findHasMany(adapter, store, identifier, link, relationship, opt ); if (DEPRECATE_RSVP_PROMISE) { - promise = _guard(promise, _bind(_objectIsAlive, store._instanceCache.getInternalModel(identifier))); + promise = _guard(promise, _bind(_objectIsAlive, record)); } return promise; } export function _findBelongsTo(store, identifier, link, relationship, options) { + const record = store._instanceCache.getRecord(identifier); let adapter = store.adapterFor(identifier.type); assert(`You tried to load a belongsTo relationship but you have no adapter (for ${identifier.type})`, adapter); @@ -79,11 +81,11 @@ export function _findBelongsTo(store, identifier, link, relationship, options) { let label = `DS: Handle Adapter#findBelongsTo of ${identifier.type} : ${relationship.type}`; promise = guardDestroyedStore(promise, store, label); - promise = _guard(promise, _bind(_objectIsAlive, store._instanceCache.getInternalModel(identifier))); + promise = _guard(promise, _bind(_objectIsAlive, record)); promise = promise.then( (adapterPayload) => { - if (!_objectIsAlive(store._instanceCache.getInternalModel(identifier))) { + if (!_objectIsAlive(record)) { if (DEPRECATE_RSVP_PROMISE) { deprecate( `A Promise for fetching ${relationship.type} did not resolve by the time your model was destroyed. This will error in a future release.`, @@ -123,7 +125,7 @@ export function _findBelongsTo(store, identifier, link, relationship, options) { ); if (DEPRECATE_RSVP_PROMISE) { - promise = _guard(promise, _bind(_objectIsAlive, store._instanceCache.getInternalModel(identifier))); + promise = _guard(promise, _bind(_objectIsAlive, record)); } return promise; @@ -229,8 +231,8 @@ function ensureRelationshipIsSetToParent(payload, parentIdentifier, store, paren } } -function getInverse(store, parentInternalModel, parentRelationship, type) { - return recordDataFindInverseRelationshipInfo(store, parentInternalModel, parentRelationship, type); +function getInverse(store, parentIdentifier, parentRelationship, type) { + return recordDataFindInverseRelationshipInfo(store, parentIdentifier, parentRelationship, type); } function recordDataFindInverseRelationshipInfo(store, parentIdentifier, parentRelationship, type) { diff --git a/packages/model/addon/-private/legacy-relationships-support.ts b/packages/model/addon/-private/legacy-relationships-support.ts index e3549748c92..03109c90790 100644 --- a/packages/model/addon/-private/legacy-relationships-support.ts +++ b/packages/model/addon/-private/legacy-relationships-support.ts @@ -141,7 +141,7 @@ export class LegacySupport { `You looked up the '${key}' relationship on a '${identifier.type}' with id ${ identifier.id || 'null' } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (\`belongsTo({ async: true })\`)`, - toReturn === null || !store._instanceCache.getInternalModel(relatedIdentifier).isEmpty + toReturn === null || !store._instanceCache.peek({ identifier: relatedIdentifier, bucket: 'recordData' })?.isEmpty?.() ); return toReturn; } @@ -447,7 +447,7 @@ export class LegacySupport { return resolve(null); } - const internalModel = resource.data ? this.store._instanceCache._internalModelForResource(resource.data) : null; + const identifier = resource.data ? this.store.identifierCache.getOrCreateRecordIdentifier(resource.data) : null; let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = resource._relationship .state as RelationshipState; @@ -458,9 +458,9 @@ export class LegacySupport { resource.links.related && (shouldForceReload || hasDematerializedInverse || isStale || (!allInverseRecordsAreLoaded && !isEmpty)); - if (internalModel) { + if (identifier) { // short circuit if we are already loading - let pendingRequest = this.store._fetchManager.getPendingFetch(internalModel.identifier, options); + let pendingRequest = this.store._fetchManager.getPendingFetch(identifier, options); if (pendingRequest) { return pendingRequest; } @@ -485,22 +485,21 @@ export class LegacySupport { return resolve(null); } - if (!internalModel) { - assert(`No InternalModel found for ${resource.lid}`, internalModel); + if (!identifier) { + assert(`No Information found for ${resource.lid}`, identifier); } - return this.store._instanceCache._fetchDataIfNeededForIdentifier(internalModel.identifier, options); + return this.store._instanceCache._fetchDataIfNeededForIdentifier(identifier, options); } let resourceIsLocal = !localDataIsEmpty && resource.data.id === null; - if (internalModel && resourceIsLocal) { - return resolve(internalModel.identifier); + if (identifier && resourceIsLocal) { + return resolve(identifier); } // fetch by data - if (internalModel && !localDataIsEmpty) { - let identifier = internalModel.identifier; + if (identifier && !localDataIsEmpty) { assertIdentifierHasId(identifier); return this.store._fetchManager.scheduleFetch(identifier, options); @@ -513,7 +512,7 @@ export class LegacySupport { destroy() { assert( - 'Cannot destroy an internalModel while its record is materialized', + 'Cannot destroy record while it is materialized', !this.record || this.record.isDestroyed || this.record.isDestroying ); this.isDestroying = true; @@ -625,7 +624,14 @@ function assertRecordsPassedToHasMany(records: RecordInstance[]) { .map((r) => `${typeof r}`) .join(', ')}`, (function () { - return records.every((record) => Object.prototype.hasOwnProperty.call(record, '_internalModel') === true); + return records.every((record) => { + try { + recordIdentifierFor(record); + return true; + } catch { + return false; + } + }); })() ); } @@ -659,9 +665,10 @@ function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is function anyUnloaded(store: Store, relationship: ManyRelationship) { let state = relationship.currentState; + const cache = store._instanceCache; const unloaded = state.find((s) => { - let im = store._instanceCache.getInternalModel(s); - return im._isDematerializing || !im.isLoaded; + let isLoaded = cache.recordIsLoaded(s); + return !isLoaded; }); return unloaded || false; @@ -684,7 +691,7 @@ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship) // treat as collection // check for unloaded records let hasEmptyRecords = resource.data.reduce((hasEmptyModel, resourceIdentifier) => { - return hasEmptyModel || internalModelForRelatedResource(store, cache, resourceIdentifier).isEmpty; + return hasEmptyModel || isEmpty(store, cache, resourceIdentifier); }, false); return !hasEmptyRecords; @@ -693,17 +700,17 @@ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship) if (!resource.data) { return true; } else { - const internalModel = internalModelForRelatedResource(store, cache, resource.data); - return !internalModel.isEmpty; + return !isEmpty(store, cache, resource.data); } } } -function internalModelForRelatedResource( +function isEmpty( store: Store, cache: IdentifierCache, resource: ResourceIdentifierObject -): InternalModel { +): boolean { const identifier = cache.getOrCreateRecordIdentifier(resource); - return store._instanceCache._internalModelForResource(identifier); + const recordData = store._instanceCache.peek({ identifier, bucket: 'recordData' }); + return !recordData || !!recordData.isEmpty?.(); } diff --git a/packages/model/addon/-private/many-array.ts b/packages/model/addon/-private/many-array.ts index 6850021063e..6a07137378a 100644 --- a/packages/model/addon/-private/many-array.ts +++ b/packages/model/addon/-private/many-array.ts @@ -306,16 +306,19 @@ export default class ManyArray extends MutableArrayWithObject { - let internalModel = this.store._instanceCache._internalModelForResource(identifier); - return internalModel.isLoaded === true; + return this.store._instanceCache.recordIsLoaded(identifier) === true; }); } diff --git a/packages/record-data/addon/-private/graph/index.ts b/packages/record-data/addon/-private/graph/index.ts index 66b2f8f596f..7ce5ba4fcca 100644 --- a/packages/record-data/addon/-private/graph/index.ts +++ b/packages/record-data/addon/-private/graph/index.ts @@ -382,9 +382,7 @@ export class Graph { // If the inverse is sync, unloading this record is treated as a client-side // delete, so we remove the inverse records from this relationship to // disconnect the graph. Because it's not async, we don't need to keep around -// the internalModel as an id-wrapper for references and because the graph is -// disconnected we can actually destroy the internalModel when checking for -// orphaned models. +// the identifier as an id-wrapper for references function destroyRelationship(rel) { if (isImplicit(rel)) { if (rel.graph.isReleasable(rel.identifier)) { diff --git a/packages/record-data/addon/-private/record-data.ts b/packages/record-data/addon/-private/record-data.ts index 3ca6bdd4ee9..6a06c3c48eb 100644 --- a/packages/record-data/addon/-private/record-data.ts +++ b/packages/record-data/addon/-private/record-data.ts @@ -47,7 +47,6 @@ export default class RecordDataDefault implements RelationshipRecordData { declare modelName: string; declare lid: string; declare identifier: StableRecordIdentifier; - declare id: string | null; declare isDestroyed: boolean; declare _isNew: boolean; declare _bfsId: number; @@ -62,7 +61,6 @@ export default class RecordDataDefault implements RelationshipRecordData { constructor(identifier: RecordIdentifier, storeWrapper: RecordDataStoreWrapper) { this.modelName = identifier.type; this.lid = identifier.lid; - this.id = identifier.id; this.identifier = identifier; this.storeWrapper = storeWrapper; @@ -75,6 +73,10 @@ export default class RecordDataDefault implements RelationshipRecordData { this.reset(); } + get id() { + return this.identifier.id; + } + // PUBLIC API getResourceIdentifier(): StableRecordIdentifier { return this.identifier; @@ -104,12 +106,6 @@ export default class RecordDataDefault implements RelationshipRecordData { this._setupRelationships(data); } - if (data.id) { - if (!this.id) { - this.id = coerceId(data.id); - } - } - if (changedKeys && changedKeys.length) { this._notifyAttributes(changedKeys); } @@ -306,7 +302,6 @@ export default class RecordDataDefault implements RelationshipRecordData { if (data.id) { // didCommit provided an ID, notify the store of it this.storeWrapper.setRecordId(this.modelName, data.id, this.lid); - this.id = coerceId(data.id); } if (data.relationships) { this._setupRelationships(data); @@ -416,13 +411,6 @@ export default class RecordDataDefault implements RelationshipRecordData { } } - // internal set coming from the model - __setId(id: string) { - if (this.id !== id) { - this.id = id; - } - } - getAttr(key: string): string { if (key in this._attributes) { return this._attributes[key]; @@ -641,7 +629,6 @@ export default class RecordDataDefault implements RelationshipRecordData { let propertyValue = options[name]; if (name === 'id') { - this.id = propertyValue; continue; } diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index 11163b2f764..e6ea37cbf28 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -1,9 +1,9 @@ -import { assert } from '@ember/debug'; +import { assert, warn } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; import { resolve } from 'rsvp'; -import type { ExistingResourceObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api'; +import type { ExistingResourceObject, NewResourceIdentifierObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api'; import type { RecordIdentifier, StableExistingRecordIdentifier, @@ -27,6 +27,8 @@ import WeakCache from '../utils/weak-cache'; import { internalModelFactoryFor, recordIdentifierFor, setRecordIdentifier } from './internal-model-factory'; import recordDataFor, { setRecordDataFor } from './record-data-for'; import { JsonApiResource } from '@ember-data/types/q/record-data-json-api'; +import { Dict } from '@ember-data/types/q/utils'; +import { isStableIdentifier } from './identifier-cache'; const RECORD_REFERENCES = new WeakCache(DEBUG ? 'reference' : ''); export const StoreMap = new WeakCache(DEBUG ? 'store' : ''); @@ -45,17 +47,44 @@ type Caches = { record: WeakMap; recordData: WeakMap; }; + + export class InstanceCache { declare store: Store; declare _storeWrapper: RecordDataStoreWrapper; + declare peekList: Dict>; + #instances: Caches = { record: new WeakMap(), recordData: new WeakMap(), }; + recordIsLoaded(identifier: StableRecordIdentifier) { + const recordData = this.peek({ identifier, bucket: 'recordData' }); + if (!recordData) { + return false; + } + const isNew = recordData.isNew?.() || false; + const isEmpty = recordData.isEmpty?.() || false; + + // if we are new we must consider ourselves loaded + if (isNew) { + return true; + } + // even if we have a past request, if we are now empty we are not loaded + // typically this is true after an unloadRecord call + + // if we are not empty, not new && we have a fulfilled request then we are loaded + // we should consider allowing for something to be loaded that is simply "not empty". + // which is how RecordState currently handles this case; however, RecordState is buggy + // in that it does not account for unloading. + return !isEmpty + } + constructor(store: Store) { this.store = store; + this.peekList = Object.create(null); this._storeWrapper = new RecordDataStoreWrapper(this.store); this.__recordDataFor = this.__recordDataFor.bind(this); @@ -63,6 +92,84 @@ export class InstanceCache { RECORD_REFERENCES._generator = (identifier) => { return new RecordReference(this.store, identifier); }; + + store.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => { + let intendedIdentifier = identifier; + if (identifier.id !== matchedIdentifier.id) { + intendedIdentifier = 'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier; + } else if (identifier.type !== matchedIdentifier.type) { + intendedIdentifier = + 'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier; + } + let altIdentifier = identifier === intendedIdentifier ? matchedIdentifier : identifier; + + // check for duplicate entities + let imHasRecord = this.#instances.record.has(intendedIdentifier); + let otherHasRecord = this.#instances.record.has(altIdentifier); + let imRecordData = this.#instances.recordData.get(intendedIdentifier) || null; + let otherRecordData = this.#instances.record.get(altIdentifier) || null; + + // we cannot merge entities when both have records + // (this may not be strictly true, we could probably swap the recordData the record points at) + if (imHasRecord && otherHasRecord) { + // TODO we probably don't need to throw these errors anymore + // once InternalModel is fully removed, as we can just "swap" + // what data source the abandoned record points at so long as + // it itself is not retained by the store in any way. + if ('id' in resourceData) { + throw new Error( + `Failed to update the 'id' for the RecordIdentifier '${identifier.type}:${identifier.id} (${identifier.lid})' to '${resourceData.id}', because that id is already in use by '${matchedIdentifier.type}:${matchedIdentifier.id} (${matchedIdentifier.lid})'` + ); + } + // TODO @runspired determine when this is even possible + assert( + `Failed to update the RecordIdentifier '${identifier.type}:${identifier.id} (${identifier.lid})' to merge with the detected duplicate identifier '${matchedIdentifier.type}:${matchedIdentifier.id} (${matchedIdentifier.lid})'` + ); + } + + // remove "other" from cache + if (otherHasRecord) { + cache.delete(altIdentifier); + this.peekList[altIdentifier.type]?.delete(altIdentifier); + } + + if (imRecordData === null && otherRecordData === null) { + // nothing more to do + return intendedIdentifier; + + // only the other has a RecordData + // OR only the other has a Record + } else if ((imRecordData === null && otherRecordData !== null) || (imRecordData && !imHasRecord && otherRecordData && otherHasRecord)) { + if (imRecordData) { + // TODO check if we are retained in any async relationships + cache.delete(intendedIdentifier); + this.peekList[intendedIdentifier.type]?.delete(intendedIdentifier); + // im.destroy(); + } + im = otherIm!; + // TODO do we need to notify the id change? + im.identifier = intendedIdentifier; + cache.set(intendedIdentifier, im); + this.peekList[intendedIdentifier.type] = this.peekList[intendedIdentifier.type] || new Set(); + this.peekList[intendedIdentifier.type]!.add(intendedIdentifier); + + // just use im + } else { + // otherIm.destroy(); + } + + /* + TODO @runspired consider adding this to make polymorphism even nicer + if (HAS_RECORD_DATA_PACKAGE) { + if (identifier.type !== matchedIdentifier.type) { + const graphFor = importSync('@ember-data/record-data/-private').graphFor; + graphFor(this).registerPolymorphicType(identifier.type, matchedIdentifier.type); + } + } + */ + + return intendedIdentifier; + }); } peek({ identifier, bucket }: { identifier: StableRecordIdentifier; bucket: 'record' }): RecordInstance | undefined; peek({ identifier, bucket }: { identifier: StableRecordIdentifier; bucket: 'recordData' }): RecordData | undefined; @@ -87,28 +194,13 @@ export class InstanceCache { this.#instances[bucket].set(identifier, value); } + getRecord(identifier: StableRecordIdentifier, properties?: CreateRecordProperties): RecordInstance { let record = this.peek({ identifier, bucket: 'record' }); if (!record) { - // TODO store this state somewhere better - const internalModel = this.getInternalModel(identifier); - - if (internalModel._isDematerializing) { - // TODO this should be an assertion, this likely means - // we have a bug to find wherein our own store is calling this - // with an identifier that should have already been disconnected. - // the destroy + fetch again case is likely either preserving the - // identifier for re-use or failing to unload it. - return null as unknown as RecordInstance; - } - - // TODO store this state somewhere better - internalModel.hasRecord = true; - if (properties && 'id' in properties) { assert(`expected id to be a string or null`, properties.id !== undefined); - internalModel.setId(properties.id); } record = this._instantiateRecord(this.getRecordData(identifier), identifier, properties); @@ -118,6 +210,27 @@ export class InstanceCache { return record; } + clear(type?: string) { + if (type === undefined) { + let keys = Object.keys(this.peekList); + keys.forEach((key) => this.clear(key)); + } else { + let identifiers = this.peekList[type]; + if (identifiers) { + identifiers.forEach((identifier) => { + // TODO we rely on not removing the main cache + // and only removing the peekList cache apparently. + // we should figure out this duality and codify whatever + // signal it is actually trying to give us. + // this.cache.delete(identifier); + this.peekList[identifier.type]!.delete(identifier); + this.unloadRecord(identifier); + // TODO we don't remove the identifier, should we? + }); + } + } + } + getReference(identifier: StableRecordIdentifier) { return RECORD_REFERENCES.lookup(identifier); } @@ -235,6 +348,8 @@ export class InstanceCache { recordData = this._createRecordData(identifier); this.#instances.recordData.set(identifier, recordData); this.getInternalModel(identifier).hasRecordData = true; + this.peekList[identifier.type] = this.peekList[identifier.type] || new Set(); + this.peekList[identifier.type]!.add(identifier); } return recordData; @@ -251,7 +366,7 @@ export class InstanceCache { __recordDataFor(resource: RecordIdentifier) { const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource); - return this.recordDataFor(identifier); + return this.getRecordData(identifier); } // TODO move this to InstanceCache @@ -270,13 +385,60 @@ export class InstanceCache { return recordData; } - // TODO string candidate for early elimination - _internalModelForResource(resource: ResourceIdentifierObject): InternalModel { - return internalModelFactoryFor(this.store).getByResource(resource); + setRecordId(modelName: string, newId: string, lid: string) { + const resource = constructResource(normalizeModelName(modelName), null, coerceId(lid)); + const maybeIdentifier = this.store.identifierCache.peekRecordIdentifier(resource); + + if (maybeIdentifier) { + // TODO handle consequences of identifier merge for notifications + this._setRecordId(modelName, newId, lid); + this.store._notificationManager.notify(maybeIdentifier, 'identity'); + } } - setRecordId(modelName: string, newId: string, lid: string) { - internalModelFactoryFor(this.store).setRecordId(modelName, newId, lid); + _setRecordId(type: string, id: string, lid: string) { + const resource: NewResourceIdentifierObject = { type, id: null, lid }; + const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource); + + let oldId = identifier.id; + let modelName = identifier.type; + + // ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record) + assert( + `'${modelName}' was saved to the server, but the response does not have an id and your record does not either.`, + !(id === null && oldId === null) + ); + + // ID absolutely can't be different than oldID if oldID is not null + // TODO this assertion and restriction may not strictly be needed in the identifiers world + assert( + `Cannot update the id for '${modelName}:${lid}' from '${oldId}' to '${id}'.`, + !(oldId !== null && id !== oldId) + ); + + // ID can be null if oldID is not null (altered ID in response for a record) + // however, this is more than likely a developer error. + if (oldId !== null && id === null) { + warn( + `Your ${modelName} record was saved to the server, but the response does not have an id.`, + !(oldId !== null && id === null) + ); + return; + } + + let existingIdentifier = this.store.identifierCache.peekRecordIdentifier({ type: modelName, id }); + + assert( + `'${modelName}' was saved to the server, but the response returned the new id '${id}', which has already been used with another record.'`, + existingIdentifier === identifier + ); + + if (identifier.id === null) { + // TODO potentially this needs to handle merged result + this.store.identifierCache.updateRecordIdentifier(identifier, { type, id }); + } + + // TODO update recordData if needed ? } _load(data: ExistingResourceObject): StableExistingRecordIdentifier { @@ -301,7 +463,7 @@ export class InstanceCache { // findRecord will be from root.loading // this cannot be loading state if we do not already have an identifier // all else will be updates - const isLoading = internalModel.isLoading || (!internalModel.isLoaded && maybeIdentifier); + const isLoading = internalModel.isLoading || (maybeIdentifier && !this.recordIsLoaded(maybeIdentifier)); const isUpdate = internalModel.isEmpty === false && !isLoading; // exclude store.push (root.empty) case @@ -320,12 +482,13 @@ export class InstanceCache { } } - if (internalModel.isNew()) { + const recordData = this.getRecordData(identifier); + if (recordData.isNew?.()) { this.store._notificationManager.notify(identifier, 'identity'); } const hasRecord = this.#instances.record.has(identifier); - this.getRecordData(identifier).pushData(data, hasRecord); + recordData.pushData(data, hasRecord); if (!isUpdate) { this.store.recordArrayManager.recordDidChange(identifier); @@ -341,34 +504,36 @@ export class InstanceCache { !record || record.isDestroyed || record.isDestroying ); - const factory = internalModelFactoryFor(this.store); + this.peekList[identifier.type]!.delete(identifier); + this.store.identifierCache.forgetRecordIdentifier(identifier); + } - let internalModel = factory.peek(identifier); - if (internalModel) { - internalModel.isDestroying = true; - factory.remove(internalModel); - internalModel._isDestroyed = true; + unloadRecord(identifier: StableRecordIdentifier) { + if (DEBUG) { + const requests = this.store.getRequestStateService().getPendingRequestsForRecord(identifier); + if ( + requests.some((req) => { + return req.type === 'mutation'; + }) + ) { + assert('You can only unload a record which is not inFlight. `' + identifier + '`'); + } } - } + const internalModel = this.getInternalModel(identifier); - recordDataFor(identifier: StableRecordIdentifier | { type: string }, isCreate?: boolean): RecordData { - let recordData: RecordData; - // TODO remove isCreate arg @deprecate if needed - if (isCreate === true) { - // TODO remove once InternalModel is no longer essential to internal state - // and just build a new identifier directly - let internalModel = internalModelFactoryFor(this.store).build({ type: identifier.type, id: null }); - let stableIdentifier = internalModel.identifier; - recordData = this.getRecordData(stableIdentifier); - recordData.clientDidCreate(); - this.store.recordArrayManager.recordDidChange(stableIdentifier); - } else { - // TODO remove once InternalModel is no longer essential to internal state - internalModelFactoryFor(this.store).lookup(identifier as StableRecordIdentifier); - recordData = this.getRecordData(identifier as StableRecordIdentifier); + // this has to occur before the internal model is removed + // for legacy compat. + this.store._instanceCache.removeRecord(identifier); + const recordData = this.#instances.recordData.get(identifier); + + if (recordData) { + // TODO is this join still necessary? + this.store._backburner.join(() => { + recordData.unloadRecord(); + }); } - return recordData; + this.store.recordArrayManager.recordDidChange(identifier); } } @@ -420,7 +585,14 @@ function assertRecordsPassedToHasMany(records: RecordInstance[]) { .map((r) => `${typeof r}`) .join(', ')}`, (function () { - return records.every((record) => Object.prototype.hasOwnProperty.call(record, '_internalModel') === true); + return records.every((record) => { + try { + recordIdentifierFor(record); + return true; + } catch { + return false; + } + }); })() ); } @@ -481,7 +653,7 @@ function preloadData(store: Store, identifier: StableRecordIdentifier, preload) jsonPayload.attributes[key] = preloadValue; } }); - store._instanceCache.recordDataFor(identifier).pushData(jsonPayload); + store._instanceCache.getRecordData(identifier).pushData(jsonPayload); } diff --git a/packages/store/addon/-private/caches/internal-model-factory.ts b/packages/store/addon/-private/caches/internal-model-factory.ts deleted file mode 100644 index 616264d1343..00000000000 --- a/packages/store/addon/-private/caches/internal-model-factory.ts +++ /dev/null @@ -1,348 +0,0 @@ -import { assert, warn } from '@ember/debug'; -import { DEBUG } from '@glimmer/env'; - -import type { - ExistingResourceObject, - NewResourceIdentifierObject, - ResourceIdentifierObject, -} from '@ember-data/types/q/ember-data-json-api'; -import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; -import type { RecordData } from '@ember-data/types/q/record-data'; -import type { RecordInstance } from '@ember-data/types/q/record-instance'; -import { Dict } from '@ember-data/types/q/utils'; - -import InternalModel from '../legacy-model-support/internal-model'; -import type Store from '../store-service'; -import constructResource from '../utils/construct-resource'; -import WeakCache from '../utils/weak-cache'; -import type { IdentifierCache } from './identifier-cache'; - -/** - @module @ember-data/store -*/ -const FactoryCache = new WeakCache(DEBUG ? 'internal-model-factory' : ''); -FactoryCache._generator = (store: Store) => { - return new InternalModelFactory(store); -}; -type NewResourceInfo = { type: string; id: string | null }; - -const RecordCache = new WeakCache(DEBUG ? 'identifier' : ''); -if (DEBUG) { - RecordCache._expectMsg = (key: RecordInstance | RecordData) => - `${String(key)} is not a record instantiated by @ember-data/store`; -} - -export function peekRecordIdentifier(record: RecordInstance | RecordData): StableRecordIdentifier | undefined { - return RecordCache.get(record); -} - -/** - Retrieves the unique referentially-stable [RecordIdentifier](/ember-data/release/classes/StableRecordIdentifier) - assigned to the given record instance. - - ```js - import { recordIdentifierFor } from "@ember-data/store"; - - // ... gain access to a record, for instance with peekRecord or findRecord - const record = store.peekRecord("user", "1"); - - // get the identifier for the record (see docs for StableRecordIdentifier) - const identifier = recordIdentifierFor(record); - - // access the identifier's properties. - const { id, type, lid } = identifier; - ``` - - @method recordIdentifierFor - @public - @static - @for @ember-data/store - @param {Object} record a record instance previously obstained from the store. - @returns {StableRecordIdentifier} - */ -export function recordIdentifierFor(record: RecordInstance | RecordData): StableRecordIdentifier { - return RecordCache.getWithError(record); -} - -export function setRecordIdentifier(record: RecordInstance | RecordData, identifier: StableRecordIdentifier): void { - if (DEBUG && RecordCache.has(record) && RecordCache.get(record) !== identifier) { - throw new Error(`${record} was already assigned an identifier`); - } - - /* - It would be nice to do a reverse check here that an identifier has not - previously been assigned a record; however, unload + rematerialization - prevents us from having a great way of doing so when CustomRecordClasses - don't necessarily give us access to a `isDestroyed` for dematerialized - instance. - */ - - RecordCache.set(record, identifier); -} - -export function internalModelFactoryFor(store: Store): InternalModelFactory { - return FactoryCache.lookup(store); -} - -/** - * The InternalModelFactory handles the lifecyle of - * instantiating, caching, and destroying InternalModel - * instances. - * - * @class InternalModelFactory - * @internal - */ -export default class InternalModelFactory { - declare identifierCache: IdentifierCache; - declare store: Store; - declare cache: Map; - declare peekList: Dict>; - - constructor(store: Store) { - this.cache = new Map(); - this.peekList = Object.create(null); - this.store = store; - this.identifierCache = store.identifierCache; - this.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => { - let intendedIdentifier = identifier; - if (identifier.id !== matchedIdentifier.id) { - intendedIdentifier = 'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier; - } else if (identifier.type !== matchedIdentifier.type) { - intendedIdentifier = - 'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier; - } - let altIdentifier = identifier === intendedIdentifier ? matchedIdentifier : identifier; - - // check for duplicate InternalModel's - const cache = this.cache; - let im = cache.get(intendedIdentifier); - let otherIm = cache.get(altIdentifier); - - // we cannot merge internalModels when both have records - // (this may not be strictly true, we could probably swap the internalModel the record points at) - if (im && otherIm && im.hasRecord && otherIm.hasRecord) { - // TODO we probably don't need to throw these errors anymore - // once InternalModel is fully removed, as we can just "swap" - // what data source the abandoned record points at so long as - // it itself is not retained by the store in any way. - if ('id' in resourceData) { - throw new Error( - `Failed to update the 'id' for the RecordIdentifier '${identifier.type}:${identifier.id} (${identifier.lid})' to '${resourceData.id}', because that id is already in use by '${matchedIdentifier.type}:${matchedIdentifier.id} (${matchedIdentifier.lid})'` - ); - } - // TODO @runspired determine when this is even possible - assert( - `Failed to update the RecordIdentifier '${identifier.type}:${identifier.id} (${identifier.lid})' to merge with the detected duplicate identifier '${matchedIdentifier.type}:${matchedIdentifier.id} (${matchedIdentifier.lid})'` - ); - } - - // remove otherIm from cache - if (otherIm) { - cache.delete(altIdentifier); - this.peekList[altIdentifier.type]?.delete(altIdentifier); - } - - if (im === null && otherIm === null) { - // nothing more to do - return intendedIdentifier; - - // only the other has an InternalModel - // OR only the other has a Record - } else if ((im === null && otherIm !== null) || (im && !im.hasRecord && otherIm && otherIm.hasRecord)) { - if (im) { - // TODO check if we are retained in any async relationships - cache.delete(intendedIdentifier); - this.peekList[intendedIdentifier.type]?.delete(intendedIdentifier); - // im.destroy(); - } - im = otherIm!; - // TODO do we need to notify the id change? - im._id = intendedIdentifier.id; - im.identifier = intendedIdentifier; - cache.set(intendedIdentifier, im); - this.peekList[intendedIdentifier.type] = this.peekList[intendedIdentifier.type] || new Set(); - this.peekList[intendedIdentifier.type]!.add(intendedIdentifier); - - // just use im - } else { - // otherIm.destroy(); - } - - /* - TODO @runspired consider adding this to make polymorphism even nicer - if (HAS_RECORD_DATA_PACKAGE) { - if (identifier.type !== matchedIdentifier.type) { - const graphFor = importSync('@ember-data/record-data/-private').graphFor; - graphFor(this).registerPolymorphicType(identifier.type, matchedIdentifier.type); - } - } - */ - - return intendedIdentifier; - }); - } - - /** - * Retrieve the InternalModel for a given { type, id, lid }. - * - * If an InternalModel does not exist, it instantiates one. - * - * If an InternalModel does exist bus has a scheduled destroy, - * the scheduled destroy will be cancelled. - * - * @method lookup - * @private - */ - lookup(resource: ResourceIdentifierObject, data?: ExistingResourceObject): InternalModel { - if (data !== undefined) { - // if we've been given data associated with this lookup - // we must first give secondary-caches for LIDs the - // opportunity to populate based on it - this.identifierCache.getOrCreateRecordIdentifier(data); - } - - const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); - const internalModel = this.peek(identifier); - - return internalModel || this._build(identifier, false); - } - - /** - * Peek the InternalModel for a given { type, id, lid }. - * - * If an InternalModel does not exist, return `null`. - * - * @method peek - * @private - */ - peek(identifier: StableRecordIdentifier): InternalModel | null { - return this.cache.get(identifier) || null; - } - - getByResource(resource: ResourceIdentifierObject): InternalModel { - const normalizedResource = constructResource(resource); - - return this.lookup(normalizedResource); - } - - setRecordId(type: string, id: string, lid: string) { - const resource: NewResourceIdentifierObject = { type, id: null, lid }; - const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); - const internalModel = this.peek(identifier); - - if (internalModel === null) { - throw new Error(`Cannot set the id ${id} on the record ${type}:${lid} as there is no such record in the cache.`); - } - - let oldId = internalModel.id; - let modelName = internalModel.modelName; - - // ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record) - assert( - `'${modelName}' was saved to the server, but the response does not have an id and your record does not either.`, - !(id === null && oldId === null) - ); - - // ID absolutely can't be different than oldID if oldID is not null - // TODO this assertion and restriction may not strictly be needed in the identifiers world - assert( - `Cannot update the id for '${modelName}:${lid}' from '${oldId}' to '${id}'.`, - !(oldId !== null && id !== oldId) - ); - - // ID can be null if oldID is not null (altered ID in response for a record) - // however, this is more than likely a developer error. - if (oldId !== null && id === null) { - warn( - `Your ${modelName} record was saved to the server, but the response does not have an id.`, - !(oldId !== null && id === null) - ); - return; - } - - let existingInternalModel = this.peekById(modelName, id); - - assert( - `'${modelName}' was saved to the server, but the response returned the new id '${id}', which has already been used with another record.'`, - !existingInternalModel || existingInternalModel === internalModel - ); - - if (identifier.id === null) { - // TODO potentially this needs to handle merged result - this.identifierCache.updateRecordIdentifier(identifier, { type, id }); - } - - internalModel.setId(id, true); - } - - peekById(type: string, id: string): InternalModel | null { - const identifier = this.identifierCache.peekRecordIdentifier({ type, id }); - return identifier ? this.cache.get(identifier) || null : null; - } - - build(newResourceInfo: NewResourceInfo): InternalModel { - return this._build(newResourceInfo, true); - } - - _build(resource: StableRecordIdentifier, isCreate: false): InternalModel; - _build(resource: NewResourceInfo, isCreate: true): InternalModel; - _build(resource: StableRecordIdentifier | NewResourceInfo, isCreate: boolean = false): InternalModel { - if (isCreate === true && resource.id) { - let existingInternalModel = this.peekById(resource.type, resource.id); - - assert( - `The id ${resource.id} has already been used with another '${resource.type}' record.`, - !existingInternalModel - ); - } - - const { identifierCache } = this; - let identifier: StableRecordIdentifier; - - if (isCreate === true) { - identifier = identifierCache.createIdentifierForNewRecord(resource); - } else { - identifier = resource as StableRecordIdentifier; - } - - // lookupFactory should really return an object that creates - // instances with the injections applied - let internalModel = new InternalModel(this.store, identifier); - this.cache.set(identifier, internalModel); - this.peekList[identifier.type] = this.peekList[identifier.type] || new Set(); - this.peekList[identifier.type]!.add(identifier); - - return internalModel; - } - - remove(internalModel: InternalModel): void { - const { identifier } = internalModel; - this.cache.delete(identifier); - this.peekList[identifier.type]!.delete(identifier); - - this.identifierCache.forgetRecordIdentifier(identifier); - } - - clear(type?: string) { - if (type === undefined) { - let keys = Object.keys(this.peekList); - keys.forEach((key) => this.clear(key)); - } else { - let identifiers = this.peekList[type]; - if (identifiers) { - identifiers.forEach((identifier) => { - let internalModel = this.peek(identifier); - - // TODO we rely on not removing the main cache - // and only removing the peekList cache apparently. - // we should figure out this duality and codify whatever - // signal it is actually trying to give us. - // this.cache.delete(identifier); - this.peekList[identifier.type]!.delete(identifier); - internalModel!.unloadRecord(); - // TODO we don't remove the identifier, should we? - }); - } - } - } -} diff --git a/packages/store/addon/-private/legacy-model-support/internal-model.ts b/packages/store/addon/-private/legacy-model-support/internal-model.ts index 5990eef7dc3..e305b8ad9e3 100644 --- a/packages/store/addon/-private/legacy-model-support/internal-model.ts +++ b/packages/store/addon/-private/legacy-model-support/internal-model.ts @@ -1,30 +1,11 @@ -import { assert } from '@ember/debug'; -import { DEBUG } from '@glimmer/env'; - import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; -import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-serializer-interface'; import type { RecordData } from '@ember-data/types/q/record-data'; -import type { JsonApiResource, JsonApiValidationError } from '@ember-data/types/q/record-data-json-api'; -import { internalModelFactoryFor, recordIdentifierFor } from '../caches/internal-model-factory'; import type Store from '../store-service'; -import type ShimModelClass from './shim-model-class'; - -/** - @module @ember-data/store -*/ - -type AdapterErrors = Error & { errors?: unknown[]; isAdapterError?: true; code?: string }; -type SerializerWithParseErrors = MinimumSerializerInterface & { - extractErrors?(store: Store, modelClass: ShimModelClass, error: AdapterErrors, recordId: string | null): any; -}; export default class InternalModel { - declare _id: string | null; - declare modelName: string; declare hasRecordData: boolean; declare _isDestroyed: boolean; - declare _isDematerializing: boolean; declare isDestroying: boolean; declare _isUpdatingId: boolean; @@ -36,80 +17,13 @@ export default class InternalModel { constructor(store: Store, identifier: StableRecordIdentifier) { this.store = store; this.identifier = identifier; - this._id = identifier.id; this._isUpdatingId = false; - this.modelName = identifier.type; - this.hasRecord = false; this.hasRecordData = false; this._isDestroyed = false; - - // During dematerialization we don't want to rematerialize the record. The - // reason this might happen is that dematerialization removes records from - // record arrays, and Ember arrays will always `objectAt(0)` and - // `objectAt(len - 1)` to test whether or not `firstObject` or `lastObject` - // have changed. - this._isDematerializing = false; - } - - get id(): string | null { - return this.identifier.id; - } - set id(value: string | null) { - if (value !== this._id) { - let newIdentifier = { type: this.identifier.type, lid: this.identifier.lid, id: value }; - // TODO potentially this needs to handle merged result - this.store.identifierCache.updateRecordIdentifier(this.identifier, newIdentifier); - if (this.hasRecord) { - // TODO this should likely *mostly* be the a different bucket - this.store._notificationManager.notify(this.identifier, 'property', 'id'); - } - } - } - - /* - * calling `InstanceCache.setRecordId` is necessary to update - * the cache index for this record if we have changed. - * - * However, since the store is not aware of whether the update - * is from us (via user set) or from a push of new data - * it will also call us so that we can notify and update state. - * - * When it does so it calls with `fromCache` so that we can - * short-circuit instead of cycling back. - * - * This differs from the short-circuit in the `_isUpdatingId` - * case in that the the cache can originate the call to setId, - * so on first entry we will still need to do our own update. - */ - setId(id: string | null, fromCache: boolean = false) { - if (this._isUpdatingId === true) { - return; - } - this._isUpdatingId = true; - let didChange = id !== this._id; - this._id = id; - - if (didChange && id !== null) { - if (!fromCache) { - this.store._instanceCache.setRecordId(this.modelName, id, this.identifier.lid); - } - // internal set of ID to get it to RecordData from DS.Model - // if we are within create we may not have a recordData yet. - if (this.hasRecordData && this._recordData.__setId) { - this._recordData.__setId(id); - } - } - - if (didChange && this.hasRecord) { - this.store._notificationManager.notify(this.identifier, 'identity'); - } - this._isUpdatingId = false; } - - // STATE we end up needing for various reasons get _recordData(): RecordData { return this.store._instanceCache.getRecordData(this.identifier); @@ -132,160 +46,35 @@ export default class InternalModel { } get isEmpty(): boolean { - return !this.hasRecordData || ((!this.isNew() || this.isDeleted()) && this._recordData.isEmpty?.()) || false; + if (!this.hasRecordData) { + return true; + } + const recordData = this._recordData; + const isNew = recordData.isNew?.() || false; + const isDeleted = recordData.isDeleted?.() || false; + const isEmpty = recordData.isEmpty?.() || false; + + return (!isNew || isDeleted) && isEmpty; } get isLoading() { const req = this.store.getRequestStateService(); const { identifier } = this; // const fulfilled = req.getLastRequestForRecord(identifier); + const isLoaded = this.store._instanceCache.recordIsLoaded(identifier); return ( - !this.isLoaded && + !isLoaded && // fulfilled === null && req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query') ); } - get isLoaded() { - // if we are new we must consider ourselves loaded - if (this.isNew()) { - return true; - } - // even if we have a past request, if we are now empty we are not loaded - // typically this is true after an unloadRecord call - - // if we are not empty, not new && we have a fulfilled request then we are loaded - // we should consider allowing for something to be loaded that is simply "not empty". - // which is how RecordState currently handles this case; however, RecordState is buggy - // in that it does not account for unloading. - return !this.isEmpty; - } - get isDestroyed(): boolean { return this._isDestroyed; } - /* - Unload the record for this internal model. This will cause the record to be - destroyed and freed up for garbage collection. It will also do a check - for cleaning up internal models. - - This check is performed by first computing the set of related internal - models. If all records in this set are unloaded, then the entire set is - destroyed. Otherwise, nothing in the set is destroyed. - - This means that this internal model will be freed up for garbage collection - once all models that refer to it via some relationship are also unloaded. - */ - unloadRecord() { - if (this.isDestroyed) { - return; - } - if (DEBUG) { - const requests = this.store.getRequestStateService().getPendingRequestsForRecord(this.identifier); - if ( - requests.some((req) => { - return req.type === 'mutation'; - }) - ) { - assert('You can only unload a record which is not inFlight. `' + this + '`'); - } - } - this._isDematerializing = true; - - // this has to occur before the internal model is removed - // for legacy compat. - const { identifier } = this; - this.store._instanceCache.removeRecord(identifier); - - // move to an empty never-loaded state - // ensure any record notifications happen prior to us - // unseting the record but after we've triggered - // destroy - this.store._backburner.join(() => { - this._recordData.unloadRecord(); - }); - - this.hasRecord = false; // this must occur after relationship removal - this.store.recordArrayManager.recordDidChange(this.identifier); - } - - destroy() { - let record = this.store._instanceCache.peek({ identifier: this.identifier, bucket: 'record' }); - assert( - 'Cannot destroy an internalModel while its record is materialized', - !record || record.isDestroyed || record.isDestroying - ); - this.isDestroying = true; - - internalModelFactoryFor(this.store).remove(this); - this._isDestroyed = true; - } - - // FOR USE DURING COMMIT PROCESS - adapterDidInvalidate(error: Error & { errors?: JsonApiValidationError[]; isAdapterError?: true; code?: string }) { - if (error && error.isAdapterError === true && error.code === 'InvalidError') { - let serializer = this.store.serializerFor(this.modelName) as SerializerWithParseErrors; - - // TODO @deprecate extractErrors being called - // TODO remove extractErrors from the default serializers. - if (serializer && typeof serializer.extractErrors === 'function') { - let errorsHash = serializer.extractErrors(this.store, this.store.modelFor(this.modelName), error, this.id); - error.errors = errorsHashToArray(errorsHash); - } - } - - if (error.errors) { - assert( - `Expected the RecordData implementation for ${this.identifier} to have a getErrors(identifier) method for retreiving errors.`, - typeof this._recordData.getErrors === 'function' - ); - - let jsonApiErrors: JsonApiValidationError[] = error.errors; - if (jsonApiErrors.length === 0) { - jsonApiErrors = [{ title: 'Invalid Error', detail: '', source: { pointer: '/data' } }]; - } - this._recordData.commitWasRejected(this.identifier, jsonApiErrors); - } else { - this._recordData.commitWasRejected(this.identifier); - } - } - toString() { - return `<${this.modelName}:${this.id}>`; - } -} - -function makeArray(value) { - return Array.isArray(value) ? value : [value]; -} - -const PRIMARY_ATTRIBUTE_KEY = 'base'; - -function errorsHashToArray(errors): JsonApiValidationError[] { - const out: JsonApiValidationError[] = []; - - if (errors) { - Object.keys(errors).forEach((key) => { - let messages = makeArray(errors[key]); - for (let i = 0; i < messages.length; i++) { - let title = 'Invalid Attribute'; - let pointer = `/data/attributes/${key}`; - if (key === PRIMARY_ATTRIBUTE_KEY) { - title = 'Invalid Document'; - pointer = `/data`; - } - out.push({ - title: title, - detail: messages[i], - source: { - pointer: pointer, - }, - }); - } - }); + return `<${this.identifier.type}:${this.identifier.id}>`; } - - return out; } diff --git a/packages/store/addon/-private/managers/record-array-manager.ts b/packages/store/addon/-private/managers/record-array-manager.ts index b6463de6b21..60ed00f313b 100644 --- a/packages/store/addon/-private/managers/record-array-manager.ts +++ b/packages/store/addon/-private/managers/record-array-manager.ts @@ -13,7 +13,6 @@ import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { Dict } from '@ember-data/types/q/utils'; import { isHiddenFromRecordArrays } from '../caches/instance-cache'; -import { internalModelFactoryFor } from '../caches/internal-model-factory'; import AdapterPopulatedRecordArray from '../record-arrays/adapter-populated-record-array'; import RecordArray from '../record-arrays/record-array'; import type Store from '../store-service'; @@ -120,7 +119,7 @@ class RecordArrayManager { return; } let hasNoPotentialDeletions = pending.length === 0; - let listSize = internalModelFactoryFor(this.store).peekList[modelName]?.size; + let listSize = this.store._instanceCache.peekList[modelName]?.size; let hasNoInsertionsOrRemovals = listSize === array.length; /* @@ -194,7 +193,7 @@ class RecordArrayManager { } _visibleIdentifiersByType(modelName: string) { - const list = internalModelFactoryFor(this.store).peekList[modelName]; + const list = this.store._instanceCache.peekList[modelName]; let all = list ? [...list.values()] : []; let visible: StableRecordIdentifier[] = []; for (let i = 0; i < all.length; i++) { diff --git a/packages/store/addon/-private/managers/record-data-store-wrapper.ts b/packages/store/addon/-private/managers/record-data-store-wrapper.ts index d79ec469410..cbf1621be91 100644 --- a/packages/store/addon/-private/managers/record-data-store-wrapper.ts +++ b/packages/store/addon/-private/managers/record-data-store-wrapper.ts @@ -180,17 +180,17 @@ export default class RecordDataStoreWrapper implements StoreWrapper { recordDataFor(type: string, id: string | null, lid: string): RecordData; recordDataFor(type: string): RecordData; recordDataFor(type: string, id?: string | null, lid?: string | null): RecordData { - let identifier: StableRecordIdentifier | { type: string }; - let isCreate: boolean = false; - if (!id && !lid) { - isCreate = true; - identifier = { type }; - } else { - const resource = constructResource(type, id, lid); - identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); - } - - return this._store._instanceCache.recordDataFor(identifier, isCreate); + // TODO @deprecate create capability. This is problematic because there's + // no outside association between this RecordData and an Identifier. It's + // likely a mistake but we said in an RFC we'd allow this. We should RFC + // enforcing someone to use the record-data and identifier-cache APIs to + // create a new identifier and then call clientDidCreate on the RecordData + // instead. + const identifier = !id && !lid ? + this.identifierCache.createIdentifierForNewRecord({ type: type }) : + this.identifierCache.getOrCreateRecordIdentifier(constructResource(type, id, lid)); + + return this._store._instanceCache.getRecordData(identifier); } setRecordId(type: string, id: string, lid: string) { diff --git a/packages/store/addon/-private/network/fetch-manager.ts b/packages/store/addon/-private/network/fetch-manager.ts index b581322cf13..dc831ffb5e1 100644 --- a/packages/store/addon/-private/network/fetch-manager.ts +++ b/packages/store/addon/-private/network/fetch-manager.ts @@ -126,10 +126,10 @@ export default class FetchManager { let adapter = this._store.adapterFor(identifier.type); let operation = options[SaveOp]; - let internalModel = snapshot._internalModel; let modelName = snapshot.modelName; let store = this._store; let modelClass = store.modelFor(modelName); + const record = store._instanceCache.getRecord(identifier); assert(`You tried to update a record but you have no adapter (for ${modelName})`, adapter); assert( @@ -139,16 +139,16 @@ export default class FetchManager { let promise = resolve().then(() => adapter[operation](store, modelClass, snapshot)); let serializer: SerializerWithParseErrors | null = store.serializerFor(modelName); - let label = `DS: Extract and notify about ${operation} completion of ${internalModel}`; + let label = `DS: Extract and notify about ${operation} completion of ${identifier}`; assert( `Your adapter's '${operation}' method must return a value, but it returned 'undefined'`, promise !== undefined ); - promise = _guard(guardDestroyedStore(promise, store, label), _bind(_objectIsAlive, internalModel)).then( + promise = _guard(guardDestroyedStore(promise, store, label), _bind(_objectIsAlive, record)).then( (adapterPayload) => { - if (!_objectIsAlive(internalModel)) { + if (!_objectIsAlive(record)) { if (DEPRECATE_RSVP_PROMISE) { deprecate( `A Promise while saving ${modelName} did not resolve by the time your model was destroyed. This will error in a future release.`, @@ -239,11 +239,8 @@ export default class FetchManager { } let resolverPromise = resolver.promise; - - // TODO replace with some form of record state cache const store = this._store; - const internalModel = store._instanceCache.getInternalModel(identifier); - const isLoading = !internalModel.isLoaded; // we don't use isLoading directly because we are the request + const isLoading = !store._instanceCache.recordIsLoaded(identifier); // we don't use isLoading directly because we are the request const promise = resolverPromise.then( (payload) => { @@ -262,8 +259,9 @@ export default class FetchManager { return identifier; }, (error) => { - if (internalModel.isEmpty || isLoading) { - internalModel.unloadRecord(); + const recordData = store._instanceCache.peek({ identifier, bucket: 'recordData' }); + if (!recordData || recordData.isEmpty?.() || isLoading) { + store._instanceCache.unloadRecord(identifier); } throw error; } diff --git a/packages/store/addon/-private/network/snapshot.ts b/packages/store/addon/-private/network/snapshot.ts index acd699461af..ee7a1595bbb 100644 --- a/packages/store/addon/-private/network/snapshot.ts +++ b/packages/store/addon/-private/network/snapshot.ts @@ -45,7 +45,6 @@ export default class Snapshot implements Snapshot { private _belongsToIds: Dict = Object.create(null); private _hasManyRelationships: Dict = Object.create(null); private _hasManyIds: Dict = Object.create(null); - declare _internalModel: InternalModel; declare _changedAttributes: ChangedAttributesHash; declare identifier: StableRecordIdentifier; @@ -63,7 +62,7 @@ export default class Snapshot implements Snapshot { * @param _store */ constructor(options: FindOptions, identifier: StableRecordIdentifier, private _store: Store) { - let internalModel = (this._internalModel = _store._instanceCache._internalModelForResource(identifier)); + const hasRecord = !!_store._instanceCache.peek({ identifier, bucket: 'record' }); this.modelName = identifier.type; /** @@ -82,7 +81,7 @@ export default class Snapshot implements Snapshot { in time" in which a snapshot is created, we greedily grab the values. */ - if (internalModel.hasRecord) { + if (hasRecord) { this._attributes; } @@ -128,7 +127,7 @@ export default class Snapshot implements Snapshot { @public */ this.modelName = identifier.type; - if (internalModel.hasRecord) { + if (hasRecord) { this._changedAttributes = this._store._instanceCache.getRecordData(identifier).changedAttributes(); } } @@ -183,7 +182,8 @@ export default class Snapshot implements Snapshot { */ get isNew(): boolean { - return this._internalModel.isNew(); + const recordData = this._store._instanceCache.peek({ identifier: this.identifier, bucket: 'recordData' }); + return recordData?.isNew?.() || false; } /** @@ -298,9 +298,8 @@ export default class Snapshot implements Snapshot { */ belongsTo(keyName: string, options?: { id?: boolean }): Snapshot | RecordId | undefined { let returnModeIsId = !!(options && options.id); - let inverseInternalModel: InternalModel | null; let result: Snapshot | RecordId | undefined; - let store = this._internalModel.store; + let store = this._store; if (returnModeIsId === true && keyName in this._belongsToIds) { return this._belongsToIds[keyName]; @@ -345,14 +344,14 @@ export default class Snapshot implements Snapshot { let value = relationship.getData(); let data = value && value.data; - inverseInternalModel = data ? store._instanceCache._internalModelForResource(data) : null; + let inverseIdentifier = data ? store.identifierCache.getOrCreateRecordIdentifier(data) : null; if (value && value.data !== undefined) { - if (inverseInternalModel && !inverseInternalModel.isDeleted()) { + if (inverseIdentifier && !store._instanceCache.getRecordData(inverseIdentifier).isDeleted?.()) { if (returnModeIsId) { - result = inverseInternalModel.id; + result = inverseIdentifier.id; } else { - result = store._instanceCache.createSnapshot(inverseInternalModel.identifier); + result = store._instanceCache.createSnapshot(inverseIdentifier); } } else { result = null; @@ -412,7 +411,7 @@ export default class Snapshot implements Snapshot { return cachedSnapshots; } - let store = this._internalModel.store; + let store = this._store; let relationshipMeta = store.getSchemaDefinitionService().relationshipsDefinitionFor({ type: this.modelName })[ keyName ]; @@ -449,14 +448,12 @@ export default class Snapshot implements Snapshot { if (value.data) { results = []; value.data.forEach((member) => { - let internalModel = store._instanceCache._internalModelForResource(member); - if (!internalModel.isDeleted()) { + let inverseIdentifier = store.identifierCache.getOrCreateRecordIdentifier(member); + if (!store._instanceCache.getRecordData(inverseIdentifier).isDeleted?.()) { if (returnModeIsIds) { - (results as RecordId[]).push( - (member as ExistingResourceIdentifierObject | NewResourceIdentifierObject).id || null - ); + (results as RecordId[]).push(inverseIdentifier.id); } else { - (results as Snapshot[]).push(store._instanceCache.createSnapshot(internalModel.identifier)); + (results as Snapshot[]).push(store._instanceCache.createSnapshot(inverseIdentifier)); } } }); diff --git a/packages/store/addon/-private/store-service.ts b/packages/store/addon/-private/store-service.ts index 11f635383b1..c593626f339 100644 --- a/packages/store/addon/-private/store-service.ts +++ b/packages/store/addon/-private/store-service.ts @@ -43,7 +43,6 @@ import edBackburner from './backburner'; import { IdentifierCache } from './caches/identifier-cache'; import { InstanceCache, recordDataIsFullyDeleted, storeFor, StoreMap } from './caches/instance-cache'; import { - internalModelFactoryFor, peekRecordIdentifier, recordIdentifierFor, setRecordIdentifier, @@ -66,6 +65,8 @@ import coerceId, { ensureStringId } from './utils/coerce-id'; import constructResource from './utils/construct-resource'; import normalizeModelName from './utils/normalize-model-name'; import promiseRecord from './utils/promise-record'; +import { JsonApiValidationError } from '@ember-data/types/q/record-data-json-api'; +import InternalModel from './legacy-model-support/internal-model'; export { storeFor }; @@ -289,10 +290,8 @@ class Store extends Service { let modelName = identifier.type; let store = this; - let recordData = this._instanceCache.recordDataFor(identifier); - let internalModel = this._instanceCache._internalModelForResource(identifier); + let recordData = this._instanceCache.getRecordData(identifier); let createOptions: any = { - _internalModel: internalModel, // TODO deprecate allowing unknown args setting _createProps: createRecordArgs, // TODO @deprecate consider deprecating accessing record properties during init which the below is necessary for @@ -459,13 +458,18 @@ class Store extends Service { // Coerce ID to a string properties.id = coerceId(properties.id); + const resource = { type: normalizedModelName, id: properties.id }; - const factory = internalModelFactoryFor(this); - const internalModel = factory.build({ type: normalizedModelName, id: properties.id }); - const { identifier } = internalModel; + if (resource.id) { + const identifier = this.identifierCache.peekRecordIdentifier(resource as ResourceIdentifierObject); - this._instanceCache.getRecordData(identifier).clientDidCreate(); - this.recordArrayManager.recordDidChange(identifier); + assert( + `The id ${properties.id} has already been used with another '${normalizeModelName}' record.`, + !identifier + ); + } + + const identifier = this.identifierCache.createIdentifierForNewRecord(resource); return this._instanceCache.getRecord(identifier, properties); }); @@ -499,24 +503,21 @@ class Store extends Service { this._backburner.join(() => { const identifier = peekRecordIdentifier(record); if (identifier) { - const internalModel = internalModelFactoryFor(this).peek(identifier); - if (internalModel) { - run(() => { - const backburner = this._backburner; - backburner.run(() => { - const recordData = this._instanceCache.peek({ identifier, bucket: 'recordData' }); + run(() => { + const backburner = this._backburner; + backburner.run(() => { + const recordData = this._instanceCache.peek({ identifier, bucket: 'recordData' }); + + if (recordData) { if (recordData?.setIsDeleted) { recordData.setIsDeleted(true); } - - if (internalModel.isNew()) { - // destroyRecord follows up deleteRecord with save(). This prevents an unecessary save for a new record - internalModel._deletedRecordWasNew = true; - internalModel.unloadRecord(); + if (recordData.isNew?.()) { + this._instanceCache.unloadRecord(identifier); } - }); + } }); - } + }); } }); } @@ -543,10 +544,7 @@ class Store extends Service { } let identifier = peekRecordIdentifier(record); if (identifier) { - let internalModel = internalModelFactoryFor(this).peek(identifier); - if (internalModel) { - internalModel.unloadRecord(); - } + this._instanceCache.unloadRecord(identifier); } } @@ -998,13 +996,12 @@ class Store extends Service { resource = constructResource(type, normalizedId); } - const internalModel = internalModelFactoryFor(this).lookup(resource); - const { identifier } = internalModel; + const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); let promise; options = options || {}; // if not loaded start loading - if (!internalModel.isLoaded) { + if (!this._instanceCache.recordIsLoaded(identifier)) { promise = this._instanceCache._fetchDataIfNeededForIdentifier(identifier, options); // Refetch if the reload option is passed @@ -1159,10 +1156,10 @@ class Store extends Service { peekRecord(identifier: ResourceIdentifierObject | string, id?: string | number): RecordInstance | null { if (arguments.length === 1 && isMaybeIdentifier(identifier)) { const stableIdentifier = this.identifierCache.peekRecordIdentifier(identifier); - const internalModel = stableIdentifier && internalModelFactoryFor(this).peek(stableIdentifier); + const isLoaded = stableIdentifier && this._instanceCache.recordIsLoaded(stableIdentifier); // TODO come up with a better mechanism for determining if we have data and could peek. // this is basically an "are we not empty" query. - return internalModel && internalModel.isLoaded ? this._instanceCache.getRecord(stableIdentifier) : null; + return isLoaded ? this._instanceCache.getRecord(stableIdentifier) : null; } if (DEBUG) { @@ -1179,9 +1176,9 @@ class Store extends Service { const normalizedId = ensureStringId(id); const resource = { type, id: normalizedId }; const stableIdentifier = this.identifierCache.peekRecordIdentifier(resource); - const internalModel = stableIdentifier && internalModelFactoryFor(this).peek(stableIdentifier); + const isLoaded = stableIdentifier && this._instanceCache.recordIsLoaded(stableIdentifier); - return internalModel && internalModel.isLoaded ? this._instanceCache.getRecord(stableIdentifier) : null; + return isLoaded ? this._instanceCache.getRecord(stableIdentifier) : null; } /** @@ -1226,9 +1223,7 @@ class Store extends Service { const resource = { type, id: trueId }; const identifier = this.identifierCache.peekRecordIdentifier(resource); - const internalModel = identifier && internalModelFactoryFor(this).peek(identifier); - - return !!internalModel && internalModel.isLoaded; + return Boolean(identifier && this._instanceCache.recordIsLoaded(identifier)); } assert(`store.hasRecordForId has been removed`); } @@ -1767,13 +1762,11 @@ class Store extends Service { !modelName || typeof modelName === 'string' ); - const factory = internalModelFactoryFor(this); - if (modelName === undefined) { - factory.clear(); + this._instanceCache.clear(); } else { let normalizedModelName = normalizeModelName(modelName); - factory.clear(normalizedModelName); + this._instanceCache.clear(normalizedModelName); } } @@ -1788,7 +1781,7 @@ class Store extends Service { @param {InternalModel} internalModel @param {Object} errors */ - recordWasInvalid(internalModel, parsedErrors, error) { + recordWasInvalid(internalModel: InternalModel, parsedErrors, error) { if (DEPRECATE_RECORD_WAS_INVALID) { deprecate( `The private API recordWasInvalid will be removed in an upcoming release. Use record.errors add/remove instead if the intent was to move the record into an invalid state manually.`, @@ -1806,7 +1799,9 @@ class Store extends Service { error = error || new Error(`unknown invalid error`); error = typeof error === 'string' ? new Error(error) : error; error._parsedErrors = parsedErrors; - internalModel.adapterDidInvalidate(error); + // TODO figure out how to handle ember-model-validator given internalModel wouldn't be available + // anymore. + adapterDidInvalidate(this, internalModel.identifier, error); } assert(`store.recordWasInvalid has been removed`); } @@ -2130,9 +2125,9 @@ class Store extends Service { saveRecord(record: RecordInstance, options: Dict = {}): Promise { assert(`Unable to initate save for a record in a disconnected state`, storeFor(record)); let identifier = recordIdentifierFor(record); - let internalModel = identifier && internalModelFactoryFor(this).peek(identifier)!; + let recordData = identifier && this._instanceCache.peek({ identifier, bucket: 'recordData' }); - if (!internalModel) { + if (!recordData) { // this commonly means we're disconnected // but just in case we reject here to prevent bad things. return reject(`Record Is Disconnected`); @@ -2143,14 +2138,13 @@ class Store extends Service { assert( `Cannot initiate a save request for an unloaded record: ${identifier}`, - !internalModel.isEmpty && !internalModel.isDestroyed + + recordData && !recordData.isEmpty?.() ); if (recordDataIsFullyDeleted(this._instanceCache, identifier)) { return resolve(record); } - - const recordData = this._instanceCache.getRecordData(identifier); recordData.willCommit(); if (isDSModel(record)) { record.errors.clear(); @@ -2161,10 +2155,9 @@ class Store extends Service { } let operation: 'createRecord' | 'deleteRecord' | 'updateRecord' = 'updateRecord'; - // TODO handle missing isNew - if (recordData.isNew && recordData.isNew()) { + if (recordData.isNew?.()) { operation = 'createRecord'; - } else if (recordData.isDeleted && recordData.isDeleted()) { + } else if (recordData.isDeleted?.()) { operation = 'deleteRecord'; } @@ -2221,7 +2214,7 @@ class Store extends Service { } else if (typeof e === 'string') { err = new Error(e); } - internalModel.adapterDidInvalidate(err); + adapterDidInvalidate(this, identifier, err); throw err; } ); @@ -2545,3 +2538,70 @@ function isDSModel(record: RecordInstance | null): record is DSModel { record.constructor.isModel === true ); } + +type AdapterErrors = Error & { errors?: unknown[]; isAdapterError?: true; code?: string }; +type SerializerWithParseErrors = MinimumSerializerInterface & { + extractErrors?(store: Store, modelClass: ShimModelClass, error: AdapterErrors, recordId: string | null): any; +}; + +function adapterDidInvalidate(store: Store, identifier: StableRecordIdentifier, error: Error & { errors?: JsonApiValidationError[]; isAdapterError?: true; code?: string }) { + if (error && error.isAdapterError === true && error.code === 'InvalidError') { + let serializer = store.serializerFor(identifier.type) as SerializerWithParseErrors; + + // TODO @deprecate extractErrors being called + // TODO remove extractErrors from the default serializers. + if (serializer && typeof serializer.extractErrors === 'function') { + let errorsHash = serializer.extractErrors(store, store.modelFor(identifier.type), error, identifier.id); + error.errors = errorsHashToArray(errorsHash); + } + } + const recordData = store._instanceCache.getRecordData(identifier); + + if (error.errors) { + assert( + `Expected the RecordData implementation for ${identifier} to have a getErrors(identifier) method for retreiving errors.`, + typeof recordData.getErrors === 'function' + ); + + let jsonApiErrors: JsonApiValidationError[] = error.errors; + if (jsonApiErrors.length === 0) { + jsonApiErrors = [{ title: 'Invalid Error', detail: '', source: { pointer: '/data' } }]; + } + recordData.commitWasRejected(identifier, jsonApiErrors); + } else { + recordData.commitWasRejected(identifier); + } +} + +function makeArray(value) { + return Array.isArray(value) ? value : [value]; +} + +const PRIMARY_ATTRIBUTE_KEY = 'base'; + +function errorsHashToArray(errors): JsonApiValidationError[] { + const out: JsonApiValidationError[] = []; + + if (errors) { + Object.keys(errors).forEach((key) => { + let messages = makeArray(errors[key]); + for (let i = 0; i < messages.length; i++) { + let title = 'Invalid Attribute'; + let pointer = `/data/attributes/${key}`; + if (key === PRIMARY_ATTRIBUTE_KEY) { + title = 'Invalid Document'; + pointer = `/data`; + } + out.push({ + title: title, + detail: messages[i], + source: { + pointer: pointer, + }, + }); + } + }); + } + + return out; +} From c3f30684ac447afd1ce318fbb21a56e448d0acc0 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Thu, 4 Aug 2022 23:42:11 -0700 Subject: [PATCH 10/29] the shape of things --- .eslintrc.js | 2 - ember-data-types/q/ds-model.ts | 2 - packages/-ember-data/addon/-private/index.ts | 2 - packages/-ember-data/addon/index.js | 2 - .../node-tests/fixtures/expected.js | 3 - .../-private/legacy-relationships-support.ts | 10 +- packages/model/addon/-private/model.js | 20 +- .../addon/-private/promise-many-array.ts | 4 +- packages/model/addon/-private/record-state.ts | 6 +- .../addon/current-deprecations.ts | 1 - .../private-build-infra/addon/deprecations.ts | 1 - .../record-data/addon/-private/coerce-id.ts | 4 +- .../addon/-private/graph/-utils.ts | 3 +- .../record-data/addon/-private/graph/index.ts | 6 +- .../record-data/addon/-private/record-data.ts | 3 +- .../-private/relationships/state/has-many.ts | 12 +- packages/store/addon/-debug/index.js | 2 +- .../addon/-private/caches/identifier-cache.ts | 4 +- .../addon/-private/caches/instance-cache.ts | 334 +++++++++++------- packages/store/addon/-private/index.ts | 5 +- .../legacy-model-support/internal-model.ts | 80 ----- .../-private/managers/record-array-manager.ts | 14 - .../managers/record-data-store-wrapper.ts | 7 +- .../store/addon/-private/network/snapshot.ts | 13 +- .../store/addon/-private/store-service.ts | 58 +-- tsconfig.json | 2 - 26 files changed, 264 insertions(+), 336 deletions(-) delete mode 100644 packages/store/addon/-private/legacy-model-support/internal-model.ts diff --git a/.eslintrc.js b/.eslintrc.js index 82b1c025c49..c14145eee93 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -195,7 +195,6 @@ module.exports = { 'ember-data-types/q/ember-data-json-api.ts', 'ember-data-types/q/ds-model.ts', 'packages/store/addon/-private/managers/record-data-store-wrapper.ts', - 'packages/store/addon/-private/caches/internal-model-factory.ts', 'packages/store/addon/-private/network/snapshot.ts', 'packages/store/addon/-private/network/snapshot-record-array.ts', 'packages/store/addon/-private/legacy-model-support/schema-definition-service.ts', @@ -205,7 +204,6 @@ module.exports = { 'packages/store/addon/-private/caches/record-data-for.ts', 'packages/store/addon/-private/utils/normalize-model-name.ts', 'packages/store/addon/-private/legacy-model-support/shim-model-class.ts', - 'packages/store/addon/-private/legacy-model-support/internal-model.ts', 'packages/store/addon/-private/network/fetch-manager.ts', 'packages/store/addon/-private/store-service.ts', 'packages/store/addon/-private/utils/coerce-id.ts', diff --git a/ember-data-types/q/ds-model.ts b/ember-data-types/q/ds-model.ts index 27eeec4d295..711e7fdcb49 100644 --- a/ember-data-types/q/ds-model.ts +++ b/ember-data-types/q/ds-model.ts @@ -2,7 +2,6 @@ import type EmberObject from '@ember/object'; import type { Errors } from '@ember-data/model/-private'; import type Store from '@ember-data/store'; -import type InternalModel from '@ember-data/store/-private/legacy-model-support/internal-model'; import type { JsonApiValidationError } from './record-data-json-api'; import type { AttributeSchema, RelationshipSchema, RelationshipsSchema } from './record-data-schemas'; @@ -12,7 +11,6 @@ export interface DSModel extends EmberObject { constructor: DSModelSchema; store: Store; errors: Errors; - _internalModel: InternalModel; toString(): string; save(): Promise; eachRelationship(callback: (this: T, key: string, meta: RelationshipSchema) => void, binding?: T): void; diff --git a/packages/-ember-data/addon/-private/index.ts b/packages/-ember-data/addon/-private/index.ts index 0c32ba10e4c..f36e0474224 100644 --- a/packages/-ember-data/addon/-private/index.ts +++ b/packages/-ember-data/addon/-private/index.ts @@ -4,11 +4,9 @@ export { default as DS } from './core'; export { Errors } from '@ember-data/model/-private'; export { Snapshot } from '@ember-data/store/-private'; -// `ember-data-model-fragments` relies on `InternalModel` // `ember-data-model-fragments' and `ember-data-change-tracker` rely on `normalizeModelName` export { AdapterPopulatedRecordArray, - InternalModel, PromiseArray, PromiseObject, RecordArray, diff --git a/packages/-ember-data/addon/index.js b/packages/-ember-data/addon/index.js index e8172e88f58..b8085ebe3d1 100644 --- a/packages/-ember-data/addon/index.js +++ b/packages/-ember-data/addon/index.js @@ -30,7 +30,6 @@ import { AdapterPopulatedRecordArray, DS, Errors, - InternalModel, ManyArray, PromiseArray, PromiseManyArray, @@ -52,7 +51,6 @@ DS.Model = Model; DS.attr = attr; DS.Errors = Errors; -DS.InternalModel = InternalModel; DS.Snapshot = Snapshot; DS.Adapter = Adapter; diff --git a/packages/-ember-data/node-tests/fixtures/expected.js b/packages/-ember-data/node-tests/fixtures/expected.js index cdd46eaad7d..0ea39b24a2b 100644 --- a/packages/-ember-data/node-tests/fixtures/expected.js +++ b/packages/-ember-data/node-tests/fixtures/expected.js @@ -73,8 +73,6 @@ module.exports = { '(private) @ember-data/store IdentifierCache#_getRecordIdentifier', '(private) @ember-data/store IdentifierCache#_mergeRecordIdentifiers', '(private) @ember-data/store IdentifierCache#peekRecordIdentifier', - '(private) @ember-data/store InternalModelFactory#lookup', - '(private) @ember-data/store InternalModelFactory#peek', '(private) @ember-data/store ManyArray#isPolymorphic', '(private) @ember-data/store ManyArray#relationship', '(private) @ember-data/store RecordArray#_createSnapshot', @@ -89,7 +87,6 @@ module.exports = { '(private) @ember-data/store Store#_push', '(private) @ember-data/store Store#find', '(private) @ember-data/store Store#init', - '(private) @ember-data/store Store#recordWasInvalid', '(public) @ember-data/adapter Adapter#coalesceFindRequests', '(public) @ember-data/adapter Adapter#createRecord', '(public) @ember-data/adapter Adapter#deleteRecord', diff --git a/packages/model/addon/-private/legacy-relationships-support.ts b/packages/model/addon/-private/legacy-relationships-support.ts index 03109c90790..2a44477df31 100644 --- a/packages/model/addon/-private/legacy-relationships-support.ts +++ b/packages/model/addon/-private/legacy-relationships-support.ts @@ -13,7 +13,6 @@ import type { import type { UpgradedMeta } from '@ember-data/record-data/-private/graph/-edge-definition'; import type { RelationshipState } from '@ember-data/record-data/-private/graph/-state'; import type Store from '@ember-data/store'; -import type { InternalModel } from '@ember-data/store/-private'; import { recordDataFor, recordIdentifierFor, storeFor } from '@ember-data/store/-private'; import type { IdentifierCache } from '@ember-data/store/-private/caches/identifier-cache'; import type { DSModel } from '@ember-data/types/q/ds-model'; @@ -141,7 +140,8 @@ export class LegacySupport { `You looked up the '${key}' relationship on a '${identifier.type}' with id ${ identifier.id || 'null' } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (\`belongsTo({ async: true })\`)`, - toReturn === null || !store._instanceCache.peek({ identifier: relatedIdentifier, bucket: 'recordData' })?.isEmpty?.() + toReturn === null || + !store._instanceCache.peek({ identifier: relatedIdentifier, bucket: 'recordData' })?.isEmpty?.() ); return toReturn; } @@ -705,11 +705,7 @@ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship) } } -function isEmpty( - store: Store, - cache: IdentifierCache, - resource: ResourceIdentifierObject -): boolean { +function isEmpty(store: Store, cache: IdentifierCache, resource: ResourceIdentifierObject): boolean { const identifier = cache.getOrCreateRecordIdentifier(resource); const recordData = store._instanceCache.peek({ identifier, bucket: 'recordData' }); return !recordData || !!recordData.isEmpty?.(); diff --git a/packages/model/addon/-private/model.js b/packages/model/addon/-private/model.js index 9f02fc2eade..25f2cf7ed31 100644 --- a/packages/model/addon/-private/model.js +++ b/packages/model/addon/-private/model.js @@ -22,8 +22,7 @@ import { DEPRECATE_SAVE_PROMISE_ACCESS, } from '@ember-data/private-build-infra/deprecations'; import { recordIdentifierFor, storeFor } from '@ember-data/store'; -import { coerceId, deprecatedPromiseObject, InternalModel, WeakCache } from '@ember-data/store/-private'; -import { recordDataFor } from '@ember-data/store/-private'; +import { coerceId, deprecatedPromiseObject, recordDataFor, WeakCache } from '@ember-data/store/-private'; import Errors from './errors'; import { LegacySupport } from './legacy-relationships-support'; @@ -138,8 +137,6 @@ class Model extends EmberObject { this.setProperties(createProps); - // TODO pass something in such that we don't need internalModel - // to get this info let store = storeFor(this); let notifications = store._notificationManager; let identity = recordIdentifierFor(this); @@ -458,8 +455,12 @@ class Model extends EmberObject { // this guard exists, because some dev-only deprecation code // (addListener via validatePropertyInjections) invokes toString before the // object is real. - if (DEBUG && !this._internalModel) { - return void 0; + if (DEBUG) { + try { + return recordIdentifierFor(this).id; + } catch { + return void 0; + } } return recordIdentifierFor(this).id; } @@ -467,7 +468,10 @@ class Model extends EmberObject { const normalizedId = coerceId(id); const identifier = recordIdentifierFor(this); let didChange = normalizedId !== identifier.id; - assert(`Cannot set ${identifier.type} record's id to ${id}, because id is already ${identifier.id}`, !didChange || identifier.id === null); + assert( + `Cannot set ${identifier.type} record's id to ${id}, because id is already ${identifier.id}`, + !didChange || identifier.id === null + ); if (normalizedId !== null && didChange) { this.store._instanceCache.setRecordId(identifier.type, normalizedId, identifier.lid); @@ -815,7 +819,7 @@ class Model extends EmberObject { rollbackAttributes() { const { currentState } = this; recordDataFor(this).rollbackAttributes(); - record.errors.clear(); + this.errors.clear(); currentState.cleanErrorRequests(); } diff --git a/packages/model/addon/-private/promise-many-array.ts b/packages/model/addon/-private/promise-many-array.ts index da91a3739b0..6f8336055d6 100644 --- a/packages/model/addon/-private/promise-many-array.ts +++ b/packages/model/addon/-private/promise-many-array.ts @@ -9,7 +9,7 @@ import { resolve } from 'rsvp'; import type { ManyArray } from 'ember-data/-private'; -import type { InternalModel } from '@ember-data/store/-private'; +import { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; export interface HasManyProxyCreateArgs { @@ -42,7 +42,7 @@ export interface HasManyProxyCreateArgs { @class PromiseManyArray @public */ -export default interface PromiseManyArray extends Omit, 'destroy'> {} +export default interface PromiseManyArray extends Omit, 'destroy'> {} export default class PromiseManyArray { declare promise: Promise | null; declare isDestroyed: boolean; diff --git a/packages/model/addon/-private/record-state.ts b/packages/model/addon/-private/record-state.ts index 832908b774e..1a76f324f5c 100644 --- a/packages/model/addon/-private/record-state.ts +++ b/packages/model/addon/-private/record-state.ts @@ -104,8 +104,9 @@ export function tagged(_target, key, desc) { } /** -Historically InternalModel managed a state machine -the currentState for which was reflected onto Model. +Historically EmberData managed a state machine +for each record, the currentState for which +was reflected onto Model. This implements the flags and stateName for backwards compat with the state tree that used to be possible (listed below). @@ -304,7 +305,6 @@ export default class RecordState { return !this.isLoaded && this.pendingCount > 0 && this.fulfilledCount === 0; } - // TODO @runspired handle "unloadRecord" see note in InternalModel @tagged get isLoaded() { if (this.isNew) { diff --git a/packages/private-build-infra/addon/current-deprecations.ts b/packages/private-build-infra/addon/current-deprecations.ts index f8b014e3a38..64b70f3a597 100644 --- a/packages/private-build-infra/addon/current-deprecations.ts +++ b/packages/private-build-infra/addon/current-deprecations.ts @@ -44,7 +44,6 @@ export default { DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS: '4.5', DEPRECATE_STORE_FIND: '4.5', DEPRECATE_HAS_RECORD: '4.5', - DEPRECATE_RECORD_WAS_INVALID: '4.5', DEPRECATE_STRING_ARG_SCHEMAS: '4.5', DEPRECATE_JSON_API_FALLBACK: '4.5', DEPRECATE_MODEL_REOPEN: '4.8', diff --git a/packages/private-build-infra/addon/deprecations.ts b/packages/private-build-infra/addon/deprecations.ts index 2a21099af5c..f0dd718a135 100644 --- a/packages/private-build-infra/addon/deprecations.ts +++ b/packages/private-build-infra/addon/deprecations.ts @@ -13,7 +13,6 @@ export const DEPRECATE_RSVP_PROMISE = deprecationState('DEPRECATE_RSVP_PROMISE') export const DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS = deprecationState('DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS'); export const DEPRECATE_STORE_FIND = deprecationState('DEPRECATE_STORE_FIND'); export const DEPRECATE_HAS_RECORD = deprecationState('DEPRECATE_HAS_RECORD'); -export const DEPRECATE_RECORD_WAS_INVALID = deprecationState('DEPRECATE_RECORD_WAS_INVALID'); export const DEPRECATE_STRING_ARG_SCHEMAS = deprecationState('DEPRECATE_STRING_ARG_SCHEMAS'); export const DEPRECATE_JSON_API_FALLBACK = deprecationState('DEPRECATE_JSON_API_FALLBACK'); export const DEPRECATE_MODEL_REOPEN = deprecationState('DEPRECATE_MODEL_REOPEN'); diff --git a/packages/record-data/addon/-private/coerce-id.ts b/packages/record-data/addon/-private/coerce-id.ts index e9f341d774a..405d647bd64 100644 --- a/packages/record-data/addon/-private/coerce-id.ts +++ b/packages/record-data/addon/-private/coerce-id.ts @@ -8,7 +8,7 @@ import { DEBUG } from '@glimmer/env'; // corresponding record, we will not know if it is a string or a number. type Coercable = string | number | boolean | null | undefined | symbol; -function coerceId(id: Coercable): string | null { +export function coerceId(id: Coercable): string | null { if (id === null || id === undefined || id === '') { return null; } @@ -35,5 +35,3 @@ export function ensureStringId(id: Coercable): string { return normalized!; } - -export default coerceId; diff --git a/packages/record-data/addon/-private/graph/-utils.ts b/packages/record-data/addon/-private/graph/-utils.ts index 713b7ed913a..3033bf15b19 100644 --- a/packages/record-data/addon/-private/graph/-utils.ts +++ b/packages/record-data/addon/-private/graph/-utils.ts @@ -1,11 +1,12 @@ import { assert, inspect, warn } from '@ember/debug'; -import { coerceId, recordDataFor as peekRecordData } from '@ember-data/store/-private'; +import { recordDataFor as peekRecordData } from '@ember-data/store/-private'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordData } from '@ember-data/types/q/record-data'; import type { RelationshipRecordData } from '@ember-data/types/q/relationship-record-data'; import type { Dict } from '@ember-data/types/q/utils'; +import { coerceId } from '../coerce-id'; import type BelongsToRelationship from '../relationships/state/belongs-to'; import type ManyRelationship from '../relationships/state/has-many'; import type ImplicitRelationship from '../relationships/state/implicit'; diff --git a/packages/record-data/addon/-private/graph/index.ts b/packages/record-data/addon/-private/graph/index.ts index 7ce5ba4fcca..cc0f6146303 100644 --- a/packages/record-data/addon/-private/graph/index.ts +++ b/packages/record-data/addon/-private/graph/index.ts @@ -59,7 +59,7 @@ export function graphFor(store: RecordDataStoreWrapper | Store): Graph { * Graph acts as the cache for relationship data. It allows for * us to ask about and update relationships for a given Identifier * without requiring other objects for that Identifier to be - * instantiated (such as `InternalModel`, `RecordData` or a `Record`) + * instantiated (such as `RecordData` or a `Record`) * * This also allows for us to make more substantive changes to relationships * with increasingly minor alterations to other portions of the internals @@ -398,9 +398,9 @@ function destroyRelationship(rel) { rel.clear(); // necessary to clear relationships in the ui from dematerialized records - // hasMany is managed by InternalModel which calls `retreiveLatest` after + // hasMany is managed by Model which calls `retreiveLatest` after // dematerializing the recordData instance. - // but sync belongsTo require this since they don't have a proxy to update. + // but sync belongsTo requires this since they don't have a proxy to update. // so we have to notify so it will "update" to null. // we should discuss whether we still care about this, probably fine to just // leave the ui relationship populated since the record is destroyed and diff --git a/packages/record-data/addon/-private/record-data.ts b/packages/record-data/addon/-private/record-data.ts index 6a06c3c48eb..393c3470863 100644 --- a/packages/record-data/addon/-private/record-data.ts +++ b/packages/record-data/addon/-private/record-data.ts @@ -16,7 +16,6 @@ import type { RelationshipRecordData, } from '@ember-data/types/q/relationship-record-data'; -import coerceId from './coerce-id'; import { isImplicit } from './graph/-utils'; import { graphFor } from './graph/index'; import type BelongsToRelationship from './relationships/state/belongs-to'; @@ -689,7 +688,7 @@ export default class RecordDataDefault implements RelationshipRecordData { } /* - Ember Data has 3 buckets for storing the value of an attribute on an internalModel. + Ember Data has 3 buckets for storing the value of an attribute. `_data` holds all of the attributes that have been acknowledged by a backend via the adapter. When rollbackAttributes is called on a model all diff --git a/packages/record-data/addon/-private/relationships/state/has-many.ts b/packages/record-data/addon/-private/relationships/state/has-many.ts index fabbed87401..449636eeda5 100755 --- a/packages/record-data/addon/-private/relationships/state/has-many.ts +++ b/packages/record-data/addon/-private/relationships/state/has-many.ts @@ -96,20 +96,20 @@ export default class ManyRelationship { let seen = Object.create(null); for (let i = 0; i < this.currentState.length; i++) { - const inverseInternalModel = this.currentState[i]; - const id = inverseInternalModel.lid; + const inverseIdentifier = this.currentState[i]; + const id = inverseIdentifier.lid; if (!seen[id]) { seen[id] = true; - callback(inverseInternalModel); + callback(inverseIdentifier); } } for (let i = 0; i < this.canonicalState.length; i++) { - const inverseInternalModel = this.canonicalState[i]; - const id = inverseInternalModel.lid; + const inverseIdentifier = this.canonicalState[i]; + const id = inverseIdentifier.lid; if (!seen[id]) { seen[id] = true; - callback(inverseInternalModel); + callback(inverseIdentifier); } } } diff --git a/packages/store/addon/-debug/index.js b/packages/store/addon/-debug/index.js index 8bc49a30cd4..c0e32c40e09 100644 --- a/packages/store/addon/-debug/index.js +++ b/packages/store/addon/-debug/index.js @@ -9,7 +9,7 @@ import { DEBUG } from '@glimmer/env'; relationship (specified via `relationshipMeta`) of the `record`. This utility should only be used internally, as both record parameters must - be an InternalModel and the `relationshipMeta` needs to be the meta + be stable record identifiers and the `relationshipMeta` needs to be the meta information about the relationship, retrieved via `record.relationshipFor(key)`. */ diff --git a/packages/store/addon/-private/caches/identifier-cache.ts b/packages/store/addon/-private/caches/identifier-cache.ts index ffdcefe6ba8..5d2598b6a0a 100644 --- a/packages/store/addon/-private/caches/identifier-cache.ts +++ b/packages/store/addon/-private/caches/identifier-cache.ts @@ -141,8 +141,8 @@ export class IdentifierCache { /** * Internal hook to allow management of merge conflicts with identifiers. * - * we allow late binding of this private internal merge so that `internalModelFactory` - * can insert itself here to handle elimination of duplicates + * we allow late binding of this private internal merge so that + * the cache can insert itself here to handle elimination of duplicates * * @method __configureMerge * @private diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index e6ea37cbf28..85472688276 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -3,17 +3,23 @@ import { DEBUG } from '@glimmer/env'; import { resolve } from 'rsvp'; -import type { ExistingResourceObject, NewResourceIdentifierObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api'; +import type { + ExistingResourceObject, + NewResourceIdentifierObject, + ResourceIdentifierObject, +} from '@ember-data/types/q/ember-data-json-api'; import type { RecordIdentifier, StableExistingRecordIdentifier, StableRecordIdentifier, } from '@ember-data/types/q/identifier'; import type { RecordData } from '@ember-data/types/q/record-data'; +import { JsonApiRelationship, JsonApiResource } from '@ember-data/types/q/record-data-json-api'; +import { RelationshipSchema } from '@ember-data/types/q/record-data-schemas'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; import type { FindOptions } from '@ember-data/types/q/store'; +import { Dict } from '@ember-data/types/q/utils'; -import InternalModel from '../legacy-model-support/internal-model'; import RecordReference from '../legacy-model-support/record-reference'; import RecordDataStoreWrapper from '../managers/record-data-store-wrapper'; import Snapshot from '../network/snapshot'; @@ -24,11 +30,60 @@ import coerceId, { ensureStringId } from '../utils/coerce-id'; import constructResource from '../utils/construct-resource'; import normalizeModelName from '../utils/normalize-model-name'; import WeakCache from '../utils/weak-cache'; -import { internalModelFactoryFor, recordIdentifierFor, setRecordIdentifier } from './internal-model-factory'; import recordDataFor, { setRecordDataFor } from './record-data-for'; -import { JsonApiResource } from '@ember-data/types/q/record-data-json-api'; -import { Dict } from '@ember-data/types/q/utils'; -import { isStableIdentifier } from './identifier-cache'; + +/** + @module @ember-data/store +*/ + +const RecordCache = new WeakCache(DEBUG ? 'identifier' : ''); +if (DEBUG) { + RecordCache._expectMsg = (key: RecordInstance | RecordData) => + `${String(key)} is not a record instantiated by @ember-data/store`; +} + +export function peekRecordIdentifier(record: RecordInstance | RecordData): StableRecordIdentifier | undefined { + return RecordCache.get(record); +} + +/** + Retrieves the unique referentially-stable [RecordIdentifier](/ember-data/release/classes/StableRecordIdentifier) + assigned to the given record instance. + ```js + import { recordIdentifierFor } from "@ember-data/store"; + // ... gain access to a record, for instance with peekRecord or findRecord + const record = store.peekRecord("user", "1"); + // get the identifier for the record (see docs for StableRecordIdentifier) + const identifier = recordIdentifierFor(record); + // access the identifier's properties. + const { id, type, lid } = identifier; + ``` + @method recordIdentifierFor + @public + @static + @for @ember-data/store + @param {Object} record a record instance previously obstained from the store. + @returns {StableRecordIdentifier} + */ +export function recordIdentifierFor(record: RecordInstance | RecordData): StableRecordIdentifier { + return RecordCache.getWithError(record); +} + +export function setRecordIdentifier(record: RecordInstance | RecordData, identifier: StableRecordIdentifier): void { + if (DEBUG && RecordCache.has(record) && RecordCache.get(record) !== identifier) { + throw new Error(`${String(record)} was already assigned an identifier`); + } + + /* + It would be nice to do a reverse check here that an identifier has not + previously been assigned a record; however, unload + rematerialization + prevents us from having a great way of doing so when CustomRecordClasses + don't necessarily give us access to a `isDestroyed` for dematerialized + instance. + */ + + RecordCache.set(record, identifier); +} const RECORD_REFERENCES = new WeakCache(DEBUG ? 'reference' : ''); export const StoreMap = new WeakCache(DEBUG ? 'store' : ''); @@ -48,13 +103,11 @@ type Caches = { recordData: WeakMap; }; - export class InstanceCache { declare store: Store; declare _storeWrapper: RecordDataStoreWrapper; declare peekList: Dict>; - #instances: Caches = { record: new WeakMap(), recordData: new WeakMap(), @@ -65,7 +118,7 @@ export class InstanceCache { if (!recordData) { return false; } - const isNew = recordData.isNew?.() || false; + const isNew = recordData.isNew?.() || false; const isEmpty = recordData.isEmpty?.() || false; // if we are new we must consider ourselves loaded @@ -79,12 +132,12 @@ export class InstanceCache { // we should consider allowing for something to be loaded that is simply "not empty". // which is how RecordState currently handles this case; however, RecordState is buggy // in that it does not account for unloading. - return !isEmpty + return !isEmpty; } constructor(store: Store) { this.store = store; - this.peekList = Object.create(null); + this.peekList = Object.create(null) as Dict>; this._storeWrapper = new RecordDataStoreWrapper(this.store); this.__recordDataFor = this.__recordDataFor.bind(this); @@ -93,72 +146,84 @@ export class InstanceCache { return new RecordReference(this.store, identifier); }; - store.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => { - let intendedIdentifier = identifier; - if (identifier.id !== matchedIdentifier.id) { - intendedIdentifier = 'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier; - } else if (identifier.type !== matchedIdentifier.type) { - intendedIdentifier = - 'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier; - } - let altIdentifier = identifier === intendedIdentifier ? matchedIdentifier : identifier; - - // check for duplicate entities - let imHasRecord = this.#instances.record.has(intendedIdentifier); - let otherHasRecord = this.#instances.record.has(altIdentifier); - let imRecordData = this.#instances.recordData.get(intendedIdentifier) || null; - let otherRecordData = this.#instances.record.get(altIdentifier) || null; - - // we cannot merge entities when both have records - // (this may not be strictly true, we could probably swap the recordData the record points at) - if (imHasRecord && otherHasRecord) { - // TODO we probably don't need to throw these errors anymore - // once InternalModel is fully removed, as we can just "swap" - // what data source the abandoned record points at so long as - // it itself is not retained by the store in any way. - if ('id' in resourceData) { - throw new Error( - `Failed to update the 'id' for the RecordIdentifier '${identifier.type}:${identifier.id} (${identifier.lid})' to '${resourceData.id}', because that id is already in use by '${matchedIdentifier.type}:${matchedIdentifier.id} (${matchedIdentifier.lid})'` + store.identifierCache.__configureMerge( + (identifier: StableRecordIdentifier, matchedIdentifier: StableRecordIdentifier, resourceData) => { + let intendedIdentifier = identifier; + if (identifier.id !== matchedIdentifier.id) { + intendedIdentifier = + 'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier; + } else if (identifier.type !== matchedIdentifier.type) { + intendedIdentifier = + 'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier; + } + let altIdentifier = identifier === intendedIdentifier ? matchedIdentifier : identifier; + + // check for duplicate entities + let imHasRecord = this.#instances.record.has(intendedIdentifier); + let otherHasRecord = this.#instances.record.has(altIdentifier); + let imRecordData = this.#instances.recordData.get(intendedIdentifier) || null; + let otherRecordData = this.#instances.recordData.get(altIdentifier) || null; + + // we cannot merge entities when both have records + // (this may not be strictly true, we could probably swap the recordData the record points at) + if (imHasRecord && otherHasRecord) { + // TODO we probably don't need to throw these errors anymore + // we can probably just "swap" what data source the abandoned + // record points at so long as + // it itself is not retained by the store in any way. + if ('id' in resourceData) { + throw new Error( + `Failed to update the 'id' for the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${ + identifier.lid + })' to '${String(resourceData.id)}', because that id is already in use by '${ + matchedIdentifier.type + }:${String(matchedIdentifier.id)} (${matchedIdentifier.lid})'` + ); + } + // TODO @runspired determine when this is even possible + assert( + `Failed to update the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${ + identifier.lid + })' to merge with the detected duplicate identifier '${matchedIdentifier.type}:${String( + matchedIdentifier.id + )} (${String(matchedIdentifier.lid)})'` ); } - // TODO @runspired determine when this is even possible - assert( - `Failed to update the RecordIdentifier '${identifier.type}:${identifier.id} (${identifier.lid})' to merge with the detected duplicate identifier '${matchedIdentifier.type}:${matchedIdentifier.id} (${matchedIdentifier.lid})'` - ); - } - - // remove "other" from cache - if (otherHasRecord) { - cache.delete(altIdentifier); - this.peekList[altIdentifier.type]?.delete(altIdentifier); - } - if (imRecordData === null && otherRecordData === null) { - // nothing more to do - return intendedIdentifier; + // remove "other" from cache + if (otherHasRecord) { + // TODO probably need to release other things + this.peekList[altIdentifier.type]?.delete(altIdentifier); + } - // only the other has a RecordData - // OR only the other has a Record - } else if ((imRecordData === null && otherRecordData !== null) || (imRecordData && !imHasRecord && otherRecordData && otherHasRecord)) { - if (imRecordData) { - // TODO check if we are retained in any async relationships - cache.delete(intendedIdentifier); - this.peekList[intendedIdentifier.type]?.delete(intendedIdentifier); - // im.destroy(); + if (imRecordData === null && otherRecordData === null) { + // nothing more to do + return intendedIdentifier; + + // only the other has a RecordData + // OR only the other has a Record + } else if ( + (imRecordData === null && otherRecordData !== null) || + (imRecordData && !imHasRecord && otherRecordData && otherHasRecord) + ) { + if (imRecordData) { + // TODO check if we are retained in any async relationships + // TODO probably need to release other things + this.peekList[intendedIdentifier.type]?.delete(intendedIdentifier); + // im.destroy(); + } + imRecordData = otherRecordData!; + // TODO do we need to notify the id change? + // TODO swap recordIdentifierFor result? + this.peekList[intendedIdentifier.type] = this.peekList[intendedIdentifier.type] || new Set(); + this.peekList[intendedIdentifier.type]!.add(intendedIdentifier); + + // just use im + } else { + // otherIm.destroy(); } - im = otherIm!; - // TODO do we need to notify the id change? - im.identifier = intendedIdentifier; - cache.set(intendedIdentifier, im); - this.peekList[intendedIdentifier.type] = this.peekList[intendedIdentifier.type] || new Set(); - this.peekList[intendedIdentifier.type]!.add(intendedIdentifier); - - // just use im - } else { - // otherIm.destroy(); - } - /* + /* TODO @runspired consider adding this to make polymorphism even nicer if (HAS_RECORD_DATA_PACKAGE) { if (identifier.type !== matchedIdentifier.type) { @@ -168,8 +233,9 @@ export class InstanceCache { } */ - return intendedIdentifier; - }); + return intendedIdentifier; + } + ); } peek({ identifier, bucket }: { identifier: StableRecordIdentifier; bucket: 'record' }): RecordInstance | undefined; peek({ identifier, bucket }: { identifier: StableRecordIdentifier; bucket: 'recordData' }): RecordData | undefined; @@ -194,7 +260,6 @@ export class InstanceCache { this.#instances[bucket].set(identifier, value); } - getRecord(identifier: StableRecordIdentifier, properties?: CreateRecordProperties): RecordInstance { let record = this.peek({ identifier, bucket: 'record' }); @@ -239,15 +304,13 @@ export class InstanceCache { identifier: StableRecordIdentifier, options: FindOptions = {} ): Promise { - const internalModel = this.getInternalModel(identifier); - // pre-loading will change the isEmpty value - // TODO stpre this state somewhere other than InternalModel - const { isEmpty, isLoading } = internalModel; + const isEmpty = _isEmpty(this, identifier); + const isLoading = _isLoading(this, identifier); if (options.preload) { this.store._backburner.join(() => { - preloadData(this.store, identifier, options.preload); + preloadData(this.store, identifier, options.preload!); }); } @@ -347,7 +410,6 @@ export class InstanceCache { if (!recordData) { recordData = this._createRecordData(identifier); this.#instances.recordData.set(identifier, recordData); - this.getInternalModel(identifier).hasRecordData = true; this.peekList[identifier.type] = this.peekList[identifier.type] || new Set(); this.peekList[identifier.type]!.add(identifier); } @@ -355,11 +417,6 @@ export class InstanceCache { return recordData; } - // TODO move InternalModel cache into InstanceCache - getInternalModel(identifier: StableRecordIdentifier) { - return this._internalModelForResource(identifier); - } - createSnapshot(identifier: StableRecordIdentifier, options: FindOptions = {}): Snapshot { return new Snapshot(options, identifier, this.store); } @@ -412,7 +469,7 @@ export class InstanceCache { // ID absolutely can't be different than oldID if oldID is not null // TODO this assertion and restriction may not strictly be needed in the identifiers world assert( - `Cannot update the id for '${modelName}:${lid}' from '${oldId}' to '${id}'.`, + `Cannot update the id for '${modelName}:${lid}' from '${String(oldId)}' to '${id}'.`, !(oldId !== null && id !== oldId) ); @@ -430,7 +487,7 @@ export class InstanceCache { assert( `'${modelName}' was saved to the server, but the response returned the new id '${id}', which has already been used with another record.'`, - existingIdentifier === identifier + existingIdentifier === identifier ); if (identifier.id === null) { @@ -455,31 +512,24 @@ export class InstanceCache { // TODO this should determine identifier via the cache before making assumptions const resource = constructResource(normalizeModelName(data.type), ensureStringId(data.id), coerceId(data.lid)); - const maybeIdentifier = this.store.identifierCache.peekRecordIdentifier(resource); - - let internalModel = internalModelFactoryFor(this.store).lookup(resource, data); + // we probably should ony peek here + let identifier = this.store.identifierCache.peekRecordIdentifier(resource); + let isUpdate = false; // store.push will be from empty // findRecord will be from root.loading // this cannot be loading state if we do not already have an identifier // all else will be updates - const isLoading = internalModel.isLoading || (maybeIdentifier && !this.recordIsLoaded(maybeIdentifier)); - const isUpdate = internalModel.isEmpty === false && !isLoading; - - // exclude store.push (root.empty) case - let identifier = internalModel.identifier; - if (isUpdate || isLoading) { - let updatedIdentifier = this.store.identifierCache.updateRecordIdentifier(identifier, data); - - if (updatedIdentifier !== identifier) { - // we encountered a merge of identifiers in which - // two identifiers (and likely two internalModels) - // existed for the same resource. Now that we have - // determined the correct identifier to use, make sure - // that we also use the correct internalModel. - identifier = updatedIdentifier; - internalModel = internalModelFactoryFor(this.store).lookup(identifier); + if (identifier) { + const isLoading = _isLoading(this, identifier) || !this.recordIsLoaded(identifier); + isUpdate = !_isEmpty(this, identifier) && !isLoading; + + // exclude store.push (root.empty) case + if (isUpdate || isLoading) { + identifier = this.store.identifierCache.updateRecordIdentifier(identifier, data); } + } else { + identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource); } const recordData = this.getRecordData(identifier); @@ -516,10 +566,9 @@ export class InstanceCache { return req.type === 'mutation'; }) ) { - assert('You can only unload a record which is not inFlight. `' + identifier + '`'); + assert(`You can only unload a record which is not inFlight. '${String(identifier)}'`); } } - const internalModel = this.getInternalModel(identifier); // this has to occur before the internal model is removed // for legacy compat. @@ -551,11 +600,7 @@ export function isHiddenFromRecordArrays(cache: InstanceCache, identifier: Stabl return true; } - // if isLoading return false - // if isDematerializing, destroyed, or has scheduled destroy return true - // TODO eliminate this internalModel need - const internalModel = cache.getInternalModel(identifier); - if (!internalModel.isEmpty || internalModel.isLoading) { + if (!_isEmpty(cache, identifier) || _isLoading(cache, identifier)) { return false; } if (recordData.isDeletionCommitted?.() || (recordData.isNew?.() && recordData.isDeleted?.())) { @@ -623,7 +668,7 @@ function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is return !!record.then; } - /* +/* When a find request is triggered on the store, the user can optionally pass in attributes and relationships to be preloaded. These are meant to behave as if they came back from the server, except the user obtained them out of band and is informing @@ -634,18 +679,24 @@ function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is Preloaded data can be attributes and relationships passed in either as IDs or as actual models. */ -function preloadData(store: Store, identifier: StableRecordIdentifier, preload) { +type PreloadRelationshipValue = RecordInstance | string; +function preloadData(store: Store, identifier: StableRecordIdentifier, preload: Dict) { let jsonPayload: JsonApiResource = {}; //TODO(Igor) consider the polymorphic case - const modelClass = store.modelFor(identifier.type); + const schemas = store.getSchemaDefinitionService(); + const relationships = schemas.relationshipsDefinitionFor(identifier); Object.keys(preload).forEach((key) => { let preloadValue = preload[key]; - let relationshipMeta = modelClass.metaForProperty(key); - if (relationshipMeta.isRelationship) { + + let relationshipMeta = relationships[key]; + if (relationshipMeta) { if (!jsonPayload.relationships) { jsonPayload.relationships = {}; } - jsonPayload.relationships[key] = preloadRelationship(modelClass, key, preloadValue); + jsonPayload.relationships[key] = preloadRelationship( + relationshipMeta, + preloadValue as PreloadRelationshipValue | null | Array + ); } else { if (!jsonPayload.attributes) { jsonPayload.attributes = {}; @@ -656,25 +707,26 @@ function preloadData(store: Store, identifier: StableRecordIdentifier, preload) store._instanceCache.getRecordData(identifier).pushData(jsonPayload); } +function preloadRelationship( + schema: RelationshipSchema, + preloadValue: PreloadRelationshipValue | null | Array +): JsonApiRelationship { + const relatedType = schema.type; -function preloadRelationship(schema, key: string, preloadValue) { - const relationshipMeta = schema.metaForProperty(key); - const relatedType = relationshipMeta.type; - let data; - if (relationshipMeta.kind === 'hasMany') { + if (schema.kind === 'hasMany') { assert('You need to pass in an array to set a hasMany property on a record', Array.isArray(preloadValue)); - data = preloadValue.map((value) => _convertPreloadRelationshipToJSON(value, relatedType)); - } else { - data = _convertPreloadRelationshipToJSON(preloadValue, relatedType); + return { data: preloadValue.map((value) => _convertPreloadRelationshipToJSON(value, relatedType)) }; } - return { data }; + + assert('You should not pass in an array to set a belongsTo property on a record', !Array.isArray(preloadValue)); + return { data: preloadValue ? _convertPreloadRelationshipToJSON(preloadValue, relatedType) : null }; } /* findRecord('user', '1', { preload: { friends: ['1'] }}); findRecord('user', '1', { preload: { friends: [record] }}); */ -function _convertPreloadRelationshipToJSON(value, type: string) { +function _convertPreloadRelationshipToJSON(value: RecordInstance | string, type: string): ResourceIdentifierObject { if (typeof value === 'string' || typeof value === 'number') { return { type, id: value }; } @@ -682,3 +734,27 @@ function _convertPreloadRelationshipToJSON(value, type: string) { // and allow identifiers to be used return recordIdentifierFor(value); } + +function _isEmpty(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { + const recordData = cache.peek({ identifier: identifier, bucket: 'recordData' }); + if (!recordData) { + return true; + } + const isNew = recordData.isNew?.() || false; + const isDeleted = recordData.isDeleted?.() || false; + const isEmpty = recordData.isEmpty?.() || false; + + return (!isNew || isDeleted) && isEmpty; +} + +function _isLoading(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { + const req = cache.store.getRequestStateService(); + // const fulfilled = req.getLastRequestForRecord(identifier); + const isLoaded = cache.recordIsLoaded(identifier); + + return ( + !isLoaded && + // fulfilled === null && + req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query') + ); +} diff --git a/packages/store/addon/-private/index.ts b/packages/store/addon/-private/index.ts index da37ffbd472..096ec00dca1 100644 --- a/packages/store/addon/-private/index.ts +++ b/packages/store/addon/-private/index.ts @@ -10,7 +10,7 @@ import _normalize from './utils/normalize-model-name'; export { default as Store, storeFor } from './store-service'; -export { recordIdentifierFor } from './caches/internal-model-factory'; +export { recordIdentifierFor } from './caches/instance-cache'; export { default as Snapshot } from './network/snapshot'; export { @@ -39,9 +39,6 @@ export function normalizeModelName(modelName: string) { export { default as coerceId } from './utils/coerce-id'; -// `ember-data-model-fragments` relies on `InternalModel` -export { default as InternalModel } from './legacy-model-support/internal-model'; - export { PromiseArray, PromiseObject, deprecatedPromiseObject } from './proxies/promise-proxies'; export { default as RecordArray } from './record-arrays/record-array'; diff --git a/packages/store/addon/-private/legacy-model-support/internal-model.ts b/packages/store/addon/-private/legacy-model-support/internal-model.ts deleted file mode 100644 index e305b8ad9e3..00000000000 --- a/packages/store/addon/-private/legacy-model-support/internal-model.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; -import type { RecordData } from '@ember-data/types/q/record-data'; - -import type Store from '../store-service'; - -export default class InternalModel { - declare hasRecordData: boolean; - declare _isDestroyed: boolean; - declare isDestroying: boolean; - declare _isUpdatingId: boolean; - - // Not typed yet - declare store: Store; - declare identifier: StableRecordIdentifier; - declare hasRecord: boolean; - - constructor(store: Store, identifier: StableRecordIdentifier) { - this.store = store; - this.identifier = identifier; - this._isUpdatingId = false; - - this.hasRecordData = false; - - this._isDestroyed = false; - } - - // STATE we end up needing for various reasons - get _recordData(): RecordData { - return this.store._instanceCache.getRecordData(this.identifier); - } - - isDeleted(): boolean { - if (this._recordData.isDeleted) { - return this._recordData.isDeleted(); - } else { - return false; - } - } - - isNew(): boolean { - if (this.hasRecordData && this._recordData.isNew) { - return this._recordData.isNew(); - } else { - return false; - } - } - - get isEmpty(): boolean { - if (!this.hasRecordData) { - return true; - } - const recordData = this._recordData; - const isNew = recordData.isNew?.() || false; - const isDeleted = recordData.isDeleted?.() || false; - const isEmpty = recordData.isEmpty?.() || false; - - return (!isNew || isDeleted) && isEmpty; - } - - get isLoading() { - const req = this.store.getRequestStateService(); - const { identifier } = this; - // const fulfilled = req.getLastRequestForRecord(identifier); - const isLoaded = this.store._instanceCache.recordIsLoaded(identifier); - - return ( - !isLoaded && - // fulfilled === null && - req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query') - ); - } - - get isDestroyed(): boolean { - return this._isDestroyed; - } - - toString() { - return `<${this.identifier.type}:${this.identifier.id}>`; - } -} diff --git a/packages/store/addon/-private/managers/record-array-manager.ts b/packages/store/addon/-private/managers/record-array-manager.ts index 60ed00f313b..e3970cc2dd2 100644 --- a/packages/store/addon/-private/managers/record-array-manager.ts +++ b/packages/store/addon/-private/managers/record-array-manager.ts @@ -26,17 +26,6 @@ export function recordArraysForIdentifier(identifier: StableRecordIdentifier): S const pendingForIdentifier: Set = new Set([]); -function getIdentifier(identifier: StableRecordIdentifier): StableRecordIdentifier { - // during dematerialization we will get an identifier that - // has already been removed from the identifiers cache - // so it will not behave as if stable. This is a bug we should fix. - // if (!isStableIdentifier(identifierOrInternalModel)) { - // console.log({ unstable: i }); - // } - - return identifier; -} - /** @class RecordArrayManager @internal @@ -325,7 +314,6 @@ class RecordArrayManager { _associateWithRecordArray(identifiers: StableRecordIdentifier[], array: RecordArray): void { for (let i = 0, l = identifiers.length; i < l; i++) { let identifier = identifiers[i]; - identifier = getIdentifier(identifier); let recordArrays = this.getRecordArraysForIdentifier(identifier); recordArrays.add(array); } @@ -340,7 +328,6 @@ class RecordArrayManager { return; } let modelName = identifier.type; - identifier = getIdentifier(identifier); if (pendingForIdentifier.has(identifier)) { return; @@ -421,7 +408,6 @@ function removeFromAdapterPopulatedRecordArrays(store: Store, identifiers: Stabl } function removeFromAll(store: Store, identifier: StableRecordIdentifier): void { - identifier = getIdentifier(identifier); const recordArrays = recordArraysForIdentifier(identifier); recordArrays.forEach(function (recordArray) { diff --git a/packages/store/addon/-private/managers/record-data-store-wrapper.ts b/packages/store/addon/-private/managers/record-data-store-wrapper.ts index cbf1621be91..6b36a793f66 100644 --- a/packages/store/addon/-private/managers/record-data-store-wrapper.ts +++ b/packages/store/addon/-private/managers/record-data-store-wrapper.ts @@ -186,9 +186,10 @@ export default class RecordDataStoreWrapper implements StoreWrapper { // enforcing someone to use the record-data and identifier-cache APIs to // create a new identifier and then call clientDidCreate on the RecordData // instead. - const identifier = !id && !lid ? - this.identifierCache.createIdentifierForNewRecord({ type: type }) : - this.identifierCache.getOrCreateRecordIdentifier(constructResource(type, id, lid)); + const identifier = + !id && !lid + ? this.identifierCache.createIdentifierForNewRecord({ type: type }) + : this.identifierCache.getOrCreateRecordIdentifier(constructResource(type, id, lid)); return this._store._instanceCache.getRecordData(identifier); } diff --git a/packages/store/addon/-private/network/snapshot.ts b/packages/store/addon/-private/network/snapshot.ts index ee7a1595bbb..4f9133f2b44 100644 --- a/packages/store/addon/-private/network/snapshot.ts +++ b/packages/store/addon/-private/network/snapshot.ts @@ -9,11 +9,7 @@ import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra'; import { DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS } from '@ember-data/private-build-infra/deprecations'; import type BelongsToRelationship from '@ember-data/record-data/addon/-private/relationships/state/belongs-to'; import type ManyRelationship from '@ember-data/record-data/addon/-private/relationships/state/has-many'; -import type { DSModel, DSModelSchema, ModelSchema } from '@ember-data/types/q/ds-model'; -import type { - ExistingResourceIdentifierObject, - NewResourceIdentifierObject, -} from '@ember-data/types/q/ember-data-json-api'; +import type { DSModelSchema, ModelSchema } from '@ember-data/types/q/ds-model'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { OptionsHash } from '@ember-data/types/q/minimum-serializer-interface'; import type { ChangedAttributesHash } from '@ember-data/types/q/record-data'; @@ -22,7 +18,6 @@ import type { RecordInstance } from '@ember-data/types/q/record-instance'; import type { FindOptions } from '@ember-data/types/q/store'; import type { Dict } from '@ember-data/types/q/utils'; -import type InternalModel from '../legacy-model-support/internal-model'; import type Store from '../store-service'; type RecordId = string | null; @@ -158,7 +153,7 @@ export default class Snapshot implements Snapshot { let attributes = (this.__attributes = Object.create(null)); let attrs = Object.keys(this._store.getSchemaDefinitionService().attributesDefinitionFor(this.identifier)); let recordData = this._store._instanceCache.getRecordData(this.identifier); - const modelClass = this._store.modelFor(this.identifier.type); + const modelClass = this._store.modelFor(this.identifier.type); const isDSModel = schemaIsDSModel(modelClass); attrs.forEach((keyName) => { if (isDSModel) { @@ -319,7 +314,7 @@ export default class Snapshot implements Snapshot { // TODO @runspired it seems this code branch would not work with CUSTOM_MODEL_CLASSes // this check is not a regression in behavior because relationships don't currently - // function without access to intimate API contracts between RecordData and InternalModel. + // function without access to intimate API contracts between RecordData and Model. // This is a requirement we should fix as soon as the relationship layer does not require // this intimate API usage. if (!HAS_RECORD_DATA_PACKAGE) { @@ -422,7 +417,7 @@ export default class Snapshot implements Snapshot { // TODO @runspired it seems this code branch would not work with CUSTOM_MODEL_CLASSes // this check is not a regression in behavior because relationships don't currently - // function without access to intimate API contracts between RecordData and InternalModel. + // function without access to intimate API contracts between RecordData and Model. // This is a requirement we should fix as soon as the relationship layer does not require // this intimate API usage. if (!HAS_RECORD_DATA_PACKAGE) { diff --git a/packages/store/addon/-private/store-service.ts b/packages/store/addon/-private/store-service.ts index c593626f339..db507bea78b 100644 --- a/packages/store/addon/-private/store-service.ts +++ b/packages/store/addon/-private/store-service.ts @@ -17,7 +17,6 @@ import { HAS_MODEL_PACKAGE, HAS_RECORD_DATA_PACKAGE } from '@ember-data/private- import { DEPRECATE_HAS_RECORD, DEPRECATE_JSON_API_FALLBACK, - DEPRECATE_RECORD_WAS_INVALID, DEPRECATE_STORE_FIND, } from '@ember-data/private-build-infra/deprecations'; import type { RecordData as RecordDataClass } from '@ember-data/record-data/-private'; @@ -33,6 +32,7 @@ import type { StableExistingRecordIdentifier, StableRecordIdentifier } from '@em import type { MinimumAdapterInterface } from '@ember-data/types/q/minimum-adapter-interface'; import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-serializer-interface'; import type { RecordData } from '@ember-data/types/q/record-data'; +import { JsonApiValidationError } from '@ember-data/types/q/record-data-json-api'; import type { RecordDataRecordWrapper } from '@ember-data/types/q/record-data-record-wrapper'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; import type { SchemaDefinitionService } from '@ember-data/types/q/schema-definition-service'; @@ -41,12 +41,15 @@ import type { Dict } from '@ember-data/types/q/utils'; import edBackburner from './backburner'; import { IdentifierCache } from './caches/identifier-cache'; -import { InstanceCache, recordDataIsFullyDeleted, storeFor, StoreMap } from './caches/instance-cache'; import { + InstanceCache, peekRecordIdentifier, + recordDataIsFullyDeleted, recordIdentifierFor, setRecordIdentifier, -} from './caches/internal-model-factory'; + storeFor, + StoreMap, +} from './caches/instance-cache'; import { setRecordDataFor } from './caches/record-data-for'; import RecordReference from './legacy-model-support/record-reference'; import { DSModelSchemaDefinitionService, getModelFactory } from './legacy-model-support/schema-definition-service'; @@ -65,8 +68,6 @@ import coerceId, { ensureStringId } from './utils/coerce-id'; import constructResource from './utils/construct-resource'; import normalizeModelName from './utils/normalize-model-name'; import promiseRecord from './utils/promise-record'; -import { JsonApiValidationError } from '@ember-data/types/q/record-data-json-api'; -import InternalModel from './legacy-model-support/internal-model'; export { storeFor }; @@ -1770,42 +1771,6 @@ class Store extends Service { } } - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is rejected with a `InvalidError`. - - @method recordWasInvalid - @private - @deprecated - @param {InternalModel} internalModel - @param {Object} errors - */ - recordWasInvalid(internalModel: InternalModel, parsedErrors, error) { - if (DEPRECATE_RECORD_WAS_INVALID) { - deprecate( - `The private API recordWasInvalid will be removed in an upcoming release. Use record.errors add/remove instead if the intent was to move the record into an invalid state manually.`, - false, - { - id: 'ember-data:deprecate-record-was-invalid', - for: 'ember-data', - until: '5.0', - since: { enabled: '4.5', available: '4.5' }, - } - ); - if (DEBUG) { - assertDestroyingStore(this, 'recordWasInvalid'); - } - error = error || new Error(`unknown invalid error`); - error = typeof error === 'string' ? new Error(error) : error; - error._parsedErrors = parsedErrors; - // TODO figure out how to handle ember-model-validator given internalModel wouldn't be available - // anymore. - adapterDidInvalidate(this, internalModel.identifier, error); - } - assert(`store.recordWasInvalid has been removed`); - } - /** Push some data for a given type into the store. @@ -1985,7 +1950,7 @@ class Store extends Service { @method _push @private @param {Object} jsonApiDoc - @return {InternalModel|Array} pushed InternalModel(s) + @return {StableRecordIdentifier|Array} identifiers for the primary records that had data loaded */ _push(jsonApiDoc): StableExistingRecordIdentifier | StableExistingRecordIdentifier[] | null { if (DEBUG) { @@ -2134,7 +2099,8 @@ class Store extends Service { } // TODO we used to check if the record was destroyed here // Casting can be removed once REQUEST_SERVICE ff is turned on - // because a `Record` is provided there will always be a matching internalModel + // because a `Record` is provided there will always be a matching + // RecordData assert( `Cannot initiate a save request for an unloaded record: ${identifier}`, @@ -2544,7 +2510,11 @@ type SerializerWithParseErrors = MinimumSerializerInterface & { extractErrors?(store: Store, modelClass: ShimModelClass, error: AdapterErrors, recordId: string | null): any; }; -function adapterDidInvalidate(store: Store, identifier: StableRecordIdentifier, error: Error & { errors?: JsonApiValidationError[]; isAdapterError?: true; code?: string }) { +function adapterDidInvalidate( + store: Store, + identifier: StableRecordIdentifier, + error: Error & { errors?: JsonApiValidationError[]; isAdapterError?: true; code?: string } +) { if (error && error.isAdapterError === true && error.code === 'InvalidError') { let serializer = store.serializerFor(identifier.type) as SerializerWithParseErrors; diff --git a/tsconfig.json b/tsconfig.json index 10951be73af..87414519978 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -43,7 +43,6 @@ "ember-data-types/q/ember-data-json-api.ts", "ember-data-types/q/ds-model.ts", "packages/store/addon/-private/managers/record-data-store-wrapper.ts", - "packages/store/addon/-private/caches/internal-model-factory.ts", "packages/store/addon/-private/network/snapshot.ts", "packages/store/addon/-private/network/snapshot-record-array.ts", "packages/store/addon/-private/legacy-model-support/schema-definition-service.ts", @@ -52,7 +51,6 @@ "packages/store/addon/-private/caches/record-data-for.ts", "packages/store/addon/-private/utils/normalize-model-name.ts", "packages/store/addon/-private/legacy-model-support/shim-model-class.ts", - "packages/store/addon/-private/legacy-model-support/internal-model.ts", "packages/store/addon/-private/network/fetch-manager.ts", "packages/store/addon/-private/store-service.ts", "packages/store/addon/-private/utils/coerce-id.ts", From e99428af90e794edfaf4f6d0810bd9eee2d2af1d Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Fri, 5 Aug 2022 02:50:29 -0700 Subject: [PATCH 11/29] fix most tests, 44 to go --- .../relationships/belongs-to-test.js | 4 +- .../integration/adapter/find-all-test.js | 4 +- .../integration/adapter/rest-adapter-test.js | 11 ++- .../rest-adapter/create-record-test.js | 8 +- .../integration/adapter/store-adapter-test.js | 7 +- .../integration/record-array-manager-test.js | 14 +-- .../adapter-populated-record-array-test.js | 9 +- .../record-data/record-data-state-test.ts | 11 ++- .../record-data/store-wrapper-test.ts | 13 ++- .../integration/records/delete-record-test.js | 33 +++---- .../tests/integration/records/load-test.js | 56 +++++++---- .../tests/integration/records/unload-test.js | 84 +++++++++-------- .../relationships/has-many-test.js | 73 ++++++-------- .../inverse-relationships-test.js | 4 +- .../tests/integration/snapshot-test.js | 16 ++-- .../tests/integration/store-test.js | 5 - packages/-ember-data/tests/unit/model-test.js | 52 +--------- .../unit/model/relationships/has-many-test.js | 2 +- .../model/relationships/record-array-test.js | 11 +-- .../-ember-data/tests/unit/private-test.js | 9 -- .../unit/record-arrays/record-array-test.js | 4 +- .../tests/unit/store/unload-test.js | 2 +- packages/-ember-data/tests/unit/utils-test.js | 94 ++++++++----------- .../-private/relationships/state/has-many.ts | 22 ++--- .../addon/-private/caches/instance-cache.ts | 56 +++++------ .../managers/record-data-store-wrapper.ts | 10 +- .../store/addon/-private/store-service.ts | 8 +- 27 files changed, 281 insertions(+), 341 deletions(-) delete mode 100644 packages/-ember-data/tests/unit/private-test.js diff --git a/packages/-ember-data/tests/acceptance/relationships/belongs-to-test.js b/packages/-ember-data/tests/acceptance/relationships/belongs-to-test.js index e4d0a7b37ea..57444bec4c4 100644 --- a/packages/-ember-data/tests/acceptance/relationships/belongs-to-test.js +++ b/packages/-ember-data/tests/acceptance/relationships/belongs-to-test.js @@ -12,7 +12,7 @@ import JSONAPIAdapter from '@ember-data/adapter/json-api'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import { LEGACY_SUPPORT } from '@ember-data/model/-private'; import JSONAPISerializer from '@ember-data/serializer/json-api'; -import Store from '@ember-data/store'; +import Store, { recordIdentifierFor } from '@ember-data/store'; import { implicitRelationshipsFor } from '../../helpers/accessors'; @@ -263,7 +263,7 @@ module('async belongs-to rendering tests', function (hooks) { }); const storeWrapper = store._instanceCache._storeWrapper; - const identifier = pete._internalModel.identifier; + const identifier = recordIdentifierFor(pete); const implicitRelationships = implicitRelationshipsFor(storeWrapper, identifier); const implicitKeys = Object.keys(implicitRelationships); const petOwnerImplicit = implicitRelationships[implicitKeys[0]]; diff --git a/packages/-ember-data/tests/integration/adapter/find-all-test.js b/packages/-ember-data/tests/integration/adapter/find-all-test.js index 12e6853b56e..45b7ec65e4f 100644 --- a/packages/-ember-data/tests/integration/adapter/find-all-test.js +++ b/packages/-ember-data/tests/integration/adapter/find-all-test.js @@ -153,9 +153,7 @@ module('integration/adapter/find-all - Finding All Records of a Type', function ); store.createRecord('person', { name: 'Carsten Nielsen' }); - await settled(); - - await settled(); + // await settled(); assert.strictEqual(allRecords.length, 1, "the record array's length is 1"); assert.strictEqual( diff --git a/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js b/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js index 1dafb1860ae..53a744b6d1e 100644 --- a/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js +++ b/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js @@ -12,6 +12,7 @@ import { setupTest } from 'ember-qunit'; import RESTAdapter from '@ember-data/adapter/rest'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import RESTSerializer from '@ember-data/serializer/rest'; +import { recordIdentifierFor } from '@ember-data/store'; import deepCopy from '@ember-data/unpublished-test-infra/test-support/deep-copy'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; @@ -695,7 +696,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { this.owner.register('model:comment', Comment); let post = store.createRecord('post'); - let internalModel = post._internalModel; + let identifier = recordIdentifierFor(post); post.deleteRecord(); await post.save(); @@ -706,7 +707,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.strictEqual(passedVerb, null, 'There is no ajax call to delete a record that has never been saved.'); assert.strictEqual(passedHash, null, 'There is no ajax call to delete a record that has never been saved.'); - assert.true(internalModel.isEmpty, 'the post is now deleted'); + const isLoaded = store._instanceCache.recordIsLoaded(identifier); + assert.false(isLoaded, 'the post is now deleted'); }); test('findAll - returning an array populates the array', async function (assert) { @@ -2475,6 +2477,9 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { } catch (err) { assert.strictEqual(err, errorThrown); assert.ok(err, 'promise rejected'); + if (err !== errorThrown) { + throw err; + } } }); @@ -2626,7 +2631,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { } }); - test('findAll resolves with a collection of Models, not DS.InternalModels', async function (assert) { + test('findAll resolves with a collection of Models, not Identifiers', async function (assert) { class Post extends Model { @attr name; @hasMany('comment', { async: true, inverse: 'post' }) comments; diff --git a/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js b/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js index 71cb850e511..83e30108db4 100644 --- a/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js +++ b/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js @@ -444,7 +444,7 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio ], }); - store.push({ + const post = store.push({ data: { type: 'post', id: '1', @@ -458,7 +458,6 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio }, }, }); - store.push({ data: { type: 'comment', @@ -474,19 +473,14 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio }, }); - const post = store.peekRecord('post', 1); const commentCount = post.get('comments.length'); - assert.strictEqual(commentCount, 1, 'the post starts life with a comment'); let comment = store.createRecord('comment', { name: 'Another Comment', post: post }); - await comment.save(); - assert.strictEqual(comment.get('post'), post, 'the comment is related to the post'); await post.reload(); - assert.strictEqual(post.get('comments.length'), 2, 'Post comment count has been updated'); }); diff --git a/packages/-ember-data/tests/integration/adapter/store-adapter-test.js b/packages/-ember-data/tests/integration/adapter/store-adapter-test.js index 7e018b32ca2..5b853d8cbf5 100644 --- a/packages/-ember-data/tests/integration/adapter/store-adapter-test.js +++ b/packages/-ember-data/tests/integration/adapter/store-adapter-test.js @@ -12,15 +12,18 @@ import RESTAdapter from '@ember-data/adapter/rest'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; import RESTSerializer from '@ember-data/serializer/rest'; +import { recordIdentifierFor } from '@ember-data/store'; import { Snapshot } from '@ember-data/store/-private'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; function moveRecordOutOfInFlight(record) { // move record out of the inflight state so the tests can clean up // correctly - let { store, _internalModel } = record; + let { store } = record; + let identifier = recordIdentifierFor(record); + // TODO this would be made nicer by a cancellation API - let pending = store.getRequestStateService().getPendingRequestsForRecord(_internalModel.identifier); + let pending = store.getRequestStateService().getPendingRequestsForRecord(identifier); pending.splice(0, pending.length); } diff --git a/packages/-ember-data/tests/integration/record-array-manager-test.js b/packages/-ember-data/tests/integration/record-array-manager-test.js index 30fe12138bf..27ccc8febe0 100644 --- a/packages/-ember-data/tests/integration/record-array-manager-test.js +++ b/packages/-ember-data/tests/integration/record-array-manager-test.js @@ -97,12 +97,12 @@ module('integration/record_array_manager', function (hooks) { let adapterPopulated = manager.createAdapterPopulatedRecordArray('person', query); let allSummary = tap(all, 'willDestroy'); let adapterPopulatedSummary = tap(adapterPopulated, 'willDestroy'); - let internalPersonModel = person._internalModel; + let identifier = recordIdentifierFor(person); assert.strictEqual(allSummary.called.length, 0, 'initial: no calls to all.willDestroy'); assert.strictEqual(adapterPopulatedSummary.called.length, 0, 'initial: no calls to adapterPopulated.willDestroy'); assert.strictEqual( - manager.getRecordArraysForIdentifier(internalPersonModel.identifier).size, + manager.getRecordArraysForIdentifier(identifier).size, 1, 'initial: expected the person to be a member of 1 recordArrays' ); @@ -112,7 +112,7 @@ module('integration/record_array_manager', function (hooks) { await settled(); assert.strictEqual( - manager.getRecordArraysForIdentifier(internalPersonModel.identifier).size, + manager.getRecordArraysForIdentifier(identifier).size, 0, 'expected the person to be a member of no recordArrays' ); @@ -123,7 +123,7 @@ module('integration/record_array_manager', function (hooks) { await settled(); assert.strictEqual( - manager.getRecordArraysForIdentifier(internalPersonModel.identifier).size, + manager.getRecordArraysForIdentifier(identifier).size, 0, 'expected the person to be a member of no recordArrays' ); @@ -285,11 +285,11 @@ module('integration/record_array_manager', function (hooks) { let createRecordArrayCalled = 0; let superCreateRecordArray = manager.createRecordArray; - manager.createRecordArray = function (modelName, internalModels) { + manager.createRecordArray = function (modelName, identifiers) { createRecordArrayCalled++; assert.strictEqual(modelName, 'car'); - assert.strictEqual(internalModels.length, 1); - assert.strictEqual(internalModels[0].id, '1'); + assert.strictEqual(identifiers.length, 1); + assert.strictEqual(identifiers[0].id, '1'); return superCreateRecordArray.apply(this, arguments); }; diff --git a/packages/-ember-data/tests/integration/record-arrays/adapter-populated-record-array-test.js b/packages/-ember-data/tests/integration/record-arrays/adapter-populated-record-array-test.js index 7cff8970f4e..1be8444eea9 100644 --- a/packages/-ember-data/tests/integration/record-arrays/adapter-populated-record-array-test.js +++ b/packages/-ember-data/tests/integration/record-arrays/adapter-populated-record-array-test.js @@ -210,6 +210,7 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula }); test('pass record array to adapter.query regardless of arity', async function (assert) { + assert.expect(10); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -224,13 +225,13 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula let superCreateAdapterPopulatedRecordArray = store.recordArrayManager.createAdapterPopulatedRecordArray; - store.recordArrayManager.createStore = function (modelName, query, internalModels, _payload) { + store.recordArrayManager.createAdapterPopulatedRecordArray = function (modelName, query, identifiers, _payload) { assert.strictEqual(arguments.length, 4); assert.strictEqual(modelName, 'person'); assert.strictEqual(query, actualQuery); assert.strictEqual(_payload, payload); - assert.strictEqual(internalModels.length, 2); + assert.strictEqual(identifiers.length, 2); return superCreateAdapterPopulatedRecordArray.apply(this, arguments); }; @@ -247,7 +248,7 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula return payload; }; - store.recordArrayManager.createStore = function (modelName, query) { + store.recordArrayManager.createAdapterPopulatedRecordArray = function (modelName, query) { assert.strictEqual(arguments.length, 2); assert.strictEqual(modelName, 'person'); @@ -258,7 +259,7 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula store.query('person', actualQuery); }); - test('loadRecord re-syncs internalModels recordArrays', async function (assert) { + test('loadRecord re-syncs identifiers recordArrays', async function (assert) { let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); diff --git a/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts b/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts index 911c09d8713..d7ba18694ae 100644 --- a/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts +++ b/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts @@ -162,7 +162,9 @@ module('integration/record-data - Record Data State', function (hooks) { return isDeletionCommitted; } - setIsDeleted(identifier, isDeleted): void {} + setIsDeleted(): void { + isDeleted = true; + } } let TestStore = Store.extend({ @@ -235,6 +237,10 @@ module('integration/record-data - Record Data State', function (hooks) { storeWrapper = sw; } + isEmpty(): boolean { + return !isNew && isDeleted; + } + isNew(): boolean { return isNew; } @@ -247,7 +253,8 @@ module('integration/record-data - Record Data State', function (hooks) { return isDeletionCommitted; } - setIsDeleted(identifier, isDeleted: boolean): void { + setIsDeleted(identifier, value: boolean): void { + isDeleted = true; calledSetIsDeleted = true; } } diff --git a/packages/-ember-data/tests/integration/record-data/store-wrapper-test.ts b/packages/-ember-data/tests/integration/record-data/store-wrapper-test.ts index 1c8409dcd80..81e819992db 100644 --- a/packages/-ember-data/tests/integration/record-data/store-wrapper-test.ts +++ b/packages/-ember-data/tests/integration/record-data/store-wrapper-test.ts @@ -283,8 +283,8 @@ module('integration/store-wrapper - RecordData StoreWrapper tests', function (ho assert.expect(3); let { owner } = this; let count = 0; - let internalModel; let recordData; + let newRecordData; class RecordDataForTest extends TestRecordData { id: string; @@ -298,7 +298,7 @@ module('integration/store-wrapper - RecordData StoreWrapper tests', function (ho if (count === 1) { recordData = storeWrapper.recordDataFor('house'); } else if (count === 2) { - internalModel = store._instanceCache._internalModelForResource({ type: 'house', lid }); + newRecordData = this; } } @@ -336,15 +336,15 @@ module('integration/store-wrapper - RecordData StoreWrapper tests', function (ho assert.ok(recordData._isNew, 'Our RecordData is new'); assert.ok( - internalModel.isNew(), - 'The internalModel for a RecordData created via Wrapper.recordDataFor(type) is in the "new" state' + newRecordData.isNew(), + 'The recordData for a RecordData created via Wrapper.recordDataFor(type) is in the "new" state' ); assert.strictEqual(count, 2, 'two TestRecordDatas have been created'); }); test('setRecordId', async function (assert) { - assert.expect(1); + assert.expect(2); let { owner } = this; class RecordDataForTest extends TestRecordData { @@ -371,8 +371,7 @@ module('integration/store-wrapper - RecordData StoreWrapper tests', function (ho store = owner.lookup('service:store'); let house = store.createRecord('house'); - // TODO there is a bug when setting id while creating the Record instance, preventing the id property lookup to work - // assert.strictEqual(house.get('id'), '17', 'setRecordId correctly set the id'); + assert.strictEqual(house.get('id'), '17', 'setRecordId correctly set the id'); assert.strictEqual( store.peekRecord('house', 17), house, diff --git a/packages/-ember-data/tests/integration/records/delete-record-test.js b/packages/-ember-data/tests/integration/records/delete-record-test.js index bee1c617f2a..921abafc9f5 100644 --- a/packages/-ember-data/tests/integration/records/delete-record-test.js +++ b/packages/-ember-data/tests/integration/records/delete-record-test.js @@ -13,6 +13,7 @@ import Adapter from '@ember-data/adapter'; import { InvalidError } from '@ember-data/adapter/error'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; +import { recordIdentifierFor } from '@ember-data/store'; module('integration/deletedRecord - Deleting Records', function (hooks) { setupTest(hooks); @@ -202,11 +203,12 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { ); assert.strictEqual(get(store.peekAll('person'), 'length'), 1, 'The new person should be in the store'); - let internalModel = record._internalModel; + let identifier = recordIdentifierFor(record); + let recordData = store._instanceCache.getRecordData(identifier); record.deleteRecord(); - assert.true(internalModel.isEmpty, 'new person state is empty'); + assert.true(recordData.isEmpty(), 'new person state is empty'); assert.strictEqual(get(store.peekAll('person'), 'length'), 0, 'The new person should be removed from the store'); }); @@ -244,11 +246,12 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { ); assert.strictEqual(get(store.peekAll('person'), 'length'), 1, 'The new person should be in the store'); - let internalModel = record._internalModel; + let identifier = recordIdentifierFor(record); + let recordData = store._instanceCache.getRecordData(identifier); await record.destroyRecord(); - assert.true(internalModel.isEmpty, 'new person state is empty'); + assert.true(recordData.isEmpty(), 'new person state is empty'); assert.strictEqual(get(store.peekAll('person'), 'length'), 0, 'The new person should be removed from the store'); }); @@ -299,17 +302,14 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { assert.strictEqual(get(store.peekAll('person'), 'length'), 1, 'The new person should be in the store'); - let internalModel = record._internalModel; + let identifier = recordIdentifierFor(record); + let recordData = store._instanceCache.getRecordData(identifier); record.deleteRecord(); - // it is uncertain that `root.empty` vs `root.deleted.saved` afterwards is correct - // but this is the expected result of `unloadRecord`. We may want a `root.deleted.saved.unloaded` state? - assert.true(internalModel.isEmpty, 'We reached the correct persisted saved state'); + assert.true(recordData.isEmpty(), 'We reached the correct persisted saved state'); assert.strictEqual(get(store.peekAll('person'), 'length'), 0, 'The new person should be removed from the store'); - - // assert.ok(cache.indexOf(internalModel) === -1, 'The internal model is removed from the cache'); - assert.true(internalModel.isDestroyed, 'The internal model is destroyed'); + assert.true(recordData.isDestroyed, 'The recordData is destroyed'); await record.save(); }); @@ -332,18 +332,15 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { assert.strictEqual(get(store.peekAll('person'), 'length'), 1, 'The new person should be in the store'); - let internalModel = record._internalModel; + let identifier = recordIdentifierFor(record); + let recordData = store._instanceCache.getRecordData(identifier); record.deleteRecord(); await settled(); - // it is uncertain that `root.empty` vs `root.deleted.saved` afterwards is correct - // but this is the expected result of `unloadRecord`. We may want a `root.deleted.saved.unloaded` state? - assert.true(internalModel.isEmpty, 'We reached the correct persisted saved state'); + assert.true(recordData.isEmpty(), 'We reached the correct persisted saved state'); assert.strictEqual(get(store.peekAll('person'), 'length'), 0, 'The new person should be removed from the store'); - - // assert.ok(cache.indexOf(internalModel) === -1, 'The internal model is removed from the cache'); - assert.true(internalModel.isDestroyed, 'The internal model is destroyed'); + assert.true(recordData.isDestroyed, 'The internal model is destroyed'); record.unloadRecord(); await settled(); diff --git a/packages/-ember-data/tests/integration/records/load-test.js b/packages/-ember-data/tests/integration/records/load-test.js index dc1410b93ed..22bca825c3e 100644 --- a/packages/-ember-data/tests/integration/records/load-test.js +++ b/packages/-ember-data/tests/integration/records/load-test.js @@ -11,6 +11,18 @@ import JSONAPISerializer from '@ember-data/serializer/json-api'; import Store from '@ember-data/store'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; +function _isLoading(cache, identifier) { + const req = cache.store.getRequestStateService(); + // const fulfilled = req.getLastRequestForRecord(identifier); + const isLoaded = cache.recordIsLoaded(identifier); + + return ( + !isLoaded && + // fulfilled === null && + req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query') + ); +} + class Person extends Model { @attr() name; @@ -143,23 +155,27 @@ module('integration/load - Loading Records', function (hooks) { }) ); - let internalModel = store._instanceCache._internalModelForResource({ type: 'person', id: '1' }); + let identifier = store.identifierCache.getOrCreateRecordIdentifier({ type: 'person', id: '1' }); + let cache = store._instanceCache; + let recordData = cache.peek({ identifier, bucket: 'recordData' }); // test that our initial state is correct - assert.true(internalModel.isEmpty, 'We begin in the empty state'); - assert.false(internalModel.isLoading, 'We have not triggered a load'); + assert.strictEqual(recordData, undefined, 'We begin in the empty state'); + assert.false(_isLoading(cache, identifier), 'We have not triggered a load'); let recordPromise = store.findRecord('person', '1'); // test that during the initial load our state is correct - assert.true(internalModel.isEmpty, 'awaiting first fetch: We remain in the empty state'); - assert.true(internalModel.isLoading, 'awaiting first fetch: We have now triggered a load'); + recordData = cache.peek({ identifier, bucket: 'recordData' }); + assert.strictEqual(recordData, undefined, 'awaiting first fetch: We remain in the empty state'); + assert.true(_isLoading(cache, identifier), 'awaiting first fetch: We have now triggered a load'); let record = await recordPromise; // test that after the initial load our state is correct - assert.false(internalModel.isEmpty, 'after first fetch: We are no longer empty'); - assert.false(internalModel.isLoading, 'after first fetch: We have loaded'); + recordData = cache.peek({ identifier, bucket: 'recordData' }); + assert.false(recordData.isEmpty(), 'after first fetch: We are no longer empty'); + assert.false(_isLoading(cache, identifier), 'after first fetch: We have loaded'); assert.false(record.isReloading, 'after first fetch: We are not reloading'); let bestFriend = await record.get('bestFriend'); @@ -167,7 +183,7 @@ module('integration/load - Loading Records', function (hooks) { // shen is our retainer for the record we are testing // that ensures unloadRecord later in this test does not fully - // discard the internalModel + // discard the identifier let shen = store.peekRecord('person', '2'); assert.strictEqual(bestFriend, shen, 'Precond: bestFriend is correct'); @@ -176,37 +192,41 @@ module('integration/load - Loading Records', function (hooks) { recordPromise = record.reload(); // test that during a reload our state is correct - assert.false(internalModel.isEmpty, 'awaiting reload: We remain non-empty'); - assert.false(internalModel.isLoading, 'awaiting reload: We are not loading again'); + assert.false(recordData.isEmpty(), 'awaiting reload: We remain non-empty'); + assert.false(_isLoading(cache, identifier), 'awaiting reload: We are not loading again'); assert.true(record.isReloading, 'awaiting reload: We are reloading'); await recordPromise; // test that after a reload our state is correct - assert.false(internalModel.isEmpty, 'after reload: We remain non-empty'); - assert.false(internalModel.isLoading, 'after reload: We have loaded'); + assert.false(recordData.isEmpty(), 'after reload: We remain non-empty'); + assert.false(_isLoading(cache, identifier), 'after reload: We have loaded'); assert.false(record.isReloading, 'after reload:: We are not reloading'); run(() => record.unloadRecord()); // test that after an unload our state is correct - assert.true(internalModel.isEmpty, 'after unload: We are empty again'); - assert.false(internalModel.isLoading, 'after unload: We are not loading'); + assert.true(recordData.isEmpty(), 'after unload: We are empty again'); + assert.false(_isLoading(cache, identifier), 'after unload: We are not loading'); assert.false(record.isReloading, 'after unload:: We are not reloading'); recordPromise = store.findRecord('person', '1'); // test that during a reload-due-to-unload our state is correct // This requires a retainer (the async bestFriend relationship) - assert.true(internalModel.isEmpty, 'awaiting second find: We remain empty'); - assert.true(internalModel.isLoading, 'awaiting second find: We are loading again'); + assert.true(recordData.isEmpty(), 'awaiting second find: We remain empty'); + let newRecordData = cache.peek({ identifier, bucket: 'recordData' }); + assert.strictEqual(newRecordData, undefined, 'We have no recordData during second find'); + assert.true(_isLoading(cache, identifier), 'awaiting second find: We are loading again'); assert.false(record.isReloading, 'awaiting second find: We are not reloading'); await recordPromise; // test that after the reload-due-to-unload our state is correct - assert.false(internalModel.isEmpty, 'after second find: We are no longer empty'); - assert.false(internalModel.isLoading, 'after second find: We have loaded'); + newRecordData = cache.peek({ identifier, bucket: 'recordData' }); + assert.true(recordData.isEmpty(), 'after second find: Our original recordData is still empty'); + assert.false(newRecordData.isEmpty(), 'after second find: We are no longer empty'); + assert.false(_isLoading(cache, identifier), 'after second find: We have loaded'); assert.false(record.isReloading, 'after second find: We are not reloading'); }); }); diff --git a/packages/-ember-data/tests/integration/records/unload-test.js b/packages/-ember-data/tests/integration/records/unload-test.js index 6e6b47b674b..68b8f5c7df0 100644 --- a/packages/-ember-data/tests/integration/records/unload-test.js +++ b/packages/-ember-data/tests/integration/records/unload-test.js @@ -2,6 +2,7 @@ import { get } from '@ember/object'; import { run } from '@ember/runloop'; +import { settled } from '@ember/test-helpers'; import { module, test } from 'qunit'; import { all, resolve } from 'rsvp'; @@ -11,6 +12,7 @@ import { setupTest } from 'ember-qunit'; import JSONAPIAdapter from '@ember-data/adapter/json-api'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; +import { recordIdentifierFor } from '@ember-data/store'; function idsFromArr(arr) { return arr.map((i) => i.id); @@ -142,46 +144,44 @@ module('integration/unload - Unloading Records', function (hooks) { adapter = store.adapterFor('application'); }); - test('can unload a single record', function (assert) { - let adam; - run(function () { - store.push({ - data: { - type: 'person', - id: '1', - attributes: { - name: 'Adam Sunderland', + test('can unload a single record', async function (assert) { + let adam = store.push({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Adam Sunderland', + }, + relationships: { + cars: { + data: [ + { + id: 1, + type: 'car', + }, + ], }, - relationships: { - cars: { - data: [ - { - id: 1, - type: 'car', - }, - ], - }, - boats: { - data: [ - { - id: 2, - type: 'boat', - }, - ], - }, + boats: { + data: [ + { + id: 2, + type: 'boat', + }, + ], }, }, - }); - adam = store.peekRecord('person', 1); + }, }); - assert.strictEqual(store.peekAll('person').get('length'), 1, 'one person record loaded'); + const people = store.peekAll('person'); + assert.strictEqual(people.length, 1, 'one person record loaded in our live array'); - run(function () { - adam.unloadRecord(); - }); + adam.unloadRecord(); + await settled(); - assert.strictEqual(store.peekAll('person').get('length'), 0, 'no person records'); + assert.strictEqual(people.length, 0, 'no person records in our live array'); + adam = store.peekRecord('person', '1'); + assert.strictEqual(adam, null, 'we have no person'); }); test('can unload all records for a given type', function (assert) { @@ -795,7 +795,8 @@ module('integration/unload - Unloading Records', function (hooks) { }); }); - const internalModel = record._internalModel; + let identifier = recordIdentifierFor(record); + let recordData = store._instanceCache.getRecordData(identifier); assert.strictEqual(record.currentState.stateName, 'root.loaded.saved', 'We are loaded initially'); // we test that we can sync call unloadRecord followed by findRecord @@ -803,11 +804,11 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(record.get('cars.firstObject.make'), 'jeep'); store.unloadRecord(record); assert.true(record.isDestroying, 'the record is destroying'); - assert.true(internalModel.isEmpty, 'Expected the previous internal model tobe unloaded'); + assert.true(recordData.isEmpty(), 'Expected the previous data to be unloaded'); return store.findRecord('person', '1').then((record) => { assert.strictEqual(record.get('cars.length'), 0, 'Expected relationship to be cleared by the new push'); - assert.strictEqual(internalModel, record._internalModel, 'the old internalModel is reused'); + assert.strictEqual(identifier, recordIdentifierFor(record), 'the old identifier is reused'); assert.strictEqual( record.currentState.stateName, 'root.loaded.saved', @@ -856,7 +857,8 @@ module('integration/unload - Unloading Records', function (hooks) { }); }); - const internalModel = record._internalModel; + let identifier = recordIdentifierFor(record); + let recordData = store._instanceCache.getRecordData(identifier); const bike = store.peekRecord('bike', '1'); assert.strictEqual(record.currentState.stateName, 'root.loaded.saved', 'We are loaded initially'); @@ -867,7 +869,7 @@ module('integration/unload - Unloading Records', function (hooks) { store.unloadRecord(record); assert.true(record.isDestroying, 'the record is destroying'); assert.false(record.isDestroyed, 'the record is NOT YET destroyed'); - assert.true(internalModel.isEmpty, 'We are unloaded after unloadRecord'); + assert.true(recordData.isEmpty(), 'We are unloaded after unloadRecord'); let wait = store.findRecord('person', '1').then((newRecord) => { assert.false(record.isDestroyed, 'the record is NOT YET destroyed'); @@ -911,13 +913,14 @@ module('integration/unload - Unloading Records', function (hooks) { }); }); - let internalModel = record._internalModel; + let identifier = recordIdentifierFor(record); + let recordData = store._instanceCache.getRecordData(identifier); assert.strictEqual(record.currentState.stateName, 'root.loaded.saved', 'We are loaded initially'); run(function () { store.unloadRecord(record); assert.true(record.isDestroying, 'the record is destroying'); - assert.true(internalModel.isEmpty, 'We are unloaded after unloadRecord'); + assert.true(recordData.isEmpty(), 'We are unloaded after unloadRecord'); }); run(function () { @@ -925,7 +928,6 @@ module('integration/unload - Unloading Records', function (hooks) { }); record = store.peekRecord('person', '1'); - internalModel = record._internalModel; assert.strictEqual(record.currentState.stateName, 'root.loaded.saved', 'We are loaded after findRecord'); }); diff --git a/packages/-ember-data/tests/integration/relationships/has-many-test.js b/packages/-ember-data/tests/integration/relationships/has-many-test.js index f3294d653e2..fc90c19f7dd 100644 --- a/packages/-ember-data/tests/integration/relationships/has-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/has-many-test.js @@ -478,7 +478,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); // This tests the case where a serializer materializes a has-many - // relationship as a internalModel that it can fetch lazily. The most + // relationship as an identifier that it can fetch lazily. The most // common use case of this is to provide a URL to a collection that // is loaded later. test("A serializer can materialize a hasMany as an opaque token that can be lazily fetched via the adapter's findHasMany hook", function (assert) { @@ -2238,7 +2238,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); }); - test('dual non-async HM <-> BT', function (assert) { + test('dual non-async HM <-> BT', async function (assert) { class Message extends Model { @attr('date') created_at; @belongsTo('user', { async: false }) iser; @@ -2266,52 +2266,41 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( return resolve(serialized); }; - let post, firstComment; - - run(function () { - store.push({ - data: { - type: 'post', - id: '1', - relationships: { - comments: { - data: [{ type: 'comment', id: '1' }], - }, + let post = store.push({ + data: { + type: 'post', + id: '1', + relationships: { + comments: { + data: [{ type: 'comment', id: '1' }], }, }, - }); - store.push({ - data: { - type: 'comment', - id: '1', - relationships: { - comments: { - post: { type: 'post', id: '1' }, - }, + }, + }); + let firstComment = store.push({ + data: { + type: 'comment', + id: '1', + relationships: { + comments: { + post: { type: 'post', id: '1' }, }, }, - }); + }, + }); - post = store.peekRecord('post', 1); - firstComment = store.peekRecord('comment', 1); + const comment = store.createRecord('comment', { post }); + await comment.save(); - store - .createRecord('comment', { - post: post, - }) - .save() - .then(function (comment) { - let commentPost = comment.get('post'); - let postComments = comment.get('post.comments'); - let postCommentsLength = comment.get('post.comments.length'); - - assert.deepEqual(post, commentPost, 'expect the new comments post, to be the correct post'); - assert.ok(postComments, 'comments should exist'); - assert.strictEqual(postCommentsLength, 2, "comment's post should have a internalModel back to comment"); - assert.ok(postComments && postComments.indexOf(firstComment) !== -1, 'expect to contain first comment'); - assert.ok(postComments && postComments.indexOf(comment) !== -1, 'expected to contain the new comment'); - }); - }); + let commentPost = comment.get('post'); + let postComments = comment.get('post.comments'); + let postCommentsLength = comment.get('post.comments.length'); + + assert.deepEqual(post, commentPost, 'expect the new comments post, to be the correct post'); + assert.ok(postComments, 'comments should exist'); + assert.strictEqual(postCommentsLength, 2, "comment's post should have an identifier back to comment"); + assert.ok(postComments && postComments.indexOf(firstComment) !== -1, 'expect to contain first comment'); + assert.ok(postComments && postComments.indexOf(comment) !== -1, 'expected to contain the new comment'); }); test('When an unloaded record is added to the hasMany, it gets fetched once the hasMany is accessed even if the hasMany has been already fetched', async function (assert) { diff --git a/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js b/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js index dd9152a8a24..09048a6e58b 100644 --- a/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js +++ b/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js @@ -4,6 +4,7 @@ import { setupTest } from 'ember-qunit'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import { graphFor } from '@ember-data/record-data/-private'; +import { recordIdentifierFor } from '@ember-data/store'; import { recordDataFor } from '@ember-data/store/-private'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; @@ -673,11 +674,10 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' const post = store.createRecord('post'); post.get('comments').pushObject(comment); + const identifier = recordIdentifierFor(comment); await comment.destroyRecord(); - const identifier = comment._internalModel.identifier; - assert.false(graphFor(store).identifiers.has(identifier), 'relationships are cleared'); assert.ok(recordData.isDestroyed, 'recordData is destroyed'); }); diff --git a/packages/-ember-data/tests/integration/snapshot-test.js b/packages/-ember-data/tests/integration/snapshot-test.js index 76c1b8a704f..b5e4daad17c 100644 --- a/packages/-ember-data/tests/integration/snapshot-test.js +++ b/packages/-ember-data/tests/integration/snapshot-test.js @@ -134,8 +134,8 @@ module('integration/snapshot - Snapshot', function (hooks) { }, }, }); - let postInternalModel = store._instanceCache._internalModelForResource({ type: 'post', id: '1' }); - let snapshot = await store._instanceCache.createSnapshot(postInternalModel.identifier); + let identifier = store.identifierCache.getOrCreateRecordIdentifier({ type: 'post', id: '1' }); + let snapshot = await store._instanceCache.createSnapshot(identifier); assert.false(postClassLoaded, 'model class is not eagerly loaded'); assert.strictEqual(snapshot.type, _Post, 'type is correct'); @@ -146,7 +146,9 @@ module('integration/snapshot - Snapshot', function (hooks) { test('an initial findRecord call has no record for internal-model when a snapshot is generated', function (assert) { assert.expect(2); store.adapterFor('application').findRecord = (store, type, id, snapshot) => { - assert.false(snapshot._internalModel.hasRecord, 'We do not have a materialized record'); + const identifier = store.identifierCache.getOrCreateRecordIdentifier({ type: 'post', id: '1' }); + const record = store._instanceCache.peek({ identifier, bucket: 'record' }); + assert.false(!!record, 'We do not have a materialized record'); assert.strictEqual(snapshot.__attributes, null, 'attributes were not populated initially'); return resolve({ data: { @@ -175,8 +177,8 @@ module('integration/snapshot - Snapshot', function (hooks) { }, }); - let postInternalModel = store._instanceCache._internalModelForResource({ type: 'post', id: '1' }); - let snapshot = store._instanceCache.createSnapshot(postInternalModel.identifier); + let identifier = store.identifierCache.getOrCreateRecordIdentifier({ type: 'post', id: '1' }); + let snapshot = store._instanceCache.createSnapshot(identifier); let expected = { author: undefined, title: 'Hello World', @@ -200,8 +202,8 @@ module('integration/snapshot - Snapshot', function (hooks) { }, }); - let postInternalModel = store._instanceCache._internalModelForResource({ type: 'post', id: '1' }); - let snapshot = store._instanceCache.createSnapshot(postInternalModel.identifier); + let identifier = store.identifierCache.getOrCreateRecordIdentifier({ type: 'post', id: '1' }); + let snapshot = store._instanceCache.createSnapshot(identifier); let expected = { author: undefined, title: 'Hello World', diff --git a/packages/-ember-data/tests/integration/store-test.js b/packages/-ember-data/tests/integration/store-test.js index 3164d2e2dc5..084c515949f 100644 --- a/packages/-ember-data/tests/integration/store-test.js +++ b/packages/-ember-data/tests/integration/store-test.js @@ -1196,11 +1196,6 @@ module('integration/store - deleteRecord', function (hooks) { await assert.expectAssertion(async () => { await car.save(); }, /Your car record was saved to the server, but the response does not have an id and no id has been set client side. Records must have ids. Please update the server response to provide an id in the response or generate the id on the client side either before saving the record or while normalizing the response./); - - // This is here to transition the model out of the inFlight state to avoid - // throwing another error when the test context is torn down, which tries - // to unload the record, which is not allowed when record is inFlight. - // car._internalModel.transitionTo('loaded.saved'); }); }); diff --git a/packages/-ember-data/tests/unit/model-test.js b/packages/-ember-data/tests/unit/model-test.js index 7ca91c61356..163a86bd06f 100644 --- a/packages/-ember-data/tests/unit/model-test.js +++ b/packages/-ember-data/tests/unit/model-test.js @@ -12,6 +12,7 @@ import JSONAPIAdapter from '@ember-data/adapter/json-api'; import Model, { attr, attr as DSattr } from '@ember-data/model'; import JSONSerializer from '@ember-data/serializer/json'; import JSONAPISerializer from '@ember-data/serializer/json-api'; +import { recordIdentifierFor } from '@ember-data/store'; import { recordDataFor } from '@ember-data/store/-private'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; @@ -354,7 +355,8 @@ module('unit/model - Model', function (hooks) { // test .get access of id assert.strictEqual(person.get('id'), null, 'initial created model id should be null'); - store._instanceCache.setRecordId('odd-person', 'john', person._internalModel.clientId); + const identifier = recordIdentifierFor(person); + store._instanceCache.setRecordId('odd-person', 'john', identifier.lid); oddId = person.get('idComputed'); assert.strictEqual(oddId, 'john', 'computed get is correct'); @@ -362,43 +364,6 @@ module('unit/model - Model', function (hooks) { assert.strictEqual(person.id, 'john', 'new id should be correctly set.'); }); - test('ID mutation (complicated)', async function (assert) { - let idChange = 0; - let compChange = 0; - const OddPerson = Model.extend({ - name: DSattr('string'), - idComputed: computed('id', function () { - // we intentionally don't access the id here - return 'not-the-id:' + compChange++; - }), - idDidChange: observer('id', function () { - idChange++; - }), - }); - this.owner.register('model:odd-person', OddPerson); - - let person = store.createRecord('odd-person'); - assert.strictEqual(person.get('idComputed'), 'not-the-id:0'); - assert.strictEqual(idChange, 0, 'we have had no changes initially'); - - let personId = person.get('id'); - assert.strictEqual(personId, null, 'initial created model id should be null'); - assert.strictEqual(idChange, 0, 'we should still have no id changes'); - - // simulate an update from the store or RecordData that doesn't - // go through the internalModelFactory - person._internalModel.setId('john'); - assert.strictEqual(idChange, 1, 'we should have one change after updating id'); - let recordData = recordDataFor(person); - assert.strictEqual( - recordData.getResourceIdentifier().id, - 'john', - 'new id should be set on the identifier on record data.' - ); - assert.strictEqual(recordData.id, 'john', 'new id should be correctly set on the record data itself.'); - assert.strictEqual(person.get('id'), 'john', 'new id should be correctly set.'); - }); - test('an ID of 0 is allowed', async function (assert) { store.push({ data: { @@ -624,12 +589,6 @@ module('unit/model - Model', function (hooks) { }, /Cannot set property isLoaded of \[object Object\] which has only a getter/); }); - class NativePostWithInternalModel extends Model { - @attr('string') - _internalModel; - @attr('string') - name; - } class NativePostWithCurrentState extends Model { @attr('string') currentState; @@ -637,7 +596,6 @@ module('unit/model - Model', function (hooks) { name; } const PROP_MAP = { - _internalModel: NativePostWithInternalModel, currentState: NativePostWithCurrentState, }; @@ -677,7 +635,7 @@ module('unit/model - Model', function (hooks) { }); } - ['_internalModel', 'currentState'].forEach(testReservedProperty); + ['currentState'].forEach(testReservedProperty); testInDebug('A subclass of Model throws an error when calling create() directly', async function (assert) { class NativePerson extends Model {} @@ -738,7 +696,7 @@ module('unit/model - Model', function (hooks) { assert.strictEqual(person.currentState.stateName, 'root.loaded.saved', 'model is in loaded state'); }); - test('internalModel is ready by `init`', async function (assert) { + test('record properties can be set during `init`', async function (assert) { let nameDidChange = 0; class OddNativePerson extends Model { diff --git a/packages/-ember-data/tests/unit/model/relationships/has-many-test.js b/packages/-ember-data/tests/unit/model/relationships/has-many-test.js index 0b78b88cee5..e8822ab4808 100644 --- a/packages/-ember-data/tests/unit/model/relationships/has-many-test.js +++ b/packages/-ember-data/tests/unit/model/relationships/has-many-test.js @@ -2135,7 +2135,7 @@ module('unit/model/relationships - hasMany', function (hooks) { tom = store.peekRecord('person', '1'); sylvain = store.peekRecord('person', '2'); // Test that since sylvain.get('tags') instanceof ManyArray, - // addInternalModels on Relationship iterates correctly. + // adding records on Relationship iterates correctly. tom.get('tags').setObjects(sylvain.get('tags')); }); diff --git a/packages/-ember-data/tests/unit/model/relationships/record-array-test.js b/packages/-ember-data/tests/unit/model/relationships/record-array-test.js index aa191f8049a..512f4a6dbd0 100644 --- a/packages/-ember-data/tests/unit/model/relationships/record-array-test.js +++ b/packages/-ember-data/tests/unit/model/relationships/record-array-test.js @@ -55,16 +55,7 @@ module('unit/model/relationships - RecordArray', function (hooks) { let tag = tags.objectAt(0); assert.strictEqual(get(tag, 'name'), 'friendly', `precond - we're working with the right tags`); - set( - tags, - 'content', - A( - records - .map((r) => r._internalModel) - .slice(1, 3) - .map((im) => im.identifier) - ) - ); + set(tags, 'content', A(records.map(recordIdentifierFor).slice(1, 3))); tag = tags.objectAt(0); assert.strictEqual(get(tag, 'name'), 'smarmy', 'the lookup was updated'); diff --git a/packages/-ember-data/tests/unit/private-test.js b/packages/-ember-data/tests/unit/private-test.js deleted file mode 100644 index b66d7782d92..00000000000 --- a/packages/-ember-data/tests/unit/private-test.js +++ /dev/null @@ -1,9 +0,0 @@ -import { module, test } from 'qunit'; - -import { InternalModel } from '@ember-data/store/-private'; - -module('-private', function () { - test('`InternalModel` is accessible via private import', function (assert) { - assert.ok(!!InternalModel); - }); -}); diff --git a/packages/-ember-data/tests/unit/record-arrays/record-array-test.js b/packages/-ember-data/tests/unit/record-arrays/record-array-test.js index 6f934aa5fac..15773d990a3 100644 --- a/packages/-ember-data/tests/unit/record-arrays/record-array-test.js +++ b/packages/-ember-data/tests/unit/record-arrays/record-array-test.js @@ -366,12 +366,12 @@ module('unit/record-arrays/record-array - DS.RecordArray', function (hooks) { assert.strictEqual( snapshot1.id, String(model1.id), - 'record array snapshot should contain the first internalModel.createSnapshot result' + 'record array snapshot should contain the first createSnapshot result' ); assert.strictEqual( snapshot2.id, String(model2.id), - 'record array snapshot should contain the second internalModel.createSnapshot result' + 'record array snapshot should contain the second createSnapshot result' ); }); diff --git a/packages/-ember-data/tests/unit/store/unload-test.js b/packages/-ember-data/tests/unit/store/unload-test.js index dc4182d5d75..797d884fc3c 100644 --- a/packages/-ember-data/tests/unit/store/unload-test.js +++ b/packages/-ember-data/tests/unit/store/unload-test.js @@ -70,7 +70,7 @@ module('unit/store/unload - Store unloading records', function (hooks) { function () { record.unloadRecord(); }, - 'You can only unload a record which is not inFlight. `' + record._internalModel.toString() + '`', + 'You can only unload a record which is not inFlight. `' + record.toString() + '`', 'can not unload dirty record' ); diff --git a/packages/-ember-data/tests/unit/utils-test.js b/packages/-ember-data/tests/unit/utils-test.js index 89080884fca..ff7a91b5c8e 100644 --- a/packages/-ember-data/tests/unit/utils-test.js +++ b/packages/-ember-data/tests/unit/utils-test.js @@ -7,6 +7,7 @@ import { setupTest } from 'ember-qunit'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import { modelHasAttributeOrRelationshipNamedType } from '@ember-data/serializer/-private'; +import { recordIdentifierFor } from '@ember-data/store'; import { assertPolymorphicType } from '@ember-data/store/-debug'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; @@ -37,41 +38,33 @@ module('unit/utils', function (hooks) { }); testInDebug('assertPolymorphicType works for subclasses', function (assert) { - let user, post, person; let store = this.owner.lookup('service:store'); - - run(() => { - store.push({ - data: [ - { - type: 'user', - id: '1', - relationships: { - messages: { - data: [], - }, + let [user, post, person] = store.push({ + data: [ + { + type: 'user', + id: '1', + relationships: { + messages: { + data: [], }, }, - { - type: 'post', - id: '1', - }, - { - type: 'person', - id: '1', - }, - ], - }); - - user = store.peekRecord('user', 1); - post = store.peekRecord('post', 1); - person = store.peekRecord('person', 1); + }, + { + type: 'post', + id: '1', + }, + { + type: 'person', + id: '1', + }, + ], }); let relationship = user.relationshipFor('messages'); - user = user._internalModel.identifier; - post = post._internalModel.identifier; - person = person._internalModel.identifier; + user = recordIdentifierFor(user); + post = recordIdentifierFor(post); + person = recordIdentifierFor(person); try { assertPolymorphicType(user, relationship, post, store); @@ -109,35 +102,28 @@ module('unit/utils', function (hooks) { }); testInDebug('assertPolymorphicType works for mixins', function (assert) { - let post, video, person; let store = this.owner.lookup('service:store'); - - run(() => { - store.push({ - data: [ - { - type: 'post', - id: '1', - }, - { - type: 'video', - id: '1', - }, - { - type: 'person', - id: '1', - }, - ], - }); - post = store.peekRecord('post', 1); - video = store.peekRecord('video', 1); - person = store.peekRecord('person', 1); + let [post, video, person] = store.push({ + data: [ + { + type: 'post', + id: '1', + }, + { + type: 'video', + id: '1', + }, + { + type: 'person', + id: '1', + }, + ], }); let relationship = post.relationshipFor('medias'); - post = post._internalModel.identifier; - video = video._internalModel.identifier; - person = person._internalModel.identifier; + post = recordIdentifierFor(post); + video = recordIdentifierFor(video); + person = recordIdentifierFor(person); try { assertPolymorphicType(post, relationship, video, store); diff --git a/packages/record-data/addon/-private/relationships/state/has-many.ts b/packages/record-data/addon/-private/relationships/state/has-many.ts index 449636eeda5..41aa0b976cf 100755 --- a/packages/record-data/addon/-private/relationships/state/has-many.ts +++ b/packages/record-data/addon/-private/relationships/state/has-many.ts @@ -90,7 +90,7 @@ export default class ManyRelationship { }); } - forAllMembers(callback: (im: StableRecordIdentifier | null) => void) { + forAllMembers(callback: (identifier: StableRecordIdentifier | null) => void) { // ensure we don't walk anything twice if an entry is // in both members and canonicalMembers let seen = Object.create(null); @@ -121,14 +121,14 @@ export default class ManyRelationship { this.canonicalState = []; } - inverseDidDematerialize(inverseRecordData: StableRecordIdentifier) { - if (!this.definition.isAsync || (inverseRecordData && isNew(inverseRecordData))) { + inverseDidDematerialize(inverseIdentifier: StableRecordIdentifier) { + if (!this.definition.isAsync || (inverseIdentifier && isNew(inverseIdentifier))) { // unloading inverse of a sync relationship is treated as a client-side // delete, so actually remove the models don't merely invalidate the cp // cache. // if the record being unloaded only exists on the client, we similarly // treat it as a client side delete - this.removeCompletelyFromOwn(inverseRecordData); + this.removeCompletelyFromOwn(inverseIdentifier); } else { this.state.hasDematerializedInverse = true; } @@ -142,16 +142,16 @@ export default class ManyRelationship { This method is useful when either a deletion or a rollback on a new record needs to entirely purge itself from an inverse relationship. */ - removeCompletelyFromOwn(recordData: StableRecordIdentifier) { - this.canonicalMembers.delete(recordData); - this.members.delete(recordData); + removeCompletelyFromOwn(identifier: StableRecordIdentifier) { + this.canonicalMembers.delete(identifier); + this.members.delete(identifier); - const canonicalIndex = this.canonicalState.indexOf(recordData); + const canonicalIndex = this.canonicalState.indexOf(identifier); if (canonicalIndex !== -1) { this.canonicalState.splice(canonicalIndex, 1); } - const currentIndex = this.currentState.indexOf(recordData); + const currentIndex = this.currentState.indexOf(identifier); if (currentIndex !== -1) { this.currentState.splice(currentIndex, 1); // This allows dematerialized inverses to be rematerialized @@ -162,8 +162,8 @@ export default class ManyRelationship { } notifyHasManyChange() { - const { store, identifier: recordData } = this; - store.notifyHasManyChange(recordData.type, recordData.id, recordData.lid, this.definition.key); + const { store, identifier } = this; + store.notifyHasManyChange(identifier.type, identifier.id, identifier.lid, this.definition.key); } getData(): CollectionResourceRelationship { diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index 85472688276..dfee3250c1a 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -30,7 +30,7 @@ import coerceId, { ensureStringId } from '../utils/coerce-id'; import constructResource from '../utils/construct-resource'; import normalizeModelName from '../utils/normalize-model-name'; import WeakCache from '../utils/weak-cache'; -import recordDataFor, { setRecordDataFor } from './record-data-for'; +import recordDataFor, { removeRecordDataFor, setRecordDataFor } from './record-data-for'; /** @module @ember-data/store @@ -386,23 +386,6 @@ export class InstanceCache { return record; } - _teardownRecord(record: RecordInstance) { - StoreMap.delete(record); - // TODO remove identifier:record cache link - this.store.teardownRecord(record); - } - - removeRecord(identifier: StableRecordIdentifier): boolean { - let record = this.peek({ identifier, bucket: 'record' }); - - if (record) { - this.#instances.record.delete(identifier); - this._teardownRecord(record); - } - - return !!record; - } - // TODO move RecordData Cache into InstanceCache getRecordData(identifier: StableRecordIdentifier) { let recordData = this.peek({ identifier, bucket: 'recordData' }); @@ -484,10 +467,9 @@ export class InstanceCache { } let existingIdentifier = this.store.identifierCache.peekRecordIdentifier({ type: modelName, id }); - assert( `'${modelName}' was saved to the server, but the response returned the new id '${id}', which has already been used with another record.'`, - existingIdentifier === identifier + !existingIdentifier || existingIdentifier === identifier ); if (identifier.id === null) { @@ -554,7 +536,7 @@ export class InstanceCache { !record || record.isDestroyed || record.isDestroying ); - this.peekList[identifier.type]!.delete(identifier); + this.peekList[identifier.type]?.delete(identifier); this.store.identifierCache.forgetRecordIdentifier(identifier); } @@ -570,19 +552,29 @@ export class InstanceCache { } } - // this has to occur before the internal model is removed - // for legacy compat. - this.store._instanceCache.removeRecord(identifier); - const recordData = this.#instances.recordData.get(identifier); + // TODO is this join still necessary? + this.store._backburner.join(() => { + const record = this.peek({ identifier, bucket: 'record' }); + const recordData = this.peek({ identifier, bucket: 'recordData' }); + this.peekList[identifier.type]?.delete(identifier); + + if (record) { + this.#instances.record.delete(identifier); + StoreMap.delete(record); + // TODO remove identifier:record cache link + this.store.teardownRecord(record); + } - if (recordData) { - // TODO is this join still necessary? - this.store._backburner.join(() => { + if (recordData) { + this.#instances.recordData.delete(identifier); recordData.unloadRecord(); - }); - } + removeRecordDataFor(identifier); + } else { + this._storeWrapper.disconnectRecord(identifier.type, identifier.id, identifier.lid); + } - this.store.recordArrayManager.recordDidChange(identifier); + this.store.recordArrayManager.recordDidChange(identifier); + }); } } @@ -735,7 +727,7 @@ function _convertPreloadRelationshipToJSON(value: RecordInstance | string, type: return recordIdentifierFor(value); } -function _isEmpty(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { +export function _isEmpty(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { const recordData = cache.peek({ identifier: identifier, bucket: 'recordData' }); if (!recordData) { return true; diff --git a/packages/store/addon/-private/managers/record-data-store-wrapper.ts b/packages/store/addon/-private/managers/record-data-store-wrapper.ts index 6b36a793f66..a674ac89da0 100644 --- a/packages/store/addon/-private/managers/record-data-store-wrapper.ts +++ b/packages/store/addon/-private/managers/record-data-store-wrapper.ts @@ -171,6 +171,7 @@ export default class RecordDataStoreWrapper implements StoreWrapper { const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); this._store._notificationManager.notify(identifier, 'state'); + if (!key || key === 'isDeletionCommitted') { this._store.recordArrayManager.recordDidChange(identifier); } @@ -191,7 +192,14 @@ export default class RecordDataStoreWrapper implements StoreWrapper { ? this.identifierCache.createIdentifierForNewRecord({ type: type }) : this.identifierCache.getOrCreateRecordIdentifier(constructResource(type, id, lid)); - return this._store._instanceCache.getRecordData(identifier); + const recordData = this._store._instanceCache.getRecordData(identifier); + + if (!id && !lid) { + recordData.clientDidCreate(); + this._store.recordArrayManager.recordDidChange(identifier); + } + + return recordData; } setRecordId(type: string, id: string, lid: string) { diff --git a/packages/store/addon/-private/store-service.ts b/packages/store/addon/-private/store-service.ts index db507bea78b..b2c33a781ed 100644 --- a/packages/store/addon/-private/store-service.ts +++ b/packages/store/addon/-private/store-service.ts @@ -42,6 +42,7 @@ import type { Dict } from '@ember-data/types/q/utils'; import edBackburner from './backburner'; import { IdentifierCache } from './caches/identifier-cache'; import { + _isEmpty, InstanceCache, peekRecordIdentifier, recordDataIsFullyDeleted, @@ -471,6 +472,9 @@ class Store extends Service { } const identifier = this.identifierCache.createIdentifierForNewRecord(resource); + const recordData = this._instanceCache.getRecordData(identifier); + recordData.clientDidCreate(); + this.recordArrayManager.recordDidChange(identifier); return this._instanceCache.getRecord(identifier, properties); }); @@ -2101,11 +2105,9 @@ class Store extends Service { // Casting can be removed once REQUEST_SERVICE ff is turned on // because a `Record` is provided there will always be a matching // RecordData - assert( `Cannot initiate a save request for an unloaded record: ${identifier}`, - - recordData && !recordData.isEmpty?.() + recordData && !_isEmpty(this._instanceCache, identifier) ); if (recordDataIsFullyDeleted(this._instanceCache, identifier)) { return resolve(record); From 04edfe97d61c8f1a0d3b2e84728d652c6fecc0cb Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Fri, 5 Aug 2022 16:42:50 -0700 Subject: [PATCH 12/29] 71 test failures to go, implemente PromiseManyArray deprecation from RFC https://github.com/emberjs/rfcs/pull/745 --- .../q/record-data-record-wrapper.ts | 12 +- ember-data-types/q/record-data.ts | 2 - .../acceptance/relationships/has-many-test.js | 276 ++++++++++++++++- .../adapter/json-api-adapter-test.js | 9 +- .../rest-adapter/create-record-test.js | 3 +- .../non-dasherized-lookups-test.js | 12 +- .../integration/records/delete-record-test.js | 7 +- .../relationships/one-to-one-test.js | 33 +- .../custom-class-model-test.ts | 4 +- .../tests/unit/store/unload-test.js | 3 +- packages/model/addon/-private/attr.js | 8 + .../-private/legacy-relationships-support.ts | 17 +- packages/model/addon/-private/model.js | 14 +- .../addon/-private/promise-many-array.ts | 291 +++++++++++------- .../addon/current-deprecations.ts | 1 + .../private-build-infra/addon/deprecations.ts | 2 +- .../record-data/addon/-private/record-data.ts | 28 -- .../adapter-populated-record-array.ts | 6 +- .../store/addon/-private/store-service.ts | 10 +- 19 files changed, 514 insertions(+), 224 deletions(-) diff --git a/ember-data-types/q/record-data-record-wrapper.ts b/ember-data-types/q/record-data-record-wrapper.ts index 8b0241ea189..079898863ac 100644 --- a/ember-data-types/q/record-data-record-wrapper.ts +++ b/ember-data-types/q/record-data-record-wrapper.ts @@ -7,7 +7,7 @@ import type { JsonApiValidationError } from './record-data-json-api'; @module @ember-data/store */ -export interface RecordDataRecordWrapper { +export interface RecordDataWrapper { rollbackAttributes(): string[]; changedAttributes(): ChangedAttributesHash; hasChangedAttributes(): boolean; @@ -16,17 +16,15 @@ export interface RecordDataRecordWrapper { getAttr(key: string): any; getHasMany(key: string): CollectionResourceRelationship; - addToHasMany(key: string, recordDatas: RecordDataRecordWrapper[], idx?: number): void; - removeFromHasMany(key: string, recordDatas: RecordDataRecordWrapper[]): void; - setDirtyHasMany(key: string, recordDatas: RecordDataRecordWrapper[]): void; + addToHasMany(key: string, recordDatas: RecordDataWrapper[], idx?: number): void; + removeFromHasMany(key: string, recordDatas: RecordDataWrapper[]): void; + setDirtyHasMany(key: string, recordDatas: RecordDataWrapper[]): void; getBelongsTo(key: string): SingleResourceRelationship; - setDirtyBelongsTo(name: string, recordData: RecordDataRecordWrapper | null): void; + setDirtyBelongsTo(name: string, recordData: RecordDataWrapper | null): void; // ----- unspecced - isAttrDirty(key: string): boolean; - removeFromInverseRelationships(): void; hasAttr(key: string): boolean; // new diff --git a/ember-data-types/q/record-data.ts b/ember-data-types/q/record-data.ts index 097a24ab7db..410e0391d32 100644 --- a/ember-data-types/q/record-data.ts +++ b/ember-data-types/q/record-data.ts @@ -45,8 +45,6 @@ export interface RecordData { didCommit(data: JsonApiResource | null): void; // ----- unspecced - isAttrDirty(key: string): boolean; - removeFromInverseRelationships(): void; hasAttr(key: string): boolean; isRecordInUse(): boolean; diff --git a/packages/-ember-data/tests/acceptance/relationships/has-many-test.js b/packages/-ember-data/tests/acceptance/relationships/has-many-test.js index 0f29c76699f..b42884fcc05 100644 --- a/packages/-ember-data/tests/acceptance/relationships/has-many-test.js +++ b/packages/-ember-data/tests/acceptance/relationships/has-many-test.js @@ -18,6 +18,7 @@ import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import { LEGACY_SUPPORT } from '@ember-data/model/-private'; import JSONAPISerializer from '@ember-data/serializer/json-api'; import Store from '@ember-data/store'; +import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; class Person extends Model { @attr() @@ -751,14 +752,212 @@ module('autotracking has-many', function (hooks) { assert.deepEqual(names, ['RGB', 'RGB'], 'rendered 2 children'); }); + deprecatedTest( + 'We can re-render hasMany w/PromiseManyArray.sortBy', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 6 }, + async function (assert) { + class ChildrenList extends Component { + @service store; + + get sortedChildren() { + return this.args.person.children.sortBy('name'); + } + + @action + createChild() { + const parent = this.args.person; + const name = 'RGB'; + this.store.createRecord('person', { name, parent }); + } + } + + let layout = hbs` + + +

{{this.sortedChildren.length}}

+
    + {{#each this.sortedChildren as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `; + this.owner.register('component:children-list', ChildrenList); + this.owner.register('template:components/children-list', layout); + + store.createRecord('person', { id: '1', name: 'Doodad' }); + this.person = store.peekRecord('person', '1'); + + await render(hbs``); + + let names = findAll('li').map((e) => e.textContent); + + assert.deepEqual(names, [], 'rendered no children'); + + await click('#createChild'); + + names = findAll('li').map((e) => e.textContent); + assert.deepEqual(names, ['RGB'], 'rendered 1 child'); + + await click('#createChild'); + + names = findAll('li').map((e) => e.textContent); + assert.deepEqual(names, ['RGB', 'RGB'], 'rendered 2 children'); + } + ); + + deprecatedTest( + 'We can re-render hasMany with sort computed macro on PromiseManyArray', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 6 }, + async function (assert) { + class ChildrenList extends Component { + @service store; + + sortProperties = ['name']; + @sort('args.person.children', 'sortProperties') sortedChildren; + + @action + createChild() { + const parent = this.args.person; + const name = 'RGB'; + this.store.createRecord('person', { name, parent }); + } + } + + let layout = hbs` + + +

{{this.sortedChildren.length}}

+
    + {{#each this.sortedChildren as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `; + this.owner.register('component:children-list', ChildrenList); + this.owner.register('template:components/children-list', layout); + + store.createRecord('person', { id: '1', name: 'Doodad' }); + this.person = store.peekRecord('person', '1'); + + await render(hbs``); + + let names = findAll('li').map((e) => e.textContent); + + assert.deepEqual(names, [], 'rendered no children'); + + await click('#createChild'); + + names = findAll('li').map((e) => e.textContent); + assert.deepEqual(names, ['RGB'], 'rendered 1 child'); + + await click('#createChild'); + + names = findAll('li').map((e) => e.textContent); + assert.deepEqual(names, ['RGB', 'RGB'], 'rendered 2 children'); + } + ); + + deprecatedTest( + 'We can re-render hasMany with PromiseManyArray.objectAt', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 6 }, + async function (assert) { + class ChildrenList extends Component { + @service store; + + get firstChild() { + return this.args.person.children.objectAt(0); + } + + @action + createChild() { + const parent = this.args.person; + const name = 'RGB'; + this.store.createRecord('person', { name, parent }); + } + } + + let layout = hbs` + + +

{{this.firstChild.name}}

+ `; + this.owner.register('component:children-list', ChildrenList); + this.owner.register('template:components/children-list', layout); + + store.createRecord('person', { id: '1', name: 'Doodad' }); + this.person = store.peekRecord('person', '1'); + + await render(hbs``); + + assert.dom('h2').hasText('', 'rendered no children'); + + await click('#createChild'); + + assert.dom('h2').hasText('RGB', 'renders first child'); + + await click('#createChild'); + + assert.dom('h2').hasText('RGB', 'renders first child'); + } + ); + + deprecatedTest( + 'We can re-render hasMany with PromiseManyArray.map', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 6 }, + async function (assert) { + class ChildrenList extends Component { + @service store; + + get children() { + return this.args.person.children.map((child) => child); + } + + @action + createChild() { + const parent = this.args.person; + const name = 'RGB'; + this.store.createRecord('person', { name, parent }); + } + } + + let layout = hbs` + + +

{{this.children.length}}

+
    + {{#each this.children as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `; + this.owner.register('component:children-list', ChildrenList); + this.owner.register('template:components/children-list', layout); + + store.createRecord('person', { id: '1', name: 'Doodad' }); + this.person = store.peekRecord('person', '1'); + + await render(hbs``); + + let names = findAll('li').map((e) => e.textContent); + + assert.deepEqual(names, [], 'rendered no children'); + + await click('#createChild'); + + names = findAll('li').map((e) => e.textContent); + assert.deepEqual(names, ['RGB'], 'rendered 1 child'); + + await click('#createChild'); + + names = findAll('li').map((e) => e.textContent); + assert.deepEqual(names, ['RGB', 'RGB'], 'rendered 2 children'); + } + ); + test('We can re-render hasMany', async function (assert) { class ChildrenList extends Component { @service store; - get sortedChildren() { - return this.args.person.children.sortBy('name'); - } - @action createChild() { const parent = this.args.person; @@ -770,9 +969,9 @@ module('autotracking has-many', function (hooks) { let layout = hbs` -

{{this.sortedChildren.length}}

+

{{@person.children.length}}

    - {{#each this.sortedChildren as |child|}} + {{#each @person.children as |child|}}
  • {{child.name}}
  • {{/each}}
@@ -805,7 +1004,7 @@ module('autotracking has-many', function (hooks) { @service store; sortProperties = ['name']; - @sort('args.person.children', 'sortProperties') sortedChildren; + @sort('args.children', 'sortProperties') sortedChildren; @action createChild() { @@ -830,8 +1029,9 @@ module('autotracking has-many', function (hooks) { store.createRecord('person', { id: '1', name: 'Doodad' }); this.person = store.peekRecord('person', '1'); + this.children = await this.person.children; - await render(hbs``); + await render(hbs``); let names = findAll('li').map((e) => e.textContent); @@ -853,7 +1053,7 @@ module('autotracking has-many', function (hooks) { @service store; get firstChild() { - return this.args.person.children.objectAt(0); + return this.args.children.objectAt(0); } @action @@ -874,8 +1074,9 @@ module('autotracking has-many', function (hooks) { store.createRecord('person', { id: '1', name: 'Doodad' }); this.person = store.peekRecord('person', '1'); + this.children = await this.person.children; - await render(hbs``); + await render(hbs``); assert.dom('h2').hasText('', 'rendered no children'); @@ -893,7 +1094,7 @@ module('autotracking has-many', function (hooks) { @service store; get children() { - return this.args.person.children.map((child) => child); + return this.args.children.map((child) => child); } @action @@ -919,8 +1120,59 @@ module('autotracking has-many', function (hooks) { store.createRecord('person', { id: '1', name: 'Doodad' }); this.person = store.peekRecord('person', '1'); + this.children = await this.person.children; - await render(hbs``); + await render(hbs``); + + let names = findAll('li').map((e) => e.textContent); + + assert.deepEqual(names, [], 'rendered no children'); + + await click('#createChild'); + + names = findAll('li').map((e) => e.textContent); + assert.deepEqual(names, ['RGB'], 'rendered 1 child'); + + await click('#createChild'); + + names = findAll('li').map((e) => e.textContent); + assert.deepEqual(names, ['RGB', 'RGB'], 'rendered 2 children'); + }); + + test('We can re-render hasMany with toArray', async function (assert) { + class ChildrenList extends Component { + @service store; + + get children() { + return this.args.children.toArray(); + } + + @action + createChild() { + const parent = this.args.person; + const name = 'RGB'; + this.store.createRecord('person', { name, parent }); + } + } + + let layout = hbs` + + +

{{this.children.length}}

+
    + {{#each this.children as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `; + this.owner.register('component:children-list', ChildrenList); + this.owner.register('template:components/children-list', layout); + + store.createRecord('person', { id: '1', name: 'Doodad' }); + this.person = store.peekRecord('person', '1'); + this.children = await this.person.children; + + await render(hbs``); let names = findAll('li').map((e) => e.textContent); diff --git a/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js b/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js index 87397505871..fdb64043c13 100644 --- a/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js +++ b/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js @@ -200,15 +200,18 @@ module('integration/adapter/json-api-adapter - JSONAPIAdapter', function (hooks) 'The author for the last post is loaded and has the correct last name' ); - assert.strictEqual(posts.firstObject.comments.length, 0, 'First post doesnt have comments'); + const firstComments = await posts.firstObject.comments; + const lastComments = await posts.lastObject.comments; + + assert.strictEqual(firstComments.length, 0, 'First post doesnt have comments'); assert.strictEqual( - posts.lastObject.comments.firstObject.text, + lastComments.firstObject.text, 'This is the first comment', 'Loads first comment for second post' ); assert.strictEqual( - posts.lastObject.comments.lastObject.text, + lastComments.lastObject.text, 'This is the second comment', 'Loads second comment for second post' ); diff --git a/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js b/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js index 83e30108db4..6e89d8076da 100644 --- a/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js +++ b/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js @@ -185,7 +185,8 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio const post = store.peekRecord('post', 1); const comment = store.createRecord('comment', { name: 'The Parley Letter' }); - post.get('comments').pushObject(comment); + const comments = await post.get('comments'); + comments.pushObject(comment); assert.strictEqual(comment.get('post'), post, 'the post has been set correctly'); diff --git a/packages/-ember-data/tests/integration/backwards-compat/non-dasherized-lookups-test.js b/packages/-ember-data/tests/integration/backwards-compat/non-dasherized-lookups-test.js index f042aa0729a..bf20ebc7d08 100644 --- a/packages/-ember-data/tests/integration/backwards-compat/non-dasherized-lookups-test.js +++ b/packages/-ember-data/tests/integration/backwards-compat/non-dasherized-lookups-test.js @@ -151,7 +151,7 @@ module( }); }); - test('looks up belongsTo using under_scored strings', function (assert) { + test('looks up belongsTo using under_scored strings', async function (assert) { assert.expect(1); let store = this.owner.lookup('service:store'); @@ -181,13 +181,11 @@ module( }); }); - run(() => { - store.findRecord('long_model_name', 1).then((longModelName) => { - const postNotes = get(longModelName, 'postNotes').toArray(); + const longModel = await store.findRecord('long_model_name', '1'); + const postNotesRel = await longModel.postNotes; + const postNotes = postNotesRel.toArray(); - assert.deepEqual(postNotes, [store.peekRecord('postNote', 1)], 'inverse records found'); - }); - }); + assert.deepEqual(postNotes, [store.peekRecord('postNote', 1)], 'inverse records found'); }); } ); diff --git a/packages/-ember-data/tests/integration/records/delete-record-test.js b/packages/-ember-data/tests/integration/records/delete-record-test.js index 921abafc9f5..5e46119609e 100644 --- a/packages/-ember-data/tests/integration/records/delete-record-test.js +++ b/packages/-ember-data/tests/integration/records/delete-record-test.js @@ -429,8 +429,9 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { // Sanity Check assert.ok(group, 'expected group to be found'); assert.strictEqual(group.get('company.name'), 'Inc.', 'group belongs to our company'); - assert.strictEqual(group.get('employees.length'), 1, 'expected 1 related record before delete'); - employee = group.get('employees').objectAt(0); + assert.strictEqual(group.employees.length, 1, 'expected 1 related record before delete'); + const employees = await group.employees; + employee = employees.objectAt(0); assert.strictEqual(employee.get('name'), 'Adam Sunderland', 'expected related records to be loaded'); await group.destroyRecord(); @@ -444,6 +445,6 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { store.push(jsonGroup); group = store.peekRecord('group', '1'); - assert.strictEqual(group.get('employees.length'), 1, 'expected 1 related record after delete and restore'); + assert.strictEqual(group.employees.length, 1, 'expected 1 related record after delete and restore'); }); }); diff --git a/packages/-ember-data/tests/integration/relationships/one-to-one-test.js b/packages/-ember-data/tests/integration/relationships/one-to-one-test.js index d2239fcf01a..766d0346915 100644 --- a/packages/-ember-data/tests/integration/relationships/one-to-one-test.js +++ b/packages/-ember-data/tests/integration/relationships/one-to-one-test.js @@ -1,4 +1,5 @@ import { run } from '@ember/runloop'; +import { settled } from '@ember/test-helpers'; import { module, test } from 'qunit'; import { Promise as EmberPromise, resolve } from 'rsvp'; @@ -1009,27 +1010,23 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }); }); - test('Rollbacking attributes of created record removes the relationship on both sides - sync', function (assert) { + test('Rollbacking attributes of created record removes the relationship on both sides - sync', async function (assert) { let store = this.owner.lookup('service:store'); - var user, job; - run(function () { - user = store.push({ - data: { - id: 1, - type: 'user', - attributes: { - name: 'Stanley', - }, + const user = store.push({ + data: { + id: 1, + type: 'user', + attributes: { + name: 'Stanley', }, - }); - - job = store.createRecord('job', { user: user }); - }); - run(function () { - job.rollbackAttributes(); + }, }); - assert.strictEqual(user.get('job'), null, 'Job got rollbacked correctly'); - assert.strictEqual(job.get('user'), null, 'Job does not have user anymore'); + const job = store.createRecord('job', { user: user }); + job.rollbackAttributes(); + await settled(); + + assert.strictEqual(user.job, null, 'Job got rollbacked correctly'); + assert.true(job.isDestroyed, 'Job is destroyed'); }); }); diff --git a/packages/-ember-data/tests/unit/custom-class-support/custom-class-model-test.ts b/packages/-ember-data/tests/unit/custom-class-support/custom-class-model-test.ts index 30df23e5a59..983d6008ba1 100644 --- a/packages/-ember-data/tests/unit/custom-class-support/custom-class-model-test.ts +++ b/packages/-ember-data/tests/unit/custom-class-support/custom-class-model-test.ts @@ -11,7 +11,7 @@ import Store from '@ember-data/store'; import type { RecordDataStoreWrapper, Snapshot } from '@ember-data/store/-private'; import type NotificationManager from '@ember-data/store/-private/managers/record-notification-manager'; import type { RecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier'; -import type { RecordDataRecordWrapper } from '@ember-data/types/q/record-data-record-wrapper'; +import type { RecordDataWrapper } from '@ember-data/types/q/record-data-record-wrapper'; import type { AttributesSchema, RelationshipsSchema } from '@ember-data/types/q/record-data-schemas'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; import type { SchemaDefinitionService } from '@ember-data/types/q/schema-definition-service'; @@ -313,7 +313,7 @@ module('unit/model - Custom Class Model', function (hooks) { }); test('store.deleteRecord', async function (assert) { - let rd: RecordDataRecordWrapper; + let rd: RecordDataWrapper; assert.expect(9); this.owner.register( 'adapter:application', diff --git a/packages/-ember-data/tests/unit/store/unload-test.js b/packages/-ember-data/tests/unit/store/unload-test.js index 797d884fc3c..e7bcf6ae673 100644 --- a/packages/-ember-data/tests/unit/store/unload-test.js +++ b/packages/-ember-data/tests/unit/store/unload-test.js @@ -9,6 +9,7 @@ import { setupTest } from 'ember-qunit'; import Adapter from '@ember-data/adapter'; import Model, { attr, belongsTo } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; +import { recordIdentifierFor } from '@ember-data/store'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; let store, tryToFind; @@ -70,7 +71,7 @@ module('unit/store/unload - Store unloading records', function (hooks) { function () { record.unloadRecord(); }, - 'You can only unload a record which is not inFlight. `' + record.toString() + '`', + `You can only unload a record which is not inFlight. '` + recordIdentifierFor(record).toString() + `'`, 'can not unload dirty record' ); diff --git a/packages/model/addon/-private/attr.js b/packages/model/addon/-private/attr.js index ec53d9e57db..f1821b82bff 100644 --- a/packages/model/addon/-private/attr.js +++ b/packages/model/addon/-private/attr.js @@ -137,6 +137,14 @@ function attr(type, options) { } } let recordData = recordDataFor(this); + // TODO hasAttr is not spec'd + // essentially this is needed because + // there is a difference between "undefined" meaning never set + // and "undefined" meaning set to "undefined". In the "key present" + // case we want to return undefined. In the "key absent" case + // we want to return getDefaultValue. RecordDataV2 can fix this + // by providing the attributes blob such that we can make our + // own determination. if (recordData.hasAttr(key)) { return recordData.getAttr(key); } else { diff --git a/packages/model/addon/-private/legacy-relationships-support.ts b/packages/model/addon/-private/legacy-relationships-support.ts index 2a44477df31..4baac480a46 100644 --- a/packages/model/addon/-private/legacy-relationships-support.ts +++ b/packages/model/addon/-private/legacy-relationships-support.ts @@ -14,9 +14,9 @@ import type { UpgradedMeta } from '@ember-data/record-data/-private/graph/-edge- import type { RelationshipState } from '@ember-data/record-data/-private/graph/-state'; import type Store from '@ember-data/store'; import { recordDataFor, recordIdentifierFor, storeFor } from '@ember-data/store/-private'; -import type { IdentifierCache } from '@ember-data/store/-private/caches/identifier-cache'; +import { IdentifierCache } from '@ember-data/store/-private/caches/identifier-cache'; import type { DSModel } from '@ember-data/types/q/ds-model'; -import type { ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api'; +import { ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordData } from '@ember-data/types/q/record-data'; import type { JsonApiRelationship } from '@ember-data/types/q/record-data-json-api'; @@ -140,8 +140,7 @@ export class LegacySupport { `You looked up the '${key}' relationship on a '${identifier.type}' with id ${ identifier.id || 'null' } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (\`belongsTo({ async: true })\`)`, - toReturn === null || - !store._instanceCache.peek({ identifier: relatedIdentifier, bucket: 'recordData' })?.isEmpty?.() + toReturn === null || store._instanceCache.recordIsLoaded(relatedIdentifier) ); return toReturn; } @@ -674,16 +673,6 @@ function anyUnloaded(store: Store, relationship: ManyRelationship) { return unloaded || false; } -/** - * Flag indicating whether all inverse records are available - * - * true if the inverse exists and is loaded (not empty) - * true if there is no inverse - * false if the inverse exists and is not loaded (empty) - * - * @internal - * @return {boolean} - */ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship): boolean { const cache = store.identifierCache; diff --git a/packages/model/addon/-private/model.js b/packages/model/addon/-private/model.js index 25f2cf7ed31..a87289149f5 100644 --- a/packages/model/addon/-private/model.js +++ b/packages/model/addon/-private/model.js @@ -818,9 +818,13 @@ class Model extends EmberObject { */ rollbackAttributes() { const { currentState } = this; + const { isNew } = currentState; recordDataFor(this).rollbackAttributes(); this.errors.clear(); currentState.cleanErrorRequests(); + if (isNew) { + this.unloadRecord(); + } } /** @@ -1805,7 +1809,7 @@ class Model extends EmberObject { let fields = Blog.fields; fields.forEach(function(kind, field) { - console.log(field, kind); + // do thing }); // prints: @@ -1987,7 +1991,7 @@ class Model extends EmberObject { let attributes = Person.attributes attributes.forEach(function(meta, name) { - console.log(name, meta); + // do thing }); // prints: @@ -2064,7 +2068,7 @@ class Model extends EmberObject { let transformedAttributes = Person.transformedAttributes transformedAttributes.forEach(function(field, type) { - console.log(field, type); + // do thing }); // prints: @@ -2137,7 +2141,7 @@ class Model extends EmberObject { } PersonModel.eachAttribute(function(name, meta) { - console.log(name, meta); + // do thing }); // prints: @@ -2206,7 +2210,7 @@ class Model extends EmberObject { }); Person.eachTransformedAttribute(function(name, type) { - console.log(name, type); + // do thing }); // prints: diff --git a/packages/model/addon/-private/promise-many-array.ts b/packages/model/addon/-private/promise-many-array.ts index 6f8336055d6..8495c28598b 100644 --- a/packages/model/addon/-private/promise-many-array.ts +++ b/packages/model/addon/-private/promise-many-array.ts @@ -1,6 +1,6 @@ import ArrayMixin, { NativeArray } from '@ember/array'; import type ArrayProxy from '@ember/array/proxy'; -import { assert } from '@ember/debug'; +import { assert, deprecate } from '@ember/debug'; import { dependentKeyCompat } from '@ember/object/compat'; import { tracked } from '@glimmer/tracking'; import Ember from 'ember'; @@ -9,8 +9,10 @@ import { resolve } from 'rsvp'; import type { ManyArray } from 'ember-data/-private'; +import { DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS } from '@ember-data/private-build-infra/deprecations'; import { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; +import { FindOptions } from '@ember-data/types/q/store'; export interface HasManyProxyCreateArgs { promise: Promise; @@ -24,16 +26,10 @@ export interface HasManyProxyCreateArgs { This class is returned as the result of accessing an async hasMany relationship on an instance of a Model extending from `@ember-data/model`. - A PromiseManyArray is an array-like proxy that also proxies certain method calls - to the underlying ManyArray in addition to being "promisified". + A PromiseManyArray is an iterable proxy that allows templates to consume related + ManyArrays and update once their contents are no longer pending. - Right now we proxy: - - * `reload()` - * `createRecord()` - - This promise-proxy behavior is primarily to ensure that async relationship interact - nicely with templates. In your JS code you should resolve the promise first. + In your JS code you should resolve the promise first. ```js const comments = await post.comments; @@ -42,10 +38,14 @@ export interface HasManyProxyCreateArgs { @class PromiseManyArray @public */ -export default interface PromiseManyArray extends Omit, 'destroy'> {} +export default interface PromiseManyArray extends Omit, 'destroy'> { + createRecord(): RecordInstance; + reload(options: FindOptions): PromiseManyArray; +} export default class PromiseManyArray { declare promise: Promise | null; declare isDestroyed: boolean; + // @deprecated (isDestroyed is not deprecated) declare isDestroying: boolean; constructor(promise: Promise, content?: ManyArray) { @@ -90,6 +90,8 @@ export default class PromiseManyArray { /** * Iterate the proxied content. Called by the glimmer iterator in #each + * We do not guarantee that forEach will always be available. This + * may eventually be made to use Symbol.Iterator once glimmer supports it. * * @method forEach * @param cb @@ -201,19 +203,6 @@ export default class PromiseManyArray { return this.content ? this.content.meta : undefined; } - /** - * Reload the relationship - * @method reload - * @public - * @param options - * @returns - */ - reload(options) { - assert('You are trying to reload an async manyArray before it has been created', this.content); - this.content.reload(options); - return this; - } - //---- Our own stuff _update(promise: Promise, content?: ManyArray) { @@ -227,22 +216,79 @@ export default class PromiseManyArray { static create({ promise, content }: HasManyProxyCreateArgs): PromiseManyArray { return new this(promise, content); } +} - // Methods on ManyArray which people should resolve the relationship first before calling - createRecord(...args) { +if (DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS) { + // TODO update RFC to also note reload deprecation + /** + * Reload the relationship + * @method reload + * @deprecated + * @public + * @param options + * @returns + */ + PromiseManyArray.prototype.reload = function reload(options: FindOptions) { + deprecate( + `The reload method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly or use the HasManyReference interface.`, + false, + { + id: 'ember-data:deprecate-promise-many-array-behaviors', + until: '5.0', + since: { enabled: '4.8', available: '4.8' }, + for: 'ember-data', + } + ); + assert('You are trying to reload an async manyArray before it has been created', this.content); + this.content.reload(options); + return this; + }; + PromiseManyArray.prototype.createRecord = function createRecord(...args) { + deprecate( + `The createRecord method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`, + false, + { + id: 'ember-data:deprecate-promise-many-array-behaviors', + until: '5.0', + since: { enabled: '4.8', available: '4.8' }, + for: 'ember-data', + } + ); assert('You are trying to createRecord on an async manyArray before it has been created', this.content); return this.content.createRecord(...args); - } - - // Properties/Methods on ArrayProxy we should deprecate - - get firstObject() { - return this.content ? this.content.firstObject : undefined; - } + }; - get lastObject() { - return this.content ? this.content.lastObject : undefined; - } + Object.defineProperty(PromiseManyArray.prototype, 'firstObject', { + get() { + deprecate( + `The firstObject property on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`, + false, + { + id: 'ember-data:deprecate-promise-many-array-behaviors', + until: '5.0', + since: { enabled: '4.8', available: '4.8' }, + for: 'ember-data', + } + ); + return this.content ? this.content.firstObject : undefined; + }, + }); + + Object.defineProperty(PromiseManyArray.prototype, 'lastObject', { + get() { + deprecate( + `The lastObject property on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`, + false, + { + id: 'ember-data:deprecate-promise-many-array-behaviors', + until: '5.0', + since: { enabled: '4.8', available: '4.8' }, + for: 'ember-data', + } + ); + return this.content ? this.content.lastObject : undefined; + }, + }); } function tapPromise(proxy: PromiseManyArray, promise: Promise) { @@ -268,78 +314,101 @@ function tapPromise(proxy: PromiseManyArray, promise: Promise) { ); } -const EmberObjectMethods = [ - 'addObserver', - 'cacheFor', - 'decrementProperty', - 'get', - 'getProperties', - 'incrementProperty', - 'notifyPropertyChange', - 'removeObserver', - 'set', - 'setProperties', - 'toggleProperty', -]; -EmberObjectMethods.forEach((method) => { - PromiseManyArray.prototype[method] = function delegatedMethod(...args) { - return Ember[method](this, ...args); - }; -}); - -const InheritedProxyMethods = [ - 'addArrayObserver', - 'addObject', - 'addObjects', - 'any', - 'arrayContentDidChange', - 'arrayContentWillChange', - 'clear', - 'compact', - 'every', - 'filter', - 'filterBy', - 'find', - 'findBy', - 'getEach', - 'includes', - 'indexOf', - 'insertAt', - 'invoke', - 'isAny', - 'isEvery', - 'lastIndexOf', - 'map', - 'mapBy', - 'objectAt', - 'objectsAt', - 'popObject', - 'pushObject', - 'pushObjects', - 'reduce', - 'reject', - 'rejectBy', - 'removeArrayObserver', - 'removeAt', - 'removeObject', - 'removeObjects', - 'replace', - 'reverseObjects', - 'setEach', - 'setObjects', - 'shiftObject', - 'slice', - 'sortBy', - 'toArray', - 'uniq', - 'uniqBy', - 'unshiftObject', - 'unshiftObjects', - 'without', -]; -InheritedProxyMethods.forEach((method) => { - PromiseManyArray.prototype[method] = function proxiedMethod(...args) { - assert(`Cannot call ${method} before content is assigned.`, this.content); - return this.content[method](...args); - }; -}); +if (DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS) { + const EmberObjectMethods = [ + 'addObserver', + 'cacheFor', + 'decrementProperty', + 'get', + 'getProperties', + 'incrementProperty', + 'notifyPropertyChange', + 'removeObserver', + 'set', + 'setProperties', + 'toggleProperty', + ]; + EmberObjectMethods.forEach((method) => { + PromiseManyArray.prototype[method] = function delegatedMethod(...args) { + deprecate( + `The ${method} method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`, + false, + { + id: 'ember-data:deprecate-promise-many-array-behaviors', + until: '5.0', + since: { enabled: '4.8', available: '4.8' }, + for: 'ember-data', + } + ); + return Ember[method](this, ...args); + }; + }); + + const InheritedProxyMethods = [ + 'addArrayObserver', + 'addObject', + 'addObjects', + 'any', + 'arrayContentDidChange', + 'arrayContentWillChange', + 'clear', + 'compact', + 'every', + 'filter', + 'filterBy', + 'find', + 'findBy', + 'getEach', + 'includes', + 'indexOf', + 'insertAt', + 'invoke', + 'isAny', + 'isEvery', + 'lastIndexOf', + 'map', + 'mapBy', + // TODO update RFC to note objectAt was deprecated (forEach was left for iteration) + 'objectAt', + 'objectsAt', + 'popObject', + 'pushObject', + 'pushObjects', + 'reduce', + 'reject', + 'rejectBy', + 'removeArrayObserver', + 'removeAt', + 'removeObject', + 'removeObjects', + 'replace', + 'reverseObjects', + 'setEach', + 'setObjects', + 'shiftObject', + 'slice', + 'sortBy', + 'toArray', + 'uniq', + 'uniqBy', + 'unshiftObject', + 'unshiftObjects', + 'without', + ]; + InheritedProxyMethods.forEach((method) => { + PromiseManyArray.prototype[method] = function proxiedMethod(...args) { + deprecate( + `The ${method} method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`, + false, + { + id: 'ember-data:deprecate-promise-many-array-behaviors', + until: '5.0', + since: { enabled: '4.8', available: '4.8' }, + for: 'ember-data', + } + ); + assert(`Cannot call ${method} before content is assigned.`, this.content); + return this.content[method](...args); + }; + }); +} diff --git a/packages/private-build-infra/addon/current-deprecations.ts b/packages/private-build-infra/addon/current-deprecations.ts index 64b70f3a597..df8eea70cb6 100644 --- a/packages/private-build-infra/addon/current-deprecations.ts +++ b/packages/private-build-infra/addon/current-deprecations.ts @@ -50,4 +50,5 @@ export default { DEPRECATE_EARLY_STATIC: '4.8', DEPRECATE_CLASSIC: '4.9', DEPRECATE_HELPERS: '4.8', + DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS: '4.8', }; diff --git a/packages/private-build-infra/addon/deprecations.ts b/packages/private-build-infra/addon/deprecations.ts index f0dd718a135..acf33fd4e0a 100644 --- a/packages/private-build-infra/addon/deprecations.ts +++ b/packages/private-build-infra/addon/deprecations.ts @@ -18,4 +18,4 @@ export const DEPRECATE_JSON_API_FALLBACK = deprecationState('DEPRECATE_JSON_API_ export const DEPRECATE_MODEL_REOPEN = deprecationState('DEPRECATE_MODEL_REOPEN'); export const DEPRECATE_EARLY_STATIC = deprecationState('DEPRECATE_EARLY_STATIC'); export const DEPRECATE_CLASSIC = deprecationState('DEPRECATE_CLASSIC'); -export const DEPRECATE_HELPERS = deprecationState('DEPRECATE_HELPERS'); +export const DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS = deprecationState('DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS'); diff --git a/packages/record-data/addon/-private/record-data.ts b/packages/record-data/addon/-private/record-data.ts index 393c3470863..dfbdb59ab6c 100644 --- a/packages/record-data/addon/-private/record-data.ts +++ b/packages/record-data/addon/-private/record-data.ts @@ -556,20 +556,6 @@ export default class RecordDataDefault implements RelationshipRecordData { return array; } - isAttrDirty(key: string): boolean { - if (this._attributes[key] === undefined) { - return false; - } - let originalValue; - if (this._inFlightAttributes[key] !== undefined) { - originalValue = this._inFlightAttributes[key]; - } else { - originalValue = this._data[key]; - } - - return originalValue !== this._attributes[key]; - } - get _attributes() { if (this.__attributes === null) { this.__attributes = Object.create(null); @@ -661,20 +647,6 @@ export default class RecordDataDefault implements RelationshipRecordData { return createOptions; } - /* - TODO IGOR AND DAVID this shouldn't be public - This method should only be called by records in the `isNew()` state OR once the record - has been deleted and that deletion has been persisted. - - It will remove this record from any associated relationships. - - If `isNew` is true (default false), it will also completely reset all - relationships to an empty state as well. - - @method removeFromInverseRelationships - @param {Boolean} isNew whether to unload from the `isNew` perspective - @private - */ removeFromInverseRelationships() { graphFor(this.storeWrapper).push({ op: 'deleteRecord', diff --git a/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts b/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts index b47e5c38856..98a643ccc76 100644 --- a/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts +++ b/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts @@ -48,9 +48,7 @@ export interface AdapterPopulatedRecordArrayCreateArgs { // GET /users?isAdmin=true store.query('user', { isAdmin: true }).then(function(admins) { - admins.then(function() { - console.log(admins.get("length")); // 42 - }); + admins.get("length"); // 42 // somewhere later in the app code, when new admins have been created // in the meantime @@ -58,7 +56,7 @@ export interface AdapterPopulatedRecordArrayCreateArgs { // GET /users?isAdmin=true admins.update().then(function() { admins.get('isUpdating'); // false - console.log(admins.get("length")); // 123 + admins.get("length"); // 123 }); admins.get('isUpdating'); // true diff --git a/packages/store/addon/-private/store-service.ts b/packages/store/addon/-private/store-service.ts index b2c33a781ed..b84369b4cb3 100644 --- a/packages/store/addon/-private/store-service.ts +++ b/packages/store/addon/-private/store-service.ts @@ -33,7 +33,7 @@ import type { MinimumAdapterInterface } from '@ember-data/types/q/minimum-adapte import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-serializer-interface'; import type { RecordData } from '@ember-data/types/q/record-data'; import { JsonApiValidationError } from '@ember-data/types/q/record-data-json-api'; -import type { RecordDataRecordWrapper } from '@ember-data/types/q/record-data-record-wrapper'; +import type { RecordDataWrapper } from '@ember-data/types/q/record-data-record-wrapper'; import type { RecordInstance } from '@ember-data/types/q/record-instance'; import type { SchemaDefinitionService } from '@ember-data/types/q/schema-definition-service'; import type { FindOptions } from '@ember-data/types/q/store'; @@ -285,7 +285,7 @@ class Store extends Service { instantiateRecord( identifier: StableRecordIdentifier, createRecordArgs: { [key: string]: unknown }, - recordDataFor: (identifier: StableRecordIdentifier) => RecordDataRecordWrapper, + recordDataFor: (identifier: StableRecordIdentifier) => RecordDataWrapper, notificationManager: NotificationManager ): DSModel | RecordInstance { if (HAS_MODEL_PACKAGE) { @@ -466,7 +466,7 @@ class Store extends Service { const identifier = this.identifierCache.peekRecordIdentifier(resource as ResourceIdentifierObject); assert( - `The id ${properties.id} has already been used with another '${normalizeModelName}' record.`, + `The id ${properties.id} has already been used with another '${normalizedModelName}' record.`, !identifier ); } @@ -1352,7 +1352,7 @@ class Store extends Service { ```javascript store.queryRecord('user', {}).then(function(user) { let username = user.get('username'); - console.log(`Currently logged in as ${username}`); + // do thing }); ``` @@ -1409,7 +1409,7 @@ class Store extends Service { ```javascript store.queryRecord('user', { username: 'unique' }).then(function(user) { - console.log(user); // null + // user is null }); ``` From b12c83066279b6070869009118d872dbc19a0ce5 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Fri, 5 Aug 2022 17:40:09 -0700 Subject: [PATCH 13/29] add better debugging capabilities --- packages/-ember-data/ember-cli-build.js | 16 ++++++--- .../private-build-infra/addon/debugging.ts | 5 +++ .../private-build-infra/addon/deprecations.ts | 1 + .../addon-build-config-for-data-package.js | 21 ++++++++--- .../private-build-infra/src/debug-macros.js | 17 +++++++-- packages/private-build-infra/src/debugging.js | 16 +++++++++ .../src/stripped-build-plugins.js | 4 +-- .../src/utilities/require-module.js | 9 ++++- packages/record-data/ember-cli-build.js | 2 +- .../addon/-private/caches/identifier-cache.ts | 35 ++++++++++++++++++- .../managers/record-notification-manager.ts | 5 +++ .../ember-cli-build.js | 2 +- .../unpublished-test-infra/ember-cli-build.js | 2 +- 13 files changed, 118 insertions(+), 17 deletions(-) create mode 100644 packages/private-build-infra/addon/debugging.ts create mode 100644 packages/private-build-infra/src/debugging.js diff --git a/packages/-ember-data/ember-cli-build.js b/packages/-ember-data/ember-cli-build.js index 7433a4ef5d6..2d78ece93ed 100644 --- a/packages/-ember-data/ember-cli-build.js +++ b/packages/-ember-data/ember-cli-build.js @@ -37,14 +37,22 @@ module.exports = function (defaults) { terserSettings.enabled = false; } - let app = new EmberAddon(defaults, { - emberData: { - compatWith, + let config = { + compatWith, + debug: { + LOG_NOTIFICATIONS: process.env.DEBUG_DATA ? true : false, + LOG_REQUEST_STATUS: process.env.DEBUG_DATA ? true : false, + LOG_IDENTIFIERS: process.env.DEBUG_DATA ? true : false, + LOG_GRAPH: process.env.DEBUG_DATA ? true : false, + LOG_INSTANCE_CACHE: process.env.DEBUG_DATA ? true : false, }, + }; + let app = new EmberAddon(defaults, { + emberData: config, babel: { // this ensures that the same build-time code stripping that is done // for library packages is also done for our tests and dummy app - plugins: [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, compatWith)], + plugins: [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, config)], }, 'ember-cli-babel': { throwUnlessParallelizable: true, diff --git a/packages/private-build-infra/addon/debugging.ts b/packages/private-build-infra/addon/debugging.ts new file mode 100644 index 00000000000..7b53cbaf3d8 --- /dev/null +++ b/packages/private-build-infra/addon/debugging.ts @@ -0,0 +1,5 @@ +export const LOG_NOTIFICATIONS = false; +export const LOG_REQUEST_STATUS = false; +export const LOG_IDENTIFIERS = false; +export const LOG_GRAPH = false; +export const LOG_INSTANCE_CACHE = false; diff --git a/packages/private-build-infra/addon/deprecations.ts b/packages/private-build-infra/addon/deprecations.ts index acf33fd4e0a..6291518a758 100644 --- a/packages/private-build-infra/addon/deprecations.ts +++ b/packages/private-build-infra/addon/deprecations.ts @@ -18,4 +18,5 @@ export const DEPRECATE_JSON_API_FALLBACK = deprecationState('DEPRECATE_JSON_API_ export const DEPRECATE_MODEL_REOPEN = deprecationState('DEPRECATE_MODEL_REOPEN'); export const DEPRECATE_EARLY_STATIC = deprecationState('DEPRECATE_EARLY_STATIC'); export const DEPRECATE_CLASSIC = deprecationState('DEPRECATE_CLASSIC'); +export const DEPRECATE_HELPERS = deprecationState('DEPRECATE_HELPERS'); export const DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS = deprecationState('DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS'); diff --git a/packages/private-build-infra/src/addon-build-config-for-data-package.js b/packages/private-build-infra/src/addon-build-config-for-data-package.js index 49b4c33ecf1..b67771de192 100644 --- a/packages/private-build-infra/src/addon-build-config-for-data-package.js +++ b/packages/private-build-infra/src/addon-build-config-for-data-package.js @@ -109,9 +109,10 @@ function addonBuildConfigForDataPackage(PackageName) { buildBabelOptions() { let babelOptions = this.options.babel || {}; let existingPlugins = babelOptions.plugins || []; - let compatVersion = this.getEmberDataConfig().compatWith || null; + let config = this.getEmberDataConfig(); + console.log({ config }); - let customPlugins = require('./stripped-build-plugins')(process.env.EMBER_ENV, this._findHost(), compatVersion); + let customPlugins = require('./stripped-build-plugins')(process.env.EMBER_ENV, this._findHost(), config); let plugins = existingPlugins.map((plugin) => { return Array.isArray(plugin) ? plugin : [plugin]; }); @@ -211,8 +212,20 @@ function addonBuildConfigForDataPackage(PackageName) { let options = (app.options = app.options || {}); options.emberData = options.emberData || {}; - - return options.emberData; + options.emberData.debug = options.emberData.debug || {}; + const debugOptions = Object.assign( + { + LOG_NOTIFICATIONS: false, + LOG_REQUEST_STATUS: false, + LOG_IDENTIFIERS: false, + LOG_GRAPH: false, + LOG_INSTANCE_CACHE: false, + }, + options.emberData.debug + ); + options.emberData.debug = debugOptions; + + return Object.assign({ compatWith: null }, options.emberData); }, }; } diff --git a/packages/private-build-infra/src/debug-macros.js b/packages/private-build-infra/src/debug-macros.js index 9ad89421fcf..64631887822 100644 --- a/packages/private-build-infra/src/debug-macros.js +++ b/packages/private-build-infra/src/debug-macros.js @@ -1,9 +1,10 @@ 'use strict'; -module.exports = function debugMacros(app, isProd, compatVersion) { +module.exports = function debugMacros(app, isProd, config) { const PACKAGES = require('./packages')(app); const FEATURES = require('./features')(isProd); - const DEPRECATIONS = require('./deprecations')(compatVersion, isProd); + const DEBUG = require('./debugging')(config.debug, isProd); + const DEPRECATIONS = require('./deprecations')(config.compatWith, isProd); const debugMacrosPath = require.resolve('babel-plugin-debug-macros'); let plugins = [ [ @@ -42,6 +43,18 @@ module.exports = function debugMacros(app, isProd, compatVersion) { }, '@ember-data/deprecation-stripping', ], + [ + debugMacrosPath, + { + flags: [ + { + source: '@ember-data/private-build-infra/debugging', + flags: DEBUG, + }, + ], + }, + '@ember-data/debugging', + ], ]; return plugins; diff --git a/packages/private-build-infra/src/debugging.js b/packages/private-build-infra/src/debugging.js new file mode 100644 index 00000000000..a7823f80cec --- /dev/null +++ b/packages/private-build-infra/src/debugging.js @@ -0,0 +1,16 @@ +'use strict'; + +const requireModule = require('./utilities/require-module'); + +function getDebugFeatures(debugConfig, isProd) { + const { default: DEBUG_FEATURES } = requireModule('@ember-data/private-build-infra/addon/debugging.ts'); + const flags = {}; + + Object.keys(DEBUG_FEATURES).forEach((flag) => { + flags[flag] = isProd ? false : debugConfig[flag] || DEBUG_FEATURES[flag]; + }); + + return flags; +} + +module.exports = getDebugFeatures; diff --git a/packages/private-build-infra/src/stripped-build-plugins.js b/packages/private-build-infra/src/stripped-build-plugins.js index fcb0890bc4c..5e19ef5496f 100644 --- a/packages/private-build-infra/src/stripped-build-plugins.js +++ b/packages/private-build-infra/src/stripped-build-plugins.js @@ -7,10 +7,10 @@ function isProduction(environment) { return /production/.test(environment); } -module.exports = function (environment, app, compatVersion) { +module.exports = function (environment, app, config) { const isProd = isProduction(environment); let plugins = []; - const DebugMacros = require('./debug-macros')(app, isProd, compatVersion); + const DebugMacros = require('./debug-macros')(app, isProd, config); let postTransformPlugins = []; if (isProd) { diff --git a/packages/private-build-infra/src/utilities/require-module.js b/packages/private-build-infra/src/utilities/require-module.js index 5865d7240be..2be6af39775 100644 --- a/packages/private-build-infra/src/utilities/require-module.js +++ b/packages/private-build-infra/src/utilities/require-module.js @@ -3,7 +3,14 @@ const fs = require('node:fs'); module.exports = function requireModule(modulePath) { const path = require.resolve(modulePath); const fileContents = fs.readFileSync(path, { encoding: 'utf8' }); - const newContents = fileContents.replace('export default ', 'return '); + let newContents; + + if (fileContents.includes('export default')) { + newContents = fileContents.replace('export default ', 'return '); + } else { + newContents = fileContents.replaceAll('export const ', 'module.exports.'); + newContents = `const module = { exports: {} };\n${newContents}\nreturn module.exports;`; + } try { const func = new Function(newContents); return { default: func() }; diff --git a/packages/record-data/ember-cli-build.js b/packages/record-data/ember-cli-build.js index a9115bf0b39..42bfee0ed33 100644 --- a/packages/record-data/ember-cli-build.js +++ b/packages/record-data/ember-cli-build.js @@ -12,7 +12,7 @@ module.exports = function (defaults) { babel: { // this ensures that the same build-time code stripping that is done // for library packages is also done for our tests and dummy app - plugins: [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, compatWith)], + plugins: [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, { compatWith })], }, 'ember-cli-babel': { throwUnlessParallelizable: true, diff --git a/packages/store/addon/-private/caches/identifier-cache.ts b/packages/store/addon/-private/caches/identifier-cache.ts index 5d2598b6a0a..592a06bbec4 100644 --- a/packages/store/addon/-private/caches/identifier-cache.ts +++ b/packages/store/addon/-private/caches/identifier-cache.ts @@ -4,6 +4,7 @@ import { assert, warn } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; +import { LOG_IDENTIFIERS } from '@ember-data/private-build-infra/debugging'; import type { ExistingResourceObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api'; import type { ForgetMethod, @@ -211,6 +212,10 @@ export class IdentifierCache { // we have definitely not seen this resource before // so we allow the user configured `GenerationMethod` to tell us let newLid = this._generate(resource, 'record'); + if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.log(`Identifiers: generated record identifier ${newLid} for resource`, resource); + } // we do this _even_ when `lid` is present because secondary lookups // may need to be populated, but we enforce not giving us something @@ -378,7 +383,21 @@ export class IdentifierCache { if (existingIdentifier) { let keyOptions = getTypeIndex(this._cache.types, identifier.type); - identifier = this._mergeRecordIdentifiers(keyOptions, identifier, existingIdentifier, data, newId as string); + let generatedIdentifier = identifier; + identifier = this._mergeRecordIdentifiers( + keyOptions, + generatedIdentifier, + existingIdentifier, + data, + newId as string + ); + if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.log( + `Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, + data + ); + } } let id = identifier.id; @@ -387,12 +406,22 @@ export class IdentifierCache { // add to our own secondary lookup table if (id !== newId && newId !== null) { + if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.log( + `Identifiers: updated id for identifier ${identifier.lid} from '${id}' to '${newId}' for resource`, + data + ); + } let keyOptions = getTypeIndex(this._cache.types, identifier.type); keyOptions.id[newId] = identifier; if (id !== null) { delete keyOptions.id[id]; } + } else if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.log(`Identifiers: updated identifier ${identifier.lid} resource`, data); } return identifier; @@ -454,6 +483,10 @@ export class IdentifierCache { IDENTIFIERS.delete(identifierObject); this._forget(identifier, 'record'); + if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.log(`Identifiers: released identifier ${identifierObject.lid}`); + } } destroy() { diff --git a/packages/store/addon/-private/managers/record-notification-manager.ts b/packages/store/addon/-private/managers/record-notification-manager.ts index 095506aa4ff..2b6518b3688 100644 --- a/packages/store/addon/-private/managers/record-notification-manager.ts +++ b/packages/store/addon/-private/managers/record-notification-manager.ts @@ -1,5 +1,6 @@ import { DEBUG } from '@glimmer/env'; +import { LOG_NOTIFICATIONS } from '@ember-data/private-build-infra/debugging'; import type { RecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type Store from '../store-service'; @@ -61,6 +62,10 @@ export default class NotificationManager { notify(identifier: RecordIdentifier, value: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): boolean; notify(identifier: RecordIdentifier, value: NotificationType, key?: string): boolean { let stableIdentifier = this.store.identifierCache.getOrCreateRecordIdentifier(identifier); + if (LOG_NOTIFICATIONS) { + // eslint-disable-next-line no-console + console.log(`Notifying: ${String(stableIdentifier)}\t${value}\t${key}`); + } let callbackMap = Cache.get(stableIdentifier); if (!callbackMap || !callbackMap.size) { return false; diff --git a/packages/unpublished-model-encapsulation-test-app/ember-cli-build.js b/packages/unpublished-model-encapsulation-test-app/ember-cli-build.js index 26256558192..2c69cf51e89 100644 --- a/packages/unpublished-model-encapsulation-test-app/ember-cli-build.js +++ b/packages/unpublished-model-encapsulation-test-app/ember-cli-build.js @@ -8,7 +8,7 @@ module.exports = function (defaults) { const isProd = process.env.EMBER_ENV === 'production'; // allows testing with env config for stripping all deprecations const compatWith = process.env.EMBER_DATA_FULL_COMPAT ? '99.0' : null; - const plugins = [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, compatWith)]; + const plugins = [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, { compatWith })]; let app = new EmberApp(defaults, { emberData: { diff --git a/packages/unpublished-test-infra/ember-cli-build.js b/packages/unpublished-test-infra/ember-cli-build.js index a210e7d8cae..be27252724e 100644 --- a/packages/unpublished-test-infra/ember-cli-build.js +++ b/packages/unpublished-test-infra/ember-cli-build.js @@ -15,7 +15,7 @@ module.exports = function (defaults) { babel: { // this ensures that the same build-time code stripping that is done // for library packages is also done for our tests and dummy app - plugins: [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, compatWith)], + plugins: [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, { compatWith })], }, 'ember-cli-babel': { throwUnlessParallelizable: true, From fe5af9cf6ef895603352bf6b5944e06e2f06aafc Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Fri, 5 Aug 2022 18:57:25 -0700 Subject: [PATCH 14/29] cleanup --- .../identifiers/configuration-test.ts | 3 + packages/-ember-data/tests/unit/utils-test.js | 1 - .../addon-build-config-for-data-package.js | 1 - .../record-data/addon/-private/graph/index.ts | 17 + packages/record-data/index.js | 1 + .../addon/-private/caches/instance-cache.ts | 409 +++++++++--------- .../store/addon/-private/store-service.ts | 6 +- 7 files changed, 232 insertions(+), 206 deletions(-) diff --git a/packages/-ember-data/tests/integration/identifiers/configuration-test.ts b/packages/-ember-data/tests/integration/identifiers/configuration-test.ts index 82316c95d29..944b69cdc60 100644 --- a/packages/-ember-data/tests/integration/identifiers/configuration-test.ts +++ b/packages/-ember-data/tests/integration/identifiers/configuration-test.ts @@ -541,6 +541,7 @@ module('Integration | Identifiers - configuration', function (hooks) { assert.strictEqual(forgetMethodCalls, 1, 'We called the forget method once'); forgetMethodCalls = 0; + debugger; // an async relationship retains const jailBird = store.push({ @@ -594,6 +595,7 @@ module('Integration | Identifiers - configuration', function (hooks) { const jailBirdIdentifier = recordIdentifierFor(jailBird); const gatekeeperIdentifier = recordIdentifierFor(gatekeeper); const jailhouseIdentifier = recordIdentifierFor(jailhouse); + console.clear(); jailBird.unloadRecord(); await settled(); @@ -605,6 +607,7 @@ module('Integration | Identifiers - configuration', function (hooks) { gatekeeper.unloadRecord(); await settled(); + // debugger; assert.strictEqual(forgetMethodCalls, 2, 'We cleaned up both identifiers'); forgetMethodCalls = 0; expectedIdentifiers.push(jailhouseIdentifier); diff --git a/packages/-ember-data/tests/unit/utils-test.js b/packages/-ember-data/tests/unit/utils-test.js index ff7a91b5c8e..6314aa8628d 100644 --- a/packages/-ember-data/tests/unit/utils-test.js +++ b/packages/-ember-data/tests/unit/utils-test.js @@ -1,5 +1,4 @@ import Mixin from '@ember/object/mixin'; -import { run } from '@ember/runloop'; import { module, test } from 'qunit'; diff --git a/packages/private-build-infra/src/addon-build-config-for-data-package.js b/packages/private-build-infra/src/addon-build-config-for-data-package.js index b67771de192..5c3409412f2 100644 --- a/packages/private-build-infra/src/addon-build-config-for-data-package.js +++ b/packages/private-build-infra/src/addon-build-config-for-data-package.js @@ -110,7 +110,6 @@ function addonBuildConfigForDataPackage(PackageName) { let babelOptions = this.options.babel || {}; let existingPlugins = babelOptions.plugins || []; let config = this.getEmberDataConfig(); - console.log({ config }); let customPlugins = require('./stripped-build-plugins')(process.env.EMBER_ENV, this._findHost(), config); let plugins = existingPlugins.map((plugin) => { diff --git a/packages/record-data/addon/-private/graph/index.ts b/packages/record-data/addon/-private/graph/index.ts index cc0f6146303..6edba75e672 100644 --- a/packages/record-data/addon/-private/graph/index.ts +++ b/packages/record-data/addon/-private/graph/index.ts @@ -1,6 +1,7 @@ import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; +import { LOG_GRAPH } from '@ember-data/private-build-infra/debugging'; import type Store from '@ember-data/store'; import type { RecordDataStoreWrapper } from '@ember-data/store/-private'; import { WeakCache } from '@ember-data/store/-private'; @@ -207,6 +208,10 @@ export class Graph { } unload(identifier: StableRecordIdentifier) { + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log(`graph: unload ${String(identifier)}`); + } const relationships = this.identifiers.get(identifier); if (relationships) { @@ -223,6 +228,10 @@ export class Graph { } remove(identifier: StableRecordIdentifier) { + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log(`graph: remove ${String(identifier)}`); + } this.unload(identifier); this.identifiers.delete(identifier); } @@ -231,6 +240,10 @@ export class Graph { * Remote state changes */ push(op: RemoteRelationshipOperation) { + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log(`graph: push ${String(op.record)}`, op); + } if (op.op === 'deleteRecord') { this._pushedUpdates.deletions.push(op); } else if (op.op === 'replaceRelatedRecord') { @@ -260,6 +273,10 @@ export class Graph { `Cannot update an implicit relationship`, op.op === 'deleteRecord' || !isImplicit(this.get(op.record, op.field)) ); + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log(`graph: update (${isRemote ? 'remote' : 'local'}) ${String(op.record)}`, op); + } switch (op.op) { case 'updateRelationship': diff --git a/packages/record-data/index.js b/packages/record-data/index.js index 3aa9a41fc1c..bd11f8d898e 100644 --- a/packages/record-data/index.js +++ b/packages/record-data/index.js @@ -21,6 +21,7 @@ module.exports = Object.assign({}, addonBaseConfig, { '@ember-data/store/-private', '@ember-data/store', '@ember-data/canary-features', + '@ember-data/private-build-infra/debugging', ]; }, }); diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index dfee3250c1a..df0ce6aa8fe 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -3,6 +3,7 @@ import { DEBUG } from '@glimmer/env'; import { resolve } from 'rsvp'; +import { LOG_INSTANCE_CACHE } from '@ember-data/private-build-infra/debugging'; import type { ExistingResourceObject, NewResourceIdentifierObject, @@ -29,7 +30,7 @@ import { assertIdentifierHasId } from '../store-service'; import coerceId, { ensureStringId } from '../utils/coerce-id'; import constructResource from '../utils/construct-resource'; import normalizeModelName from '../utils/normalize-model-name'; -import WeakCache from '../utils/weak-cache'; +import WeakCache, { DebugWeakCache } from '../utils/weak-cache'; import recordDataFor, { removeRecordDataFor, setRecordDataFor } from './record-data-for'; /** @@ -85,7 +86,6 @@ export function setRecordIdentifier(record: RecordInstance | RecordData, identif RecordCache.set(record, identifier); } -const RECORD_REFERENCES = new WeakCache(DEBUG ? 'reference' : ''); export const StoreMap = new WeakCache(DEBUG ? 'store' : ''); export function storeFor(record: RecordInstance): Store | undefined { @@ -101,16 +101,19 @@ export function storeFor(record: RecordInstance): Store | undefined { type Caches = { record: WeakMap; recordData: WeakMap; + reference: DebugWeakCache; }; export class InstanceCache { declare store: Store; declare _storeWrapper: RecordDataStoreWrapper; declare peekList: Dict>; + declare __recordDataFor: (resource: RecordIdentifier) => RecordData; #instances: Caches = { record: new WeakMap(), recordData: new WeakMap(), + reference: new WeakCache(DEBUG ? 'reference' : ''), }; recordIsLoaded(identifier: StableRecordIdentifier) { @@ -140,9 +143,12 @@ export class InstanceCache { this.peekList = Object.create(null) as Dict>; this._storeWrapper = new RecordDataStoreWrapper(this.store); - this.__recordDataFor = this.__recordDataFor.bind(this); + this.__recordDataFor = (resource: RecordIdentifier) => { + const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource); + return this.getRecordData(identifier); + }; - RECORD_REFERENCES._generator = (identifier) => { + this.#instances.reference._generator = (identifier) => { return new RecordReference(this.store, identifier); }; @@ -248,33 +254,135 @@ export class InstanceCache { }): RecordData | RecordInstance | undefined { return this.#instances[bucket]?.get(identifier); } - set({ - identifier, - bucket, - value, - }: { - identifier: StableRecordIdentifier; - bucket: 'record'; - value: RecordInstance; - }): void { - this.#instances[bucket].set(identifier, value); - } getRecord(identifier: StableRecordIdentifier, properties?: CreateRecordProperties): RecordInstance { let record = this.peek({ identifier, bucket: 'record' }); if (!record) { - if (properties && 'id' in properties) { - assert(`expected id to be a string or null`, properties.id !== undefined); + const recordData = this.getRecordData(identifier); + const createOptions = recordData._initRecordCreateOptions( + normalizeProperties(this.store, identifier, properties) + ); + record = this.store.instantiateRecord( + identifier, + createOptions, + this.__recordDataFor, + this.store._notificationManager + ); + setRecordIdentifier(record, identifier); + setRecordDataFor(record, recordData); + StoreMap.set(record, this.store); + this.#instances.record.set(identifier, record); + + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: created Record for ${String(identifier)}`, properties); } - - record = this._instantiateRecord(this.getRecordData(identifier), identifier, properties); - this.set({ identifier, bucket: 'record', value: record }); } return record; } + getRecordData(identifier: StableRecordIdentifier) { + let recordData = this.peek({ identifier, bucket: 'recordData' }); + + if (!recordData) { + recordData = this.store.createRecordDataFor(identifier.type, identifier.id, identifier.lid, this._storeWrapper); + setRecordDataFor(identifier, recordData); + // TODO this is invalid for v2 recordData but required + // for v1 recordData. Remember to remove this once the + // RecordData manager handles converting recordData to identifier + setRecordIdentifier(recordData, identifier); + + this.#instances.recordData.set(identifier, recordData); + this.peekList[identifier.type] = this.peekList[identifier.type] || new Set(); + this.peekList[identifier.type]!.add(identifier); + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: created RecordData for ${String(identifier)}`); + } + } + + return recordData; + } + + getReference(identifier: StableRecordIdentifier) { + return this.#instances.reference.lookup(identifier); + } + + createSnapshot(identifier: StableRecordIdentifier, options: FindOptions = {}): Snapshot { + return new Snapshot(options, identifier, this.store); + } + + destroyRecord(identifier: StableRecordIdentifier) { + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: destroying record for ${String(identifier)}`); + } + const record = this.#instances.record.get(identifier); + assert( + 'Cannot destroy record while it is still materialized', + !record || record.isDestroyed || record.isDestroying + ); + + this.peekList[identifier.type]?.delete(identifier); + this.store.identifierCache.forgetRecordIdentifier(identifier); + } + + unloadRecord(identifier: StableRecordIdentifier) { + if (DEBUG) { + const requests = this.store.getRequestStateService().getPendingRequestsForRecord(identifier); + if ( + requests.some((req) => { + return req.type === 'mutation'; + }) + ) { + assert(`You can only unload a record which is not inFlight. '${String(identifier)}'`); + } + } + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: unloading record for ${String(identifier)}`); + } + + // TODO is this join still necessary? + this.store._backburner.join(() => { + const record = this.peek({ identifier, bucket: 'record' }); + const recordData = this.peek({ identifier, bucket: 'recordData' }); + this.peekList[identifier.type]?.delete(identifier); + + if (record) { + this.#instances.record.delete(identifier); + StoreMap.delete(record); + // TODO remove identifier:record cache link + this.store.teardownRecord(record); + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: destroyed record for ${String(identifier)}`); + } + } + + if (recordData) { + this.#instances.recordData.delete(identifier); + recordData.unloadRecord(); + removeRecordDataFor(identifier); + + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`); + } + } else { + this._storeWrapper.disconnectRecord(identifier.type, identifier.id, identifier.lid); + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: disconnected record for ${String(identifier)}`); + } + } + + this.store.recordArrayManager.recordDidChange(identifier); + }); + } + clear(type?: string) { if (type === undefined) { let keys = Object.keys(this.peekList); @@ -296,10 +404,7 @@ export class InstanceCache { } } - getReference(identifier: StableRecordIdentifier) { - return RECORD_REFERENCES.lookup(identifier); - } - + // TODO this should move into the network layer _fetchDataIfNeededForIdentifier( identifier: StableRecordIdentifier, options: FindOptions = {} @@ -329,158 +434,62 @@ export class InstanceCache { return promise; } - _instantiateRecord( - recordData: RecordData, - identifier: StableRecordIdentifier, - properties?: { [key: string]: unknown } - ) { - // assert here - if (properties !== undefined) { - assert( - `You passed '${typeof properties}' as properties for record creation instead of an object.`, - typeof properties === 'object' && properties !== null - ); - - const { type } = identifier; + // TODO this should move into something coordinating operations + setRecordId(modelName: string, id: string, lid: string) { + const type = normalizeModelName(modelName); + const resource = constructResource(type, null, coerceId(lid)); + const identifier = this.store.identifierCache.peekRecordIdentifier(resource); - // convert relationship Records to RecordDatas before passing to RecordData - let defs = this.store.getSchemaDefinitionService().relationshipsDefinitionFor({ type }); - - if (defs !== null) { - let keys = Object.keys(properties); - let relationshipValue; + if (identifier) { + let oldId = identifier.id; - for (let i = 0; i < keys.length; i++) { - let prop = keys[i]; - let def = defs[prop]; + // ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record) + assert( + `'${type}' was saved to the server, but the response does not have an id and your record does not either.`, + !(id === null && oldId === null) + ); - if (def !== undefined) { - if (def.kind === 'hasMany') { - if (DEBUG) { - assertRecordsPassedToHasMany(properties[prop] as RecordInstance[]); - } - relationshipValue = extractRecordDatasFromRecords(properties[prop] as RecordInstance[]); - } else { - relationshipValue = extractRecordDataFromRecord(properties[prop] as RecordInstance); - } + // ID absolutely can't be different than oldID if oldID is not null + // TODO this assertion and restriction may not strictly be needed in the identifiers world + assert( + `Cannot update the id for '${type}:${lid}' from '${String(oldId)}' to '${id}'.`, + !(oldId !== null && id !== oldId) + ); - properties[prop] = relationshipValue; - } - } + // ID can be null if oldID is not null (altered ID in response for a record) + // however, this is more than likely a developer error. + if (oldId !== null && id === null) { + warn( + `Your ${type} record was saved to the server, but the response does not have an id.`, + !(oldId !== null && id === null) + ); + return; } - } - - // TODO guard against initRecordOptions no being there - let createOptions = recordData._initRecordCreateOptions(properties); - //TODO Igor pass a wrapper instead of RD - let record = this.store.instantiateRecord( - identifier, - createOptions, - // eslint-disable-next-line @typescript-eslint/unbound-method - this.__recordDataFor, - this.store._notificationManager - ); - setRecordIdentifier(record, identifier); - setRecordDataFor(record, recordData); - StoreMap.set(record, this.store); - return record; - } - - // TODO move RecordData Cache into InstanceCache - getRecordData(identifier: StableRecordIdentifier) { - let recordData = this.peek({ identifier, bucket: 'recordData' }); - - if (!recordData) { - recordData = this._createRecordData(identifier); - this.#instances.recordData.set(identifier, recordData); - this.peekList[identifier.type] = this.peekList[identifier.type] || new Set(); - this.peekList[identifier.type]!.add(identifier); - } - - return recordData; - } - - createSnapshot(identifier: StableRecordIdentifier, options: FindOptions = {}): Snapshot { - return new Snapshot(options, identifier, this.store); - } - __recordDataFor(resource: RecordIdentifier) { - const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource); - return this.getRecordData(identifier); - } - - // TODO move this to InstanceCache - _createRecordData(identifier: StableRecordIdentifier): RecordData { - const recordData = this.store.createRecordDataFor( - identifier.type, - identifier.id, - identifier.lid, - this._storeWrapper - ); - setRecordDataFor(identifier, recordData); - // TODO this is invalid for v2 recordData but required - // for v1 recordData. Remember to remove this once the - // RecordData manager handles converting recordData to identifier - setRecordIdentifier(recordData, identifier); - return recordData; - } - - setRecordId(modelName: string, newId: string, lid: string) { - const resource = constructResource(normalizeModelName(modelName), null, coerceId(lid)); - const maybeIdentifier = this.store.identifierCache.peekRecordIdentifier(resource); - - if (maybeIdentifier) { - // TODO handle consequences of identifier merge for notifications - this._setRecordId(modelName, newId, lid); - this.store._notificationManager.notify(maybeIdentifier, 'identity'); - } - } - - _setRecordId(type: string, id: string, lid: string) { - const resource: NewResourceIdentifierObject = { type, id: null, lid }; - const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource); - - let oldId = identifier.id; - let modelName = identifier.type; - - // ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record) - assert( - `'${modelName}' was saved to the server, but the response does not have an id and your record does not either.`, - !(id === null && oldId === null) - ); - - // ID absolutely can't be different than oldID if oldID is not null - // TODO this assertion and restriction may not strictly be needed in the identifiers world - assert( - `Cannot update the id for '${modelName}:${lid}' from '${String(oldId)}' to '${id}'.`, - !(oldId !== null && id !== oldId) - ); + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`); + } - // ID can be null if oldID is not null (altered ID in response for a record) - // however, this is more than likely a developer error. - if (oldId !== null && id === null) { - warn( - `Your ${modelName} record was saved to the server, but the response does not have an id.`, - !(oldId !== null && id === null) + let existingIdentifier = this.store.identifierCache.peekRecordIdentifier({ type, id }); + assert( + `'${type}' was saved to the server, but the response returned the new id '${id}', which has already been used with another record.'`, + !existingIdentifier || existingIdentifier === identifier ); - return; - } - let existingIdentifier = this.store.identifierCache.peekRecordIdentifier({ type: modelName, id }); - assert( - `'${modelName}' was saved to the server, but the response returned the new id '${id}', which has already been used with another record.'`, - !existingIdentifier || existingIdentifier === identifier - ); + if (identifier.id === null) { + // TODO potentially this needs to handle merged result + this.store.identifierCache.updateRecordIdentifier(identifier, { type, id }); + } - if (identifier.id === null) { - // TODO potentially this needs to handle merged result - this.store.identifierCache.updateRecordIdentifier(identifier, { type, id }); + // TODO update recordData if needed ? + // TODO handle consequences of identifier merge for notifications + this.store._notificationManager.notify(identifier, 'identity'); } - - // TODO update recordData if needed ? } - _load(data: ExistingResourceObject): StableExistingRecordIdentifier { + // TODO this should move into something coordinating operations + loadData(data: ExistingResourceObject): StableExistingRecordIdentifier { // TODO type should be pulled from the identifier for debug let modelName = data.type; assert( @@ -528,54 +537,52 @@ export class InstanceCache { return identifier as StableExistingRecordIdentifier; } +} - destroyRecord(identifier: StableRecordIdentifier) { - const record = this.#instances.record.get(identifier); +function normalizeProperties( + store: Store, + identifier: StableRecordIdentifier, + properties?: { [key: string]: unknown } +): { [key: string]: unknown } | undefined { + // assert here + if (properties !== undefined) { + if ('id' in properties) { + assert(`expected id to be a string or null`, properties.id !== undefined); + } assert( - 'Cannot destroy record while it is still materialized', - !record || record.isDestroyed || record.isDestroying + `You passed '${typeof properties}' as properties for record creation instead of an object.`, + typeof properties === 'object' && properties !== null ); - this.peekList[identifier.type]?.delete(identifier); - this.store.identifierCache.forgetRecordIdentifier(identifier); - } + const { type } = identifier; - unloadRecord(identifier: StableRecordIdentifier) { - if (DEBUG) { - const requests = this.store.getRequestStateService().getPendingRequestsForRecord(identifier); - if ( - requests.some((req) => { - return req.type === 'mutation'; - }) - ) { - assert(`You can only unload a record which is not inFlight. '${String(identifier)}'`); - } - } + // convert relationship Records to RecordDatas before passing to RecordData + let defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({ type }); - // TODO is this join still necessary? - this.store._backburner.join(() => { - const record = this.peek({ identifier, bucket: 'record' }); - const recordData = this.peek({ identifier, bucket: 'recordData' }); - this.peekList[identifier.type]?.delete(identifier); + if (defs !== null) { + let keys = Object.keys(properties); + let relationshipValue; - if (record) { - this.#instances.record.delete(identifier); - StoreMap.delete(record); - // TODO remove identifier:record cache link - this.store.teardownRecord(record); - } + for (let i = 0; i < keys.length; i++) { + let prop = keys[i]; + let def = defs[prop]; - if (recordData) { - this.#instances.recordData.delete(identifier); - recordData.unloadRecord(); - removeRecordDataFor(identifier); - } else { - this._storeWrapper.disconnectRecord(identifier.type, identifier.id, identifier.lid); - } + if (def !== undefined) { + if (def.kind === 'hasMany') { + if (DEBUG) { + assertRecordsPassedToHasMany(properties[prop] as RecordInstance[]); + } + relationshipValue = extractRecordDatasFromRecords(properties[prop] as RecordInstance[]); + } else { + relationshipValue = extractRecordDataFromRecord(properties[prop] as RecordInstance); + } - this.store.recordArrayManager.recordDidChange(identifier); - }); + properties[prop] = relationshipValue; + } + } + } } + return properties; } export function isHiddenFromRecordArrays(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { diff --git a/packages/store/addon/-private/store-service.ts b/packages/store/addon/-private/store-service.ts index b84369b4cb3..6e44d359611 100644 --- a/packages/store/addon/-private/store-service.ts +++ b/packages/store/addon/-private/store-service.ts @@ -1966,7 +1966,7 @@ class Store extends Service { if (included) { for (i = 0, length = included.length; i < length; i++) { - this._instanceCache._load(included[i]); + this._instanceCache.loadData(included[i]); } } @@ -1975,7 +1975,7 @@ class Store extends Service { let identifiers = new Array(length); for (i = 0; i < length; i++) { - identifiers[i] = this._instanceCache._load(jsonApiDoc.data[i]); + identifiers[i] = this._instanceCache.loadData(jsonApiDoc.data[i]); } return identifiers; } @@ -1991,7 +1991,7 @@ class Store extends Service { typeof jsonApiDoc.data === 'object' ); - return this._instanceCache._load(jsonApiDoc.data); + return this._instanceCache.loadData(jsonApiDoc.data); }); // this typecast is necessary because `backburner.join` is mistyped to return void From 43682768c79b1fcd02fe129f5c862c9292b93eca Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sat, 6 Aug 2022 01:01:07 -0700 Subject: [PATCH 15/29] further rationalization --- .../identifiers/configuration-test.ts | 3 - .../integration/records/create-record-test.js | 14 ++-- packages/debug/addon/index.js | 8 +- .../record-data/addon/-private/record-data.ts | 82 +++++++------------ .../addon/-private/caches/identifier-cache.ts | 14 +++- .../addon/-private/caches/instance-cache.ts | 57 +++++++------ .../managers/record-data-store-wrapper.ts | 29 ++----- 7 files changed, 91 insertions(+), 116 deletions(-) diff --git a/packages/-ember-data/tests/integration/identifiers/configuration-test.ts b/packages/-ember-data/tests/integration/identifiers/configuration-test.ts index 944b69cdc60..82316c95d29 100644 --- a/packages/-ember-data/tests/integration/identifiers/configuration-test.ts +++ b/packages/-ember-data/tests/integration/identifiers/configuration-test.ts @@ -541,7 +541,6 @@ module('Integration | Identifiers - configuration', function (hooks) { assert.strictEqual(forgetMethodCalls, 1, 'We called the forget method once'); forgetMethodCalls = 0; - debugger; // an async relationship retains const jailBird = store.push({ @@ -595,7 +594,6 @@ module('Integration | Identifiers - configuration', function (hooks) { const jailBirdIdentifier = recordIdentifierFor(jailBird); const gatekeeperIdentifier = recordIdentifierFor(gatekeeper); const jailhouseIdentifier = recordIdentifierFor(jailhouse); - console.clear(); jailBird.unloadRecord(); await settled(); @@ -607,7 +605,6 @@ module('Integration | Identifiers - configuration', function (hooks) { gatekeeper.unloadRecord(); await settled(); - // debugger; assert.strictEqual(forgetMethodCalls, 2, 'We cleaned up both identifiers'); forgetMethodCalls = 0; expectedIdentifiers.push(jailhouseIdentifier); diff --git a/packages/-ember-data/tests/integration/records/create-record-test.js b/packages/-ember-data/tests/integration/records/create-record-test.js index 996d8b011ff..7437c483719 100644 --- a/packages/-ember-data/tests/integration/records/create-record-test.js +++ b/packages/-ember-data/tests/integration/records/create-record-test.js @@ -1,3 +1,5 @@ +import { settled } from '@ember/test-helpers'; + import { module, test } from 'qunit'; import { resolve } from 'rsvp'; @@ -79,21 +81,19 @@ module('Store.createRecord() coverage', function (hooks) { }); // check that we are properly configured - assert.strictEqual(pet.get('owner'), chris, 'Precondition: Our owner is Chris'); + assert.strictEqual(pet.owner, chris, 'Precondition: Our owner is Chris'); let pets = chris .get('pets') .toArray() - .map((pet) => pet.get('name')); + .map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Precondition: Chris has Shen as a pet'); + debugger; pet.unloadRecord(); - assert.strictEqual(pet.get('owner'), null, 'Shen no longer has an owner'); + assert.strictEqual(pet.owner, null, 'Shen no longer has an owner'); // check that the relationship has been dissolved - pets = chris - .get('pets') - .toArray() - .map((pet) => pet.get('name')); + pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, [], 'Chris no longer has any pets'); }); diff --git a/packages/debug/addon/index.js b/packages/debug/addon/index.js index a8a99fdd748..666b7795ecc 100644 --- a/packages/debug/addon/index.js +++ b/packages/debug/addon/index.js @@ -81,7 +81,7 @@ export default DataAdapter.extend({ */ watchModelTypes(typesAdded, typesUpdated) { const store = get(this, 'store'); - const __createRecordData = store._instanceCache._createRecordData; + const __getRecordData = store._instanceCache.getRecordData; const _releaseMethods = []; const discoveredTypes = typesMapFor(store); @@ -91,15 +91,15 @@ export default DataAdapter.extend({ }); // Overwrite _createRecordData so newly added models will get added to the list - store._instanceCache._createRecordData = (identifier) => { + store._instanceCache.getRecordData = (identifier) => { // defer to ensure first-create does not result in an infinite loop, see https://github.com/emberjs/data/issues/8006 next(() => this.watchTypeIfUnseen(store, discoveredTypes, identifier.type, typesAdded, typesUpdated, _releaseMethods)); - return __createRecordData.call(store._instanceCache, identifier); + return __getRecordData.call(store._instanceCache, identifier); }; let release = () => { _releaseMethods.forEach((fn) => fn()); - store._instanceCache._createRecordData = __createRecordData; + store._instanceCache.getRecordData = __getRecordData; // reset the list so the models can be added if the inspector is re-opened // the entries are set to false instead of removed, since the models still exist in the app // we just need the inspector to become aware of them diff --git a/packages/record-data/addon/-private/record-data.ts b/packages/record-data/addon/-private/record-data.ts index dfbdb59ab6c..b9e639a9688 100644 --- a/packages/record-data/addon/-private/record-data.ts +++ b/packages/record-data/addon/-private/record-data.ts @@ -1,12 +1,10 @@ /** * @module @ember-data/record-data */ -import { assert } from '@ember/debug'; -import { _backburner as emberBackburner } from '@ember/runloop'; import { isEqual } from '@ember/utils'; import type { RecordDataStoreWrapper } from '@ember-data/store/-private'; -import { recordDataFor, recordIdentifierFor, removeRecordDataFor } from '@ember-data/store/-private'; +import { recordIdentifierFor } from '@ember-data/store/-private'; import type { CollectionResourceRelationship } from '@ember-data/types/q/ember-data-json-api'; import type { RecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { ChangedAttributesHash, RecordData } from '@ember-data/types/q/record-data'; @@ -21,8 +19,6 @@ import { graphFor } from './graph/index'; import type BelongsToRelationship from './relationships/state/belongs-to'; import type ManyRelationship from './relationships/state/has-many'; -let nextBfsId = 1; - const EMPTY_ITERATOR = { iterator() { return { @@ -48,11 +44,9 @@ export default class RecordDataDefault implements RelationshipRecordData { declare identifier: StableRecordIdentifier; declare isDestroyed: boolean; declare _isNew: boolean; - declare _bfsId: number; declare __attributes: any; declare __inFlightAttributes: any; declare __data: any; - declare _scheduledDestroy: any; declare _isDeleted: boolean; declare _isDeletionCommited: boolean; declare storeWrapper: RecordDataStoreWrapper; @@ -66,9 +60,6 @@ export default class RecordDataDefault implements RelationshipRecordData { this.isDestroyed = false; this._isNew = false; this._isDeleted = false; - // Used during the mark phase of unloading to avoid checking the same internal - // model twice in the same scan - this._bfsId = 0; this.reset(); } @@ -428,38 +419,25 @@ export default class RecordDataDefault implements RelationshipRecordData { if (this.isDestroyed) { return; } - graphFor(this.storeWrapper).unload(this.identifier); + const { storeWrapper } = this; + graphFor(storeWrapper).unload(this.identifier); this.reset(); - if (!this._scheduledDestroy) { - this._scheduledDestroy = emberBackburner.schedule('destroy', this, '_cleanupOrphanedRecordDatas'); - } - } - _cleanupOrphanedRecordDatas() { - let relatedRecordDatas = this._allRelatedRecordDatas(); - if (areAllModelsUnloaded(relatedRecordDatas)) { + let relatedIdentifiers = this._allRelatedRecordDatas(); + if (areAllModelsUnloaded(this.storeWrapper, relatedIdentifiers)) { // we don't have a backburner queue yet since // we scheduled this into ember's destroy // disconnectRecord called from destroy will teardown // relationships. We do this to queue that. this.storeWrapper._store._backburner.join(() => { - for (let i = 0; i < relatedRecordDatas.length; ++i) { - let recordData = relatedRecordDatas[i]; - if (!recordData.isDestroyed) { - // TODO @runspired we do not currently destroy RecordData instances *except* via this relationship - // traversal. This seems like an oversight since the store should be able to notify destroy. - removeRecordDataFor(recordData.identifier); - recordData.destroy(); - } + for (let i = 0; i < relatedIdentifiers.length; ++i) { + let identifier = relatedIdentifiers[i]; + storeWrapper.disconnectRecord(identifier.type, identifier.id, identifier.lid); } }); } - this._scheduledDestroy = null; - } - destroy() { this.isDestroyed = true; - this.storeWrapper.disconnectRecord(this.modelName, this.id, this.lid); } isRecordInUse() { @@ -496,7 +474,7 @@ export default class RecordDataDefault implements RelationshipRecordData { while (k < members.length) { let member = members[k++]; if (member !== null) { - return recordDataFor(member); + return member; } } k = 0; @@ -521,34 +499,31 @@ export default class RecordDataDefault implements RelationshipRecordData { }; /* - Computes the set of internal models reachable from this internal model. + Computes the set of Identifiers reachable from this Identifier. Reachability is determined over the relationship graph (ie a graph where - nodes are internal models and edges are belongs to or has many + nodes are identifiers and edges are belongs to or has many relationships). - Returns an array including `this` and all internal models reachable - from `this`. + Returns an array including `this` and all identifiers reachable + from `this.identifier`. */ - _allRelatedRecordDatas(): RecordDataDefault[] { - let array: RecordDataDefault[] = []; - let queue: RecordDataDefault[] = []; - let bfsId = nextBfsId++; - queue.push(this); - this._bfsId = bfsId; + _allRelatedRecordDatas(): StableRecordIdentifier[] { + let array: StableRecordIdentifier[] = []; + let queue: StableRecordIdentifier[] = []; + let seen = new Set(); + queue.push(this.identifier); while (queue.length > 0) { - let node = queue.shift() as RecordDataDefault; - array.push(node); + let identifier = queue.shift()!; + array.push(identifier); + seen.add(identifier); const iterator = this._directlyRelatedRecordDatasIterable().iterator(); for (let obj = iterator.next(); !obj.done; obj = iterator.next()) { - const recordData = obj.value; - if (recordData && recordData instanceof RecordDataDefault) { - assert('Internal Error: seen a future bfs iteration', recordData._bfsId <= bfsId); - if (recordData._bfsId < bfsId) { - queue.push(recordData); - recordData._bfsId = bfsId; - } + const identifier = obj.value; + if (identifier && !seen.has(identifier)) { + seen.add(identifier); + queue.push(identifier); } } } @@ -746,9 +721,10 @@ export default class RecordDataDefault implements RelationshipRecordData { } } -function areAllModelsUnloaded(recordDatas) { - for (let i = 0; i < recordDatas.length; ++i) { - if (recordDatas[i].isRecordInUse()) { +function areAllModelsUnloaded(wrapper: RecordDataStoreWrapper, identifiers: StableRecordIdentifier[]): boolean { + for (let i = 0; i < identifiers.length; ++i) { + let identifer = identifiers[i]; + if (wrapper.isRecordInUse(identifer.type, identifer.id, identifer.lid)) { return false; } } diff --git a/packages/store/addon/-private/caches/identifier-cache.ts b/packages/store/addon/-private/caches/identifier-cache.ts index 592a06bbec4..b2a9d48209f 100644 --- a/packages/store/addon/-private/caches/identifier-cache.ts +++ b/packages/store/addon/-private/caches/identifier-cache.ts @@ -214,7 +214,7 @@ export class IdentifierCache { let newLid = this._generate(resource, 'record'); if (LOG_IDENTIFIERS) { // eslint-disable-next-line no-console - console.log(`Identifiers: generated record identifier ${newLid} for resource`, resource); + console.log(`Identifiers: lid ${newLid} determined for resource`, resource); } // we do this _even_ when `lid` is present because secondary lookups @@ -252,6 +252,11 @@ export class IdentifierCache { // TODO exists temporarily to support `peekAll` // but likely to move keyOptions._allIdentifiers.push(identifier); + + if (LOG_IDENTIFIERS && shouldGenerate) { + // eslint-disable-next-line no-console + console.log(`Identifiers: generated ${String(identifier)} for`, resource); + } } // populate our own secondary lookup table @@ -285,6 +290,13 @@ export class IdentifierCache { * @private */ peekRecordIdentifier(resource: ResourceIdentifierObject | Identifier): StableRecordIdentifier | undefined { + if (LOG_IDENTIFIERS) { + const identifier = this._getRecordIdentifier(resource, false); + // eslint-disable-next-line no-console + console.log(`Identifiers: cache peek found ${String(identifier)}`, resource); + + return identifier; + } return this._getRecordIdentifier(resource, false); } diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index df0ce6aa8fe..6432310be96 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -1,14 +1,13 @@ import { assert, warn } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; +import { importSync } from '@embroider/macros'; import { resolve } from 'rsvp'; +import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra'; import { LOG_INSTANCE_CACHE } from '@ember-data/private-build-infra/debugging'; -import type { - ExistingResourceObject, - NewResourceIdentifierObject, - ResourceIdentifierObject, -} from '@ember-data/types/q/ember-data-json-api'; +import type { Graph, peekGraph } from '@ember-data/record-data/-private/graph/index'; +import type { ExistingResourceObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api'; import type { RecordIdentifier, StableExistingRecordIdentifier, @@ -33,6 +32,16 @@ import normalizeModelName from '../utils/normalize-model-name'; import WeakCache, { DebugWeakCache } from '../utils/weak-cache'; import recordDataFor, { removeRecordDataFor, setRecordDataFor } from './record-data-for'; +let _peekGraph: peekGraph; +if (HAS_RECORD_DATA_PACKAGE) { + let __peekGraph: peekGraph; + _peekGraph = (wrapper: Store | RecordDataStoreWrapper): Graph | undefined => { + let a = (importSync('@ember-data/record-data/-private') as { peekGraph: peekGraph }).peekGraph; + __peekGraph = __peekGraph || a; + return __peekGraph(wrapper); + }; +} + /** @module @ember-data/store */ @@ -314,19 +323,25 @@ export class InstanceCache { return new Snapshot(options, identifier, this.store); } - destroyRecord(identifier: StableRecordIdentifier) { - if (LOG_INSTANCE_CACHE) { - // eslint-disable-next-line no-console - console.log(`InstanceCache: destroying record for ${String(identifier)}`); - } + disconnect(identifier: StableRecordIdentifier) { const record = this.#instances.record.get(identifier); assert( 'Cannot destroy record while it is still materialized', !record || record.isDestroyed || record.isDestroying ); - this.peekList[identifier.type]?.delete(identifier); + if (HAS_RECORD_DATA_PACKAGE) { + let graph = _peekGraph(this.store); + if (graph) { + graph.remove(identifier); + } + } + this.store.identifierCache.forgetRecordIdentifier(identifier); + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: disconnected ${String(identifier)}`); + } } unloadRecord(identifier: StableRecordIdentifier) { @@ -366,20 +381,15 @@ export class InstanceCache { this.#instances.recordData.delete(identifier); recordData.unloadRecord(); removeRecordDataFor(identifier); - - if (LOG_INSTANCE_CACHE) { - // eslint-disable-next-line no-console - console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`); - } } else { - this._storeWrapper.disconnectRecord(identifier.type, identifier.id, identifier.lid); - if (LOG_INSTANCE_CACHE) { - // eslint-disable-next-line no-console - console.log(`InstanceCache: disconnected record for ${String(identifier)}`); - } + this.disconnect(identifier); } this.store.recordArrayManager.recordDidChange(identifier); + if (LOG_INSTANCE_CACHE) { + // eslint-disable-next-line no-console + console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`); + } }); } @@ -490,7 +500,6 @@ export class InstanceCache { // TODO this should move into something coordinating operations loadData(data: ExistingResourceObject): StableExistingRecordIdentifier { - // TODO type should be pulled from the identifier for debug let modelName = data.type; assert( `You must include an 'id' for ${modelName} in an object passed to 'push'`, @@ -501,9 +510,7 @@ export class InstanceCache { this.store.getSchemaDefinitionService().doesTypeExist(modelName) ); - // TODO this should determine identifier via the cache before making assumptions const resource = constructResource(normalizeModelName(data.type), ensureStringId(data.id), coerceId(data.lid)); - // we probably should ony peek here let identifier = this.store.identifierCache.peekRecordIdentifier(resource); let isUpdate = false; @@ -520,7 +527,7 @@ export class InstanceCache { identifier = this.store.identifierCache.updateRecordIdentifier(identifier, data); } } else { - identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource); + identifier = this.store.identifierCache.getOrCreateRecordIdentifier(data); } const recordData = this.getRecordData(identifier); diff --git a/packages/store/addon/-private/managers/record-data-store-wrapper.ts b/packages/store/addon/-private/managers/record-data-store-wrapper.ts index a674ac89da0..2621cad10a6 100644 --- a/packages/store/addon/-private/managers/record-data-store-wrapper.ts +++ b/packages/store/addon/-private/managers/record-data-store-wrapper.ts @@ -1,7 +1,4 @@ -import { importSync } from '@embroider/macros'; - import type { RelationshipDefinition } from '@ember-data/model/-private/relationship-meta'; -import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { RecordData } from '@ember-data/types/q/record-data'; import type { @@ -23,17 +20,6 @@ function metaIsRelationshipDefinition(meta: RelationshipSchema): meta is Relatio return typeof (meta as RelationshipDefinition)._inverseKey === 'function'; } -let peekGraph; -if (HAS_RECORD_DATA_PACKAGE) { - let _peekGraph; - peekGraph = (wrapper) => { - _peekGraph = - _peekGraph || - (importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')).peekGraph; - return _peekGraph(wrapper); - }; -} - export default class RecordDataStoreWrapper implements StoreWrapper { declare _willNotify: boolean; declare _pendingNotifies: Map>; @@ -210,9 +196,9 @@ export default class RecordDataStoreWrapper implements StoreWrapper { isRecordInUse(type: string, id: string, lid?: string | null): boolean; isRecordInUse(type: string, id: string | null, lid?: string | null): boolean { const resource = constructResource(type, id, lid); - const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); + const identifier = this.identifierCache.peekRecordIdentifier(resource); - const record = this._store._instanceCache.peek({ identifier, bucket: 'record' }); + const record = identifier && this._store._instanceCache.peek({ identifier, bucket: 'record' }); return record ? !(record.isDestroyed || record.isDestroying) : false; } @@ -221,13 +207,10 @@ export default class RecordDataStoreWrapper implements StoreWrapper { disconnectRecord(type: string, id: string, lid?: string | null): void; disconnectRecord(type: string, id: string | null, lid?: string | null): void { const resource = constructResource(type, id, lid); - const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource); - if (HAS_RECORD_DATA_PACKAGE) { - let graph = peekGraph(this); - if (graph) { - graph.remove(identifier); - } + const identifier = this.identifierCache.peekRecordIdentifier(resource); + + if (identifier) { + this._store._instanceCache.disconnect(identifier); } - this._store._instanceCache.destroyRecord(identifier); } } From 551e9fc3b8945515b208091ae4c11f2948a5c180 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sat, 6 Aug 2022 14:36:09 -0700 Subject: [PATCH 16/29] logging improvements --- .../addon/-private/caches/identifier-cache.ts | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/store/addon/-private/caches/identifier-cache.ts b/packages/store/addon/-private/caches/identifier-cache.ts index b2a9d48209f..da29ec4c699 100644 --- a/packages/store/addon/-private/caches/identifier-cache.ts +++ b/packages/store/addon/-private/caches/identifier-cache.ts @@ -173,9 +173,17 @@ export class IdentifierCache { throw new Error(`The supplied identifier ${resource} does not belong to this store instance`); } } + if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.log(`Identifiers: Peeked Identifier was already Stable ${String(resource)}`); + } return resource; } + if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.groupCollapsed('Identifiers: Peeking/Generating Identifier', resource); + } let lid = coerceId(resource.lid); let identifier: StableRecordIdentifier | undefined = lid !== null ? this._cache.lids[lid] : undefined; @@ -256,6 +264,12 @@ export class IdentifierCache { if (LOG_IDENTIFIERS && shouldGenerate) { // eslint-disable-next-line no-console console.log(`Identifiers: generated ${String(identifier)} for`, resource); + if (resource[DEBUG_IDENTIFIER_BUCKET]) { + // eslint-disable-next-line no-console + console.trace( + `[WARNING] Identifiers: generated a new identifier from a previously used identifier. This is likely a bug.` + ); + } } } @@ -276,6 +290,15 @@ export class IdentifierCache { } } + if (LOG_IDENTIFIERS) { + if (!identifier && !shouldGenerate) { + // eslint-disable-next-line no-console + console.log(`Identifiers: cache MISS`, resource); + } + // eslint-disable-next-line no-console + console.groupEnd(); + } + return identifier; } @@ -290,13 +313,6 @@ export class IdentifierCache { * @private */ peekRecordIdentifier(resource: ResourceIdentifierObject | Identifier): StableRecordIdentifier | undefined { - if (LOG_IDENTIFIERS) { - const identifier = this._getRecordIdentifier(resource, false); - // eslint-disable-next-line no-console - console.log(`Identifiers: cache peek found ${String(identifier)}`, resource); - - return identifier; - } return this._getRecordIdentifier(resource, false); } @@ -351,6 +367,11 @@ export class IdentifierCache { // TODO move this outta here? keyOptions._allIdentifiers.push(identifier); + if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.log(`Identifiers: createded identifier ${String(identifier)} for newly generated resource`, data); + } + return identifier; } @@ -552,6 +573,10 @@ function makeStableRecordIdentifier( let { type, id, lid } = recordIdentifier; return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${type}:${id} (${lid})`; }, + toJSON() { + let { type, id, lid } = recordIdentifier; + return { type, id, lid }; + }, }; wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated; wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket; From 41e31720fa9d689ba08943c65f8462803fbb229d Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sat, 6 Aug 2022 16:58:57 -0700 Subject: [PATCH 17/29] progress --- packages/model/addon/-private/model.js | 5 +- packages/model/addon/-private/record-state.ts | 48 +++++++++++-------- .../record-data/addon/-private/graph/index.ts | 20 ++++++++ .../relationships/state/belongs-to.ts | 19 +++++++- .../-private/relationships/state/has-many.ts | 14 ++++++ .../addon/-private/caches/identifier-cache.ts | 13 +++-- .../addon/-private/caches/instance-cache.ts | 4 +- .../managers/record-data-store-wrapper.ts | 1 + .../managers/record-notification-manager.ts | 48 +++++++++++++------ 9 files changed, 129 insertions(+), 43 deletions(-) diff --git a/packages/model/addon/-private/model.js b/packages/model/addon/-private/model.js index a87289149f5..79b9482acf3 100644 --- a/packages/model/addon/-private/model.js +++ b/packages/model/addon/-private/model.js @@ -119,6 +119,7 @@ function computeOnce(target, key, desc) { */ class Model extends EmberObject { @service store; + #notifications; init(options = {}) { if (DEBUG && !options._secretInit && !options._createProps) { @@ -141,13 +142,15 @@ class Model extends EmberObject { let notifications = store._notificationManager; let identity = recordIdentifierFor(this); - notifications.subscribe(identity, (identifier, type, key) => { + this.#notifications = notifications.subscribe(identity, (identifier, type, key) => { notifyChanges(identifier, type, key, this, store); }); } willDestroy() { LEGACY_SUPPORT.get(this)?.destroy(); + this.___recordState?.destroy(); + storeFor(this)._notificationManager.unsubscribe(this.#notifications); } /** diff --git a/packages/model/addon/-private/record-state.ts b/packages/model/addon/-private/record-state.ts index 1a76f324f5c..42f891e93ec 100644 --- a/packages/model/addon/-private/record-state.ts +++ b/packages/model/addon/-private/record-state.ts @@ -154,6 +154,7 @@ export default class RecordState { declare recordData: RecordData; declare _errorRequests: any[]; declare _lastError: any; + declare handler: object; constructor(record: Model) { const store = storeFor(record)!; @@ -233,27 +234,34 @@ export default class RecordState { } } - notifications.subscribe(identity, (identifier: StableRecordIdentifier, type: NotificationType, key?: string) => { - switch (type) { - case 'state': - this.notify('isNew'); - this.notify('isDeleted'); - this.notify('isDirty'); - break; - case 'attributes': - this.notify('isEmpty'); - this.notify('isDirty'); - break; - case 'unload': - this.notify('isNew'); - this.notify('isDeleted'); - break; - case 'errors': - this.updateInvalidErrors(this.record.errors); - this.notify('isValid'); - break; + this.handler = notifications.subscribe( + identity, + (identifier: StableRecordIdentifier, type: NotificationType, key?: string) => { + switch (type) { + case 'state': + this.notify('isNew'); + this.notify('isDeleted'); + this.notify('isDirty'); + break; + case 'attributes': + this.notify('isEmpty'); + this.notify('isDirty'); + break; + case 'unload': + this.notify('isNew'); + this.notify('isDeleted'); + break; + case 'errors': + this.updateInvalidErrors(this.record.errors); + this.notify('isValid'); + break; + } } - }); + ); + } + + destroy() { + this.store._notificationManager.unsubscribe(this.handler); } notify(key) { diff --git a/packages/record-data/addon/-private/graph/index.ts b/packages/record-data/addon/-private/graph/index.ts index 6edba75e672..d0780707b66 100644 --- a/packages/record-data/addon/-private/graph/index.ts +++ b/packages/record-data/addon/-private/graph/index.ts @@ -51,6 +51,7 @@ function getWrapper(store: RecordDataStoreWrapper | Store): RecordDataStoreWrapp export function peekGraph(store: RecordDataStoreWrapper | Store): Graph | undefined { return Graphs.get(getWrapper(store)); } +export type peekGraph = typeof peekGraph; export function graphFor(store: RecordDataStoreWrapper | Store): Graph { return Graphs.lookup(getWrapper(store)); @@ -89,6 +90,7 @@ export class Graph { }; declare _updatedRelationships: Set; declare _transaction: Set | null; + declare _removing: StableRecordIdentifier | null; constructor(store: RecordDataStoreWrapper) { this._definitionCache = Object.create(null); @@ -100,6 +102,7 @@ export class Graph { this._pushedUpdates = { belongsTo: [], hasMany: [], deletions: [] }; this._updatedRelationships = new Set(); this._transaction = null; + this._removing = null; } has(identifier: StableRecordIdentifier, propertyName: string): boolean { @@ -232,8 +235,11 @@ export class Graph { // eslint-disable-next-line no-console console.log(`graph: remove ${String(identifier)}`); } + assert(`Cannot remove ${String(identifier)} while still removing ${String(this._removing)}`, !this._removing); + this._removing = identifier; this.unload(identifier); this.identifiers.delete(identifier); + this._removing = null; } /* @@ -334,6 +340,10 @@ export class Graph { if (!this._willSyncRemote) { return; } + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.groupCollapsed(`Graph: Initialized Transaction`); + } this._transaction = new Set(); this._willSyncRemote = false; const { deletions, hasMany, belongsTo } = this._pushedUpdates; @@ -357,6 +367,10 @@ export class Graph { _addToTransaction(relationship: ManyRelationship | BelongsToRelationship) { assert(`expected a transaction`, this._transaction !== null); + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log(`Graph: ${relationship.identifier} ${relationship.definition.key} added to transaction`); + } relationship.transactionRef++; this._transaction.add(relationship); } @@ -365,6 +379,12 @@ export class Graph { if (this._transaction) { this._transaction.forEach((v) => (v.transactionRef = 0)); this._transaction = null; + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log(`Graph: transaction finalized`); + // eslint-disable-next-line no-console + console.groupEnd(); + } } } diff --git a/packages/record-data/addon/-private/relationships/state/belongs-to.ts b/packages/record-data/addon/-private/relationships/state/belongs-to.ts index 1cc8592637c..672d7f6959b 100644 --- a/packages/record-data/addon/-private/relationships/state/belongs-to.ts +++ b/packages/record-data/addon/-private/relationships/state/belongs-to.ts @@ -1,3 +1,4 @@ +import { LOG_GRAPH } from '@ember-data/private-build-infra/debugging'; import type { RecordDataStoreWrapper } from '@ember-data/store/-private'; import type { Links, Meta, PaginationLinks } from '@ember-data/types/q/ember-data-json-api'; import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; @@ -149,8 +150,22 @@ export default class BelongsToRelationship { } notifyBelongsToChange() { - let recordData = this.identifier; - this.store.notifyBelongsToChange(recordData.type, recordData.id, recordData.lid, this.definition.key); + const identifier = this.identifier; + if (identifier === this.graph._removing) { + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log( + `Graph: ignoring belongsToChange for removed identifier ${String(identifier)} ${this.definition.key}` + ); + } + return; + } + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log(`Graph: notifying belongsToChange for ${String(identifier)} ${this.definition.key}`); + } + + this.store.notifyBelongsToChange(identifier.type, identifier.id, identifier.lid, this.definition.key); } clear() { diff --git a/packages/record-data/addon/-private/relationships/state/has-many.ts b/packages/record-data/addon/-private/relationships/state/has-many.ts index 41aa0b976cf..05a186e4027 100755 --- a/packages/record-data/addon/-private/relationships/state/has-many.ts +++ b/packages/record-data/addon/-private/relationships/state/has-many.ts @@ -1,5 +1,6 @@ import { assert } from '@ember/debug'; +import { LOG_GRAPH } from '@ember-data/private-build-infra/debugging'; import type { RecordDataStoreWrapper } from '@ember-data/store/-private'; import type { CollectionResourceRelationship, @@ -163,6 +164,19 @@ export default class ManyRelationship { notifyHasManyChange() { const { store, identifier } = this; + if (identifier === this.graph._removing) { + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log( + `Graph: ignoring hasManyChange for removed identifier ${String(identifier)} ${this.definition.key}` + ); + } + return; + } + if (LOG_GRAPH) { + // eslint-disable-next-line no-console + console.log(`Graph: notifying hasManyChange for ${String(identifier)} ${this.definition.key}`); + } store.notifyHasManyChange(identifier.type, identifier.id, identifier.lid, this.definition.key); } diff --git a/packages/store/addon/-private/caches/identifier-cache.ts b/packages/store/addon/-private/caches/identifier-cache.ts index da29ec4c699..dd205cb64d7 100644 --- a/packages/store/addon/-private/caches/identifier-cache.ts +++ b/packages/store/addon/-private/caches/identifier-cache.ts @@ -180,17 +180,22 @@ export class IdentifierCache { return resource; } - if (LOG_IDENTIFIERS) { - // eslint-disable-next-line no-console - console.groupCollapsed('Identifiers: Peeking/Generating Identifier', resource); - } let lid = coerceId(resource.lid); let identifier: StableRecordIdentifier | undefined = lid !== null ? this._cache.lids[lid] : undefined; if (identifier !== undefined) { + if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.log(`Identifiers: cache HIT ${identifier}`, resource); + } return identifier; } + if (LOG_IDENTIFIERS) { + // eslint-disable-next-line no-console + console.groupCollapsed(`Identifiers: ${shouldGenerate ? 'Generating' : 'Peeking'} Identifier`, resource); + } + if (shouldGenerate === false) { if (!('type' in resource) || !('id' in resource) || !resource.type || !resource.id) { return; diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index 6432310be96..0b5c2f4278f 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -357,7 +357,7 @@ export class InstanceCache { } if (LOG_INSTANCE_CACHE) { // eslint-disable-next-line no-console - console.log(`InstanceCache: unloading record for ${String(identifier)}`); + console.groupCollapsed(`InstanceCache: unloading record for ${String(identifier)}`); } // TODO is this join still necessary? @@ -389,6 +389,8 @@ export class InstanceCache { if (LOG_INSTANCE_CACHE) { // eslint-disable-next-line no-console console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`); + // eslint-disable-next-line no-console + console.groupEnd(); } }); } diff --git a/packages/store/addon/-private/managers/record-data-store-wrapper.ts b/packages/store/addon/-private/managers/record-data-store-wrapper.ts index 2621cad10a6..e761039e598 100644 --- a/packages/store/addon/-private/managers/record-data-store-wrapper.ts +++ b/packages/store/addon/-private/managers/record-data-store-wrapper.ts @@ -211,6 +211,7 @@ export default class RecordDataStoreWrapper implements StoreWrapper { if (identifier) { this._store._instanceCache.disconnect(identifier); + this._pendingNotifies.delete(identifier); } } } diff --git a/packages/store/addon/-private/managers/record-notification-manager.ts b/packages/store/addon/-private/managers/record-notification-manager.ts index 2b6518b3688..c33759af3f2 100644 --- a/packages/store/addon/-private/managers/record-notification-manager.ts +++ b/packages/store/addon/-private/managers/record-notification-manager.ts @@ -1,12 +1,14 @@ +import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; import { LOG_NOTIFICATIONS } from '@ember-data/private-build-infra/debugging'; -import type { RecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier'; +import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; +import { isStableIdentifier } from '../caches/identifier-cache'; import type Store from '../store-service'; import WeakCache from '../utils/weak-cache'; -type UnsubscribeToken = Object; +type UnsubscribeToken = object; const Cache = new WeakCache>( DEBUG ? 'subscribers' : '' @@ -25,8 +27,12 @@ export type NotificationType = | 'property'; // 'property' is an internal EmberData only transition period concept. export interface NotificationCallback { - (identifier: RecordIdentifier, notificationType: 'attributes' | 'relationships' | 'property', key?: string): void; - (identifier: RecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): void; + ( + identifier: StableRecordIdentifier, + notificationType: 'attributes' | 'relationships' | 'property', + key?: string + ): void; + (identifier: StableRecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): void; (identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void; } @@ -45,12 +51,12 @@ export function unsubscribe(token: UnsubscribeToken) { export default class NotificationManager { constructor(private store: Store) {} - subscribe(identifier: RecordIdentifier, callback: NotificationCallback): UnsubscribeToken { - let stableIdentifier = this.store.identifierCache.getOrCreateRecordIdentifier(identifier); - let map = Cache.lookup(stableIdentifier); + subscribe(identifier: StableRecordIdentifier, callback: NotificationCallback): UnsubscribeToken { + assert(`Expected to receive a stable Identifier to subscribe to`, isStableIdentifier(identifier)); + let map = Cache.lookup(identifier); let unsubToken = {}; map.set(unsubToken, callback); - Tokens.set(unsubToken, stableIdentifier); + Tokens.set(unsubToken, identifier); return unsubToken; } @@ -58,20 +64,32 @@ export default class NotificationManager { unsubscribe(token); } - notify(identifier: RecordIdentifier, value: 'attributes' | 'relationships' | 'property', key?: string): boolean; - notify(identifier: RecordIdentifier, value: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): boolean; - notify(identifier: RecordIdentifier, value: NotificationType, key?: string): boolean { - let stableIdentifier = this.store.identifierCache.getOrCreateRecordIdentifier(identifier); + notify(identifier: StableRecordIdentifier, value: 'attributes' | 'relationships' | 'property', key?: string): boolean; + notify(identifier: StableRecordIdentifier, value: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): boolean; + notify(identifier: StableRecordIdentifier, value: NotificationType, key?: string): boolean { + if (!isStableIdentifier(identifier)) { + if (LOG_NOTIFICATIONS) { + // eslint-disable-next-line no-console + console.log( + `Notifying: Expected to receive a stable Identifier to notify '${value}' '${key}' with, but ${String( + identifier + )} is not in the cache`, + identifier + ); + } + return false; + } + if (LOG_NOTIFICATIONS) { // eslint-disable-next-line no-console - console.log(`Notifying: ${String(stableIdentifier)}\t${value}\t${key}`); + console.log(`Notifying: ${String(identifier)}\t${value}\t${key}`); } - let callbackMap = Cache.get(stableIdentifier); + let callbackMap = Cache.get(identifier); if (!callbackMap || !callbackMap.size) { return false; } callbackMap.forEach((cb) => { - cb(stableIdentifier, value, key); + cb(identifier, value, key); }); return true; } From 8ba573ddffbf861298b00b4cfc6d16d4c5e20ec7 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sun, 7 Aug 2022 00:27:53 -0700 Subject: [PATCH 18/29] down to 36 failed tests --- .../integration/records/create-record-test.js | 3 - .../records/relationship-changes-test.js | 229 +++---- .../relationships/has-many-test.js | 606 ++++++++---------- .../tests/unit/promise-proxies-test.js | 101 +-- packages/model/addon/-private/attr.js | 3 + packages/model/addon/-private/belongs-to.js | 4 + packages/model/addon/-private/has-many.js | 3 + .../-private/legacy-relationships-support.ts | 4 - packages/model/addon/-private/many-array.ts | 5 +- packages/model/addon/-private/model.js | 14 +- .../addon/-private/promise-many-array.ts | 37 +- packages/model/addon/-private/record-state.ts | 2 +- .../addon/-private/caches/instance-cache.ts | 9 +- .../addon/-private/caches/record-data-for.ts | 2 +- 14 files changed, 503 insertions(+), 519 deletions(-) diff --git a/packages/-ember-data/tests/integration/records/create-record-test.js b/packages/-ember-data/tests/integration/records/create-record-test.js index 7437c483719..c6d1ebef89c 100644 --- a/packages/-ember-data/tests/integration/records/create-record-test.js +++ b/packages/-ember-data/tests/integration/records/create-record-test.js @@ -1,5 +1,3 @@ -import { settled } from '@ember/test-helpers'; - import { module, test } from 'qunit'; import { resolve } from 'rsvp'; @@ -89,7 +87,6 @@ module('Store.createRecord() coverage', function (hooks) { .map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Precondition: Chris has Shen as a pet'); - debugger; pet.unloadRecord(); assert.strictEqual(pet.owner, null, 'Shen no longer has an owner'); // check that the relationship has been dissolved diff --git a/packages/-ember-data/tests/integration/records/relationship-changes-test.js b/packages/-ember-data/tests/integration/records/relationship-changes-test.js index 3396170747f..493098e1e2f 100644 --- a/packages/-ember-data/tests/integration/records/relationship-changes-test.js +++ b/packages/-ember-data/tests/integration/records/relationship-changes-test.js @@ -10,6 +10,7 @@ import { setupTest } from 'ember-qunit'; import Adapter from '@ember-data/adapter'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; +import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; const Author = Model.extend({ name: attr('string'), @@ -108,12 +109,80 @@ module('integration/records/relationship-changes - Relationship changes', functi }); if (!gte('4.0.0')) { - test('Calling push with relationship triggers observers once if the relationship was empty and is added to', function (assert) { + deprecatedTest( + 'Calling push with relationship triggers observers once if the relationship was empty and is added to', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 3 }, + function (assert) { + assert.expect(1); + + let store = this.owner.lookup('service:store'); + let person = null; + let observerCount = 0; + + run(() => { + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: { + firstName: 'Yehuda', + lastName: 'Katz', + }, + relationships: { + siblings: { + data: [], + }, + }, + }, + }); + person = store.peekRecord('person', 'wat'); + }); + + run(() => { + person.addObserver('siblings.[]', function () { + observerCount++; + }); + // prime the pump + person.get('siblings'); + }); + + run(() => { + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: {}, + relationships: { + siblings: { + data: [sibling1Ref], + }, + }, + }, + included: [sibling1], + }); + }); + + run(() => { + assert.ok(observerCount >= 1, 'siblings observer should be triggered at least once'); + }); + } + ); + } + + deprecatedTest( + 'Calling push with relationship recalculates computed alias property if the relationship was empty and is added to', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, + function (assert) { assert.expect(1); let store = this.owner.lookup('service:store'); - let person = null; - let observerCount = 0; + + let Obj = EmberObject.extend({ + person: null, + siblings: alias('person.siblings'), + }); + + const obj = Obj.create(); run(() => { store.push({ @@ -131,15 +200,7 @@ module('integration/records/relationship-changes - Relationship changes', functi }, }, }); - person = store.peekRecord('person', 'wat'); - }); - - run(() => { - person.addObserver('siblings.[]', function () { - observerCount++; - }); - // prime the pump - person.get('siblings'); + set(obj, 'person', store.peekRecord('person', 'wat')); }); run(() => { @@ -159,118 +220,70 @@ module('integration/records/relationship-changes - Relationship changes', functi }); run(() => { - assert.ok(observerCount >= 1, 'siblings observer should be triggered at least once'); + let cpResult = get(obj, 'siblings').toArray(); + assert.strictEqual(cpResult.length, 1, 'siblings cp should have recalculated'); + obj.destroy(); }); - }); - } + } + ); - test('Calling push with relationship recalculates computed alias property if the relationship was empty and is added to', function (assert) { - assert.expect(1); + deprecatedTest( + 'Calling push with relationship recalculates computed alias property to firstObject if the relationship was empty and is added to', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, + function (assert) { + assert.expect(1); - let store = this.owner.lookup('service:store'); + let store = this.owner.lookup('service:store'); - let Obj = EmberObject.extend({ - person: null, - siblings: alias('person.siblings'), - }); + let Obj = EmberObject.extend({ + person: null, + firstSibling: alias('person.siblings.firstObject'), + }); - const obj = Obj.create(); + const obj = Obj.create(); - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: { - firstName: 'Yehuda', - lastName: 'Katz', - }, - relationships: { - siblings: { - data: [], + run(() => { + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: { + firstName: 'Yehuda', + lastName: 'Katz', }, - }, - }, - }); - set(obj, 'person', store.peekRecord('person', 'wat')); - }); - - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: {}, - relationships: { - siblings: { - data: [sibling1Ref], + relationships: { + siblings: { + data: [], + }, }, }, - }, - included: [sibling1], + }); + set(obj, 'person', store.peekRecord('person', 'wat')); }); - }); - - run(() => { - let cpResult = get(obj, 'siblings').toArray(); - assert.strictEqual(cpResult.length, 1, 'siblings cp should have recalculated'); - obj.destroy(); - }); - }); - test('Calling push with relationship recalculates computed alias property to firstObject if the relationship was empty and is added to', function (assert) { - assert.expect(1); - - let store = this.owner.lookup('service:store'); - - let Obj = EmberObject.extend({ - person: null, - firstSibling: alias('person.siblings.firstObject'), - }); - - const obj = Obj.create(); - - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: { - firstName: 'Yehuda', - lastName: 'Katz', - }, - relationships: { - siblings: { - data: [], + run(() => { + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: {}, + relationships: { + siblings: { + data: [sibling1Ref], + }, }, }, - }, + included: [sibling1], + }); }); - set(obj, 'person', store.peekRecord('person', 'wat')); - }); - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: {}, - relationships: { - siblings: { - data: [sibling1Ref], - }, - }, - }, - included: [sibling1], + run(() => { + let cpResult = get(obj, 'firstSibling'); + assert.strictEqual(get(cpResult, 'id'), '1', 'siblings cp should have recalculated'); + obj.destroy(); }); - }); - - run(() => { - let cpResult = get(obj, 'firstSibling'); - assert.strictEqual(get(cpResult, 'id'), '1', 'siblings cp should have recalculated'); - obj.destroy(); - }); - }); + } + ); test('Calling push with relationship triggers observers once if the relationship was not empty and was added to', function (assert) { assert.expect(1); diff --git a/packages/-ember-data/tests/integration/relationships/has-many-test.js b/packages/-ember-data/tests/integration/relationships/has-many-test.js index fc90c19f7dd..b82feb2e110 100644 --- a/packages/-ember-data/tests/integration/relationships/has-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/has-many-test.js @@ -15,6 +15,7 @@ import RESTAdapter from '@ember-data/adapter/rest'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; import RESTSerializer from '@ember-data/serializer/rest'; +import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; import { getRelationshipStateForRecord, hasRelationshipForRecord } from '../../helpers/accessors'; @@ -260,44 +261,38 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }; - run(() => { - store.push({ - data: postData, - included: [ - { - type: 'user', - id: '2', - }, - { - type: 'user', - id: '3', - }, - { - type: 'user', - id: '4', - }, - ], - }); + let user = store.push({ + data: postData, + included: [ + { + type: 'user', + id: '2', + }, + { + type: 'user', + id: '3', + }, + { + type: 'user', + id: '4', + }, + ], }); - let user = store.peekRecord('user', 1); - let contacts = user.get('contacts'); - store.adapterFor('user').deleteRecord = function () { - return { data: { type: 'user', id: 2 } }; + return { data: { type: 'user', id: '2' } }; }; + let contacts = user.contacts; assert.deepEqual( - contacts.map((c) => c.get('id')), + contacts.map((c) => c.id), ['2', '3', '4'], 'user should have expected contacts' ); - run(() => { - contacts.addObject(store.createRecord('user', { id: 5 })); - contacts.addObject(store.createRecord('user', { id: 6 })); - contacts.addObject(store.createRecord('user', { id: 7 })); - }); + contacts.addObject(store.createRecord('user', { id: '5', name: 'chris' })); + contacts.addObject(store.createRecord('user', { id: '6' })); + contacts.addObject(store.createRecord('user', { id: '7' })); assert.deepEqual( contacts.map((c) => c.get('id')), @@ -360,27 +355,23 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }; - run(() => { - store.push({ - data: postData, - included: [ - { - type: 'user', - id: 2, - }, - { - type: 'user', - id: 3, - }, - { - type: 'user', - id: 4, - }, - ], - }); + let user = store.push({ + data: postData, + included: [ + { + type: 'user', + id: 2, + }, + { + type: 'user', + id: 3, + }, + { + type: 'user', + id: 4, + }, + ], }); - - let user = store.peekRecord('user', 1); let contacts = user.get('contacts'); store.adapterFor('user').deleteRecord = function () { @@ -405,10 +396,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( 'user should have expected contacts' ); - run(() => { - store.peekRecord('user', 2).unloadRecord(); - store.peekRecord('user', 6).unloadRecord(); - }); + store.peekRecord('user', 2).unloadRecord(); + store.peekRecord('user', 6).unloadRecord(); assert.deepEqual( contacts.map((c) => c.get('id')), @@ -417,10 +406,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( ); assert.strictEqual(contacts, user.get('contacts')); - run(() => { - contacts.addObject(store.createRecord('user', { id: 8 })); - }); - + contacts.addObject(store.createRecord('user', { id: 8 })); assert.deepEqual( contacts.map((c) => c.get('id')), ['3', '4', '5', '7', '8'], @@ -555,7 +541,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); }); - test('Accessing a hasMany backed by a link multiple times triggers only one request', function (assert) { + test('Accessing a hasMany backed by a link multiple times triggers only one request', async function (assert) { assert.expect(2); class Message extends Model { @attr('date') created_at; @@ -578,23 +564,18 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - let post; - - run(() => { - store.push({ - data: { - type: 'post', - id: '1', - relationships: { - comments: { - links: { - related: '/posts/1/comments', - }, + let post = store.push({ + data: { + type: 'post', + id: '1', + relationships: { + comments: { + links: { + related: '/posts/1/comments', }, }, }, - }); - post = store.peekRecord('post', 1); + }, }); let count = 0; @@ -610,32 +591,29 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( ], }; resolve(value); - }, 100); + }, 1); }); }; let promise1, promise2; - run(() => { - promise1 = post.get('comments'); - //Invalidate the post.comments CP - store.push({ - data: { - type: 'comment', - id: '1', - relationships: { - message: { - data: { type: 'post', id: '1' }, - }, + promise1 = post.comments; + //Invalidate the post.comments CP + store.push({ + data: { + type: 'comment', + id: '1', + relationships: { + message: { + data: { type: 'post', id: '1' }, }, }, - }); - promise2 = post.get('comments'); + }, }); + promise2 = post.comments; - return all([promise1, promise2]).then(() => { - assert.strictEqual(promise1.get('promise'), promise2.get('promise'), 'Same promise is returned both times'); - }); + await all([promise1, promise2]); + assert.strictEqual(promise1.promise, promise2.promise, 'Same promise is returned both times'); }); test('A hasMany backed by a link remains a promise after a record has been added to it', function (assert) { @@ -1147,7 +1125,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( adapter.findRecord = function () { return resolve({ data: { - id: 1, + id: '1', type: 'post', relationships: { comments: { @@ -1173,19 +1151,19 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( } }; - let post = await store.findRecord('post', 1); - let comments = post.get('comments'); + let post = await store.findRecord('post', '1'); + let commentsPromiseArray = post.comments; let manyArray; try { - manyArray = await comments; + manyArray = await commentsPromiseArray; assert.ok(false, 'Expected exception to be raised'); } catch (e) { assert.ok(true, `An error was thrown on the first reload of comments: ${e.message}`); - manyArray = await comments.reload(); + manyArray = await commentsPromiseArray.reload(); } - assert.true(manyArray.get('isLoaded'), 'the reload worked, comments are now loaded'); + assert.true(manyArray.isLoaded, 'the reload worked, comments are now loaded'); try { await manyArray.reload(); @@ -1466,67 +1444,71 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); }); - test('PromiseArray proxies createRecord to its ManyArray once the hasMany is loaded', function (assert) { - assert.expect(4); - class Message extends Model { - @attr('date') created_at; - @belongsTo('user', { async: false }) iser; - } + deprecatedTest( + 'PromiseArray proxies createRecord to its ManyArray once the hasMany is loaded', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, + function (assert) { + assert.expect(4); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - class Post extends Message { - @attr title; - @hasMany('comment', { async: true, inverse: 'message' }) comments; - } + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - class Comment extends Message { - @attr body; - @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; - } - this.owner.register('model:post', Post); - this.owner.register('model:comment', Comment); - this.owner.register('model:message', Message); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); - adapter.findHasMany = function (store, snapshot, link, relationship) { - return resolve({ - data: [ - { id: 1, type: 'comment', attributes: { body: 'First' } }, - { id: 2, type: 'comment', attributes: { body: 'Second' } }, - ], - }); - }; - let post; + adapter.findHasMany = function (store, snapshot, link, relationship) { + return resolve({ + data: [ + { id: 1, type: 'comment', attributes: { body: 'First' } }, + { id: 2, type: 'comment', attributes: { body: 'Second' } }, + ], + }); + }; + let post; - run(function () { - store.push({ - data: { - type: 'post', - id: '1', - relationships: { - comments: { - links: { - related: 'someLink', + run(function () { + store.push({ + data: { + type: 'post', + id: '1', + relationships: { + comments: { + links: { + related: 'someLink', + }, }, }, }, - }, + }); + post = store.peekRecord('post', 1); }); - post = store.peekRecord('post', 1); - }); - run(function () { - post.get('comments').then(function (comments) { - assert.true(comments.get('isLoaded'), 'comments are loaded'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); + run(function () { + post.get('comments').then(function (comments) { + assert.true(comments.get('isLoaded'), 'comments are loaded'); + assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); - let newComment = post.get('comments').createRecord({ body: 'Third' }); - assert.strictEqual(newComment.get('body'), 'Third', 'new comment is returned'); - assert.strictEqual(comments.get('length'), 3, 'comments have 3 length, including new record'); + let newComment = post.get('comments').createRecord({ body: 'Third' }); + assert.strictEqual(newComment.get('body'), 'Third', 'new comment is returned'); + assert.strictEqual(comments.get('length'), 3, 'comments have 3 length, including new record'); + }); }); - }); - }); + } + ); test('An updated `links` value should invalidate a relationship cache', async function (assert) { assert.expect(8); @@ -2416,7 +2398,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( } }); - testInDebug('An async hasMany does not fetch with a model created with no options', function (assert) { + testInDebug('An async hasMany does not fetch with a model created with no options', async function (assert) { let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); adapter.findRecord = function () { @@ -2441,8 +2423,9 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - post.get('comments').pushObject(comment); - assert.ok(post.get('comments').length, 1, 'expected length for comments'); + const comments = await post.comments; + comments.pushObject(comment); + assert.ok(post.comments.length, 1, 'expected length for comments'); }); test('After removing and unloading a record, a hasMany relationship should still be valid', function (assert) { @@ -2629,92 +2612,71 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( ); }); - test('Rollbacking attributes for deleted record restores implicit relationship correctly when the hasMany side has been deleted - async', function (assert) { + test('Rollbacking attributes for deleted record restores implicit relationship correctly when the hasMany side has been deleted - async', async function (assert) { let store = this.owner.lookup('service:store'); - - let book, chapter; - - run(() => { - store.push({ - data: { - type: 'book', - id: '1', - attributes: { - title: "Stanley's Amazing Adventures", - }, - relationships: { - chapters: { - data: [{ type: 'chapter', id: '2' }], - }, + let book = store.push({ + data: { + type: 'book', + id: '1', + attributes: { + title: "Stanley's Amazing Adventures", + }, + relationships: { + chapters: { + data: [{ type: 'chapter', id: '2' }], }, }, - included: [ - { - type: 'chapter', - id: '2', - attributes: { - title: 'Sailing the Seven Seas', - }, + }, + included: [ + { + type: 'chapter', + id: '2', + attributes: { + title: 'Sailing the Seven Seas', }, - ], - }); - book = store.peekRecord('book', 1); - chapter = store.peekRecord('chapter', 2); + }, + ], }); + let chapter = store.peekRecord('chapter', '2'); - run(() => { - chapter.deleteRecord(); - chapter.rollbackAttributes(); - }); + chapter.deleteRecord(); + chapter.rollbackAttributes(); - return run(() => { - return book.get('chapters').then((fetchedChapters) => { - assert.strictEqual(fetchedChapters.objectAt(0), chapter, 'Book has a chapter after rollback attributes'); - }); - }); + const fetchedChapters = await book.chapters; + assert.strictEqual(fetchedChapters.objectAt(0), chapter, 'Book has a chapter after rollback attributes'); }); - test('Rollbacking attributes for deleted record restores implicit relationship correctly when the hasMany side has been deleted - sync', function (assert) { + test('Rollbacking attributes for deleted record restores implicit relationship correctly when the hasMany side has been deleted - sync', async function (assert) { let store = this.owner.lookup('service:store'); - - let book, chapter; - - run(() => { - store.push({ - data: { - type: 'book', - id: '1', - attributes: { - title: "Stanley's Amazing Adventures", - }, - relationships: { - chapters: { - data: [{ type: 'chapter', id: '2' }], - }, + let chapter = store.push({ + data: { + type: 'chapter', + id: '1', + attributes: { + title: "Stanley's Amazing Adventures", + }, + relationships: { + pages: { + data: [{ type: 'page', id: '2' }], }, }, - included: [ - { - type: 'chapter', - id: '2', - attributes: { - title: 'Sailing the Seven Seas', - }, + }, + included: [ + { + type: 'page', + id: '2', + attributes: { + title: 'Sailing the Seven Seas', }, - ], - }); - book = store.peekRecord('book', 1); - chapter = store.peekRecord('chapter', 2); + }, + ], }); + let page = store.peekRecord('page', '2'); - run(() => { - chapter.deleteRecord(); - chapter.rollbackAttributes(); - }); + page.deleteRecord(); + page.rollbackAttributes(); - run(() => { - assert.strictEqual(book.get('chapters.firstObject'), chapter, 'Book has a chapter after rollback attributes'); - }); + assert.strictEqual(chapter.pages.firstObject, page, 'Chapter has a page after rollback attributes'); }); test('Rollbacking attributes for deleted record restores implicit relationship correctly when the belongsTo side has been deleted - async', function (assert) { @@ -3164,73 +3126,62 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( let store = this.owner.lookup('service:store'); - run(() => { - store.push({ - data: [ - { - type: 'post', - id: '1', - relationships: { - comments: { - data: [ - { type: 'comment', id: '1' }, - { type: 'comment', id: '2' }, - { type: 'comment', id: '3' }, - ], - }, + store.push({ + data: [ + { + type: 'post', + id: '1', + relationships: { + comments: { + data: [ + { type: 'comment', id: '1' }, + { type: 'comment', id: '2' }, + { type: 'comment', id: '3' }, + ], }, }, - { - type: 'comment', - id: '1', - }, - { - type: 'comment', - id: '2', - }, - { - type: 'comment', - id: '3', - }, - ], - }); + }, + { + type: 'comment', + id: '1', + }, + { + type: 'comment', + id: '2', + }, + { + type: 'comment', + id: '3', + }, + ], }); - return run(() => { - return store.findRecord('post', 1).then((post) => { - let comments = post.get('comments'); - assert.strictEqual(comments.get('length'), 3, 'Initial comments count'); + const post = await store.findRecord('post', '1'); + let commentsPromiseArray = post.comments; + let comments = await commentsPromiseArray; + assert.strictEqual(commentsPromiseArray.length, 3, 'Initial comments count'); - // Add comment #4 - let comment = store.createRecord('comment'); - comments.addObject(comment); + // Add comment #4 + let comment = store.createRecord('comment'); + comments.addObject(comment); - return comment - .save() - .then(() => { - let comments = post.get('comments'); - assert.strictEqual(comments.get('length'), 4, 'Comments count after first add'); + await comment.save(); + commentsPromiseArray = post.comments; + assert.strictEqual(commentsPromiseArray.length, 4, 'Comments count after first add'); - // Delete comment #4 - return comments.get('lastObject').destroyRecord(); - }) - .then(() => { - let comments = post.get('comments'); - let length = comments.get('length'); + // Delete comment #4 + await comments.lastObject.destroyRecord(); - assert.strictEqual(length, 3, 'Comments count after destroy'); + commentsPromiseArray = post.comments; + assert.strictEqual(commentsPromiseArray.length, 3, 'Comments count after destroy'); - // Add another comment #4 - let comment = store.createRecord('comment'); - comments.addObject(comment); - return comment.save(); - }) - .then(() => { - let comments = post.get('comments'); - assert.strictEqual(comments.get('length'), 4, 'Comments count after second add'); - }); - }); - }); + // Add another comment #4 + comment = store.createRecord('comment'); + comments.addObject(comment); + await comment.save(); + + commentsPromiseArray = post.comments; + assert.strictEqual(commentsPromiseArray.length, 4, 'Comments count after second add'); }); test('hasMany hasAnyRelationshipData async loaded', function (assert) { @@ -3972,60 +3923,64 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); }); - test('PromiseArray proxies createRecord to its ManyArray before the hasMany is loaded', function (assert) { - assert.expect(1); - class Message extends Model { - @attr('date') created_at; - @belongsTo('user', { async: false }) iser; - } + deprecatedTest( + 'PromiseArray proxies createRecord to its ManyArray before the hasMany is loaded', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, + function (assert) { + assert.expect(1); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - class Post extends Message { - @attr title; - @hasMany('comment', { async: true, inverse: 'message' }) comments; - } + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - class Comment extends Message { - @attr body; - @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; - } - this.owner.register('model:post', Post); - this.owner.register('model:comment', Comment); - this.owner.register('model:message', Message); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); - adapter.findHasMany = function (store, record, link, relationship) { - return resolve({ - data: [ - { id: 1, type: 'comment', attributes: { body: 'First' } }, - { id: 2, type: 'comment', attributes: { body: 'Second' } }, - ], - }); - }; + adapter.findHasMany = function (store, record, link, relationship) { + return resolve({ + data: [ + { id: 1, type: 'comment', attributes: { body: 'First' } }, + { id: 2, type: 'comment', attributes: { body: 'Second' } }, + ], + }); + }; - return run(() => { - let post = store.push({ - data: { - type: 'post', - id: 1, - relationships: { - comments: { - links: { - related: 'someLink', + return run(() => { + let post = store.push({ + data: { + type: 'post', + id: 1, + relationships: { + comments: { + links: { + related: 'someLink', + }, }, }, }, - }, - }); + }); - let comments = post.get('comments'); - comments.createRecord(); - return comments.then((comments) => { - assert.strictEqual(comments.get('length'), 3, 'comments have 3 length, including new record'); + let comments = post.get('comments'); + comments.createRecord(); + return comments.then((comments) => { + assert.strictEqual(comments.get('length'), 3, 'comments have 3 length, including new record'); + }); }); - }); - }); + } + ); test('deleteRecord + unloadRecord', async function (assert) { class User extends Model { @@ -4079,7 +4034,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); let user = store.peekRecord('user', 'user-1'); - let posts = user.get('posts'); + let postsPromiseArray = user.posts; + let posts = await postsPromiseArray; store.adapterFor('post').deleteRecord = function () { // just acknowledge all deletes, but with a noop @@ -4087,30 +4043,34 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }; assert.deepEqual( - posts.map((x) => x.get('id')), + posts.map((x) => x.id), ['post-1', 'post-2', 'post-3', 'post-4', 'post-5'] ); + assert.strictEqual(postsPromiseArray.length, 5, 'promise array length is correct'); await store.peekRecord('post', 'post-2').destroyRecord(); assert.deepEqual( - posts.map((x) => x.get('id')), + posts.map((x) => x.id), ['post-1', 'post-3', 'post-4', 'post-5'] ); + assert.strictEqual(postsPromiseArray.length, 4, 'promise array length is correct'); await store.peekRecord('post', 'post-3').destroyRecord(); assert.deepEqual( - posts.map((x) => x.get('id')), + posts.map((x) => x.id), ['post-1', 'post-4', 'post-5'] ); + assert.strictEqual(postsPromiseArray.length, 3, 'promise array length is correct'); await store.peekRecord('post', 'post-4').destroyRecord(); assert.deepEqual( - posts.map((x) => x.get('id')), + posts.map((x) => x.id), ['post-1', 'post-5'] ); + assert.strictEqual(postsPromiseArray.length, 2, 'promise array length is correct'); }); test('unloading and reloading a record with hasMany relationship - #3084', function (assert) { diff --git a/packages/-ember-data/tests/unit/promise-proxies-test.js b/packages/-ember-data/tests/unit/promise-proxies-test.js index 374a028a218..6e10d39ad34 100644 --- a/packages/-ember-data/tests/unit/promise-proxies-test.js +++ b/packages/-ember-data/tests/unit/promise-proxies-test.js @@ -9,70 +9,79 @@ import { setupTest } from 'ember-qunit'; import Adapter from '@ember-data/adapter'; import Model, { belongsTo } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; +import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; module('PromiseManyArray', function () { - test('.reload should NOT leak the internal promise, rather return another promiseArray', function (assert) { - assert.expect(1); + deprecatedTest( + '.reload should NOT leak the internal promise, rather return another promiseArray', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, + function (assert) { + assert.expect(1); - let content = A(); + let content = A(); - content.reload = () => EmberPromise.resolve(content); + content.reload = () => EmberPromise.resolve(content); - let array = DS.PromiseManyArray.create({ - content, - }); + let array = DS.PromiseManyArray.create({ + content, + }); - let reloaded = array.reload(); + let reloaded = array.reload(); - assert.strictEqual(reloaded, array); - }); + assert.strictEqual(reloaded, array); + } + ); - test('.reload should be stable', async function (assert) { - assert.expect(19); + deprecatedTest( + '.reload should be stable', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, + async function (assert) { + assert.expect(19); - let content = A(); - let array; + let content = A(); + let array; - content.reload = () => { - let p = EmberPromise.resolve(content); - array._update(p); - return p; - }; - let promise = EmberPromise.resolve(content); + content.reload = () => { + let p = EmberPromise.resolve(content); + array._update(p); + return p; + }; + let promise = EmberPromise.resolve(content); - array = DS.PromiseManyArray.create({ - promise, - }); + array = DS.PromiseManyArray.create({ + promise, + }); - assert.false(array.isRejected, 'should NOT be rejected'); - assert.true(array.isPending, 'should be pending'); - assert.false(array.isSettled, 'should NOT be settled'); - assert.false(array.isFulfilled, 'should NOT be fulfilled'); + assert.false(array.isRejected, 'should NOT be rejected'); + assert.true(array.isPending, 'should be pending'); + assert.false(array.isSettled, 'should NOT be settled'); + assert.false(array.isFulfilled, 'should NOT be fulfilled'); - await array; - assert.false(array.isRejected, 'should NOT be rejected'); - assert.false(array.isPending, 'should NOT be pending'); - assert.true(array.isSettled, 'should be settled'); - assert.true(array.isFulfilled, 'should be fulfilled'); + await array; + assert.false(array.isRejected, 'should NOT be rejected'); + assert.false(array.isPending, 'should NOT be pending'); + assert.true(array.isSettled, 'should be settled'); + assert.true(array.isFulfilled, 'should be fulfilled'); - let reloaded = array.reload(); + let reloaded = array.reload(); - assert.false(array.isRejected, 'should NOT be rejected'); - assert.true(array.isPending, 'should be pending'); - assert.false(array.isSettled, 'should NOT be settled'); - assert.false(array.isFulfilled, 'should NOT be fulfilled'); + assert.false(array.isRejected, 'should NOT be rejected'); + assert.true(array.isPending, 'should be pending'); + assert.false(array.isSettled, 'should NOT be settled'); + assert.false(array.isFulfilled, 'should NOT be fulfilled'); - assert.ok(reloaded instanceof DS.PromiseManyArray); - assert.strictEqual(reloaded, array); + assert.ok(reloaded instanceof DS.PromiseManyArray); + assert.strictEqual(reloaded, array); - let value = await reloaded; - assert.false(array.isRejected, 'should NOT be rejected'); - assert.false(array.isPending, 'should NOT be pending'); - assert.true(array.isSettled, 'should be settled'); - assert.true(array.isFulfilled, 'should be fulfilled'); + let value = await reloaded; + assert.false(array.isRejected, 'should NOT be rejected'); + assert.false(array.isPending, 'should NOT be pending'); + assert.true(array.isSettled, 'should be settled'); + assert.true(array.isFulfilled, 'should be fulfilled'); - assert.strictEqual(content, value); - }); + assert.strictEqual(content, value); + } + ); test('.set to new promise should be like reload', async function (assert) { assert.expect(18); diff --git a/packages/model/addon/-private/attr.js b/packages/model/addon/-private/attr.js index f1821b82bff..8d0c836a093 100644 --- a/packages/model/addon/-private/attr.js +++ b/packages/model/addon/-private/attr.js @@ -136,6 +136,9 @@ function attr(type, options) { ); } } + if (this.isDestroyed) { + return; + } let recordData = recordDataFor(this); // TODO hasAttr is not spec'd // essentially this is needed because diff --git a/packages/model/addon/-private/belongs-to.js b/packages/model/addon/-private/belongs-to.js index 06491f59f46..b198b63fbe3 100644 --- a/packages/model/addon/-private/belongs-to.js +++ b/packages/model/addon/-private/belongs-to.js @@ -172,6 +172,10 @@ function belongsTo(modelName, options) { } } + if (this.isDestroyed) { + return null; + } + return support.getBelongsTo(key); }, set(key, value) { diff --git a/packages/model/addon/-private/has-many.js b/packages/model/addon/-private/has-many.js index 8277b1e226b..5722c411dd9 100644 --- a/packages/model/addon/-private/has-many.js +++ b/packages/model/addon/-private/has-many.js @@ -189,6 +189,9 @@ function hasMany(type, options) { ); } } + if (this.isDestroyed) { + return []; + } return LEGACY_SUPPORT.lookup(this).getHasMany(key); }, set(key, records) { diff --git a/packages/model/addon/-private/legacy-relationships-support.ts b/packages/model/addon/-private/legacy-relationships-support.ts index 4baac480a46..0f1ac067a6a 100644 --- a/packages/model/addon/-private/legacy-relationships-support.ts +++ b/packages/model/addon/-private/legacy-relationships-support.ts @@ -510,10 +510,6 @@ export class LegacySupport { } destroy() { - assert( - 'Cannot destroy record while it is materialized', - !this.record || this.record.isDestroyed || this.record.isDestroying - ); this.isDestroying = true; const cache = this._manyArrayCache; diff --git a/packages/model/addon/-private/many-array.ts b/packages/model/addon/-private/many-array.ts index 6a07137378a..65c94026cfe 100644 --- a/packages/model/addon/-private/many-array.ts +++ b/packages/model/addon/-private/many-array.ts @@ -313,11 +313,8 @@ export default class ManyArray extends MutableArrayWithObject Date: Sun, 7 Aug 2022 01:39:02 -0700 Subject: [PATCH 19/29] down to 17 failed tests --- .../relationships/json-api-links-test.js | 189 +++++++++--------- .../relationships/one-to-one-test.js | 39 ++-- .../relationships/promise-many-array-test.js | 153 +++++++------- .../serializers/json-api-serializer-test.js | 176 ++++++++-------- .../serializers/json-serializer-test.js | 13 +- .../tests/integration/snapshot-test.js | 8 +- .../unit/model/relationships/has-many-test.js | 23 +-- .../tests/unit/promise-proxies-test.js | 101 +++++----- packages/model/addon/-private/attr.js | 2 +- packages/model/addon/-private/belongs-to.js | 10 +- packages/model/addon/-private/has-many.js | 2 +- packages/model/addon/-private/model.js | 5 + 12 files changed, 357 insertions(+), 364 deletions(-) diff --git a/packages/-ember-data/tests/integration/relationships/json-api-links-test.js b/packages/-ember-data/tests/integration/relationships/json-api-links-test.js index ccf48f82150..347d0e15c9b 100644 --- a/packages/-ember-data/tests/integration/relationships/json-api-links-test.js +++ b/packages/-ember-data/tests/integration/relationships/json-api-links-test.js @@ -721,8 +721,8 @@ module('integration/relationship/json-api-links | Relationship fetching', functi run(() => pets.reload()); }); - test(`get+unload+get hasMany with ${description}`, function (assert) { - assert.expect(3); + test(`get+unload+get hasMany with ${description}`, async function (assert) { + assert.expect(5); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -755,14 +755,16 @@ module('integration/relationship/json-api-links | Relationship fetching', functi }; // setup user - let user = run(() => store.push(deepCopy(payloads.user))); - let pets = run(() => user.get('pets')); + let user = store.push(deepCopy(payloads.user)); + let pets = await user.pets; assert.ok(!!pets, 'We found our pets'); if (!petRelDataWasEmpty) { - run(() => pets.objectAt(0).unloadRecord()); - run(() => user.get('pets')); + pets.objectAt(0).unloadRecord(); + assert.strictEqual(pets.length, 0, 'we unloaded'); + await user.pets; + assert.strictEqual(pets.length, 1, 'we reloaded'); } else { assert.ok(true, `We cant dirty a relationship we have no knowledge of`); } @@ -1019,8 +1021,8 @@ module('integration/relationship/json-api-links | Relationship fetching', functi run(() => pets.reload()); }); - test(`get+unload+get hasMany with ${description}`, function (assert) { - assert.expect(2); + test(`get+unload+get hasMany with ${description}`, async function (assert) { + assert.expect(4); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1042,14 +1044,16 @@ module('integration/relationship/json-api-links | Relationship fetching', functi }; // setup user and pets - let user = run(() => store.push(deepCopy(payloads.user))); - run(() => store.push(deepCopy(payloads.pets))); - let pets = run(() => user.get('pets')); + let user = store.push(deepCopy(payloads.user)); + store.push(deepCopy(payloads.pets)); + let pets = await user.pets; assert.ok(!!pets, 'We found our pets'); - run(() => pets.objectAt(0).unloadRecord()); - run(() => user.get('pets')); + pets.objectAt(0).unloadRecord(); + assert.strictEqual(pets.length, 0, 'we unloaded our pet'); + await user.pets; + assert.strictEqual(pets.length, 1, 'we have our pet again'); }); test(`get+reload belongsTo with ${description}`, function (assert) { @@ -1377,8 +1381,8 @@ module('integration/relationship/json-api-links | Relationship fetching', functi run(() => pets.reload()); }); - test(`get+unload+get hasMany with data, no links`, function (assert) { - assert.expect(3); + test(`get+unload+get hasMany with data, no links`, async function (assert) { + assert.expect(5); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1411,32 +1415,30 @@ module('integration/relationship/json-api-links | Relationship fetching', functi assert.ok(false, 'We should not call findHasMany'); }; - // setup user - let user = run(() => - store.push({ - data: { - type: 'user', - id: '1', - attributes: { - name: '@runspired', + let user = store.push({ + data: { + type: 'user', + id: '1', + attributes: { + name: '@runspired', + }, + relationships: { + pets: { + data: [{ type: 'pet', id: '1' }], }, - relationships: { - pets: { - data: [{ type: 'pet', id: '1' }], - }, - home: { - data: { type: 'home', id: '1' }, - }, + home: { + data: { type: 'home', id: '1' }, }, }, - }) - ); - let pets = run(() => user.get('pets')); + }, + }); + let pets = await user.pets; assert.ok(!!pets, 'We found our pets'); - - run(() => pets.objectAt(0).unloadRecord()); - run(() => user.get('pets')); + pets.objectAt(0).unloadRecord(); + assert.strictEqual(pets.length, 0, 'we unloaded our pet'); + await user.pets; + assert.strictEqual(pets.length, 1, 'we reloaded our pet'); }); test(`get+reload belongsTo with data, no links`, function (assert) { @@ -1563,8 +1565,8 @@ module('integration/relationship/json-api-links | Relationship fetching', functi }); // missing data setup from the other side, no links - test(`get+reload hasMany with missing data setup from the other side, no links`, function (assert) { - assert.expect(2); + test(`get+reload hasMany with missing data setup from the other side, no links`, async function (assert) { + assert.expect(4); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1598,43 +1600,43 @@ module('integration/relationship/json-api-links | Relationship fetching', functi }; // setup user and pet - let user = run(() => - store.push({ - data: { - type: 'user', + let user = store.push({ + data: { + type: 'user', + id: '1', + attributes: { + name: '@runspired', + }, + relationships: {}, + }, + included: [ + { + type: 'pet', id: '1', attributes: { - name: '@runspired', + name: 'Shen', }, - relationships: {}, - }, - included: [ - { - type: 'pet', - id: '1', - attributes: { - name: 'Shen', - }, - relationships: { - owner: { - data: { - type: 'user', - id: '1', - }, + relationships: { + owner: { + data: { + type: 'user', + id: '1', }, }, }, - ], - }) - ); - let pets = run(() => user.get('pets')); + }, + ], + }); + let pets = await user.pets; + assert.strictEqual(pets.length, 1, 'we setup the pets'); assert.ok(!!pets, 'We found our pets'); - run(() => pets.reload()); + await pets.reload(); + assert.strictEqual(pets.length, 1, 'still only the one'); }); - test(`get+unload+get hasMany with missing data setup from the other side, no links`, function (assert) { - assert.expect(2); + test(`get+unload+get hasMany with missing data setup from the other side, no links`, async function (assert) { + assert.expect(5); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1668,45 +1670,46 @@ module('integration/relationship/json-api-links | Relationship fetching', functi }; // setup user and pet - let user = run(() => - store.push({ - data: { - type: 'user', + let user = store.push({ + data: { + type: 'user', + id: '1', + attributes: { + name: '@runspired', + }, + relationships: {}, + }, + included: [ + { + type: 'pet', id: '1', attributes: { - name: '@runspired', + name: 'Shen', }, - relationships: {}, - }, - included: [ - { - type: 'pet', - id: '1', - attributes: { - name: 'Shen', - }, - relationships: { - owner: { - data: { - type: 'user', - id: '1', - }, + relationships: { + owner: { + data: { + type: 'user', + id: '1', }, }, }, - ], - }) - ); + }, + ], + }); - // should trigger a fetch bc we don't consider `pets` to have complete knowledge - let pets = run(() => user.get('pets')); + // should not trigger a fetch bc even though we don't consider `pets` to have complete knowledge + // we have no knowledge with which to initate a request. + let pets = await user.pets; assert.ok(!!pets, 'We found our pets'); - - run(() => pets.objectAt(0).unloadRecord()); + assert.strictEqual(pets.length, 1, 'we loaded our pets'); + pets.objectAt(0).unloadRecord(); + assert.strictEqual(pets.length, 0, 'we unloaded our pets'); // should trigger a findRecord for the unloaded pet - run(() => user.get('pets')); + await user.pets; + assert.strictEqual(pets.length, 1, 'we reloaded our pets'); }); test(`get+reload belongsTo with missing data setup from the other side, no links`, function (assert) { diff --git a/packages/-ember-data/tests/integration/relationships/one-to-one-test.js b/packages/-ember-data/tests/integration/relationships/one-to-one-test.js index 766d0346915..a4b86d8e8f1 100644 --- a/packages/-ember-data/tests/integration/relationships/one-to-one-test.js +++ b/packages/-ember-data/tests/integration/relationships/one-to-one-test.js @@ -982,32 +982,27 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun assert.strictEqual(job.get('user'), user, 'Job still has the user'); }); - test('Rollbacking attributes of created record removes the relationship on both sides - async', function (assert) { + test('Rollbacking attributes of created record removes the relationship on both sides - async', async function (assert) { let store = this.owner.lookup('service:store'); - var stanleysFriend, stanley; - run(function () { - stanleysFriend = store.push({ - data: { - id: 2, - type: 'user', - attributes: { - name: "Stanley's friend", - }, + const stanleysFriend = store.push({ + data: { + id: 2, + type: 'user', + attributes: { + name: "Stanley's friend", }, - }); - - stanley = store.createRecord('user', { bestFriend: stanleysFriend }); - }); - run(function () { - stanley.rollbackAttributes(); - stanleysFriend.get('bestFriend').then(function (fetchedUser) { - assert.strictEqual(fetchedUser, null, 'Stanley got rollbacked correctly'); - }); - stanley.get('bestFriend').then(function (fetchedUser) { - assert.strictEqual(fetchedUser, null, 'Stanleys friend did got removed'); - }); + }, }); + const stanley = store.createRecord('user', { bestFriend: stanleysFriend }); + + stanley.rollbackAttributes(); + + let fetchedUser = await stanleysFriend.bestFriend; + assert.strictEqual(fetchedUser, null, 'Stanley got rollbacked correctly'); + // TODO we should figure out how to handle the fact that we disconnect things. Right now we're asserting eagerly. + fetchedUser = await stanley.bestFriend; + assert.strictEqual(fetchedUser, null, 'Stanleys friend did get removed'); }); test('Rollbacking attributes of created record removes the relationship on both sides - sync', async function (assert) { diff --git a/packages/-ember-data/tests/integration/relationships/promise-many-array-test.js b/packages/-ember-data/tests/integration/relationships/promise-many-array-test.js index 98967d05e81..ec6a2945134 100644 --- a/packages/-ember-data/tests/integration/relationships/promise-many-array-test.js +++ b/packages/-ember-data/tests/integration/relationships/promise-many-array-test.js @@ -8,6 +8,7 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import Model, { attr, hasMany } from '@ember-data/model'; +import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; module('PromiseManyArray', (hooks) => { setupRenderingTest(hooks); @@ -37,86 +38,92 @@ module('PromiseManyArray', (hooks) => { assert.strictEqual(replaceFn, group.members.replace, 'we have the same function for replace'); group.members.replace(0, 1); assert.strictEqual(group.members.length, 3, 'updated length is correct'); + // we'll want to use a different test for this but will want to still ensure we are not side-affected + assert.expectDeprecation({ id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 2 }); }); - test('PromiseManyArray can be subscribed to by computed chains', async function (assert) { - const { owner } = this; - class Person extends Model { - @attr('string') name; - } - class Group extends Model { - @hasMany('person', { async: true, inverse: null }) members; - - @computed('members.@each.id') - get memberIds() { - return this.members.map((m) => m.id); + deprecatedTest( + 'PromiseManyArray can be subscribed to by computed chains', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 16 }, + async function (assert) { + const { owner } = this; + class Person extends Model { + @attr('string') name; } + class Group extends Model { + @hasMany('person', { async: true, inverse: null }) members; - @filterBy('members', 'name', 'John') - johns; - } - owner.register('model:person', Person); - owner.register('model:group', Group); - owner.register( - 'serializer:application', - class extends EmberObject { - normalizeResponse(_, __, data) { - return data; + @computed('members.@each.id') + get memberIds() { + return this.members.map((m) => m.id); } + + @filterBy('members', 'name', 'John') + johns; } - ); - - let _id = 0; - const names = ['Bob', 'John', 'Michael', 'John', 'Larry', 'Lucy']; - owner.register( - 'adapter:application', - class extends EmberObject { - findRecord() { - const name = names[_id++]; - const data = { - type: 'person', - id: `${_id}`, - attributes: { - name, - }, - }; - return { data }; + owner.register('model:person', Person); + owner.register('model:group', Group); + owner.register( + 'serializer:application', + class extends EmberObject { + normalizeResponse(_, __, data) { + return data; + } } - } - ); - const store = owner.lookup('service:store'); - - const group = store.push({ - data: { - type: 'group', - id: '1', - relationships: { - members: { - data: [ - { type: 'person', id: '1' }, - { type: 'person', id: '2' }, - { type: 'person', id: '3' }, - { type: 'person', id: '4' }, - { type: 'person', id: '5' }, - { type: 'person', id: '6' }, - ], + ); + + let _id = 0; + const names = ['Bob', 'John', 'Michael', 'John', 'Larry', 'Lucy']; + owner.register( + 'adapter:application', + class extends EmberObject { + findRecord() { + const name = names[_id++]; + const data = { + type: 'person', + id: `${_id}`, + attributes: { + name, + }, + }; + return { data }; + } + } + ); + const store = owner.lookup('service:store'); + + const group = store.push({ + data: { + type: 'group', + id: '1', + relationships: { + members: { + data: [ + { type: 'person', id: '1' }, + { type: 'person', id: '2' }, + { type: 'person', id: '3' }, + { type: 'person', id: '4' }, + { type: 'person', id: '5' }, + { type: 'person', id: '6' }, + ], + }, }, }, - }, - }); - - // access the group data - let memberIds = group.memberIds; - let johnRecords = group.johns; - assert.strictEqual(memberIds.length, 0, 'member ids is 0 initially'); - assert.strictEqual(johnRecords.length, 0, 'john ids is 0 initially'); - - await settled(); - - memberIds = group.memberIds; - johnRecords = group.johns; - assert.strictEqual(memberIds.length, 6, 'memberIds length is correct'); - assert.strictEqual(johnRecords.length, 2, 'johnRecords length is correct'); - assert.strictEqual(group.members.length, 6, 'members length is correct'); - }); + }); + + // access the group data + let memberIds = group.memberIds; + let johnRecords = group.johns; + assert.strictEqual(memberIds.length, 0, 'member ids is 0 initially'); + assert.strictEqual(johnRecords.length, 0, 'john ids is 0 initially'); + + await settled(); + + memberIds = group.memberIds; + johnRecords = group.johns; + assert.strictEqual(memberIds.length, 6, 'memberIds length is correct'); + assert.strictEqual(johnRecords.length, 2, 'johnRecords length is correct'); + assert.strictEqual(group.members.length, 6, 'members length is correct'); + } + ); }); diff --git a/packages/-ember-data/tests/integration/serializers/json-api-serializer-test.js b/packages/-ember-data/tests/integration/serializers/json-api-serializer-test.js index 81bad529623..fd0126b0baa 100644 --- a/packages/-ember-data/tests/integration/serializers/json-api-serializer-test.js +++ b/packages/-ember-data/tests/integration/serializers/json-api-serializer-test.js @@ -55,68 +55,64 @@ module('integration/serializers/json-api-serializer - JSONAPISerializer', functi this.owner.register('serializer:application', class extends JSONAPISerializer {}); }); - test('Calling pushPayload works', function (assert) { + test('Calling pushPayload works', async function (assert) { let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('application'); - run(function () { - serializer.pushPayload(store, { - data: { - type: 'users', - id: '1', - attributes: { - 'first-name': 'Yehuda', - 'last-name': 'Katz', + serializer.pushPayload(store, { + data: { + type: 'users', + id: '1', + attributes: { + 'first-name': 'Yehuda', + 'last-name': 'Katz', + }, + relationships: { + company: { + data: { type: 'companies', id: '2' }, }, - relationships: { - company: { - data: { type: 'companies', id: '2' }, - }, - handles: { - data: [ - { type: 'github-handles', id: '3' }, - { type: 'twitter-handles', id: '4' }, - ], - }, + handles: { + data: [ + { type: 'github-handles', id: '3' }, + { type: 'twitter-handles', id: '4' }, + ], }, }, - included: [ - { - type: 'companies', - id: '2', - attributes: { - name: 'Tilde Inc.', - }, + }, + included: [ + { + type: 'companies', + id: '2', + attributes: { + name: 'Tilde Inc.', }, - { - type: 'github-handles', - id: '3', - attributes: { - username: 'wycats', - }, + }, + { + type: 'github-handles', + id: '3', + attributes: { + username: 'wycats', }, - { - type: 'twitter-handles', - id: '4', - attributes: { - nickname: '@wycats', - }, + }, + { + type: 'twitter-handles', + id: '4', + attributes: { + nickname: '@wycats', }, - ], - }); - - var user = store.peekRecord('user', 1); - - assert.strictEqual(get(user, 'firstName'), 'Yehuda', 'firstName is correct'); - assert.strictEqual(get(user, 'lastName'), 'Katz', 'lastName is correct'); - assert.strictEqual(get(user, 'company.name'), 'Tilde Inc.', 'company.name is correct'); - assert.strictEqual( - get(user, 'handles.firstObject.username'), - 'wycats', - 'handles.firstObject.username is correct' - ); - assert.strictEqual(get(user, 'handles.lastObject.nickname'), '@wycats', 'handles.lastObject.nickname is correct'); + }, + ], }); + + const user = store.peekRecord('user', 1); + const company = await user.company; + const handles = await user.handles; + + assert.strictEqual(user.firstName, 'Yehuda', 'firstName is correct'); + assert.strictEqual(user.lastName, 'Katz', 'lastName is correct'); + assert.strictEqual(company.name, 'Tilde Inc.', 'company.name is correct'); + assert.strictEqual(handles.firstObject.username, 'wycats', 'handles.firstObject.username is correct'); + assert.strictEqual(handles.lastObject.nickname, '@wycats', 'handles.lastObject.nickname is correct'); }); testInDebug('Warns when normalizing an unknown type', function (assert) { @@ -666,7 +662,7 @@ module('integration/serializers/json-api-serializer - JSONAPISerializer', functi }); }); - test('it should include an empty list when serializing an empty hasMany relationship', function (assert) { + test('it should include an empty list when serializing an empty hasMany relationship', async function (assert) { this.owner.register( 'serializer:user', JSONAPISerializer.extend({ @@ -678,50 +674,50 @@ module('integration/serializers/json-api-serializer - JSONAPISerializer', functi let store = this.owner.lookup('service:store'); - run(function () { - store.serializerFor('user').pushPayload(store, { - data: { - type: 'users', - id: 1, - relationships: { - handles: { - data: [ - { type: 'handles', id: 1 }, - { type: 'handles', id: 2 }, - ], - }, + store.serializerFor('user').pushPayload(store, { + data: { + type: 'users', + id: '1', + relationships: { + handles: { + data: [ + { type: 'handles', id: '1' }, + { type: 'handles', id: '2' }, + ], }, }, - included: [ - { type: 'handles', id: 1 }, - { type: 'handles', id: 2 }, - ], - }); + }, + included: [ + { type: 'handles', id: '1' }, + { type: 'handles', id: '2' }, + ], + }); - let user = store.peekRecord('user', 1); - let handle1 = store.peekRecord('handle', 1); - let handle2 = store.peekRecord('handle', 2); - user.get('handles').removeObject(handle1); - user.get('handles').removeObject(handle2); + let user = store.peekRecord('user', '1'); + let handle1 = store.peekRecord('handle', '1'); + let handle2 = store.peekRecord('handle', '2'); - let serialized = user.serialize({ includeId: true }); + const handles = await user.handles; + handles.removeObject(handle1); + handles.removeObject(handle2); - assert.deepEqual(serialized, { - data: { - type: 'users', - id: '1', - attributes: { - 'first-name': null, - 'last-name': null, - title: null, - }, - relationships: { - handles: { - data: [], - }, + let serialized = user.serialize({ includeId: true }); + + assert.deepEqual(serialized, { + data: { + type: 'users', + id: '1', + attributes: { + 'first-name': null, + 'last-name': null, + title: null, + }, + relationships: { + handles: { + data: [], }, }, - }); + }, }); }); diff --git a/packages/-ember-data/tests/integration/serializers/json-serializer-test.js b/packages/-ember-data/tests/integration/serializers/json-serializer-test.js index 4366eb6c821..15c47964f93 100644 --- a/packages/-ember-data/tests/integration/serializers/json-serializer-test.js +++ b/packages/-ember-data/tests/integration/serializers/json-serializer-test.js @@ -895,7 +895,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { assert.notOk(payload.hasOwnProperty(serializedProperty), 'Does not add the key to instance'); }); - test('Serializer respects `serialize: true` on the attrs hash for a `hasMany` property', function (assert) { + test('Serializer respects `serialize: true` on the attrs hash for a `hasMany` property', async function (assert) { assert.expect(1); class Post extends Model { @attr('string') title; @@ -922,14 +922,13 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let post = store.createRecord('post', { title: 'Rails is omakase' }); let comment = store.createRecord('comment', { body: 'Omakase is delicious', post: post }); - run(function () { - post.get('comments').pushObject(comment); - }); + const comments = await post.comments; + comments.pushObject(comment); - var serializer = store.serializerFor('post'); - var serializedProperty = serializer.keyForRelationship('comments', 'hasMany'); + const serializer = store.serializerFor('post'); + const serializedProperty = serializer.keyForRelationship('comments', 'hasMany'); + const payload = serializer.serialize(post._createSnapshot()); - var payload = serializer.serialize(post._createSnapshot()); assert.ok(payload.hasOwnProperty(serializedProperty), 'Add the key to instance'); }); diff --git a/packages/-ember-data/tests/integration/snapshot-test.js b/packages/-ember-data/tests/integration/snapshot-test.js index b5e4daad17c..5353bb24772 100644 --- a/packages/-ember-data/tests/integration/snapshot-test.js +++ b/packages/-ember-data/tests/integration/snapshot-test.js @@ -1069,7 +1069,7 @@ module('integration/snapshot - Snapshot', function (hooks) { ); }); - test('snapshot.hasMany() respects the order of items in the relationship', function (assert) { + test('snapshot.hasMany() respects the order of items in the relationship', async function (assert) { assert.expect(3); store.push({ @@ -1115,9 +1115,9 @@ module('integration/snapshot - Snapshot', function (hooks) { }); let comment3 = store.peekRecord('comment', 3); let post = store.peekRecord('post', 4); - - post.get('comments').removeObject(comment3); - post.get('comments').insertAt(0, comment3); + const comments = await post.comments; + comments.removeObject(comment3); + comments.insertAt(0, comment3); let snapshot = post._createSnapshot(); let relationship = snapshot.hasMany('comments'); diff --git a/packages/-ember-data/tests/unit/model/relationships/has-many-test.js b/packages/-ember-data/tests/unit/model/relationships/has-many-test.js index e8822ab4808..a1f6f311008 100644 --- a/packages/-ember-data/tests/unit/model/relationships/has-many-test.js +++ b/packages/-ember-data/tests/unit/model/relationships/has-many-test.js @@ -2354,7 +2354,7 @@ module('unit/model/relationships - hasMany', function (hooks) { return EmberPromise.all([people]); }); - test('hasMany proxy is destroyed', function (assert) { + test('hasMany proxy is destroyed', async function (assert) { const Tag = Model.extend({ name: attr('string'), people: hasMany('person'), @@ -2370,22 +2370,17 @@ module('unit/model/relationships - hasMany', function (hooks) { let store = this.owner.lookup('service:store'); let tag = store.createRecord('tag'); - let peopleProxy = tag.get('people'); + let peopleProxy = tag.people; + let people = await peopleProxy; - return peopleProxy.then((people) => { - run(() => { - tag.unloadRecord(); - // TODO Check all unloading behavior - assert.false(people.isDestroying, 'people is NOT destroying sync after unloadRecord'); - assert.false(people.isDestroyed, 'people is NOT destroyed sync after unloadRecord'); + tag.unloadRecord(); + assert.true(people.isDestroying, 'people is destroying sync after unloadRecord'); + assert.true(peopleProxy.isDestroying, 'peopleProxy is destroying after the run post unloadRecord'); + assert.true(peopleProxy.isDestroyed, 'peopleProxy is destroyed after the run post unloadRecord'); - assert.false(peopleProxy.isDestroying, 'peopleProxy is NOT destroying sync after unloadRecord'); - assert.false(peopleProxy.isDestroyed, 'peopleProxy is NOT destroyed sync after unloadRecord'); - }); + await settled(); - assert.true(peopleProxy.isDestroying, 'peopleProxy is destroying after the run post unloadRecord'); - assert.true(peopleProxy.isDestroyed, 'peopleProxy is destroyed after the run post unloadRecord'); - }); + assert.true(people.isDestroyed, 'people is destroyed after unloadRecord'); }); test('findHasMany - can push the same record in twice and fetch the link', async function (assert) { diff --git a/packages/-ember-data/tests/unit/promise-proxies-test.js b/packages/-ember-data/tests/unit/promise-proxies-test.js index 6e10d39ad34..374a028a218 100644 --- a/packages/-ember-data/tests/unit/promise-proxies-test.js +++ b/packages/-ember-data/tests/unit/promise-proxies-test.js @@ -9,79 +9,70 @@ import { setupTest } from 'ember-qunit'; import Adapter from '@ember-data/adapter'; import Model, { belongsTo } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; -import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; module('PromiseManyArray', function () { - deprecatedTest( - '.reload should NOT leak the internal promise, rather return another promiseArray', - { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, - function (assert) { - assert.expect(1); + test('.reload should NOT leak the internal promise, rather return another promiseArray', function (assert) { + assert.expect(1); - let content = A(); + let content = A(); - content.reload = () => EmberPromise.resolve(content); + content.reload = () => EmberPromise.resolve(content); - let array = DS.PromiseManyArray.create({ - content, - }); + let array = DS.PromiseManyArray.create({ + content, + }); - let reloaded = array.reload(); + let reloaded = array.reload(); - assert.strictEqual(reloaded, array); - } - ); + assert.strictEqual(reloaded, array); + }); - deprecatedTest( - '.reload should be stable', - { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, - async function (assert) { - assert.expect(19); + test('.reload should be stable', async function (assert) { + assert.expect(19); - let content = A(); - let array; + let content = A(); + let array; - content.reload = () => { - let p = EmberPromise.resolve(content); - array._update(p); - return p; - }; - let promise = EmberPromise.resolve(content); + content.reload = () => { + let p = EmberPromise.resolve(content); + array._update(p); + return p; + }; + let promise = EmberPromise.resolve(content); - array = DS.PromiseManyArray.create({ - promise, - }); + array = DS.PromiseManyArray.create({ + promise, + }); - assert.false(array.isRejected, 'should NOT be rejected'); - assert.true(array.isPending, 'should be pending'); - assert.false(array.isSettled, 'should NOT be settled'); - assert.false(array.isFulfilled, 'should NOT be fulfilled'); + assert.false(array.isRejected, 'should NOT be rejected'); + assert.true(array.isPending, 'should be pending'); + assert.false(array.isSettled, 'should NOT be settled'); + assert.false(array.isFulfilled, 'should NOT be fulfilled'); - await array; - assert.false(array.isRejected, 'should NOT be rejected'); - assert.false(array.isPending, 'should NOT be pending'); - assert.true(array.isSettled, 'should be settled'); - assert.true(array.isFulfilled, 'should be fulfilled'); + await array; + assert.false(array.isRejected, 'should NOT be rejected'); + assert.false(array.isPending, 'should NOT be pending'); + assert.true(array.isSettled, 'should be settled'); + assert.true(array.isFulfilled, 'should be fulfilled'); - let reloaded = array.reload(); + let reloaded = array.reload(); - assert.false(array.isRejected, 'should NOT be rejected'); - assert.true(array.isPending, 'should be pending'); - assert.false(array.isSettled, 'should NOT be settled'); - assert.false(array.isFulfilled, 'should NOT be fulfilled'); + assert.false(array.isRejected, 'should NOT be rejected'); + assert.true(array.isPending, 'should be pending'); + assert.false(array.isSettled, 'should NOT be settled'); + assert.false(array.isFulfilled, 'should NOT be fulfilled'); - assert.ok(reloaded instanceof DS.PromiseManyArray); - assert.strictEqual(reloaded, array); + assert.ok(reloaded instanceof DS.PromiseManyArray); + assert.strictEqual(reloaded, array); - let value = await reloaded; - assert.false(array.isRejected, 'should NOT be rejected'); - assert.false(array.isPending, 'should NOT be pending'); - assert.true(array.isSettled, 'should be settled'); - assert.true(array.isFulfilled, 'should be fulfilled'); + let value = await reloaded; + assert.false(array.isRejected, 'should NOT be rejected'); + assert.false(array.isPending, 'should NOT be pending'); + assert.true(array.isSettled, 'should be settled'); + assert.true(array.isFulfilled, 'should be fulfilled'); - assert.strictEqual(content, value); - } - ); + assert.strictEqual(content, value); + }); test('.set to new promise should be like reload', async function (assert) { assert.expect(18); diff --git a/packages/model/addon/-private/attr.js b/packages/model/addon/-private/attr.js index 8d0c836a093..cba8de9a1f4 100644 --- a/packages/model/addon/-private/attr.js +++ b/packages/model/addon/-private/attr.js @@ -136,7 +136,7 @@ function attr(type, options) { ); } } - if (this.isDestroyed) { + if (this.isDestroyed || this.isDestroying) { return; } let recordData = recordDataFor(this); diff --git a/packages/model/addon/-private/belongs-to.js b/packages/model/addon/-private/belongs-to.js index b198b63fbe3..29f7f6ebac4 100644 --- a/packages/model/addon/-private/belongs-to.js +++ b/packages/model/addon/-private/belongs-to.js @@ -143,6 +143,12 @@ function belongsTo(modelName, options) { return computed({ get(key) { + // this is a legacy behavior we may not carry into a new model setup + // it's better to error on disconnected records so users find errors + // in their logic. + if (this.isDestroying || this.isDestroyed) { + return null; + } const support = LEGACY_SUPPORT.lookup(this); if (DEBUG) { @@ -172,10 +178,6 @@ function belongsTo(modelName, options) { } } - if (this.isDestroyed) { - return null; - } - return support.getBelongsTo(key); }, set(key, value) { diff --git a/packages/model/addon/-private/has-many.js b/packages/model/addon/-private/has-many.js index 5722c411dd9..4ff13456d05 100644 --- a/packages/model/addon/-private/has-many.js +++ b/packages/model/addon/-private/has-many.js @@ -189,7 +189,7 @@ function hasMany(type, options) { ); } } - if (this.isDestroyed) { + if (this.isDestroyed || this.isDestroying) { return []; } return LEGACY_SUPPORT.lookup(this).getHasMany(key); diff --git a/packages/model/addon/-private/model.js b/packages/model/addon/-private/model.js index 022e18791e7..8845a4a774b 100644 --- a/packages/model/addon/-private/model.js +++ b/packages/model/addon/-private/model.js @@ -492,6 +492,11 @@ class Model extends EmberObject { } } + // TODO just write a nice toString + toStringExtension() { + return this.id; + } + /** @property currentState @private From 1a983b5a876515ad793da9a5e3fe10eb28b84fb4 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sun, 7 Aug 2022 23:36:41 -0700 Subject: [PATCH 20/29] cleanup --- CHANGELOG.md | 30 +- .../relationships/belongs-to-test.js | 44 +- .../adapter/json-api-adapter-test.js | 4 +- .../integration/adapter/rest-adapter-test.js | 122 ++--- .../rest-adapter/create-record-test.js | 46 +- .../adapter/rest-adapter/find-record-test.js | 36 +- .../tests/integration/application-test.js | 6 +- .../tests/integration/inverse-test.js | 2 +- .../polymorphic-belongs-to-test.js | 11 +- .../integration/record-array-manager-test.js | 4 +- .../tests/integration/record-array-test.js | 22 +- .../adapter-populated-record-array-test.js | 91 +--- .../record-arrays/peeked-records-test.js | 2 +- .../record-data/record-data-errors-test.ts | 14 +- .../record-data/record-data-state-test.ts | 20 +- .../record-data/record-data-test.ts | 18 +- .../record-data/store-wrapper-test.ts | 2 +- .../record-data/unloading-record-data-test.js | 8 +- .../records/collection-save-test.js | 2 +- .../integration/records/create-record-test.js | 26 +- .../integration/records/delete-record-test.js | 28 +- .../integration/records/edit-record-test.js | 90 ++-- .../tests/integration/records/load-test.js | 4 +- .../records/relationship-changes-test.js | 16 +- .../tests/integration/records/reload-test.js | 36 +- .../integration/records/rematerialize-test.js | 22 +- .../tests/integration/records/save-test.js | 22 +- .../tests/integration/records/unload-test.js | 247 +++++----- .../integration/references/has-many-test.js | 34 +- .../relationships/belongs-to-test.js | 123 +++-- .../relationships/has-many-test.js | 299 ++++++------- .../inverse-relationship-load-test.js | 422 +++++++++--------- .../inverse-relationships-test.js | 150 +++---- .../relationships/json-api-links-test.js | 46 +- .../relationships/many-to-many-test.js | 78 ++-- .../relationships/nested-relationship-test.js | 6 +- .../relationships/one-to-many-test.js | 130 +++--- .../relationships/one-to-one-test.js | 72 +-- .../polymorphic-mixins-belongs-to-test.js | 12 +- .../polymorphic-mixins-has-many-test.js | 20 +- .../embedded-records-mixin-test.js | 12 +- .../serializers/json-serializer-test.js | 2 +- .../serializers/rest-serializer-test.js | 32 +- .../tests/integration/snapshot-test.js | 10 +- .../tests/integration/store-test.js | 30 +- .../tests/integration/store/query-test.js | 4 +- .../group-records-for-find-many-test.js | 2 +- .../-ember-data/tests/unit/many-array-test.js | 4 +- packages/-ember-data/tests/unit/model-test.js | 76 ++-- .../tests/unit/model/errors-test.js | 34 +- .../tests/unit/model/merge-test.js | 62 +-- .../model/relationships/belongs-to-test.js | 8 +- .../unit/model/relationships/has-many-test.js | 94 ++-- .../model/relationships/record-array-test.js | 4 +- .../unit/model/rollback-attributes-test.js | 132 +++--- .../tests/unit/promise-proxies-test.js | 2 +- .../adapter-populated-record-array-test.js | 52 +-- .../unit/record-arrays/record-array-test.js | 20 +- .../tests/unit/store/adapter-interop-test.js | 30 +- .../tests/unit/store/create-record-test.js | 8 +- .../tests/unit/store/finders-test.js | 4 +- .../-ember-data/tests/unit/store/push-test.js | 56 +-- .../tests/unit/store/unload-test.js | 2 +- .../polymorphic-relationship-payloads-test.js | 40 +- packages/adapter/addon/error.js | 2 +- packages/adapter/addon/json-api.ts | 2 +- packages/adapter/addon/rest.ts | 4 +- packages/debug/addon/index.js | 12 +- packages/model/addon/-private/belongs-to.js | 2 +- packages/model/addon/-private/errors.ts | 6 +- packages/model/addon/-private/has-many.js | 5 +- .../-private/legacy-relationships-support.ts | 2 +- packages/model/addon/-private/many-array.ts | 6 + packages/model/addon/-private/model.js | 66 +-- .../addon/-private/references/belongs-to.ts | 4 +- .../addon/-private/references/has-many.ts | 4 +- .../addon/-private/embedded-records-mixin.js | 2 +- packages/serializer/addon/json-api.js | 2 +- .../addon/-private/caches/instance-cache.ts | 41 +- .../-private/managers/record-array-manager.ts | 19 +- .../store/addon/-private/network/finders.js | 12 +- .../-private/network/snapshot-record-array.ts | 4 +- .../addon/-private/proxies/promise-proxies.ts | 8 +- .../adapter-populated-record-array.ts | 4 +- .../-private/record-arrays/record-array.ts | 12 +- .../store/addon/-private/store-service.ts | 18 +- .../tests/integration/belongs-to-test.js | 12 +- .../tests/integration/has-many-test.js | 18 +- .../app/models/person.js | 2 +- .../tests/integration/relationships-test.js | 6 +- .../addon-test-support/legacy.js | 2 +- 91 files changed, 1611 insertions(+), 1753 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b5fe697cd..0968e63d5db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2445,10 +2445,10 @@ it should be pretty straight forward to update current code to the public Snapshot API: ```js -post.get('id') => postSnapshot.id -post.get('title') => postSnapshot.attr('title') -post.get('author') => postSnapshot.belongsTo('author') -post.get('comments') => postSnapshot.hasMany('comments') +post.id => postSnapshot.id +post.title => postSnapshot.attr('title') +post.author => postSnapshot.belongsTo('author') +post.comments => postSnapshot.hasMany('comments') post.constructor => postSnapshot.type; post.constructor.typeKey => postSnapshot.typeKey ``` @@ -2525,7 +2525,7 @@ To access attributes you should now use the `attr` function. ```js // Ember Data 1.0.0-beta.14.1 -post.get('title'); +post.title; // Ember Data 1.0.0-beta.15 postSnapshot.attr('title'); ``` @@ -2534,7 +2534,7 @@ To access a belongsTo relationship you should use `.belongsTo()` method. ```js // Ember Data 1.0.0-beta.14.1 -post.get('author'); +post.author; // Ember Data 1.0.0-beta.15 postSnapshot.belongsTo('author'); ``` @@ -2543,7 +2543,7 @@ To access a hasMany relationship you should use `.hasMany()` method. ```js // Ember Data 1.0.0-beta.14.1 -post.get('comments'); +post.comments; // Ember Data 1.0.0-beta.15 postSnapshot.hasMany('comments'); ``` @@ -2619,13 +2619,13 @@ var post = store.push('post', { author: 'Tomster', }); -post.get('title'); // => 'Ember.js is fantastic' -post.get('author'); // => 'Tomster' +post.title; // => 'Ember.js is fantastic' +post.author; // => 'Tomster' store.push('post', { id: 1, author: 'Tom Dale' }); -post.get('title'); // => 'Ember.js is fantastic' -post.get('author'); // => 'Tom Dale' +post.title; // => 'Ember.js is fantastic' +post.author; // => 'Tom Dale' ``` This also mean that properties missing in the payload will no longer be reset, @@ -2698,10 +2698,10 @@ underlying array you will now need to use the `.toArray()` method. ```javascript // Ember Data 1.0.0-beta.12 -record.get('myHasManyRelationship').get('content').map(...); +record.myHasManyRelationship.content.map(...); // Ember Data 1.0.0-beta.14 -record.get('myHasManyRelationship').toArray().map(...); +record.myHasManyRelationship.toArray().map(...); ``` Additionally if you were using the `RecordArray`'s `.addRecord()` and @@ -2736,7 +2736,7 @@ Additionally if you were using the `RecordArray`'s `.addRecord()` and - [Feature thrownError] tag errorThrown from jQuery onto the jqXHR like ic-ajax does. - Cache relationships meta in production - Deprecate store.update() -- hasMany relationships are no longer `RecordArray`, but `ManyArray`. To access the underlying array use `relationship.toArray()` instead of `relationship.get('content')`. +- hasMany relationships are no longer `RecordArray`, but `ManyArray`. To access the underlying array use `relationship.toArray()` instead of `relationship.content`. ### Ember Data 1.0.0-beta.12 (November 25, 2014) @@ -2765,7 +2765,7 @@ Ember Data's build. You should upgrade to Ember 1.8 as soon as you can. ##### Observing `data` For Changes Has Been Removed -Although `model.get('data')` has been private in Ember Data for a long time, we +Although `model.data` has been private in Ember Data for a long time, we have noticed users may subscribe to changes on `data` for any change to the model's attributes. This means that the following code: diff --git a/packages/-ember-data/tests/acceptance/relationships/belongs-to-test.js b/packages/-ember-data/tests/acceptance/relationships/belongs-to-test.js index 57444bec4c4..8ec1b0b7ace 100644 --- a/packages/-ember-data/tests/acceptance/relationships/belongs-to-test.js +++ b/packages/-ember-data/tests/acceptance/relationships/belongs-to-test.js @@ -290,11 +290,11 @@ module('async belongs-to rendering tests', function (hooks) { assert.strictEqual(petOwnerImplicit.canonicalMembers.size, 2); - let petOwner = await goofy.get('petOwner'); - assert.strictEqual(petOwner.get('name'), 'Pete', 'We have the expected owner for goofy'); + let petOwner = await goofy.petOwner; + assert.strictEqual(petOwner.name, 'Pete', 'We have the expected owner for goofy'); - petOwner = await tweety.get('petOwner'); - assert.strictEqual(petOwner.get('name'), 'Pete', 'We have the expected owner for tweety'); + petOwner = await tweety.petOwner; + assert.strictEqual(petOwner.name, 'Pete', 'We have the expected owner for tweety'); await goofy.destroyRecord(); assert.ok(goofy.isDeleted, 'goofy is deleted after calling destroyRecord'); @@ -317,8 +317,8 @@ module('async belongs-to rendering tests', function (hooks) { }, }); - petOwner = await jerry.get('petOwner'); - assert.strictEqual(petOwner.get('name'), 'Pete'); + petOwner = await jerry.petOwner; + assert.strictEqual(petOwner.name, 'Pete'); assert.strictEqual(petOwnerImplicit.canonicalMembers.size, 1); @@ -363,7 +363,7 @@ module('async belongs-to rendering tests', function (hooks) { let shen = store.peekRecord('pet', '1'); let pirate = store.peekRecord('pet', '2'); - let bestDog = await chris.get('bestDog'); + let bestDog = await chris.bestDog; this.set('chris', chris); @@ -373,38 +373,38 @@ module('async belongs-to rendering tests', function (hooks) { await settled(); assert.strictEqual(this.element.textContent.trim(), '', 'initially there is no name for bestDog'); - assert.strictEqual(shen.get('bestHuman'), null, 'precond - Shen has no best human'); - assert.strictEqual(pirate.get('bestHuman'), null, 'precond - pirate has no best human'); + assert.strictEqual(shen.bestHuman, null, 'precond - Shen has no best human'); + assert.strictEqual(pirate.bestHuman, null, 'precond - pirate has no best human'); assert.strictEqual(bestDog, null, 'precond - Chris has no best dog'); // locally update chris.set('bestDog', shen); - bestDog = await chris.get('bestDog'); + bestDog = await chris.bestDog; await settled(); assert.strictEqual(this.element.textContent.trim(), 'Shen'); - assert.strictEqual(shen.get('bestHuman'), chris, "scene 1 - Chris is Shen's best human"); - assert.strictEqual(pirate.get('bestHuman'), null, 'scene 1 - pirate has no best human'); + assert.strictEqual(shen.bestHuman, chris, "scene 1 - Chris is Shen's best human"); + assert.strictEqual(pirate.bestHuman, null, 'scene 1 - pirate has no best human'); assert.strictEqual(bestDog, shen, "scene 1 - Shen is Chris's best dog"); // locally update to a different value chris.set('bestDog', pirate); - bestDog = await chris.get('bestDog'); + bestDog = await chris.bestDog; await settled(); assert.strictEqual(this.element.textContent.trim(), 'Pirate'); - assert.strictEqual(shen.get('bestHuman'), null, "scene 2 - Chris is no longer Shen's best human"); - assert.strictEqual(pirate.get('bestHuman'), chris, 'scene 2 - pirate now has Chris as best human'); + assert.strictEqual(shen.bestHuman, null, "scene 2 - Chris is no longer Shen's best human"); + assert.strictEqual(pirate.bestHuman, chris, 'scene 2 - pirate now has Chris as best human'); assert.strictEqual(bestDog, pirate, "scene 2 - Pirate is now Chris's best dog"); // locally clear the relationship chris.set('bestDog', null); - bestDog = await chris.get('bestDog'); + bestDog = await chris.bestDog; await settled(); assert.strictEqual(this.element.textContent.trim(), ''); - assert.strictEqual(shen.get('bestHuman'), null, "scene 3 - Chris remains no longer Shen's best human"); - assert.strictEqual(pirate.get('bestHuman'), null, 'scene 3 - pirate no longer has Chris as best human'); + assert.strictEqual(shen.bestHuman, null, "scene 3 - Chris remains no longer Shen's best human"); + assert.strictEqual(pirate.bestHuman, null, 'scene 3 - pirate no longer has Chris as best human'); assert.strictEqual(bestDog, null, 'scene 3 - Chris has no best dog'); }); }); @@ -531,14 +531,14 @@ module('async belongs-to rendering tests', function (hooks) { assert.false(!!relationship.link, 'The relationship does not have a link'); try { - let result = await sedona.get('parent.content'); + let result = await sedona.parent.content; assert.strictEqual(result, null, 're-access is safe'); } catch (e) { assert.ok(false, `Accessing resulted in rejected promise error: ${e.message}`); } try { - await sedona.get('parent'); + await sedona.parent; assert.ok(false, 're-access should throw original rejection'); } catch (e) { assert.ok(true, `Accessing resulted in rejected promise error: ${e.message}`); @@ -558,7 +558,7 @@ module('async belongs-to rendering tests', function (hooks) { adapter.setupPayloads(assert, [new ServerError([], error)]); try { - await sedona.get('parent'); + await sedona.parent; assert.ok(false, `should have rejected`); } catch (e) { assert.strictEqual(e.message, error, `should have rejected with '${error}'`); @@ -571,7 +571,7 @@ module('async belongs-to rendering tests', function (hooks) { assert.strictEqual(this.element.textContent.trim(), '', 'we have no parent'); try { - await sedona.get('parent'); + await sedona.parent; assert.ok(false, `should have rejected`); } catch (e) { assert.strictEqual(e.message, error, `should have rejected with '${error}'`); diff --git a/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js b/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js index fdb64043c13..fcfd50dd601 100644 --- a/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js +++ b/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js @@ -190,12 +190,12 @@ module('integration/adapter/json-api-adapter - JSONAPIAdapter', function (hooks) assert.strictEqual(posts.lastObject.title, 'Tomster rules', 'The title for the second post is correct'); assert.strictEqual( - posts.firstObject.author.get('firstName'), + posts.firstObject.author.firstName, 'Yehuda', 'The author for the first post is loaded and has the correct first name' ); assert.strictEqual( - posts.lastObject.author.get('lastName'), + posts.lastObject.author.lastName, 'Katz', 'The author for the last post is loaded and has the correct last name' ); diff --git a/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js b/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js index 53a744b6d1e..b8d3ed0ec53 100644 --- a/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js +++ b/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js @@ -148,8 +148,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.strictEqual(passedVerb, 'PUT'); assert.deepEqual(passedHash.data, { post: { name: 'The Parley Letter' } }); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.strictEqual(post.get('name'), 'The Parley Letter', 'the post was updated'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.strictEqual(post.name, 'The Parley Letter', 'the post was updated'); }); test('updateRecord - passes the requestType to buildURL', async function (assert) { @@ -217,8 +217,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.strictEqual(passedVerb, 'PUT'); assert.deepEqual(passedHash.data, { post: { name: 'The Parley Letter' } }); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.strictEqual(post.get('name'), 'Dat Parley Letter', 'the post was updated'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.strictEqual(post.name, 'Dat Parley Letter', 'the post was updated'); }); test('updateRecord - a payload with updates applies the updates (with legacy singular name)', async function (assert) { @@ -252,8 +252,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.strictEqual(passedVerb, 'PUT'); assert.deepEqual(passedHash.data, { post: { name: 'The Parley Letter' } }); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.strictEqual(post.get('name'), 'Dat Parley Letter', 'the post was updated'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.strictEqual(post.name, 'Dat Parley Letter', 'the post was updated'); }); test('updateRecord - a payload with sideloaded updates pushes the updates', async function (assert) { @@ -279,12 +279,12 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.strictEqual(passedVerb, 'POST'); assert.deepEqual(passedHash.data, { post: { name: 'The Parley Letter' } }); - assert.strictEqual(post.get('id'), '1', 'the post has the updated ID'); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.strictEqual(post.get('name'), 'Dat Parley Letter', 'the post was updated'); + assert.strictEqual(post.id, '1', 'the post has the updated ID'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.strictEqual(post.name, 'Dat Parley Letter', 'the post was updated'); let comment = store.peekRecord('comment', 1); - assert.strictEqual(comment.get('name'), 'FIRST', 'The comment was sideloaded'); + assert.strictEqual(comment.name, 'FIRST', 'The comment was sideloaded'); }); test('updateRecord - a payload with sideloaded updates pushes the updates', async function (assert) { @@ -321,11 +321,11 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.strictEqual(passedVerb, 'PUT'); assert.deepEqual(passedHash.data, { post: { name: 'The Parley Letter' } }); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.strictEqual(post.get('name'), 'Dat Parley Letter', 'the post was updated'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.strictEqual(post.name, 'Dat Parley Letter', 'the post was updated'); let comment = store.peekRecord('comment', 1); - assert.strictEqual(comment.get('name'), 'FIRST', 'The comment was sideloaded'); + assert.strictEqual(comment.name, 'FIRST', 'The comment was sideloaded'); }); test("updateRecord - a serializer's primary key and attributes are consulted when building the payload", async function (assert) { @@ -421,14 +421,14 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { await store.findRecord('comment', 2); let post = await store.findRecord('post', 1); let newComment = store.peekRecord('comment', 2); - let comments = post.get('comments'); + let comments = post.comments; // Replace the comment with a new one comments.popObject(); comments.pushObject(newComment); await post.save(); - assert.strictEqual(post.get('comments.length'), 1, 'the post has the correct number of comments'); + assert.strictEqual(post.comments.length, 1, 'the post has the correct number of comments'); assert.strictEqual(post.get('comments.firstObject.name'), 'Yes. Yes it is.', 'the post has the correct comment'); }); @@ -474,12 +474,12 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { }); let post = await store.peekRecord('post', 1); - assert.strictEqual(post.get('comments.length'), 1, 'the post has one comment'); + assert.strictEqual(post.comments.length, 1, 'the post has one comment'); post.set('name', 'Everyone uses Rails'); post = await post.save(); - assert.strictEqual(post.get('comments.length'), 0, 'the post has the no comments'); + assert.strictEqual(post.comments.length, 0, 'the post has the no comments'); }); test('updateRecord - hasMany relationships set locally will be removed with empty response', async function (assert) { @@ -523,11 +523,11 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { let comment = await store.peekRecord('comment', 1); let comments = post.comments; comments.pushObject(comment); - assert.strictEqual(post.get('comments.length'), 1, 'the post has one comment'); + assert.strictEqual(post.comments.length, 1, 'the post has one comment'); post = await post.save(); - assert.strictEqual(post.get('comments.length'), 0, 'the post has the no comments'); + assert.strictEqual(post.comments.length, 0, 'the post has the no comments'); }); test('deleteRecord - an empty payload is a basic success', async function (assert) { @@ -563,8 +563,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.strictEqual(passedVerb, 'DELETE'); assert.strictEqual(passedHash, undefined); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.true(post.get('isDeleted'), 'the post is now deleted'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.true(post.isDeleted, 'the post is now deleted'); }); test('deleteRecord - passes the requestType to buildURL', async function (assert) { @@ -636,11 +636,11 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.strictEqual(passedVerb, 'DELETE'); assert.strictEqual(passedHash, undefined); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.true(post.get('isDeleted'), 'the post is now deleted'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.true(post.isDeleted, 'the post is now deleted'); let comment = store.peekRecord('comment', 1); - assert.strictEqual(comment.get('name'), 'FIRST', 'The comment was sideloaded'); + assert.strictEqual(comment.name, 'FIRST', 'The comment was sideloaded'); }); test('deleteRecord - a payload with sidloaded updates pushes the updates when the original record is omitted', async function (assert) { @@ -676,11 +676,11 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.strictEqual(passedVerb, 'DELETE'); assert.strictEqual(passedHash, undefined); - assert.false(post.get('hasDirtyAttributes'), "the original post isn't dirty anymore"); - assert.true(post.get('isDeleted'), 'the original post is now deleted'); + assert.false(post.hasDirtyAttributes, "the original post isn't dirty anymore"); + assert.true(post.isDeleted, 'the original post is now deleted'); let newPost = store.peekRecord('post', 2); - assert.strictEqual(newPost.get('name'), 'The Parley Letter', 'The new post was added to the store'); + assert.strictEqual(newPost.name, 'The Parley Letter', 'The new post was added to the store'); }); test('deleteRecord - deleting a newly created record should not throw an error', async function (assert) { @@ -701,8 +701,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { post.deleteRecord(); await post.save(); - assert.true(post.get('isDeleted'), 'the post is now deleted'); - assert.false(post.get('isError'), 'the post is not an error'); + assert.true(post.isDeleted, 'the post is now deleted'); + assert.false(post.isError, 'the post is not an error'); assert.strictEqual(passedUrl, null, 'There is no ajax call to delete a record that has never been saved.'); assert.strictEqual(passedVerb, null, 'There is no ajax call to delete a record that has never been saved.'); assert.strictEqual(passedHash, null, 'There is no ajax call to delete a record that has never been saved.'); @@ -742,8 +742,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.deepEqual(post2.getProperties('id', 'name'), { id: '2', name: 'The Parley Letter' }, 'Post 2 is loaded'); - assert.strictEqual(posts.get('length'), 2, 'The posts are in the array'); - assert.true(posts.get('isLoaded'), 'The RecordArray is loaded'); + assert.strictEqual(posts.length, 2, 'The posts are in the array'); + assert.true(posts.isLoaded, 'The RecordArray is loaded'); assert.deepEqual(posts.toArray(), [post1, post2], 'The correct records are in the array'); }); @@ -857,8 +857,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.deepEqual(post1.getProperties('id', 'name'), { id: '1', name: 'Rails is omakase' }, 'Post 1 is loaded'); assert.deepEqual(post2.getProperties('id', 'name'), { id: '2', name: 'The Parley Letter' }, 'Post 2 is loaded'); - assert.strictEqual(posts.get('length'), 2, 'The posts are in the array'); - assert.true(posts.get('isLoaded'), 'The RecordArray is loaded'); + assert.strictEqual(posts.length, 2, 'The posts are in the array'); + assert.true(posts.isLoaded, 'The RecordArray is loaded'); assert.deepEqual(posts.toArray(), [post1, post2], 'The correct records are in the array'); }); @@ -989,7 +989,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { }); let posts = await store.query('post', { page: 2 }); - assert.strictEqual(posts.get('meta.offset'), 5, 'Reponse metadata can be accessed with recordArray.meta'); + assert.strictEqual(posts.meta.offset, 5, 'Reponse metadata can be accessed with recordArray.meta'); }); test("query - each record array can have it's own meta object", async function (assert) { @@ -1010,15 +1010,15 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { }); let posts = await store.query('post', { page: 2 }); - assert.strictEqual(posts.get('meta.offset'), 5, 'Reponse metadata can be accessed with recordArray.meta'); + assert.strictEqual(posts.meta.offset, 5, 'Reponse metadata can be accessed with recordArray.meta'); ajaxResponse({ meta: { offset: 1 }, posts: [{ id: 1, name: 'Rails is very expensive sushi' }], }); let newPosts = await store.query('post', { page: 1 }); - assert.strictEqual(newPosts.get('meta.offset'), 1, 'new array has correct metadata'); - assert.strictEqual(posts.get('meta.offset'), 5, 'metadata on the old array hasnt been clobbered'); + assert.strictEqual(newPosts.meta.offset, 1, 'new array has correct metadata'); + assert.strictEqual(posts.meta.offset, 5, 'metadata on the old array hasnt been clobbered'); }); test('query - returning an array populates the array', async function (assert) { @@ -1051,8 +1051,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.deepEqual(post1.getProperties('id', 'name'), { id: '1', name: 'Rails is omakase' }, 'Post 1 is loaded'); assert.deepEqual(post2.getProperties('id', 'name'), { id: '2', name: 'The Parley Letter' }, 'Post 2 is loaded'); - assert.strictEqual(posts.get('length'), 2, 'The posts are in the array'); - assert.true(posts.get('isLoaded'), 'The RecordArray is loaded'); + assert.strictEqual(posts.length, 2, 'The posts are in the array'); + assert.true(posts.isLoaded, 'The RecordArray is loaded'); assert.deepEqual(posts.toArray(), [post1, post2], 'The correct records are in the array'); }); @@ -1117,8 +1117,8 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.deepEqual(post2.getProperties('id', 'name'), { id: '2', name: 'The Parley Letter' }, 'Post 2 is loaded'); - assert.strictEqual(posts.get('length'), 2, 'The posts are in the array'); - assert.true(posts.get('isLoaded'), 'The RecordArray is loaded'); + assert.strictEqual(posts.length, 2, 'The posts are in the array'); + assert.true(posts.isLoaded, 'The RecordArray is loaded'); assert.deepEqual(posts.toArray(), [post1, post2], 'The correct records are in the array'); }); @@ -1180,7 +1180,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { }); let post = await store.queryRecord('post', { slug: 'ember-js-rocks' }); - assert.deepEqual(post.get('name'), 'Ember.js rocks'); + assert.deepEqual(post.name, 'Ember.js rocks'); }); test('queryRecord - returning sideloaded data loads the data', async function (assert) { @@ -1327,7 +1327,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { ], }); - await post.get('comments'); + await post.comments; assert.strictEqual(passedUrl, '/comments'); assert.deepEqual(passedHash, { data: { ids: ['1', '2', '3'] } }); }); @@ -1379,7 +1379,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { ], }); - await post.get('comments'); + await post.comments; assert.strictEqual(passedUrl, '/findMany/comment'); }); @@ -1424,7 +1424,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { ], }); - await post.get('comments'); + await post.comments; assert.strictEqual(passedUrl, '/comments/3'); assert.deepEqual(passedHash.data, {}); }); @@ -1472,7 +1472,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { ], }); - let comments = await post.get('comments'); + let comments = await post.comments; let comment1 = store.peekRecord('comment', 1); let comment2 = store.peekRecord('comment', 2); let comment3 = store.peekRecord('comment', 3); @@ -1531,7 +1531,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { posts: [{ id: 2, name: 'The Parley Letter' }], }); - let comments = await post.get('comments'); + let comments = await post.comments; let comment1 = store.peekRecord('comment', 1); let comment2 = store.peekRecord('comment', 2); @@ -1608,7 +1608,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { ], }); - let comments = await post.get('comments'); + let comments = await post.comments; let comment1 = store.peekRecord('comment', 1); let comment2 = store.peekRecord('comment', 2); let comment3 = store.peekRecord('comment', 3); @@ -1663,7 +1663,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { ], }); - let comments = await post.get('comments'); + let comments = await post.comments; assert.strictEqual(passedUrl, '/posts/1/comments'); assert.strictEqual(passedVerb, 'GET'); assert.strictEqual(passedHash, undefined); @@ -1731,7 +1731,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { ], }); - await post.get('comments'); + await post.comments; }); test('findMany - returning sideloaded data loads the data (with JSONApi Links)', async function (assert) { @@ -1777,7 +1777,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { posts: [{ id: 2, name: 'The Parley Letter' }], }); - let comments = await post.get('comments'); + let comments = await post.comments; let comment1 = store.peekRecord('comment', 1); let comment2 = store.peekRecord('comment', 2); let comment3 = store.peekRecord('comment', 3); @@ -1844,7 +1844,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { { _ID_: 3, _NAME_: 'What is omakase?' }, ], }); - return post.get('comments'); + return post.comments; }) .then((comments) => { let comment1 = store.peekRecord('comment', 1); @@ -1900,7 +1900,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { let comment = await store.findRecord('comment', '1'); ajaxResponse({ post: { id: 1, name: 'Rails is omakase' } }); - await comment.get('post'); + await comment.post; }); testInDebug( @@ -1942,7 +1942,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.expectWarning(async () => { try { - await post.get('comments'); + await post.comments; } catch (e) { assert.strictEqual( e.message, @@ -2002,7 +2002,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { let post = store.peekRecord('post', 2); - await post.get('comments'); + await post.comments; }); test('groupRecordsForFindMany groups records correctly when singular URLs are encoded as query params', async function (assert) { @@ -2054,7 +2054,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { let post = store.peekRecord('post', 2); - await post.get('comments'); + await post.comments; }); test('normalizeKey - to set up _ids and _id', async function (assert) { @@ -2128,9 +2128,9 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { }); let post = await store.findRecord('post', 1); - assert.strictEqual(post.get('authorName'), '@d2h'); - assert.strictEqual(post.get('author.name'), 'D2H'); - assert.deepEqual(post.get('comments').mapBy('body'), ['Rails is unagi', 'What is omakase?']); + assert.strictEqual(post.authorName, '@d2h'); + assert.strictEqual(post.author.name, 'D2H'); + assert.deepEqual(post.comments.mapBy('body'), ['Rails is unagi', 'What is omakase?']); }); test('groupRecordsForFindMany splits up calls for large ids', async function (assert) { @@ -2186,7 +2186,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { return reject(); }; - post.get('comments'); + post.comments; }); test('groupRecordsForFindMany groups calls for small ids', async function (assert) { @@ -2240,7 +2240,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { return resolve({ comments: [{ id: a100 }, { id: b100 }] }); }; - await post.get('comments'); + await post.comments; }); test('calls adapter.handleResponse with the jqXHR and json', async function (assert) { diff --git a/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js b/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js index 6e89d8076da..010142b8f97 100644 --- a/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js +++ b/packages/-ember-data/tests/integration/adapter/rest-adapter/create-record-test.js @@ -51,8 +51,8 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio assert.strictEqual(passedVerb, 'POST'); assert.deepEqual(passedHash.data, { post: { id: 'some-uuid', name: 'The Parley Letter' } }); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.strictEqual(post.get('name'), 'The Parley Letter', 'the post was updated'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.strictEqual(post.name, 'The Parley Letter', 'the post was updated'); }); test('createRecord - passes buildURL the requestType', async function (assert) { @@ -111,9 +111,9 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio assert.strictEqual(passedVerb, 'POST'); assert.deepEqual(passedHash.data, { post: { name: 'The Parley Letter' } }); - assert.strictEqual(post.get('id'), '1', 'the post has the updated ID'); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.strictEqual(post.get('name'), 'Dat Parley Letter', 'the post was updated'); + assert.strictEqual(post.id, '1', 'the post has the updated ID'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.strictEqual(post.name, 'Dat Parley Letter', 'the post was updated'); }); test('createRecord - a payload with a new ID and data applies the updates (with legacy singular name)', async function (assert) { @@ -142,9 +142,9 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio assert.strictEqual(passedVerb, 'POST'); assert.deepEqual(passedHash.data, { post: { name: 'The Parley Letter' } }); - assert.strictEqual(post.get('id'), '1', 'the post has the updated ID'); - assert.false(post.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.strictEqual(post.get('name'), 'Dat Parley Letter', 'the post was updated'); + assert.strictEqual(post.id, '1', 'the post has the updated ID'); + assert.false(post.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.strictEqual(post.name, 'Dat Parley Letter', 'the post was updated'); }); test("createRecord - findMany doesn't overwrite owner", async function (assert) { @@ -185,16 +185,16 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio const post = store.peekRecord('post', 1); const comment = store.createRecord('comment', { name: 'The Parley Letter' }); - const comments = await post.get('comments'); + const comments = await post.comments; comments.pushObject(comment); - assert.strictEqual(comment.get('post'), post, 'the post has been set correctly'); + assert.strictEqual(comment.post, post, 'the post has been set correctly'); await comment.save(); - assert.false(comment.get('hasDirtyAttributes'), "the post isn't dirty anymore"); - assert.strictEqual(comment.get('name'), 'Dat Parley Letter', 'the post was updated'); - assert.strictEqual(comment.get('post'), post, 'the post is still set'); + assert.false(comment.hasDirtyAttributes, "the post isn't dirty anymore"); + assert.strictEqual(comment.name, 'Dat Parley Letter', 'the post was updated'); + assert.strictEqual(comment.post, post, 'the post is still set'); }); test("createRecord - a serializer's primary key and attributes are consulted when building the payload", async function (assert) { @@ -474,15 +474,15 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio }, }); - const commentCount = post.get('comments.length'); + const commentCount = post.comments.length; assert.strictEqual(commentCount, 1, 'the post starts life with a comment'); let comment = store.createRecord('comment', { name: 'Another Comment', post: post }); await comment.save(); - assert.strictEqual(comment.get('post'), post, 'the comment is related to the post'); + assert.strictEqual(comment.post, post, 'the comment is related to the post'); await post.reload(); - assert.strictEqual(post.get('comments.length'), 2, 'Post comment count has been updated'); + assert.strictEqual(post.comments.length, 2, 'Post comment count has been updated'); }); test('createRecord - sideloaded belongsTo relationships are both marked as loaded', async function (assert) { @@ -513,9 +513,9 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio const post = store.createRecord('post', { name: 'man' }); const record = await post.save(); - assert.true(store.peekRecord('post', '1').get('comment.isLoaded'), "post's comment isLoaded (via store)"); - assert.true(store.peekRecord('comment', '1').get('post.isLoaded'), "comment's post isLoaded (via store)"); - assert.true(record.get('comment.isLoaded'), "post's comment isLoaded (via record)"); + assert.true(store.peekRecord('post', '1').comment.isLoaded, "post's comment isLoaded (via store)"); + assert.true(store.peekRecord('comment', '1').post.isLoaded, "comment's post isLoaded (via store)"); + assert.true(record.comment.isLoaded, "post's comment isLoaded (via record)"); assert.true(record.get('comment.post.isLoaded'), "post's comment's post isLoaded (via record)"); }); @@ -599,11 +599,11 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio await post.save(); - assert.strictEqual(post.get('comments.length'), 0, 'post has 0 comments'); + assert.strictEqual(post.comments.length, 0, 'post has 0 comments'); - post.get('comments').pushObject(comment); + post.comments.pushObject(comment); - assert.strictEqual(post.get('comments.length'), 1, 'post has 1 comment'); + assert.strictEqual(post.comments.length, 1, 'post has 1 comment'); ajaxResponse(adapter, { post: [{ id: '1', name: 'Rails is omakase', comments: [2] }], @@ -612,6 +612,6 @@ module('integration/adapter/rest_adapter - REST Adapter - createRecord', functio await post.save(); - assert.strictEqual(post.get('comments.length'), 1, 'post has 1 comment'); + assert.strictEqual(post.comments.length, 1, 'post has 1 comment'); }); }); diff --git a/packages/-ember-data/tests/integration/adapter/rest-adapter/find-record-test.js b/packages/-ember-data/tests/integration/adapter/rest-adapter/find-record-test.js index c6ee08bdb2f..588addd526d 100644 --- a/packages/-ember-data/tests/integration/adapter/rest-adapter/find-record-test.js +++ b/packages/-ember-data/tests/integration/adapter/rest-adapter/find-record-test.js @@ -49,8 +49,8 @@ module('integration/adapter/rest_adapter - REST Adapter - findRecord', function assert.strictEqual(passedVerb, 'GET'); assert.deepEqual(passedHash.data, {}); - assert.strictEqual(post.get('id'), '1'); - assert.strictEqual(post.get('name'), 'Rails is omakase'); + assert.strictEqual(post.id, '1'); + assert.strictEqual(post.name, 'Rails is omakase'); }); // Ok Identifier tests @@ -116,8 +116,8 @@ module('integration/adapter/rest_adapter - REST Adapter - findRecord', function assert.strictEqual(passedVerb, 'GET'); assert.deepEqual(passedHash.data, {}); - assert.strictEqual(post.get('id'), '1'); - assert.strictEqual(post.get('name'), 'Rails is omakase'); + assert.strictEqual(post.id, '1'); + assert.strictEqual(post.name, 'Rails is omakase'); // stress tests let peekPost = store.peekRecord(findRecordArgs); @@ -257,8 +257,8 @@ module('integration/adapter/rest_adapter - REST Adapter - findRecord', function assert.strictEqual(passedUrl, '/posts/1'); assert.strictEqual(passedVerb, 'GET'); assert.deepEqual(passedHash.data, {}); - assert.strictEqual(post.get('id'), '1'); - assert.strictEqual(post.get('name'), 'Rails is omakase'); + assert.strictEqual(post.id, '1'); + assert.strictEqual(post.name, 'Rails is omakase'); }); test('findRecord - payload with sideloaded records of the same type', async function (assert) { @@ -291,13 +291,13 @@ module('integration/adapter/rest_adapter - REST Adapter - findRecord', function assert.strictEqual(passedVerb, 'GET'); assert.deepEqual(passedHash.data, {}); - assert.strictEqual(post.get('id'), '1'); - assert.strictEqual(post.get('name'), 'Rails is omakase'); + assert.strictEqual(post.id, '1'); + assert.strictEqual(post.name, 'Rails is omakase'); const post2 = await store.peekRecord('post', '2'); - assert.strictEqual(post2.get('id'), '2'); - assert.strictEqual(post2.get('name'), 'The Parley Letter'); + assert.strictEqual(post2.id, '2'); + assert.strictEqual(post2.name, 'The Parley Letter'); }); test('findRecord - payload with sideloaded records of a different type', async function (assert) { @@ -327,12 +327,12 @@ module('integration/adapter/rest_adapter - REST Adapter - findRecord', function assert.strictEqual(passedUrl, '/posts/1'); assert.strictEqual(passedVerb, 'GET'); assert.deepEqual(passedHash.data, {}); - assert.strictEqual(post.get('id'), '1'); - assert.strictEqual(post.get('name'), 'Rails is omakase'); + assert.strictEqual(post.id, '1'); + assert.strictEqual(post.name, 'Rails is omakase'); const comment = await store.peekRecord('comment', '1'); - assert.strictEqual(comment.get('id'), '1'); + assert.strictEqual(comment.id, '1'); }); test('findRecord - payload with an serializer-specified primary key', async function (assert) { @@ -366,8 +366,8 @@ module('integration/adapter/rest_adapter - REST Adapter - findRecord', function assert.strictEqual(passedUrl, '/posts/1'); assert.strictEqual(passedVerb, 'GET'); assert.deepEqual(passedHash.data, {}); - assert.strictEqual(post.get('id'), '1'); - assert.strictEqual(post.get('name'), 'Rails is omakase'); + assert.strictEqual(post.id, '1'); + assert.strictEqual(post.name, 'Rails is omakase'); }); test('findRecord - payload with a serializer-specified attribute mapping', async function (assert) { @@ -407,9 +407,9 @@ module('integration/adapter/rest_adapter - REST Adapter - findRecord', function assert.strictEqual(passedUrl, '/posts/1'); assert.strictEqual(passedVerb, 'GET'); assert.deepEqual(passedHash.data, {}); - assert.strictEqual(post.get('id'), '1'); - assert.strictEqual(post.get('name'), 'Rails is omakase'); - assert.strictEqual(post.get('createdAt'), 2013); + assert.strictEqual(post.id, '1'); + assert.strictEqual(post.name, 'Rails is omakase'); + assert.strictEqual(post.createdAt, 2013); }); test('findRecord - passes `include` as a query parameter to ajax', async function (assert) { diff --git a/packages/-ember-data/tests/integration/application-test.js b/packages/-ember-data/tests/integration/application-test.js index 47fe3614a8d..99f4d8552c6 100644 --- a/packages/-ember-data/tests/integration/application-test.js +++ b/packages/-ember-data/tests/integration/application-test.js @@ -130,12 +130,12 @@ module('integration/application - Using the store as a service', function (hooks test('The store can be injected as a service', async function (assert) { let doodleService = this.owner.lookup('service:doodle'); - assert.ok(doodleService.get('store') instanceof Store, 'the store can be used as a service'); + assert.ok(doodleService.store instanceof Store, 'the store can be used as a service'); }); test('There can be multiple store services', function (assert) { let doodleService = this.owner.lookup('service:doodle'); - let store = doodleService.get('store'); + let store = doodleService.store; let secondService = this.owner.lookup('service:second-store'); assert.ok(secondService instanceof Store, 'the store can be used as a service'); @@ -196,7 +196,7 @@ module('integration/application - Attaching initializer', function (hooks) { let store = this.owner.lookup('service:store'); assert.ok( - store && store.get('isCustomStore'), + store && store.isCustomStore, 'ember-data initializer does not overwrite the previous registered service store' ); }); diff --git a/packages/-ember-data/tests/integration/inverse-test.js b/packages/-ember-data/tests/integration/inverse-test.js index 3295605f898..9e229b40822 100644 --- a/packages/-ember-data/tests/integration/inverse-test.js +++ b/packages/-ember-data/tests/integration/inverse-test.js @@ -280,7 +280,7 @@ module('integration/inverse_test - inverseFor', function (hooks) { }, }); reflexiveModel = store.peekRecord('reflexive-model', 1); - reflexiveModel.get('reflexiveProp'); + reflexiveModel.reflexiveProp; }, /Detected a reflexive relationship by the name of 'reflexiveProp'/); }); }); diff --git a/packages/-ember-data/tests/integration/polymorphic-belongs-to-test.js b/packages/-ember-data/tests/integration/polymorphic-belongs-to-test.js index 4140626adad..f3cd4e9f311 100644 --- a/packages/-ember-data/tests/integration/polymorphic-belongs-to-test.js +++ b/packages/-ember-data/tests/integration/polymorphic-belongs-to-test.js @@ -63,7 +63,7 @@ module('integration/polymorphic-belongs-to - Polymorphic BelongsTo', function (h store.push(payload); let book = store.peekRecord('book', 1); - assert.strictEqual(book.get('author.id'), '1'); + assert.strictEqual(book.author.id, '1'); let payloadThatResetsBelongToRelationship = { data: { @@ -79,7 +79,7 @@ module('integration/polymorphic-belongs-to - Polymorphic BelongsTo', function (h }; store.push(payloadThatResetsBelongToRelationship); - assert.strictEqual(book.get('author'), null); + assert.strictEqual(book.author, null); }); test('using store.push with a null value for a payload in relationships sets the Models relationship to null - async relationship', function (assert) { @@ -122,12 +122,11 @@ module('integration/polymorphic-belongs-to - Polymorphic BelongsTo', function (h }, }; - return book - .get('author') + return book.author .then((author) => { - assert.strictEqual(author.get('id'), '1'); + assert.strictEqual(author.id, '1'); store.push(payloadThatResetsBelongToRelationship); - return book.get('author'); + return book.author; }) .then((author) => { assert.strictEqual(author, null); diff --git a/packages/-ember-data/tests/integration/record-array-manager-test.js b/packages/-ember-data/tests/integration/record-array-manager-test.js index 27ccc8febe0..9f99f8f9652 100644 --- a/packages/-ember-data/tests/integration/record-array-manager-test.js +++ b/packages/-ember-data/tests/integration/record-array-manager-test.js @@ -249,7 +249,7 @@ module('integration/record_array_manager', function (hooks) { assert.strictEqual(recordArray.modelName, 'foo'); assert.true(recordArray.isLoaded); assert.strictEqual(recordArray.manager, manager); - assert.deepEqual(recordArray.get('content'), []); + assert.deepEqual(recordArray.content, []); assert.deepEqual(recordArray.toArray(), []); }); @@ -271,7 +271,7 @@ module('integration/record_array_manager', function (hooks) { assert.strictEqual(recordArray.modelName, 'foo', 'has modelName'); assert.true(recordArray.isLoaded, 'isLoaded is true'); assert.strictEqual(recordArray.manager, manager, 'recordArray has manager'); - assert.deepEqual(recordArray.get('content'), [recordIdentifierFor(record)], 'recordArray has content'); + assert.deepEqual(recordArray.content, [recordIdentifierFor(record)], 'recordArray has content'); assert.deepEqual(recordArray.toArray(), [record], 'toArray works'); }); diff --git a/packages/-ember-data/tests/integration/record-array-test.js b/packages/-ember-data/tests/integration/record-array-test.js index 62a19fefb09..a5e40dd05aa 100644 --- a/packages/-ember-data/tests/integration/record-array-test.js +++ b/packages/-ember-data/tests/integration/record-array-test.js @@ -134,7 +134,7 @@ module('unit/record-array - RecordArray', function (hooks) { await settled(); - assert.strictEqual(recordArray.get('length'), 0, 'Has no more records'); + assert.strictEqual(recordArray.length, 0, 'Has no more records'); store.push({ data: { type: 'person', @@ -147,8 +147,8 @@ module('unit/record-array - RecordArray', function (hooks) { await settled(); - assert.strictEqual(recordArray.get('length'), 0, 'length has not been updated'); - assert.strictEqual(recordArray.get('content'), null, 'content has not been updated'); + assert.strictEqual(recordArray.length, 0, 'length has not been updated'); + assert.strictEqual(recordArray.content, null, 'content has not been updated'); }); test('a loaded record is removed from a record array when it is deleted', async function (assert) { @@ -198,11 +198,11 @@ module('unit/record-array - RecordArray', function (hooks) { let scumbag = await store.findRecord('person', 1); let tag = await store.findRecord('tag', 1); - let recordArray = tag.get('people'); + let recordArray = tag.people; recordArray.addObject(scumbag); - assert.strictEqual(scumbag.get('tag'), tag, "precond - the scumbag's tag has been set"); + assert.strictEqual(scumbag.tag, tag, "precond - the scumbag's tag has been set"); assert.strictEqual(get(recordArray, 'length'), 1, 'precond - record array has one item'); assert.strictEqual(get(recordArray.objectAt(0), 'name'), 'Scumbag Dale', 'item at index 0 is record with id 1'); @@ -265,8 +265,8 @@ module('unit/record-array - RecordArray', function (hooks) { scumbag.deleteRecord(); - assert.strictEqual(tag.get('people.length'), 1, 'record is not removed from the record array'); - assert.strictEqual(tag.get('people').objectAt(0), scumbag, 'tag still has the scumbag'); + assert.strictEqual(tag.people.length, 1, 'record is not removed from the record array'); + assert.strictEqual(tag.people.objectAt(0), scumbag, 'tag still has the scumbag'); }); test("a loaded record is not removed from both the record array and from the belongs to, even if the belongsTo side isn't defined", async function (assert) { @@ -313,13 +313,13 @@ module('unit/record-array - RecordArray', function (hooks) { let tag = store.peekRecord('tag', 1); let tool = store.peekRecord('tool', 1); - assert.strictEqual(tag.get('people.length'), 1, 'record is in the record array'); - assert.strictEqual(tool.get('person'), scumbag, 'the tool belongs to the record'); + assert.strictEqual(tag.people.length, 1, 'record is in the record array'); + assert.strictEqual(tool.person, scumbag, 'the tool belongs to the record'); scumbag.deleteRecord(); - assert.strictEqual(tag.get('people.length'), 1, 'record is stil in the record array'); - assert.strictEqual(tool.get('person'), scumbag, 'the tool still belongs to the record'); + assert.strictEqual(tag.people.length, 1, 'record is stil in the record array'); + assert.strictEqual(tool.person, scumbag, 'the tool still belongs to the record'); }); // GitHub Issue #168 diff --git a/packages/-ember-data/tests/integration/record-arrays/adapter-populated-record-array-test.js b/packages/-ember-data/tests/integration/record-arrays/adapter-populated-record-array-test.js index 1be8444eea9..55ff859dd4a 100644 --- a/packages/-ember-data/tests/integration/record-arrays/adapter-populated-record-array-test.js +++ b/packages/-ember-data/tests/integration/record-arrays/adapter-populated-record-array-test.js @@ -14,7 +14,7 @@ import { recordIdentifierFor } from '@ember-data/store'; const Person = Model.extend({ name: attr('string'), toString() { - return ``; + return ``; }, }); @@ -72,13 +72,13 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula payload ); - assert.strictEqual(recordArray.get('length'), 3, 'expected recordArray to contain exactly 3 records'); + assert.strictEqual(recordArray.length, 3, 'expected recordArray to contain exactly 3 records'); - recordArray.get('firstObject').destroyRecord(); + recordArray.firstObject.destroyRecord(); await settled(); - assert.strictEqual(recordArray.get('length'), 2, 'expected recordArray to contain exactly 2 records'); + assert.strictEqual(recordArray.length, 2, 'expected recordArray to contain exactly 2 records'); }); test('stores the metadata off the payload', async function (assert) { @@ -119,7 +119,7 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula results.map((r) => recordIdentifierFor(r)), payload ); - assert.strictEqual(recordArray.get('meta.foo'), 'bar', 'expected meta.foo to be bar from payload'); + assert.strictEqual(recordArray.meta.foo, 'bar', 'expected meta.foo to be bar from payload'); }); test('stores the links off the payload', async function (assert) { @@ -161,11 +161,7 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula payload ); - assert.strictEqual( - recordArray.get('links.first'), - '/foo?page=1', - 'expected links.first to be "/foo?page=1" from payload' - ); + assert.strictEqual(recordArray.links.first, '/foo?page=1', 'expected links.first to be "/foo?page=1" from payload'); }); test('recordArray.replace() throws error', async function (assert) { @@ -183,7 +179,8 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula ); }); - test('pass record array to adapter.query regardless of arity', async function (assert) { + test('pass record array to adapter.query regardless of its arity', async function (assert) { + assert.expect(2); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -193,70 +190,24 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula { id: '2', type: 'person', attributes: { name: 'Scumbag Katz' } }, ], }; - - adapter.query = function (store, type, query) { - // Due to #6232, we now expect 5 arguments regardless of arity - assert.strictEqual(arguments.length, 5, 'expect 5 arguments in query'); - return payload; - }; - - await store.query('person', {}); - - adapter.query = function (store, type, query, recordArray) { - assert.strictEqual(arguments.length, 5); - return payload; - }; - store.query('person', {}); - }); - - test('pass record array to adapter.query regardless of arity', async function (assert) { - assert.expect(10); - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); - - let payload = { - data: [ - { id: '1', type: 'person', attributes: { name: 'Scumbag Dale' } }, - { id: '2', type: 'person', attributes: { name: 'Scumbag Katz' } }, - ], - }; - let actualQuery = {}; - let superCreateAdapterPopulatedRecordArray = store.recordArrayManager.createAdapterPopulatedRecordArray; - - store.recordArrayManager.createAdapterPopulatedRecordArray = function (modelName, query, identifiers, _payload) { - assert.strictEqual(arguments.length, 4); - - assert.strictEqual(modelName, 'person'); - assert.strictEqual(query, actualQuery); - assert.strictEqual(_payload, payload); - assert.strictEqual(identifiers.length, 2); - return superCreateAdapterPopulatedRecordArray.apply(this, arguments); - }; - + // arity 3 adapter.query = function (store, type, query) { // Due to #6232, we now expect 5 arguments regardless of arity - assert.strictEqual(arguments.length, 5); + assert.strictEqual(arguments.length, 5, 'we receive 5 args to adapter query with arity 3'); return payload; }; await store.query('person', actualQuery); + // arity 4 adapter.query = function (store, type, query, _recordArray) { - assert.strictEqual(arguments.length, 5); + assert.strictEqual(arguments.length, 5, 'we receive 5 args to adapter query with arity 4'); return payload; }; - store.recordArrayManager.createAdapterPopulatedRecordArray = function (modelName, query) { - assert.strictEqual(arguments.length, 2); - - assert.strictEqual(modelName, 'person'); - assert.strictEqual(query, actualQuery); - return superCreateAdapterPopulatedRecordArray.apply(this, arguments); - }; - - store.query('person', actualQuery); + await store.query('person', actualQuery); }); test('loadRecord re-syncs identifiers recordArrays', async function (assert) { @@ -321,24 +272,24 @@ module('integration/record-arrays/adapter_populated_record_array - AdapterPopula queryArr = await store.query('person', { slice: 1 }); findArray = await store.findAll('person'); - assert.strictEqual(queryArr.get('length'), 0, 'No records for this query'); - assert.false(queryArr.get('isUpdating'), 'Record array isUpdating state updated'); - assert.strictEqual(findArray.get('length'), 1, 'All records are included in collection array'); + assert.strictEqual(queryArr.length, 0, 'No records for this query'); + assert.false(queryArr.isUpdating, 'Record array isUpdating state updated'); + assert.strictEqual(findArray.length, 1, 'All records are included in collection array'); // a new element gets pushed in record array array.push({ id: '2', type: 'person', attributes: { name: 'Scumbag Katz' } }); await queryArr.update(); - assert.strictEqual(queryArr.get('length'), 1, 'The new record is returned and added in adapter populated array'); - assert.false(queryArr.get('isUpdating'), 'Record array isUpdating state updated'); - assert.strictEqual(findArray.get('length'), 2, 'find returns 2 records'); + assert.strictEqual(queryArr.length, 1, 'The new record is returned and added in adapter populated array'); + assert.false(queryArr.isUpdating, 'Record array isUpdating state updated'); + assert.strictEqual(findArray.length, 2, 'find returns 2 records'); // element gets removed array.pop(0); await queryArr.update(); - assert.strictEqual(queryArr.get('length'), 0, 'Record removed from array'); + assert.strictEqual(queryArr.length, 0, 'Record removed from array'); // record not removed from the model collection - assert.strictEqual(findArray.get('length'), 2, 'Record still remains in collection array'); + assert.strictEqual(findArray.length, 2, 'Record still remains in collection array'); }); }); diff --git a/packages/-ember-data/tests/integration/record-arrays/peeked-records-test.js b/packages/-ember-data/tests/integration/record-arrays/peeked-records-test.js index 4bd1cc71e40..87f0c2e32bf 100644 --- a/packages/-ember-data/tests/integration/record-arrays/peeked-records-test.js +++ b/packages/-ember-data/tests/integration/record-arrays/peeked-records-test.js @@ -13,7 +13,7 @@ let store; const Person = Model.extend({ name: attr('string'), toString() { - return ``; + return ``; }, }); diff --git a/packages/-ember-data/tests/integration/record-data/record-data-errors-test.ts b/packages/-ember-data/tests/integration/record-data/record-data-errors-test.ts index 2283f3bbab4..39d904d4c38 100644 --- a/packages/-ember-data/tests/integration/record-data/record-data-errors-test.ts +++ b/packages/-ember-data/tests/integration/record-data/record-data-errors-test.ts @@ -286,13 +286,13 @@ module('integration/record-data - Custom RecordData Errors', function (hooks) { data: [personHash], }); let person = store.peekRecord('person', '1'); - let nameError = person.get('errors').errorsFor('name').get('firstObject'); + let nameError = person.errors.errorsFor('name').firstObject; assert.strictEqual(nameError.attribute, 'name', 'error shows up on name'); - assert.false(person.get('isValid'), 'person is not valid'); + assert.false(person.isValid, 'person is not valid'); errorsToReturn = []; storeWrapper.notifyErrorsChange('person', '1'); - assert.true(person.get('isValid'), 'person is valid'); - assert.strictEqual(person.get('errors').errorsFor('name').length, 0, 'no errors on name'); + assert.true(person.isValid, 'person is valid'); + assert.strictEqual(person.errors.errorsFor('name').length, 0, 'no errors on name'); errorsToReturn = [ { title: 'Invalid Attribute', @@ -303,9 +303,9 @@ module('integration/record-data - Custom RecordData Errors', function (hooks) { }, ]; storeWrapper.notifyErrorsChange('person', '1'); - assert.false(person.get('isValid'), 'person is valid'); - assert.strictEqual(person.get('errors').errorsFor('name').length, 0, 'no errors on name'); - let lastNameError = person.get('errors').errorsFor('lastName').get('firstObject'); + assert.false(person.isValid, 'person is valid'); + assert.strictEqual(person.errors.errorsFor('name').length, 0, 'no errors on name'); + let lastNameError = person.errors.errorsFor('lastName').firstObject; assert.strictEqual(lastNameError.attribute, 'lastName', 'error shows up on lastName'); }); }); diff --git a/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts b/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts index d7ba18694ae..c985628dd77 100644 --- a/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts +++ b/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts @@ -278,31 +278,29 @@ module('integration/record-data - Record Data State', function (hooks) { isNew = true; storeWrapper.notifyStateChange('person', '1', null, 'isNew'); - assert.true(person.get('isNew'), 'person is new'); + assert.true(person.isNew, 'person is new'); isNew = false; isDeleted = true; storeWrapper.notifyStateChange('person', '1', null, 'isDeleted'); storeWrapper.notifyStateChange('person', '1', null, 'isNew'); - assert.false(person.get('isNew'), 'person is not new'); - assert.true(person.get('isDeleted'), 'person is deleted'); + assert.false(person.isNew, 'person is not new'); + assert.true(person.isDeleted, 'person is deleted'); isNew = false; isDeleted = false; storeWrapper.notifyStateChange('person', '1', null, 'isDeleted'); - assert.false(person.get('isNew'), 'person is not new'); - assert.false(person.get('isDeleted'), 'person is not deleted'); + assert.false(person.isNew, 'person is not new'); + assert.false(person.isDeleted, 'person is not deleted'); person.deleteRecord(); - assert.false(person.get('isDeleted'), 'calling deleteRecord does not automatically set isDeleted flag to true'); + assert.false(person.isDeleted, 'calling deleteRecord does not automatically set isDeleted flag to true'); assert.true(calledSetIsDeleted, 'called setIsDeleted'); - assert.strictEqual(people.get('length'), 1, 'live array starting length is 1'); + assert.strictEqual(people.length, 1, 'live array starting length is 1'); isDeletionCommitted = true; - Ember.run(() => { - storeWrapper.notifyStateChange('person', '1', null, 'isDeletionCommitted'); - }); - assert.strictEqual(people.get('length'), 0, 'commiting a deletion updates the live array'); + storeWrapper.notifyStateChange('person', '1', null, 'isDeletionCommitted'); + assert.strictEqual(people.length, 0, 'commiting a deletion updates the live array'); }); }); diff --git a/packages/-ember-data/tests/integration/record-data/record-data-test.ts b/packages/-ember-data/tests/integration/record-data/record-data-test.ts index d493bfecc07..dac1231495e 100644 --- a/packages/-ember-data/tests/integration/record-data/record-data-test.ts +++ b/packages/-ember-data/tests/integration/record-data/record-data-test.ts @@ -402,14 +402,14 @@ module('integration/record-data - Custom RecordData Implementations', function ( }); let person = store.peekRecord('person', '1'); - assert.strictEqual(person.get('name'), 'new attribute'); + assert.strictEqual(person.name, 'new attribute'); assert.strictEqual(calledGet, 1, 'called getAttr for initial get'); person.set('name', 'new value'); assert.strictEqual(calledGet, 2, 'called getAttr during set'); - assert.strictEqual(person.get('name'), 'new value'); + assert.strictEqual(person.name, 'new value'); assert.strictEqual(calledGet, 2, 'did not call getAttr after set'); person.notifyPropertyChange('name'); - assert.strictEqual(person.get('name'), 'new attribute'); + assert.strictEqual(person.name, 'new attribute'); assert.strictEqual(calledGet, 3, 'called getAttr after notifyPropertyChange'); assert.deepEqual( person.changedAttributes(), @@ -466,10 +466,10 @@ module('integration/record-data - Custom RecordData Implementations', function ( let house = store.peekRecord('house', '1'); let runspired = store.peekRecord('person', '2'); - assert.strictEqual(house.get('landlord.name'), 'David', 'belongsTo get correctly looked up'); + assert.strictEqual(house.landlord.name, 'David', 'belongsTo get correctly looked up'); house.set('landlord', runspired); - assert.strictEqual(house.get('landlord.name'), 'David', 'belongsTo does not change if RD did not notify'); + assert.strictEqual(house.landlord.name, 'David', 'belongsTo does not change if RD did not notify'); }); test('Record Data custom belongsTo', async function (assert) { @@ -518,13 +518,13 @@ module('integration/record-data - Custom RecordData Implementations', function ( }); let house = store.peekRecord('house', '1'); - assert.strictEqual(house.get('landlord.name'), 'David', 'belongsTo get correctly looked up'); + assert.strictEqual(house.landlord.name, 'David', 'belongsTo get correctly looked up'); let runspired = store.peekRecord('person', '2'); house.set('landlord', runspired); // This is intentionally !== runspired to test the custom RD implementation - assert.strictEqual(house.get('landlord.name'), 'Igor', 'RecordData sets the custom belongsTo value'); + assert.strictEqual(house.landlord.name, 'Igor', 'RecordData sets the custom belongsTo value'); }); test('Record Data controls hasMany notifications', async function (assert) { @@ -597,7 +597,7 @@ module('integration/record-data - Custom RecordData Implementations', function ( }); let house = store.peekRecord('house', '1'); - let people = house.get('tenants'); + let people = house.tenants; let david = store.peekRecord('person', '1'); let runspired = store.peekRecord('person', '2'); let igor = store.peekRecord('person', '3'); @@ -699,7 +699,7 @@ module('integration/record-data - Custom RecordData Implementations', function ( }); let house = store.peekRecord('house', '1'); - let people = house.get('tenants'); + let people = house.tenants; let david = store.peekRecord('person', '1'); let runspired = store.peekRecord('person', '2'); let igor = store.peekRecord('person', '3'); diff --git a/packages/-ember-data/tests/integration/record-data/store-wrapper-test.ts b/packages/-ember-data/tests/integration/record-data/store-wrapper-test.ts index 81e819992db..8648bc3045c 100644 --- a/packages/-ember-data/tests/integration/record-data/store-wrapper-test.ts +++ b/packages/-ember-data/tests/integration/record-data/store-wrapper-test.ts @@ -371,7 +371,7 @@ module('integration/store-wrapper - RecordData StoreWrapper tests', function (ho store = owner.lookup('service:store'); let house = store.createRecord('house'); - assert.strictEqual(house.get('id'), '17', 'setRecordId correctly set the id'); + assert.strictEqual(house.id, '17', 'setRecordId correctly set the id'); assert.strictEqual( store.peekRecord('house', 17), house, diff --git a/packages/-ember-data/tests/integration/record-data/unloading-record-data-test.js b/packages/-ember-data/tests/integration/record-data/unloading-record-data-test.js index aa77905639f..ea144bea3b5 100644 --- a/packages/-ember-data/tests/integration/record-data/unloading-record-data-test.js +++ b/packages/-ember-data/tests/integration/record-data/unloading-record-data-test.js @@ -149,10 +149,10 @@ module('RecordData Compatibility', function (hooks) { }, ], }); - let pets = chris.get('pets'); + let pets = chris.pets; let shen = pets.objectAt(0); - assert.strictEqual(shen.get('name'), 'Shen', 'We found Shen'); + assert.strictEqual(shen.name, 'Shen', 'We found Shen'); assert.ok(recordDataFor(chris) instanceof RecordData, 'We used the default record-data for person'); assert.ok(recordDataFor(shen) instanceof CustomRecordData, 'We used the custom record-data for pets'); @@ -207,10 +207,10 @@ module('RecordData Compatibility', function (hooks) { }, ], }); - let pets = chris.get('pets'); + let pets = chris.pets; let shen = pets.objectAt(0); - assert.strictEqual(shen.get('name'), 'Shen', 'We found Shen'); + assert.strictEqual(shen.name, 'Shen', 'We found Shen'); try { run(() => shen.unloadRecord()); diff --git a/packages/-ember-data/tests/integration/records/collection-save-test.js b/packages/-ember-data/tests/integration/records/collection-save-test.js index f9130400594..d665106c9eb 100644 --- a/packages/-ember-data/tests/integration/records/collection-save-test.js +++ b/packages/-ember-data/tests/integration/records/collection-save-test.js @@ -97,7 +97,7 @@ module('integration/records/collection_save - Save Collection of Records', funct .then((post) => { // the ID here is '2' because the second post saves on the first attempt, // while the first post saves on the second attempt - assert.strictEqual(posts.get('firstObject.id'), '2', 'The post ID made it through'); + assert.strictEqual(posts.firstObject.id, '2', 'The post ID made it through'); }); }); }); diff --git a/packages/-ember-data/tests/integration/records/create-record-test.js b/packages/-ember-data/tests/integration/records/create-record-test.js index c6d1ebef89c..7c9ab555f70 100644 --- a/packages/-ember-data/tests/integration/records/create-record-test.js +++ b/packages/-ember-data/tests/integration/records/create-record-test.js @@ -82,7 +82,7 @@ module('Store.createRecord() coverage', function (hooks) { assert.strictEqual(pet.owner, chris, 'Precondition: Our owner is Chris'); let pets = chris - .get('pets') + .pets .toArray() .map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Precondition: Chris has Shen as a pet'); @@ -116,23 +116,15 @@ module('Store.createRecord() coverage', function (hooks) { }); // check that we are properly configured - assert.strictEqual(pet.get('owner'), chris, 'Precondition: Our owner is Chris'); + assert.strictEqual(pet.owner, chris, 'Precondition: Our owner is Chris'); - let pets = chris - .get('pets') - .toArray() - .map((pet) => pet.get('name')); + let pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Precondition: Chris has Shen as a pet'); - chris.unloadRecord(); - - assert.strictEqual(pet.get('owner'), null, 'Shen no longer has an owner'); + assert.strictEqual(pet.owner, null, 'Shen no longer has an owner'); // check that the relationship has been dissolved - pets = chris - .get('pets') - .toArray() - .map((pet) => pet.get('name')); + pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, [], 'Chris no longer has any pets'); }); @@ -196,8 +188,8 @@ module('Store.createRecord() coverage', function (hooks) { bestHuman: chris, }); - let bestHuman = shen.get('bestHuman'); - let bestDog = await chris.get('bestDog'); + let bestHuman = shen.bestHuman; + let bestDog = await chris.bestDog; // check that we are properly configured assert.strictEqual(bestHuman, chris, 'Precondition: Shen has bestHuman as Chris'); @@ -205,8 +197,8 @@ module('Store.createRecord() coverage', function (hooks) { await shen.save(); - bestHuman = shen.get('bestHuman'); - bestDog = await chris.get('bestDog'); + bestHuman = shen.bestHuman; + bestDog = await chris.bestDog; // check that the relationship has remained established assert.strictEqual(bestHuman, chris, 'Shen bestHuman is still Chris'); diff --git a/packages/-ember-data/tests/integration/records/delete-record-test.js b/packages/-ember-data/tests/integration/records/delete-record-test.js index 5e46119609e..0561de410e6 100644 --- a/packages/-ember-data/tests/integration/records/delete-record-test.js +++ b/packages/-ember-data/tests/integration/records/delete-record-test.js @@ -59,15 +59,15 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { let all = store.peekAll('person'); // pre-condition - assert.strictEqual(all.get('length'), 2, 'pre-condition: 2 records in array'); + assert.strictEqual(all.length, 2, 'pre-condition: 2 records in array'); - run(adam, 'deleteRecord'); + adam.deleteRecord(); - assert.strictEqual(all.get('length'), 2, '2 records in array after deleteRecord'); + assert.strictEqual(all.length, 2, '2 records in array after deleteRecord'); - run(adam, 'save'); + await adam.save(); - assert.strictEqual(all.get('length'), 1, '1 record in array after deleteRecord and save'); + assert.strictEqual(all.length, 1, '1 record in array after deleteRecord and save'); }); test('deleting a record that is part of a hasMany removes it from the hasMany recordArray', async function (assert) { @@ -122,12 +122,12 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { let person = store.peekRecord('person', '1'); // Sanity Check we are in the correct state. - assert.strictEqual(group.get('people.length'), 2, 'expected 2 related records before delete'); - assert.strictEqual(person.get('name'), 'Adam Sunderland', 'expected related records to be loaded'); + assert.strictEqual(group.people.length, 2, 'expected 2 related records before delete'); + assert.strictEqual(person.name, 'Adam Sunderland', 'expected related records to be loaded'); await person.destroyRecord(); - assert.strictEqual(group.get('people.length'), 1, 'expected 1 related records after delete'); + assert.strictEqual(group.people.length, 1, 'expected 1 related records after delete'); }); test('records can be deleted during record array enumeration', async function (assert) { @@ -161,7 +161,7 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { var all = store.peekAll('person'); // pre-condition - assert.strictEqual(all.get('length'), 2, 'expected 2 records'); + assert.strictEqual(all.length, 2, 'expected 2 records'); run(function () { all.forEach(function (record) { @@ -169,7 +169,7 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { }); }); - assert.strictEqual(all.get('length'), 0, 'expected 0 records'); + assert.strictEqual(all.length, 0, 'expected 0 records'); assert.strictEqual(all.objectAt(0), undefined, "can't get any records"); }); @@ -428,17 +428,17 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { // Sanity Check assert.ok(group, 'expected group to be found'); - assert.strictEqual(group.get('company.name'), 'Inc.', 'group belongs to our company'); + assert.strictEqual(group.company.name, 'Inc.', 'group belongs to our company'); assert.strictEqual(group.employees.length, 1, 'expected 1 related record before delete'); const employees = await group.employees; employee = employees.objectAt(0); - assert.strictEqual(employee.get('name'), 'Adam Sunderland', 'expected related records to be loaded'); + assert.strictEqual(employee.name, 'Adam Sunderland', 'expected related records to be loaded'); await group.destroyRecord(); await employee.destroyRecord(); - assert.strictEqual(store.peekAll('employee').get('length'), 0, 'no employee record loaded'); - assert.strictEqual(store.peekAll('group').get('length'), 0, 'no group record loaded'); + assert.strictEqual(store.peekAll('employee').length, 0, 'no employee record loaded'); + assert.strictEqual(store.peekAll('group').length, 0, 'no group record loaded'); // Server pushes the same group and employee once more after they have been destroyed client-side. (The company is a long-lived record) store.push(jsonEmployee); diff --git a/packages/-ember-data/tests/integration/records/edit-record-test.js b/packages/-ember-data/tests/integration/records/edit-record-test.js index 87d15bdf012..102c9ad496b 100644 --- a/packages/-ember-data/tests/integration/records/edit-record-test.js +++ b/packages/-ember-data/tests/integration/records/edit-record-test.js @@ -65,23 +65,23 @@ module('Editing a Record', function (hooks) { }); // check that we are properly configured - assert.strictEqual(pet.get('owner'), null, 'Precondition: Our owner is null'); + assert.strictEqual(pet.owner, null, 'Precondition: Our owner is null'); let pets = chris - .get('pets') + .pets .toArray() - .map((pet) => pet.get('name')); + .map((pet) => pet.name); assert.deepEqual(pets, [], 'Precondition: Chris has no pets'); pet.set('owner', chris); - assert.strictEqual(pet.get('owner'), chris, 'Shen has Chris as an owner'); + assert.strictEqual(pet.owner, chris, 'Shen has Chris as an owner'); // check that the relationship has been established pets = chris - .get('pets') + .pets .toArray() - .map((pet) => pet.get('name')); + .map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Chris has Shen as a pet'); }); @@ -105,23 +105,23 @@ module('Editing a Record', function (hooks) { }); // check that we are properly configured - assert.strictEqual(pet.get('owner'), null, 'Precondition: Our owner is null'); + assert.strictEqual(pet.owner, null, 'Precondition: Our owner is null'); let pets = chris - .get('pets') + .pets .toArray() - .map((pet) => pet.get('name')); + .map((pet) => pet.name); assert.deepEqual(pets, [], 'Precondition: Chris has no pets'); pet.set('owner', chris); - assert.strictEqual(pet.get('owner'), chris, 'Shen has Chris as an owner'); + assert.strictEqual(pet.owner, chris, 'Shen has Chris as an owner'); // check that the relationship has been established pets = chris - .get('pets') + .pets .toArray() - .map((pet) => pet.get('name')); + .map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Chris has Shen as a pet'); }); @@ -137,23 +137,23 @@ module('Editing a Record', function (hooks) { }); // check that we are properly configured - assert.strictEqual(pet.get('owner'), null, 'Precondition: Our owner is null'); + assert.strictEqual(pet.owner, null, 'Precondition: Our owner is null'); let pets = chris - .get('pets') + .pets .toArray() - .map((pet) => pet.get('name')); + .map((pet) => pet.name); assert.deepEqual(pets, [], 'Precondition: Chris has no pets'); pet.set('owner', chris); - assert.strictEqual(pet.get('owner'), chris, 'Shen has Chris as an owner'); + assert.strictEqual(pet.owner, chris, 'Shen has Chris as an owner'); // check that the relationship has been established pets = chris - .get('pets') + .pets .toArray() - .map((pet) => pet.get('name')); + .map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Chris has Shen as a pet'); }); @@ -177,23 +177,23 @@ module('Editing a Record', function (hooks) { }); // check that we are properly configured - assert.strictEqual(pet.get('owner'), null, 'Precondition: Our owner is null'); + assert.strictEqual(pet.owner, null, 'Precondition: Our owner is null'); let pets = chris - .get('pets') + .pets .toArray() - .map((pet) => pet.get('name')); + .map((pet) => pet.name); assert.deepEqual(pets, [], 'Precondition: Chris has no pets'); pet.set('owner', chris); - assert.strictEqual(pet.get('owner'), chris, 'Shen has Chris as an owner'); + assert.strictEqual(pet.owner, chris, 'Shen has Chris as an owner'); // check that the relationship has been established pets = chris - .get('pets') + .pets .toArray() - .map((pet) => pet.get('name')); + .map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Chris has Shen as a pet'); }); @@ -259,14 +259,14 @@ module('Editing a Record', function (hooks) { }, }); - assert.strictEqual(shen.get('owner'), chris, 'Precondition: Chris is the current owner'); - assert.strictEqual(rocky.get('owner'), chris, 'Precondition: Chris is the current owner'); + assert.strictEqual(shen.owner, chris, 'Precondition: Chris is the current owner'); + assert.strictEqual(rocky.owner, chris, 'Precondition: Chris is the current owner'); let pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, ['Shen', 'Rocky'], 'Precondition: Chris has Shen and Rocky as pets'); shen.set('owner', john); - assert.strictEqual(shen.get('owner'), john, 'After Update: John is the new owner of Shen'); + assert.strictEqual(shen.owner, john, 'After Update: John is the new owner of Shen'); pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, ['Rocky'], 'After Update: Chris has Rocky as a pet'); @@ -276,8 +276,8 @@ module('Editing a Record', function (hooks) { chris.unloadRecord(); - assert.strictEqual(rocky.get('owner'), null, 'After Unload: Rocky has no owner'); - assert.strictEqual(shen.get('owner'), john, 'After Unload: John should still be the owner of Shen'); + assert.strictEqual(rocky.owner, null, 'After Unload: Rocky has no owner'); + assert.strictEqual(shen.owner, john, 'After Unload: John should still be the owner of Shen'); pets = john.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'After Unload: John still has Shen as a pet'); @@ -313,8 +313,8 @@ module('Editing a Record', function (hooks) { }); // check that we are properly configured - let chrisBestFriend = await chris.get('bestFriend'); - let jamesBestFriend = await james.get('bestFriend'); + let chrisBestFriend = await chris.bestFriend; + let jamesBestFriend = await james.bestFriend; assert.strictEqual(chrisBestFriend, null, 'Precondition: Chris has no best friend'); assert.strictEqual(jamesBestFriend, null, 'Precondition: James has no best friend'); @@ -322,8 +322,8 @@ module('Editing a Record', function (hooks) { chris.set('bestFriend', james); // check that the relationship has been established - chrisBestFriend = await chris.get('bestFriend'); - jamesBestFriend = await james.get('bestFriend'); + chrisBestFriend = await chris.bestFriend; + jamesBestFriend = await james.bestFriend; assert.strictEqual(chrisBestFriend, james, 'Chris has James as a best friend'); assert.strictEqual(jamesBestFriend, chris, 'James has Chris as a best friend'); @@ -349,8 +349,8 @@ module('Editing a Record', function (hooks) { }); // check that we are properly configured - let chrisBestFriend = await chris.get('bestFriend'); - let jamesBestFriend = await james.get('bestFriend'); + let chrisBestFriend = await chris.bestFriend; + let jamesBestFriend = await james.bestFriend; assert.strictEqual(chrisBestFriend, null, 'Precondition: Chris has no best friend'); assert.strictEqual(jamesBestFriend, null, 'Precondition: James has no best friend'); @@ -358,8 +358,8 @@ module('Editing a Record', function (hooks) { chris.set('bestFriend', james); // check that the relationship has been established - chrisBestFriend = await chris.get('bestFriend'); - jamesBestFriend = await james.get('bestFriend'); + chrisBestFriend = await chris.bestFriend; + jamesBestFriend = await james.bestFriend; assert.strictEqual(chrisBestFriend, james, 'Chris has James as a best friend'); assert.strictEqual(jamesBestFriend, chris, 'James has Chris as a best friend'); @@ -377,8 +377,8 @@ module('Editing a Record', function (hooks) { }); // check that we are properly configured - let chrisBestFriend = await chris.get('bestFriend'); - let jamesBestFriend = await james.get('bestFriend'); + let chrisBestFriend = await chris.bestFriend; + let jamesBestFriend = await james.bestFriend; assert.strictEqual(chrisBestFriend, null, 'Precondition: Chris has no best friend'); assert.strictEqual(jamesBestFriend, null, 'Precondition: James has no best friend'); @@ -386,8 +386,8 @@ module('Editing a Record', function (hooks) { chris.set('bestFriend', james); // check that the relationship has been established - chrisBestFriend = await chris.get('bestFriend'); - jamesBestFriend = await james.get('bestFriend'); + chrisBestFriend = await chris.bestFriend; + jamesBestFriend = await james.bestFriend; assert.strictEqual(chrisBestFriend, james, 'Chris has James as a best friend'); assert.strictEqual(jamesBestFriend, chris, 'James has Chris as a best friend'); @@ -413,8 +413,8 @@ module('Editing a Record', function (hooks) { }); // check that we are properly configured - let chrisBestFriend = await chris.get('bestFriend'); - let jamesBestFriend = await james.get('bestFriend'); + let chrisBestFriend = await chris.bestFriend; + let jamesBestFriend = await james.bestFriend; assert.strictEqual(chrisBestFriend, null, 'Precondition: Chris has no best friend'); assert.strictEqual(jamesBestFriend, null, 'Precondition: James has no best friend'); @@ -422,8 +422,8 @@ module('Editing a Record', function (hooks) { chris.set('bestFriend', james); // check that the relationship has been established - chrisBestFriend = await chris.get('bestFriend'); - jamesBestFriend = await james.get('bestFriend'); + chrisBestFriend = await chris.bestFriend; + jamesBestFriend = await james.bestFriend; assert.strictEqual(chrisBestFriend, james, 'Chris has James as a best friend'); assert.strictEqual(jamesBestFriend, chris, 'James has Chris as a best friend'); diff --git a/packages/-ember-data/tests/integration/records/load-test.js b/packages/-ember-data/tests/integration/records/load-test.js index 22bca825c3e..6c0cf503aeb 100644 --- a/packages/-ember-data/tests/integration/records/load-test.js +++ b/packages/-ember-data/tests/integration/records/load-test.js @@ -178,8 +178,8 @@ module('integration/load - Loading Records', function (hooks) { assert.false(_isLoading(cache, identifier), 'after first fetch: We have loaded'); assert.false(record.isReloading, 'after first fetch: We are not reloading'); - let bestFriend = await record.get('bestFriend'); - let trueBestFriend = await bestFriend.get('bestFriend'); + let bestFriend = await record.bestFriend; + let trueBestFriend = await bestFriend.bestFriend; // shen is our retainer for the record we are testing // that ensures unloadRecord later in this test does not fully diff --git a/packages/-ember-data/tests/integration/records/relationship-changes-test.js b/packages/-ember-data/tests/integration/records/relationship-changes-test.js index 493098e1e2f..3bfbc79dc30 100644 --- a/packages/-ember-data/tests/integration/records/relationship-changes-test.js +++ b/packages/-ember-data/tests/integration/records/relationship-changes-test.js @@ -143,7 +143,7 @@ module('integration/records/relationship-changes - Relationship changes', functi observerCount++; }); // prime the pump - person.get('siblings'); + person.siblings; }); run(() => { @@ -317,7 +317,7 @@ module('integration/records/relationship-changes - Relationship changes', functi observerCount++; }); // prime the pump - person.get('siblings'); + person.siblings; }); run(() => { @@ -373,7 +373,7 @@ module('integration/records/relationship-changes - Relationship changes', functi observerCount++; }); // prime the pump - person.get('siblings'); + person.siblings; }); run(() => { @@ -429,7 +429,7 @@ module('integration/records/relationship-changes - Relationship changes', functi observerCount++; }); // prime the pump - person.get('siblings'); + person.siblings; }); run(() => { @@ -486,7 +486,7 @@ module('integration/records/relationship-changes - Relationship changes', functi run(() => { // prime the pump - person.get('siblings'); + person.siblings; person.addObserver('siblings.[]', observerMethod); }); @@ -793,7 +793,7 @@ module('integration/records/relationship-changes - Relationship changes', functi }, }; - let siblings = run(() => person.get('siblings')); + let siblings = run(() => person.siblings); // flush initial state since // nothing is consuming us. @@ -873,7 +873,7 @@ module('integration/records/relationship-changes - Relationship changes', functi }, }; - let siblings = run(() => person.get('siblings')); + let siblings = run(() => person.siblings); // flush initial state since // nothing is consuming us. // else the test will fail because we will @@ -932,7 +932,7 @@ module('integration/records/relationship-changes - Relationship changes', functi ], }); - post.get('author'); + post.author; post.addObserver('author', function () { observerCount++; diff --git a/packages/-ember-data/tests/integration/records/reload-test.js b/packages/-ember-data/tests/integration/records/reload-test.js index 6f688b4257c..e6787d2b00a 100644 --- a/packages/-ember-data/tests/integration/records/reload-test.js +++ b/packages/-ember-data/tests/integration/records/reload-test.js @@ -114,7 +114,7 @@ module('integration/reload - Reloading Records', function (hooks) { }, findRecord() { - assert.true(tom.get('isReloading'), 'Tom is reloading'); + assert.true(tom.isReloading, 'Tom is reloading'); if (count++ === 0) { return reject(); } else { @@ -130,15 +130,15 @@ module('integration/reload - Reloading Records', function (hooks) { assert.ok(true, 'we throw an error'); }); - assert.true(tom.get('isError'), 'Tom is now errored'); - assert.false(tom.get('isReloading'), 'Tom is no longer reloading'); + assert.true(tom.isError, 'Tom is now errored'); + assert.false(tom.isReloading, 'Tom is no longer reloading'); let person = await tom.reload(); assert.strictEqual(person, tom, 'The resolved value is the record'); - assert.false(tom.get('isError'), 'Tom is no longer errored'); - assert.false(tom.get('isReloading'), 'Tom is no longer reloading'); - assert.strictEqual(tom.get('name'), 'Thomas Dale', 'the updates apply'); + assert.false(tom.isError, 'Tom is no longer errored'); + assert.false(tom.isReloading, 'Tom is no longer reloading'); + assert.strictEqual(tom.name, 'Thomas Dale', 'the updates apply'); }); test('When a record is loaded a second time, isLoaded stays true', async function (assert) { @@ -245,16 +245,16 @@ module('integration/reload - Reloading Records', function (hooks) { let person = await store.findRecord('person', '1'); tom = person; - assert.strictEqual(person.get('name'), 'Tom', 'precond'); + assert.strictEqual(person.name, 'Tom', 'precond'); - let tags = await person.get('tags'); + let tags = await person.tags; assert.deepEqual(tags.mapBy('name'), ['hipster', 'hair']); person = await tom.reload(); - assert.strictEqual(person.get('name'), 'Tom', 'precond'); + assert.strictEqual(person.name, 'Tom', 'precond'); - tags = await person.get('tags'); + tags = await person.tags; assert.deepEqual(tags.mapBy('name'), ['hipster', 'hair'], 'The tags are still there'); }); @@ -311,7 +311,7 @@ module('integration/reload - Reloading Records', function (hooks) { }); let ownerRef = shen.belongsTo('owner'); - let owner = shen.get('owner'); + let owner = shen.owner; let ownerViaRef = await ownerRef.reload(); assert.strictEqual(owner, ownerViaRef, 'We received the same reference via reload'); @@ -360,7 +360,7 @@ module('integration/reload - Reloading Records', function (hooks) { let ownerRef = shen.belongsTo('owner'); let ownerViaRef = await ownerRef.reload(); - let owner = shen.get('owner'); + let owner = shen.owner; assert.strictEqual(owner, ownerViaRef, 'We received the same reference via reload'); }); @@ -416,7 +416,7 @@ module('integration/reload - Reloading Records', function (hooks) { }); let ownersRef = shen.hasMany('owners'); - let owners = shen.get('owners'); + let owners = shen.owners; let ownersViaRef = await ownersRef.reload(); assert.strictEqual(owners.objectAt(0), ownersViaRef.objectAt(0), 'We received the same reference via reload'); @@ -465,7 +465,7 @@ module('integration/reload - Reloading Records', function (hooks) { let ownersRef = shen.hasMany('owners'); let ownersViaRef = await ownersRef.reload(); - let owners = shen.get('owners'); + let owners = shen.owners; assert.strictEqual(owners.objectAt(0), ownersViaRef.objectAt(0), 'We received the same reference via reload'); }); @@ -526,7 +526,7 @@ module('integration/reload - Reloading Records', function (hooks) { }); let ownerRef = shen.belongsTo('owner'); - let owner = shen.get('owner'); + let owner = shen.owner; let ownerViaRef = await ownerRef.reload(); assert.strictEqual(owner, ownerViaRef, 'We received the same reference via reload'); @@ -578,7 +578,7 @@ module('integration/reload - Reloading Records', function (hooks) { let ownerRef = shen.belongsTo('owner'); let ownerViaRef = await ownerRef.reload(); - let owner = shen.get('owner'); + let owner = shen.owner; assert.strictEqual(owner, ownerViaRef, 'We received the same reference via reload'); }); @@ -639,7 +639,7 @@ module('integration/reload - Reloading Records', function (hooks) { }); let ownersRef = shen.hasMany('owners'); - let owners = shen.get('owners'); + let owners = shen.owners; let ownersViaRef = await ownersRef.reload(); assert.strictEqual(owners.objectAt(0), ownersViaRef.objectAt(0), 'We received the same reference via reload'); @@ -693,7 +693,7 @@ module('integration/reload - Reloading Records', function (hooks) { let ownersRef = shen.hasMany('owners'); let ownersViaRef = await ownersRef.reload(); - let owners = shen.get('owners'); + let owners = shen.owners; assert.strictEqual(owners.objectAt(0), ownersViaRef.objectAt(0), 'We received the same reference via reload'); }); diff --git a/packages/-ember-data/tests/integration/records/rematerialize-test.js b/packages/-ember-data/tests/integration/records/rematerialize-test.js index 2bdf32e3e4d..4a98370c759 100644 --- a/packages/-ember-data/tests/integration/records/rematerialize-test.js +++ b/packages/-ember-data/tests/integration/records/rematerialize-test.js @@ -82,7 +82,7 @@ module('integration/unload - Rematerializing Unloaded Records', function (hooks) }); let person = store.peekRecord('person', 1); - assert.strictEqual(person.get('cars.length'), 1, 'The inital length of cars is correct'); + assert.strictEqual(person.cars.length, 1, 'The inital length of cars is correct'); assert.notStrictEqual(store.peekRecord('person', '1'), null, 'The person is in the store'); assert.true( @@ -115,9 +115,9 @@ module('integration/unload - Rematerializing Unloaded Records', function (hooks) }); }); - let rematerializedPerson = bob.get('person'); - assert.strictEqual(rematerializedPerson.get('id'), '1'); - assert.strictEqual(rematerializedPerson.get('name'), 'Adam Sunderland'); + let rematerializedPerson = bob.person; + assert.strictEqual(rematerializedPerson.id, '1'); + assert.strictEqual(rematerializedPerson.name, 'Adam Sunderland'); // the person is rematerialized; the previous person is *not* re-used assert.notEqual(rematerializedPerson, adam, 'the person is rematerialized, not recycled'); }); @@ -221,11 +221,11 @@ module('integration/unload - Rematerializing Unloaded Records', function (hooks) assert.notStrictEqual(store.peekRecord('boat', '1'), null, 'The boat is in the store'); assert.true(!!store.identifierCache.peekRecordIdentifier({ lid: '@lid:boat-1' }), 'The boat identifier is loaded'); - let boats = await adam.get('boats'); - assert.strictEqual(boats.get('length'), 2, 'Before unloading boats.length is correct'); + let boats = await adam.boats; + assert.strictEqual(boats.length, 2, 'Before unloading boats.length is correct'); boaty.unloadRecord(); - assert.strictEqual(boats.get('length'), 1, 'after unloading boats.length is correct'); + assert.strictEqual(boats.length, 1, 'after unloading boats.length is correct'); // assert our new cache state assert.strictEqual(store.peekRecord('boat', '1'), null, 'The boat is unloaded'); @@ -235,13 +235,13 @@ module('integration/unload - Rematerializing Unloaded Records', function (hooks) ); // cause a rematerialization, this should also cause us to fetch boat '1' again - boats = run(() => adam.get('boats')); + boats = run(() => adam.boats); let rematerializedBoaty = boats.objectAt(1); assert.ok(!!rematerializedBoaty, 'We have a boat!'); - assert.strictEqual(adam.get('boats.length'), 2, 'boats.length correct after rematerialization'); - assert.strictEqual(rematerializedBoaty.get('id'), '1', 'Rematerialized boat has the right id'); - assert.strictEqual(rematerializedBoaty.get('name'), 'Boaty McBoatface', 'Rematerialized boat has the right name'); + assert.strictEqual(adam.boats.length, 2, 'boats.length correct after rematerialization'); + assert.strictEqual(rematerializedBoaty.id, '1', 'Rematerialized boat has the right id'); + assert.strictEqual(rematerializedBoaty.name, 'Boaty McBoatface', 'Rematerialized boat has the right name'); assert.notStrictEqual(rematerializedBoaty, boaty, 'the boat is rematerialized, not recycled'); assert.notStrictEqual(store.peekRecord('boat', '1'), null, 'The boat is loaded'); diff --git a/packages/-ember-data/tests/integration/records/save-test.js b/packages/-ember-data/tests/integration/records/save-test.js index 9e5111f574c..fa39d027a1c 100644 --- a/packages/-ember-data/tests/integration/records/save-test.js +++ b/packages/-ember-data/tests/integration/records/save-test.js @@ -38,14 +38,14 @@ module('integration/records/save - Save Record', function (hooks) { if (DEPRECATE_SAVE_PROMISE_ACCESS) { // `save` returns a PromiseObject which allows to call get on it - assert.strictEqual(saved.get('id'), undefined); + assert.strictEqual(saved.id, undefined); } deferred.resolve({ data: { id: 123, type: 'post' } }); let model = await saved; assert.ok(true, 'save operation was resolved'); if (DEPRECATE_SAVE_PROMISE_ACCESS) { - assert.strictEqual(saved.get('id'), '123'); + assert.strictEqual(saved.id, '123'); assert.strictEqual(saved.id, undefined); } else { assert.strictEqual(saved.id, undefined); @@ -107,7 +107,7 @@ module('integration/records/save - Save Record', function (hooks) { } ) .then(function (post) { - assert.strictEqual(post.get('id'), '123', 'The post ID made it through'); + assert.strictEqual(post.id, '123', 'The post ID made it through'); }); }); }); @@ -125,12 +125,12 @@ module('integration/records/save - Save Record', function (hooks) { run(function () { post.save().then(null, function () { - assert.ok(post.get('isError')); - assert.strictEqual(post.get('currentState.stateName'), 'root.loaded.created.uncommitted'); + assert.ok(post.isError); + assert.strictEqual(post.currentState.stateName, 'root.loaded.created.uncommitted'); post.save().then(null, function () { - assert.ok(post.get('isError')); - assert.strictEqual(post.get('currentState.stateName'), 'root.loaded.created.uncommitted'); + assert.ok(post.isError); + assert.strictEqual(post.currentState.stateName, 'root.loaded.created.uncommitted'); }); }); }); @@ -156,10 +156,10 @@ module('integration/records/save - Save Record', function (hooks) { run(function () { post.save().then(null, function () { - assert.false(post.get('isValid')); + assert.false(post.isValid); post.save().then(null, function () { - assert.false(post.get('isValid')); + assert.false(post.isValid); }); }); }); @@ -179,10 +179,10 @@ module('integration/records/save - Save Record', function (hooks) { run(function () { post.save().then(null, function () { - assert.false(post.get('isValid')); + assert.false(post.isValid); post.save().then(null, function () { - assert.false(post.get('isValid')); + assert.false(post.isValid); }); }); }); diff --git a/packages/-ember-data/tests/integration/records/unload-test.js b/packages/-ember-data/tests/integration/records/unload-test.js index 68b8f5c7df0..516d69fbe24 100644 --- a/packages/-ember-data/tests/integration/records/unload-test.js +++ b/packages/-ember-data/tests/integration/records/unload-test.js @@ -228,16 +228,16 @@ module('integration/unload - Unloading Records', function (hooks) { bob = store.peekRecord('car', 1); }); - assert.strictEqual(store.peekAll('person').get('length'), 2, 'two person records loaded'); - assert.strictEqual(store.peekAll('car').get('length'), 1, 'one car record loaded'); + assert.strictEqual(store.peekAll('person').length, 2, 'two person records loaded'); + assert.strictEqual(store.peekAll('car').length, 1, 'one car record loaded'); run(function () { - car.get('person'); + car.person; store.unloadAll('person'); }); - assert.strictEqual(store.peekAll('person').get('length'), 0); - assert.strictEqual(store.peekAll('car').get('length'), 1); + assert.strictEqual(store.peekAll('person').length, 0); + assert.strictEqual(store.peekAll('car').length, 1); run(function () { store.push({ @@ -252,7 +252,7 @@ module('integration/unload - Unloading Records', function (hooks) { }); car = store.peekRecord('car', 1); - let person = car.get('person'); + let person = car.person; assert.ok(!!car, 'We have a car'); assert.notOk(person, 'We dont have a person'); @@ -301,15 +301,15 @@ module('integration/unload - Unloading Records', function (hooks) { bob = store.peekRecord('car', 1); }); - assert.strictEqual(store.peekAll('person').get('length'), 2, 'two person records loaded'); - assert.strictEqual(store.peekAll('car').get('length'), 1, 'one car record loaded'); + assert.strictEqual(store.peekAll('person').length, 2, 'two person records loaded'); + assert.strictEqual(store.peekAll('car').length, 1, 'one car record loaded'); run(function () { store.unloadAll(); }); - assert.strictEqual(store.peekAll('person').get('length'), 0); - assert.strictEqual(store.peekAll('car').get('length'), 0); + assert.strictEqual(store.peekAll('person').length, 0); + assert.strictEqual(store.peekAll('car').length, 0); }); test('removes findAllCache after unloading all records', function (assert) { @@ -338,14 +338,14 @@ module('integration/unload - Unloading Records', function (hooks) { let bob = store.peekRecord('person', 2); }); - assert.strictEqual(store.peekAll('person').get('length'), 2, 'two person records loaded'); + assert.strictEqual(store.peekAll('person').length, 2, 'two person records loaded'); run(function () { store.peekAll('person'); store.unloadAll('person'); }); - assert.strictEqual(store.peekAll('person').get('length'), 0, 'zero person records loaded'); + assert.strictEqual(store.peekAll('person').length, 0, 'zero person records loaded'); }); test('unloading all records also updates record array from peekAll()', function (assert) { @@ -373,12 +373,12 @@ module('integration/unload - Unloading Records', function (hooks) { }); let all = store.peekAll('person'); - assert.strictEqual(all.get('length'), 2); + assert.strictEqual(all.length, 2); run(function () { store.unloadAll('person'); }); - assert.strictEqual(all.get('length'), 0); + assert.strictEqual(all.length, 0); }); function makeBoatOneForPersonOne() { @@ -423,8 +423,8 @@ module('integration/unload - Unloading Records', function (hooks) { assert.notStrictEqual(store.peekRecord('boat', '1'), null); // ensure the relationship was established (we reach through the async proxy here) - let peopleBoats = await person.get('boats'); - let boatPerson = await boat.get('person'); + let peopleBoats = await person.boats; + let boatPerson = await boat.person; assert.strictEqual(relationshipState.canonicalState.length, 1, 'canonical member size should be 1'); assert.strictEqual(relationshipState.members.size, 1, 'members size should be 1'); @@ -492,8 +492,8 @@ module('integration/unload - Unloading Records', function (hooks) { assert.notStrictEqual(store.peekRecord('boat', '1'), null); // ensure the relationship was established (we reach through the async proxy here) - let peopleBoats = run(() => person.get('boats.content')); - let boatPerson = run(() => boat.get('person.content')); + let peopleBoats = run(() => person.boats.content); + let boatPerson = run(() => boat.person.content); assert.strictEqual(relationshipState.canonicalState.length, 1, 'canonical member size should be 1'); assert.strictEqual(relationshipState.members.size, 1, 'members size should be 1'); @@ -510,7 +510,7 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(relationshipState.members.size, 1, 'members size should still be 1'); assert.strictEqual(get(peopleBoats, 'length'), 0, 'Our person thinks they have no boats'); - run(() => person.get('boats')); + run(() => person.boats); store.peekRecord('boat', '1'); @@ -546,8 +546,8 @@ module('integration/unload - Unloading Records', function (hooks) { assert.notStrictEqual(store.peekRecord('boat', '1'), null); // ensure the relationship was established (we reach through the async proxy here) - let peopleBoats = run(() => person.get('boats.content')); - let boatPerson = run(() => boat.get('person.content')); + let peopleBoats = run(() => person.boats.content); + let boatPerson = run(() => boat.person.content); assert.deepEqual(idsFromArr(relationshipState.canonicalState), ['1'], 'canonical member size should be 1'); assert.deepEqual(idsFromArr(relationshipState.currentState), ['1'], 'members size should be 1'); @@ -807,7 +807,7 @@ module('integration/unload - Unloading Records', function (hooks) { assert.true(recordData.isEmpty(), 'Expected the previous data to be unloaded'); return store.findRecord('person', '1').then((record) => { - assert.strictEqual(record.get('cars.length'), 0, 'Expected relationship to be cleared by the new push'); + assert.strictEqual(record.cars.length, 0, 'Expected relationship to be cleared by the new push'); assert.strictEqual(identifier, recordIdentifierFor(record), 'the old identifier is reused'); assert.strictEqual( record.currentState.stateName, @@ -862,7 +862,7 @@ module('integration/unload - Unloading Records', function (hooks) { const bike = store.peekRecord('bike', '1'); assert.strictEqual(record.currentState.stateName, 'root.loaded.saved', 'We are loaded initially'); - assert.strictEqual(record.get('bike.name'), 'mr bike'); + assert.strictEqual(record.bike.name, 'mr bike'); // we test that we can sync call unloadRecord followed by findRecord let wait = run(() => { @@ -873,7 +873,7 @@ module('integration/unload - Unloading Records', function (hooks) { let wait = store.findRecord('person', '1').then((newRecord) => { assert.false(record.isDestroyed, 'the record is NOT YET destroyed'); - assert.strictEqual(newRecord.get('bike'), bike, 'the newRecord should retain knowledge of the bike'); + assert.strictEqual(newRecord.bike, bike, 'the newRecord should retain knowledge of the bike'); }); assert.false(record.isDestroyed, 'the record is NOT YET destroyed'); @@ -993,16 +993,16 @@ module('integration/unload - Unloading Records', function (hooks) { }); let adam = store.peekRecord('person', 1); - assert.strictEqual(adam.get('cars.length'), 0, 'cars hasMany starts off empty'); + assert.strictEqual(adam.cars.length, 0, 'cars hasMany starts off empty'); run(() => pushCar()); - assert.strictEqual(adam.get('cars.length'), 1, 'pushing car setups inverse relationship'); + assert.strictEqual(adam.cars.length, 1, 'pushing car setups inverse relationship'); - run(() => adam.get('cars.firstObject').unloadRecord()); - assert.strictEqual(adam.get('cars.length'), 0, 'unloading car cleaned up hasMany'); + run(() => adam.cars.firstObject.unloadRecord()); + assert.strictEqual(adam.cars.length, 0, 'unloading car cleaned up hasMany'); run(() => pushCar()); - assert.strictEqual(adam.get('cars.length'), 1, 'pushing car again setups inverse relationship'); + assert.strictEqual(adam.cars.length, 1, 'pushing car again setups inverse relationship'); }); test('1:1 sync unload', function (assert) { @@ -1032,12 +1032,12 @@ module('integration/unload - Unloading Records', function (hooks) { let person = store.peekRecord('person', 1); let house = store.peekRecord('house', 2); - assert.strictEqual(person.get('house.id'), '2', 'initially relationship established lhs'); - assert.strictEqual(house.get('person.id'), '1', 'initially relationship established rhs'); + assert.strictEqual(person.house.id, '2', 'initially relationship established lhs'); + assert.strictEqual(house.person.id, '1', 'initially relationship established rhs'); run(() => house.unloadRecord()); - assert.strictEqual(person.get('house'), null, 'unloading acts as a delete for sync relationships'); + assert.strictEqual(person.house, null, 'unloading acts as a delete for sync relationships'); assert.strictEqual(store.peekRecord('house', '2'), null, 'unloaded record gone from store'); house = run(() => @@ -1050,8 +1050,8 @@ module('integration/unload - Unloading Records', function (hooks) { ); assert.notStrictEqual(store.peekRecord('house', '2'), null, 'unloaded record can be restored'); - assert.strictEqual(person.get('house'), null, 'restoring unloaded record does not restore relationship'); - assert.strictEqual(house.get('person'), null, 'restoring unloaded record does not restore relationship'); + assert.strictEqual(person.house, null, 'restoring unloaded record does not restore relationship'); + assert.strictEqual(house.person, null, 'restoring unloaded record does not restore relationship'); run(() => store.push({ @@ -1070,8 +1070,8 @@ module('integration/unload - Unloading Records', function (hooks) { }) ); - assert.strictEqual(person.get('house.id'), '2', 'after unloading, relationship can be restored'); - assert.strictEqual(house.get('person.id'), '1', 'after unloading, relationship can be restored'); + assert.strictEqual(person.house.id, '2', 'after unloading, relationship can be restored'); + assert.strictEqual(house.person.id, '1', 'after unloading, relationship can be restored'); }); test('1:many sync unload 1 side', function (assert) { @@ -1111,19 +1111,19 @@ module('integration/unload - Unloading Records', function (hooks) { let person = store.peekRecord('person', 1); let car2 = store.peekRecord('car', 2); let car3 = store.peekRecord('car', 3); - let cars = person.get('cars'); + let cars = person.cars; assert.false(cars.isDestroyed, 'ManyArray not destroyed'); - assert.deepEqual(person.get('cars').mapBy('id'), ['2', '3'], 'initialy relationship established lhs'); - assert.strictEqual(car2.get('person.id'), '1', 'initially relationship established rhs'); - assert.strictEqual(car3.get('person.id'), '1', 'initially relationship established rhs'); + assert.deepEqual(person.cars.mapBy('id'), ['2', '3'], 'initialy relationship established lhs'); + assert.strictEqual(car2.person.id, '1', 'initially relationship established rhs'); + assert.strictEqual(car3.person.id, '1', 'initially relationship established rhs'); run(() => person.unloadRecord()); assert.strictEqual(store.peekRecord('person', '1'), null, 'unloaded record gone from store'); - assert.strictEqual(car2.get('person'), null, 'unloading acts as delete for sync relationships'); - assert.strictEqual(car3.get('person'), null, 'unloading acts as delete for sync relationships'); + assert.strictEqual(car2.person, null, 'unloading acts as delete for sync relationships'); + assert.strictEqual(car3.person, null, 'unloading acts as delete for sync relationships'); assert.true(cars.isDestroyed, 'ManyArray destroyed'); person = run(() => @@ -1136,9 +1136,9 @@ module('integration/unload - Unloading Records', function (hooks) { ); assert.notStrictEqual(store.peekRecord('person', '1'), null, 'unloaded record can be restored'); - assert.deepEqual(person.get('cars').mapBy('id'), [], 'restoring unloaded record does not restore relationship'); - assert.strictEqual(car2.get('person'), null, 'restoring unloaded record does not restore relationship'); - assert.strictEqual(car3.get('person'), null, 'restoring unloaded record does not restore relationship'); + assert.deepEqual(person.cars.mapBy('id'), [], 'restoring unloaded record does not restore relationship'); + assert.strictEqual(car2.person, null, 'restoring unloaded record does not restore relationship'); + assert.strictEqual(car3.person, null, 'restoring unloaded record does not restore relationship'); run(() => store.push({ @@ -1163,9 +1163,9 @@ module('integration/unload - Unloading Records', function (hooks) { }) ); - assert.strictEqual(car2.get('person.id'), '1', 'after unloading, relationship can be restored'); - assert.strictEqual(car3.get('person.id'), '1', 'after unloading, relationship can be restored'); - assert.deepEqual(person.get('cars').mapBy('id'), ['2', '3'], 'after unloading, relationship can be restored'); + assert.strictEqual(car2.person.id, '1', 'after unloading, relationship can be restored'); + assert.strictEqual(car3.person.id, '1', 'after unloading, relationship can be restored'); + assert.deepEqual(person.cars.mapBy('id'), ['2', '3'], 'after unloading, relationship can be restored'); }); test('1:many sync unload many side', function (assert) { @@ -1205,20 +1205,20 @@ module('integration/unload - Unloading Records', function (hooks) { let person = store.peekRecord('person', 1); let car2 = store.peekRecord('car', 2); let car3 = store.peekRecord('car', 3); - let cars = person.get('cars'); + let cars = person.cars; assert.false(cars.isDestroyed, 'ManyArray not destroyed'); - assert.deepEqual(person.get('cars').mapBy('id'), ['2', '3'], 'initialy relationship established lhs'); - assert.strictEqual(car2.get('person.id'), '1', 'initially relationship established rhs'); - assert.strictEqual(car3.get('person.id'), '1', 'initially relationship established rhs'); + assert.deepEqual(person.cars.mapBy('id'), ['2', '3'], 'initialy relationship established lhs'); + assert.strictEqual(car2.person.id, '1', 'initially relationship established rhs'); + assert.strictEqual(car3.person.id, '1', 'initially relationship established rhs'); run(() => car2.unloadRecord()); assert.strictEqual(store.peekRecord('car', '2'), null, 'unloaded record gone from store'); assert.false(cars.isDestroyed, 'ManyArray not destroyed'); - assert.deepEqual(person.get('cars').mapBy('id'), ['3'], 'unload sync relationship acts as delete'); - assert.strictEqual(car3.get('person.id'), '1', 'unloading one of a sync hasMany does not affect the rest'); + assert.deepEqual(person.cars.mapBy('id'), ['3'], 'unload sync relationship acts as delete'); + assert.strictEqual(car3.person.id, '1', 'unloading one of a sync hasMany does not affect the rest'); car2 = run(() => store.push({ @@ -1230,8 +1230,8 @@ module('integration/unload - Unloading Records', function (hooks) { ); assert.notStrictEqual(store.peekRecord('car', '2'), null, 'unloaded record can be restored'); - assert.deepEqual(person.get('cars').mapBy('id'), ['3'], 'restoring unloaded record does not restore relationship'); - assert.strictEqual(car2.get('person'), null, 'restoring unloaded record does not restore relationship'); + assert.deepEqual(person.cars.mapBy('id'), ['3'], 'restoring unloaded record does not restore relationship'); + assert.strictEqual(car2.person, null, 'restoring unloaded record does not restore relationship'); run(() => store.push({ @@ -1256,8 +1256,8 @@ module('integration/unload - Unloading Records', function (hooks) { }) ); - assert.strictEqual(car2.get('person.id'), '1', 'after unloading, relationship can be restored'); - assert.deepEqual(person.get('cars').mapBy('id'), ['2', '3'], 'after unloading, relationship can be restored'); + assert.strictEqual(car2.person.id, '1', 'after unloading, relationship can be restored'); + assert.deepEqual(person.cars.mapBy('id'), ['2', '3'], 'after unloading, relationship can be restored'); }); test('many:many sync unload', function (assert) { @@ -1318,13 +1318,13 @@ module('integration/unload - Unloading Records', function (hooks) { let person2 = store.peekRecord('person', 2); let group3 = store.peekRecord('group', 3); let group4 = store.peekRecord('group', 4); - let p2groups = person2.get('groups'); - let g3people = group3.get('people'); + let p2groups = person2.groups; + let g3people = group3.people; - assert.deepEqual(person1.get('groups').mapBy('id'), ['3', '4'], 'initially established relationship lhs'); - assert.deepEqual(person2.get('groups').mapBy('id'), ['3', '4'], 'initially established relationship lhs'); - assert.deepEqual(group3.get('people').mapBy('id'), ['1', '2'], 'initially established relationship lhs'); - assert.deepEqual(group4.get('people').mapBy('id'), ['1', '2'], 'initially established relationship lhs'); + assert.deepEqual(person1.groups.mapBy('id'), ['3', '4'], 'initially established relationship lhs'); + assert.deepEqual(person2.groups.mapBy('id'), ['3', '4'], 'initially established relationship lhs'); + assert.deepEqual(group3.people.mapBy('id'), ['1', '2'], 'initially established relationship lhs'); + assert.deepEqual(group4.people.mapBy('id'), ['1', '2'], 'initially established relationship lhs'); assert.false(p2groups.isDestroyed, 'groups is not destroyed'); assert.false(g3people.isDestroyed, 'people is not destroyed'); @@ -1335,12 +1335,12 @@ module('integration/unload - Unloading Records', function (hooks) { assert.false(g3people.isDestroyed, 'people (inverse) is not destroyed'); assert.deepEqual( - person1.get('groups').mapBy('id'), + person1.groups.mapBy('id'), ['3', '4'], 'unloaded record in many:many does not affect inverse of inverse' ); - assert.deepEqual(group3.get('people').mapBy('id'), ['1'], 'unloading acts as delete for sync relationships'); - assert.deepEqual(group4.get('people').mapBy('id'), ['1'], 'unloading acts as delete for sync relationships'); + assert.deepEqual(group3.people.mapBy('id'), ['1'], 'unloading acts as delete for sync relationships'); + assert.deepEqual(group4.people.mapBy('id'), ['1'], 'unloading acts as delete for sync relationships'); assert.strictEqual(store.peekRecord('person', '2'), null, 'unloading removes record from store'); @@ -1354,17 +1354,9 @@ module('integration/unload - Unloading Records', function (hooks) { ); assert.notStrictEqual(store.peekRecord('person', '2'), null, 'unloaded record can be restored'); - assert.deepEqual(person2.get('groups').mapBy('id'), [], 'restoring unloaded record does not restore relationship'); - assert.deepEqual( - group3.get('people').mapBy('id'), - ['1'], - 'restoring unloaded record does not restore relationship' - ); - assert.deepEqual( - group4.get('people').mapBy('id'), - ['1'], - 'restoring unloaded record does not restore relationship' - ); + assert.deepEqual(person2.groups.mapBy('id'), [], 'restoring unloaded record does not restore relationship'); + assert.deepEqual(group3.people.mapBy('id'), ['1'], 'restoring unloaded record does not restore relationship'); + assert.deepEqual(group4.people.mapBy('id'), ['1'], 'restoring unloaded record does not restore relationship'); run(() => store.push({ @@ -1389,9 +1381,9 @@ module('integration/unload - Unloading Records', function (hooks) { }) ); - assert.deepEqual(person2.get('groups').mapBy('id'), ['3', '4'], 'after unloading, relationship can be restored'); - assert.deepEqual(group3.get('people').mapBy('id'), ['1', '2'], 'after unloading, relationship can be restored'); - assert.deepEqual(group4.get('people').mapBy('id'), ['1', '2'], 'after unloading, relationship can be restored'); + assert.deepEqual(person2.groups.mapBy('id'), ['3', '4'], 'after unloading, relationship can be restored'); + assert.deepEqual(group3.people.mapBy('id'), ['1', '2'], 'after unloading, relationship can be restored'); + assert.deepEqual(group4.people.mapBy('id'), ['1', '2'], 'after unloading, relationship can be restored'); }); test('1:1 async unload', function (assert) { @@ -1429,11 +1421,10 @@ module('integration/unload - Unloading Records', function (hooks) { let mortgage; return run(() => - person - .get('mortgage') + person.mortgage .then((asyncRecord) => { mortgage = asyncRecord; - return mortgage.get('person'); + return mortgage.person; }) .then(() => { assert.strictEqual(mortgage.belongsTo('person').id(), '1', 'initially relationship established lhs'); @@ -1443,7 +1434,7 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(person.belongsTo('mortgage').id(), '2', 'unload async is not treated as delete'); - return person.get('mortgage'); + return person.mortgage; }) .then((refetchedMortgage) => { assert.notEqual(mortgage, refetchedMortgage, 'the previously loaded record is not reused'); @@ -1518,12 +1509,11 @@ module('integration/unload - Unloading Records', function (hooks) { let boats, boat2, boat3; return run(() => - person - .get('boats') + person.boats .then((asyncRecords) => { boats = asyncRecords; [boat2, boat3] = boats.toArray(); - return all([boat2, boat3].map((b) => b.get('person'))); + return all([boat2, boat3].map((b) => b.person)); }) .then(() => { assert.deepEqual(person.hasMany('boats').ids(), ['2', '3'], 'initially relationship established lhs'); @@ -1538,7 +1528,7 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(boat2.belongsTo('person').id(), '1', 'unload async is not treated as delete'); assert.strictEqual(boat3.belongsTo('person').id(), '1', 'unload async is not treated as delete'); - return boat2.get('person'); + return boat2.person; }) .then((refetchedPerson) => { assert.notEqual(person, refetchedPerson, 'the previously loaded record is not reused'); @@ -1756,13 +1746,13 @@ module('integration/unload - Unloading Records', function (hooks) { let person = store.peekRecord('person', 1); let book = store.peekRecord('book', 2); - return book.get('person').then(() => { - assert.strictEqual(person.get('favoriteBook.id'), '2', 'initially relationship established lhs'); + return book.person.then(() => { + assert.strictEqual(person.favoriteBook.id, '2', 'initially relationship established lhs'); assert.strictEqual(book.belongsTo('person').id(), '1', 'initially relationship established rhs'); run(() => book.unloadRecord()); - assert.strictEqual(person.get('book'), undefined, 'unloading acts as a delete for sync relationships'); + assert.strictEqual(person.book, undefined, 'unloading acts as a delete for sync relationships'); assert.strictEqual(store.peekRecord('book', '2'), null, 'unloaded record gone from store'); book = run(() => @@ -1775,7 +1765,7 @@ module('integration/unload - Unloading Records', function (hooks) { ); assert.notStrictEqual(store.peekRecord('book', '2'), null, 'unloaded record can be restored'); - assert.strictEqual(person.get('book'), undefined, 'restoring unloaded record does not restore relationship'); + assert.strictEqual(person.book, undefined, 'restoring unloaded record does not restore relationship'); assert.strictEqual( book.belongsTo('person').id(), null, @@ -1799,8 +1789,8 @@ module('integration/unload - Unloading Records', function (hooks) { }) ); - assert.strictEqual(person.get('favoriteBook.id'), '2', 'after unloading, relationship can be restored'); - assert.strictEqual(book.get('person.id'), '1', 'after unloading, relationship can be restored'); + assert.strictEqual(person.favoriteBook.id, '2', 'after unloading, relationship can be restored'); + assert.strictEqual(book.person.id, '1', 'after unloading, relationship can be restored'); }); }); @@ -1847,23 +1837,22 @@ module('integration/unload - Unloading Records', function (hooks) { let book = store.peekRecord('book', 2); return run(() => - book - .get('person') + book.person .then(() => { - assert.strictEqual(person.get('favoriteBook.id'), '2', 'initially relationship established lhs'); + assert.strictEqual(person.favoriteBook.id, '2', 'initially relationship established lhs'); assert.strictEqual(book.belongsTo('person').id(), '1', 'initially relationship established rhs'); run(() => person.unloadRecord()); assert.strictEqual(book.belongsTo('person').id(), '1', 'unload async is not treated as delete'); - return book.get('person'); + return book.person; }) .then((refetchedPerson) => { assert.notEqual(person, refetchedPerson, 'the previously loaded record is not reused'); assert.strictEqual(book.belongsTo('person').id(), '1', 'unload async is not treated as delete'); - assert.strictEqual(refetchedPerson.get('favoriteBook.id'), '2', 'unload async is not treated as delete'); + assert.strictEqual(refetchedPerson.favoriteBook.id, '2', 'unload async is not treated as delete'); assert.strictEqual(findRecordCalls, 1); }) ); @@ -1906,10 +1895,10 @@ module('integration/unload - Unloading Records', function (hooks) { let person = store.peekRecord('person', 1); let spoon2 = store.peekRecord('spoon', 2); let spoon3 = store.peekRecord('spoon', 3); - let spoons = person.get('favoriteSpoons'); + let spoons = person.favoriteSpoons; assert.false(spoons.isDestroyed, 'ManyArray not destroyed'); - assert.deepEqual(person.get('favoriteSpoons').mapBy('id'), ['2', '3'], 'initialy relationship established lhs'); + assert.deepEqual(person.favoriteSpoons.mapBy('id'), ['2', '3'], 'initialy relationship established lhs'); assert.strictEqual(spoon2.belongsTo('person').id(), '1', 'initially relationship established rhs'); assert.strictEqual(spoon3.belongsTo('person').id(), '1', 'initially relationship established rhs'); @@ -1918,7 +1907,7 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(store.peekRecord('spoon', '2'), null, 'unloaded record gone from store'); assert.false(spoons.isDestroyed, 'ManyArray not destroyed'); - assert.deepEqual(person.get('favoriteSpoons').mapBy('id'), ['3'], 'unload sync relationship acts as delete'); + assert.deepEqual(person.favoriteSpoons.mapBy('id'), ['3'], 'unload sync relationship acts as delete'); assert.strictEqual( spoon3.belongsTo('person').id(), '1', @@ -1936,7 +1925,7 @@ module('integration/unload - Unloading Records', function (hooks) { assert.notStrictEqual(store.peekRecord('spoon', '2'), null, 'unloaded record can be restored'); assert.deepEqual( - person.get('favoriteSpoons').mapBy('id'), + person.favoriteSpoons.mapBy('id'), ['3'], 'restoring unloaded record does not restore relationship' ); @@ -1970,11 +1959,7 @@ module('integration/unload - Unloading Records', function (hooks) { ); assert.strictEqual(spoon2.belongsTo('person').id(), '1', 'after unloading, relationship can be restored'); - assert.deepEqual( - person.get('favoriteSpoons').mapBy('id'), - ['2', '3'], - 'after unloading, relationship can be restored' - ); + assert.deepEqual(person.favoriteSpoons.mapBy('id'), ['2', '3'], 'after unloading, relationship can be restored'); }); test('1 async : many sync unload async side', async function (assert) { @@ -2029,9 +2014,9 @@ module('integration/unload - Unloading Records', function (hooks) { ); let spoon2 = store.peekRecord('spoon', 2); let spoon3 = store.peekRecord('spoon', 3); - let spoons = person.get('favoriteSpoons'); + let spoons = person.favoriteSpoons; - assert.deepEqual(person.get('favoriteSpoons').mapBy('id'), ['2', '3'], 'initially relationship established lhs'); + assert.deepEqual(person.favoriteSpoons.mapBy('id'), ['2', '3'], 'initially relationship established lhs'); assert.strictEqual(spoon2.belongsTo('person').id(), '1', 'initially relationship established rhs'); assert.strictEqual(spoon3.belongsTo('person').id(), '1', 'initially relationship established rhs'); @@ -2043,11 +2028,11 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(spoon2.belongsTo('person').id(), '1', 'unload async is not treated as delete'); assert.strictEqual(spoon3.belongsTo('person').id(), '1', 'unload async is not treated as delete'); - const refetchedPerson = await spoon2.get('person'); + const refetchedPerson = await spoon2.person; assert.notEqual(person, refetchedPerson, 'the previously loaded record is not reused'); - assert.deepEqual(person.get('favoriteSpoons').mapBy('id'), ['2', '3'], 'unload async is not treated as delete'); + assert.deepEqual(person.favoriteSpoons.mapBy('id'), ['2', '3'], 'unload async is not treated as delete'); assert.strictEqual(spoon2.belongsTo('person').id(), '1', 'unload async is not treated as delete'); assert.strictEqual(spoon3.belongsTo('person').id(), '1', 'unload async is not treated as delete'); @@ -2103,8 +2088,8 @@ module('integration/unload - Unloading Records', function (hooks) { const [show2, show3] = shows.toArray(); assert.deepEqual(person.hasMany('favoriteShows').ids(), ['2', '3'], 'initially relationship established lhs'); - assert.strictEqual(show2.get('person.id'), '1', 'initially relationship established rhs'); - assert.strictEqual(show3.get('person.id'), '1', 'initially relationship established rhs'); + assert.strictEqual(show2.person.id, '1', 'initially relationship established rhs'); + assert.strictEqual(show3.person.id, '1', 'initially relationship established rhs'); assert.deepEqual(shows.mapBy('id'), ['2', '3'], 'many array is initially set up correctly'); show2.unloadRecord(); @@ -2175,15 +2160,14 @@ module('integration/unload - Unloading Records', function (hooks) { let shows, show2, show3; return run(() => - person - .get('favoriteShows') + person.favoriteShows .then((asyncRecords) => { shows = asyncRecords; [show2, show3] = shows.toArray(); assert.deepEqual(person.hasMany('favoriteShows').ids(), ['2', '3'], 'initially relationship established lhs'); - assert.strictEqual(show2.get('person.id'), '1', 'initially relationship established rhs'); - assert.strictEqual(show3.get('person.id'), '1', 'initially relationship established rhs'); + assert.strictEqual(show2.person.id, '1', 'initially relationship established rhs'); + assert.strictEqual(show3.person.id, '1', 'initially relationship established rhs'); assert.deepEqual(shows.mapBy('id'), ['2', '3'], 'many array is initially set up correctly'); run(() => person.unloadRecord()); @@ -2191,8 +2175,8 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(store.peekRecord('person', '1'), null, 'unloaded record gone from store'); assert.true(shows.isDestroyed, 'previous manyarray immediately destroyed'); - assert.strictEqual(show2.get('person.id'), undefined, 'unloading acts as delete for sync relationships'); - assert.strictEqual(show3.get('person.id'), undefined, 'unloading acts as delete for sync relationships'); + assert.strictEqual(show2.person.id, undefined, 'unloading acts as delete for sync relationships'); + assert.strictEqual(show3.person.id, undefined, 'unloading acts as delete for sync relationships'); person = run(() => store.push({ @@ -2209,16 +2193,8 @@ module('integration/unload - Unloading Records', function (hooks) { [], 'restoring unloaded record does not restore relationship' ); - assert.strictEqual( - show2.get('person.id'), - undefined, - 'restoring unloaded record does not restore relationship' - ); - assert.strictEqual( - show3.get('person.id'), - undefined, - 'restoring unloaded record does not restore relationship' - ); + assert.strictEqual(show2.person.id, undefined, 'restoring unloaded record does not restore relationship'); + assert.strictEqual(show3.person.id, undefined, 'restoring unloaded record does not restore relationship'); run(() => store.push({ @@ -2245,7 +2221,7 @@ module('integration/unload - Unloading Records', function (hooks) { assert.deepEqual(person.hasMany('favoriteShows').ids(), ['2', '3'], 'relationship can be restored'); - return person.get('favoriteShows'); + return person.favoriteShows; }) .then((refetchedShows) => { assert.notEqual(refetchedShows, shows, 'ManyArray not reused'); @@ -2314,8 +2290,7 @@ module('integration/unload - Unloading Records', function (hooks) { let boats, boat2, boat3; return run(() => - person - .get('boats') + person.boats .then((asyncRecords) => { boats = asyncRecords; [boat2, boat3] = boats.toArray(); @@ -2328,13 +2303,13 @@ module('integration/unload - Unloading Records', function (hooks) { isUnloaded = true; run(() => { boat2.unloadRecord(); - person.get('boats'); + person.boats; }); assert.deepEqual(boats.mapBy('id'), ['3'], 'unloaded boat is removed from ManyArray'); }) .then(() => { - return run(() => person.get('boats')); + return run(() => person.boats); }) .then((newBoats) => { assert.strictEqual(newBoats.length, 1, 'new ManyArray has only 1 boat after unload'); diff --git a/packages/-ember-data/tests/integration/references/has-many-test.js b/packages/-ember-data/tests/integration/references/has-many-test.js index ade9edae091..459608d8a83 100755 --- a/packages/-ember-data/tests/integration/references/has-many-test.js +++ b/packages/-ember-data/tests/integration/references/has-many-test.js @@ -292,8 +292,8 @@ module('integration/references/has-many', function (hooks) { personsReference.push(data).then(function (records) { assert.ok(records instanceof DS.ManyArray, 'push resolves with the referenced records'); assert.strictEqual(get(records, 'length'), 2); - assert.strictEqual(records.objectAt(0).get('name'), 'Vito'); - assert.strictEqual(records.objectAt(1).get('name'), 'Michael'); + assert.strictEqual(records.objectAt(0).name, 'Vito'); + assert.strictEqual(records.objectAt(1).name, 'Michael'); done(); }); @@ -326,7 +326,7 @@ module('integration/references/has-many', function (hooks) { personsReference.push(data).then(function (records) { assert.ok(records instanceof DS.ManyArray, 'push resolves with the referenced records'); assert.strictEqual(get(records, 'length'), 1); - assert.strictEqual(records.objectAt(0).get('name'), 'Vito'); + assert.strictEqual(records.objectAt(0).name, 'Vito'); done(); }); @@ -384,8 +384,8 @@ module('integration/references/has-many', function (hooks) { personsReference.push(payload).then(function (records) { assert.ok(records instanceof DS.ManyArray, 'push resolves with the referenced records'); assert.strictEqual(get(records, 'length'), 2); - assert.strictEqual(records.objectAt(0).get('name'), 'Vito'); - assert.strictEqual(records.objectAt(1).get('name'), 'Michael'); + assert.strictEqual(records.objectAt(0).name, 'Vito'); + assert.strictEqual(records.objectAt(1).name, 'Michael'); done(); }); @@ -427,8 +427,8 @@ module('integration/references/has-many', function (hooks) { const records = await pushResult; assert.ok(records instanceof DS.ManyArray, 'push resolves with the referenced records'); assert.strictEqual(get(records, 'length'), 2); - assert.strictEqual(records.objectAt(0).get('name'), 'Vito'); - assert.strictEqual(records.objectAt(1).get('name'), 'Michael'); + assert.strictEqual(records.objectAt(0).name, 'Vito'); + assert.strictEqual(records.objectAt(1).name, 'Michael'); }); test('push valid json:api', async function (assert) { @@ -461,8 +461,8 @@ module('integration/references/has-many', function (hooks) { const records = await pushResult; assert.ok(records instanceof DS.ManyArray, 'push resolves with the referenced records'); assert.strictEqual(get(records, 'length'), 2); - assert.strictEqual(records.objectAt(0).get('name'), 'Vito'); - assert.strictEqual(records.objectAt(1).get('name'), 'Michael'); + assert.strictEqual(records.objectAt(0).name, 'Vito'); + assert.strictEqual(records.objectAt(1).name, 'Michael'); }); test('value() returns null when reference is not yet loaded', function (assert) { @@ -613,8 +613,8 @@ module('integration/references/has-many', function (hooks) { personsReference.load({ adapterOptions }).then(function (records) { assert.ok(records instanceof DS.ManyArray, 'push resolves with the referenced records'); assert.strictEqual(get(records, 'length'), 2); - assert.strictEqual(records.objectAt(0).get('name'), 'Vito'); - assert.strictEqual(records.objectAt(1).get('name'), 'Michael'); + assert.strictEqual(records.objectAt(0).name, 'Vito'); + assert.strictEqual(records.objectAt(1).name, 'Michael'); done(); }); @@ -663,8 +663,8 @@ module('integration/references/has-many', function (hooks) { personsReference.load({ adapterOptions }).then(function (records) { assert.ok(records instanceof DS.ManyArray, 'push resolves with the referenced records'); assert.strictEqual(get(records, 'length'), 2); - assert.strictEqual(records.objectAt(0).get('name'), 'Vito'); - assert.strictEqual(records.objectAt(1).get('name'), 'Michael'); + assert.strictEqual(records.objectAt(0).name, 'Vito'); + assert.strictEqual(records.objectAt(1).name, 'Michael'); done(); }); @@ -806,8 +806,8 @@ module('integration/references/has-many', function (hooks) { personsReference.reload({ adapterOptions }).then(function (records) { assert.ok(records instanceof DS.ManyArray, 'push resolves with the referenced records'); assert.strictEqual(get(records, 'length'), 2); - assert.strictEqual(records.objectAt(0).get('name'), 'Vito Coreleone'); - assert.strictEqual(records.objectAt(1).get('name'), 'Michael Coreleone'); + assert.strictEqual(records.objectAt(0).name, 'Vito Coreleone'); + assert.strictEqual(records.objectAt(1).name, 'Michael Coreleone'); done(); }); @@ -872,8 +872,8 @@ module('integration/references/has-many', function (hooks) { .then(function (records) { assert.ok(records instanceof DS.ManyArray, 'push resolves with the referenced records'); assert.strictEqual(get(records, 'length'), 2); - assert.strictEqual(records.objectAt(0).get('name'), 'Vito Coreleone'); - assert.strictEqual(records.objectAt(1).get('name'), 'Michael Coreleone'); + assert.strictEqual(records.objectAt(0).name, 'Vito Coreleone'); + assert.strictEqual(records.objectAt(1).name, 'Michael Coreleone'); done(); }); diff --git a/packages/-ember-data/tests/integration/relationships/belongs-to-test.js b/packages/-ember-data/tests/integration/relationships/belongs-to-test.js index 09728cb189f..8757e5d8801 100644 --- a/packages/-ember-data/tests/integration/relationships/belongs-to-test.js +++ b/packages/-ember-data/tests/integration/relationships/belongs-to-test.js @@ -191,7 +191,7 @@ module('integration/relationship/belongs-to BelongsTo Relationships (new-style)' let person = await store.findRecord('person', '1'); let petRequest = store.findRecord('pet', '1'); - let personPetRequest = person.get('bestDog'); + let personPetRequest = person.bestDog; let personPet = await personPetRequest; let pet = await petRequest; @@ -236,31 +236,31 @@ module('integration/relationship/belongs-to BelongsTo Relationships (new-style)' let shen = store.peekRecord('pet', '1'); let pirate = store.peekRecord('pet', '2'); - let bestDog = await chris.get('bestDog'); + let bestDog = await chris.bestDog; - assert.strictEqual(shen.get('bestHuman'), null, 'precond - Shen has no best human'); - assert.strictEqual(pirate.get('bestHuman'), null, 'precond - pirate has no best human'); + assert.strictEqual(shen.bestHuman, null, 'precond - Shen has no best human'); + assert.strictEqual(pirate.bestHuman, null, 'precond - pirate has no best human'); assert.strictEqual(bestDog, null, 'precond - Chris has no best dog'); chris.set('bestDog', shen); - bestDog = await chris.get('bestDog'); + bestDog = await chris.bestDog; - assert.strictEqual(shen.get('bestHuman'), chris, "scene 1 - Chris is Shen's best human"); - assert.strictEqual(pirate.get('bestHuman'), null, 'scene 1 - pirate has no best human'); + assert.strictEqual(shen.bestHuman, chris, "scene 1 - Chris is Shen's best human"); + assert.strictEqual(pirate.bestHuman, null, 'scene 1 - pirate has no best human'); assert.strictEqual(bestDog, shen, "scene 1 - Shen is Chris's best dog"); chris.set('bestDog', pirate); - bestDog = await chris.get('bestDog'); + bestDog = await chris.bestDog; - assert.strictEqual(shen.get('bestHuman'), null, "scene 2 - Chris is no longer Shen's best human"); - assert.strictEqual(pirate.get('bestHuman'), chris, 'scene 2 - pirate now has Chris as best human'); + assert.strictEqual(shen.bestHuman, null, "scene 2 - Chris is no longer Shen's best human"); + assert.strictEqual(pirate.bestHuman, chris, 'scene 2 - pirate now has Chris as best human'); assert.strictEqual(bestDog, pirate, "scene 2 - Pirate is now Chris's best dog"); chris.set('bestDog', null); - bestDog = await chris.get('bestDog'); + bestDog = await chris.bestDog; - assert.strictEqual(shen.get('bestHuman'), null, "scene 3 - Chris remains no longer Shen's best human"); - assert.strictEqual(pirate.get('bestHuman'), null, 'scene 3 - pirate no longer has Chris as best human'); + assert.strictEqual(shen.bestHuman, null, "scene 3 - Chris remains no longer Shen's best human"); + assert.strictEqual(pirate.bestHuman, null, 'scene 3 - pirate no longer has Chris as best human'); assert.strictEqual(bestDog, null, 'scene 3 - Chris has no best dog'); }); }); @@ -389,8 +389,8 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function const app = store.peekRecord('app', '1'); const team = store.peekRecord('team', '1'); - assert.strictEqual(app.get('team.id'), team.get('id'), 'sets team correctly on app'); - assert.deepEqual(team.get('apps').toArray().mapBy('id'), ['1'], 'sets apps correctly on team'); + assert.strictEqual(app.team.id, team.id, 'sets team correctly on app'); + assert.deepEqual(team.apps.toArray().mapBy('id'), ['1'], 'sets apps correctly on team'); adapter.shouldBackgroundReloadRecord = () => false; adapter.updateRecord = (store, type, snapshot) => { @@ -413,8 +413,8 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function return run(() => { app.set('name', 'Hello'); return app.save().then(() => { - assert.strictEqual(app.get('team.id'), undefined, 'team removed from app relationship'); - assert.deepEqual(team.get('apps').toArray().mapBy('id'), [], 'app removed from team apps relationship'); + assert.strictEqual(app.team.id, undefined, 'team removed from app relationship'); + assert.deepEqual(team.apps.toArray().mapBy('id'), [], 'app removed from team apps relationship'); }); }); }); @@ -466,7 +466,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function return run(() => { return store.findRecord('post', 1).then((post) => { - post.get('user'); + post.user; }); }); }); @@ -493,7 +493,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }, }, }); - post.get('user'); + post.user; }); }, `Assertion Failed: Encountered a relationship identifier without an id for the belongsTo relationship 'user' on , expected a json-api identifier but found '{"id":null,"type":"user"}'. Please check your serializer and make sure it is serializing the relationship payload into a JSON API format.`); @@ -514,7 +514,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }, }, }); - post.get('user'); + post.user; }); }, `Assertion Failed: Encountered a relationship identifier without a type for the belongsTo relationship 'user' on , expected a json-api identifier with type 'user' but found '{"id":"1","type":null}'. Please check your serializer and make sure it is serializing the relationship payload into a JSON API format.`); }); @@ -652,7 +652,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function message: store.findRecord('post', 1), comment: store.findRecord('comment', 2), }).then((records) => { - assert.strictEqual(records.comment.get('message'), records.message); + assert.strictEqual(records.comment.message, records.message); }); }); }); @@ -760,11 +760,11 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function return store .findRecord('person', 1) .then((person) => { - return person.get('group'); + return person.group; }) .then((group) => { assert.ok(group instanceof Group, 'A group object is loaded'); - assert.strictEqual(group.get('id'), '1', 'It is the group we are expecting'); + assert.strictEqual(group.id, '1', 'It is the group we are expecting'); }); }); }); @@ -812,11 +812,11 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function return run(() => { return store.findRecord('person', 1).then((person) => { - return person.get('seat').then((seat) => { + return person.seat.then((seat) => { // this assertion fails too - // ok(seat.get('person') === person, 'parent relationship should be populated'); + // ok(seat.person === person, 'parent relationship should be populated'); seat.set('person', person); - assert.ok(person.get('seat').then, 'seat should be a PromiseObject'); + assert.ok(person.seat.then, 'seat should be a PromiseObject'); }); }); }); @@ -868,7 +868,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function return store .findRecord('person', '1') .then((person) => { - return person.get('group'); + return person.group; }) .then((group) => { assert.strictEqual(group, null, 'group should be null'); @@ -908,7 +908,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function let person = store.createRecord('person', { group: groupPromise, }); - assert.strictEqual(person.get('group.content'), group); + assert.strictEqual(person.group.content, group); }); }); @@ -923,7 +923,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function igor.set('favouriteMessage', post); - assert.strictEqual(igor.get('favouriteMessage.title'), "Igor's unimaginative blog post"); + assert.strictEqual(igor.favouriteMessage.title, "Igor's unimaginative blog post"); }); }); @@ -1132,7 +1132,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }); assert.expectAssertion(() => { - message.get('user'); + message.user; }, /You looked up the 'user' relationship on a 'message' with id 1 but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async \(`belongsTo\({ async: true }\)`\)/); }); @@ -1178,7 +1178,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function author.deleteRecord(); author.rollbackAttributes(); - return book.get('author').then((fetchedAuthor) => { + return book.author.then((fetchedAuthor) => { assert.strictEqual(fetchedAuthor, author, 'Book has an author after rollback attributes'); }); }); @@ -1223,7 +1223,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function author.rollbackAttributes(); }); - assert.strictEqual(book.get('author'), author, 'Book has an author after rollback attributes'); + assert.strictEqual(book.author, author, 'Book has an author after rollback attributes'); }); testInDebug('Passing a model as type to belongsTo should not work', function (assert) { @@ -1460,7 +1460,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function run(() => { user = store.createRecord('user'); - user.get('favouriteMessage'); + user.favouriteMessage; assert.ok( hasRelationshipForRecord(user, 'favouriteMessage'), 'Newly created record with relationships in params passed in its constructor should have relationships' @@ -1507,8 +1507,8 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }, }); - return book.get('author').then((author) => { - assert.strictEqual(author.get('name'), 'This is author', 'author name is correct'); + return book.author.then((author) => { + assert.strictEqual(author.name, 'This is author', 'author name is correct'); }); }); }); @@ -1556,9 +1556,9 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }, }); - const author = await book.get('author'); + const author = await book.author; - assert.strictEqual(author.get('name'), 'This is author', 'author name is correct'); + assert.strictEqual(author.name, 'This is author', 'author name is correct'); }); test('Relationship data should take precedence over related link when local record data is available', function (assert) { @@ -1608,8 +1608,8 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function ], }); - return book.get('author').then((author) => { - assert.strictEqual(author.get('name'), 'This is author', 'author name is correct'); + return book.author.then((author) => { + assert.strictEqual(author.name, 'This is author', 'author name is correct'); }); }); }); @@ -1673,8 +1673,8 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }, }); - book.get('author').then((author) => { - assert.strictEqual(author.get('name'), 'This is author', 'author name is correct'); + book.author.then((author) => { + assert.strictEqual(author.name, 'This is author', 'author name is correct'); }); }); }); @@ -1735,10 +1735,9 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function ], }); - return book - .get('author') + return book.author .then((author) => { - assert.strictEqual(author.get('name'), 'This is author', 'author name is correct'); + assert.strictEqual(author.name, 'This is author', 'author name is correct'); }) .then(() => { store.push({ @@ -1755,8 +1754,8 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }, }); - return book.get('author').then((author) => { - assert.strictEqual(author.get('name'), 'This is updated author', 'author name is correct'); + return book.author.then((author) => { + assert.strictEqual(author.name, 'This is updated author', 'author name is correct'); }); }); }); @@ -1808,10 +1807,9 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function ], }); - return book - .get('author') + return book.author .then((author) => { - assert.strictEqual(author.get('name'), 'This is author', 'author name is correct'); + assert.strictEqual(author.name, 'This is author', 'author name is correct'); }) .then(() => { store.push({ @@ -1828,8 +1826,8 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }, }); - return book.get('author').then((author) => { - assert.strictEqual(author.get('name'), 'This is author', 'author name is correct'); + return book.author.then((author) => { + assert.strictEqual(author.name, 'This is author', 'author name is correct'); }); }); }); @@ -1877,10 +1875,10 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function .then((_chapter) => { chapter = _chapter; - return chapter.get('book'); + return chapter.book; }) .then((book) => { - assert.strictEqual(book.get('name'), 'book title'); + assert.strictEqual(book.name, 'book title'); adapter.findBelongsTo = function () { return resolve({ @@ -1895,7 +1893,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function return chapter.belongsTo('book').reload(); }) .then((book) => { - assert.strictEqual(book.get('name'), 'updated book title'); + assert.strictEqual(book.name, 'updated book title'); }); }); }); @@ -1939,14 +1937,14 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }; return run(() => { - let book = chapter.get('book'); - assert.strictEqual(book.get('name'), 'book title'); + let book = chapter.book; + assert.strictEqual(book.name, 'book title'); return chapter .belongsTo('book') .reload() .then(function (book) { - assert.strictEqual(book.get('name'), 'updated book title'); + assert.strictEqual(book.name, 'updated book title'); }); }); }); @@ -1987,10 +1985,9 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }; return run(() => { - return chapter - .get('book') + return chapter.book .then((book) => { - assert.strictEqual(book.get('name'), 'book title'); + assert.strictEqual(book.name, 'book title'); adapter.findRecord = function () { return resolve({ @@ -2005,7 +2002,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function return chapter.belongsTo('book').reload(); }) .then((book) => { - assert.strictEqual(book.get('name'), 'updated book title'); + assert.strictEqual(book.name, 'updated book title'); }); }); }); @@ -2026,7 +2023,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }, }, }); - chapter.get('book'); + chapter.book; }); }, /Encountered a relationship identifier without a type for the belongsTo relationship 'book' on , expected a json-api identifier with type 'book'/); }); @@ -2064,7 +2061,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }); run(() => { - chapter.get('book'); + chapter.book; }); assert.strictEqual(count, 0); diff --git a/packages/-ember-data/tests/integration/relationships/has-many-test.js b/packages/-ember-data/tests/integration/relationships/has-many-test.js index b82feb2e110..839e58c78ce 100644 --- a/packages/-ember-data/tests/integration/relationships/has-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/has-many-test.js @@ -166,7 +166,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - post.get('comments'); + post.comments; }); }, `Assertion Failed: Encountered a relationship identifier without an id for the hasMany relationship 'comments' on , expected a json-api identifier but found '{"id":null,"type":"comment"}'. Please check your serializer and make sure it is serializing the relationship payload into a JSON API format.`); @@ -184,7 +184,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }, }); - post.get('comments'); + post.comments; }); }, `Assertion Failed: Encountered a relationship identifier without a type for the hasMany relationship 'comments' on , expected a json-api identifier with type 'comment' but found '{"id":"1","type":null}'. Please check your serializer and make sure it is serializing the relationship payload into a JSON API format.`); }); @@ -225,7 +225,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); return store.findRecord('post', 1).then((post) => { - return post.get('comments'); + return post.comments; }); }); }); @@ -295,7 +295,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( contacts.addObject(store.createRecord('user', { id: '7' })); assert.deepEqual( - contacts.map((c) => c.get('id')), + contacts.map((c) => c.id), ['2', '3', '4', '5', '6', '7'], 'user should have expected contacts' ); @@ -304,11 +304,11 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( await store.peekRecord('user', 6).destroyRecord(); assert.deepEqual( - contacts.map((c) => c.get('id')), + contacts.map((c) => c.id), ['3', '4', '5', '7'], `user's contacts should have expected contacts` ); - assert.strictEqual(contacts, user.get('contacts')); + assert.strictEqual(contacts, user.contacts); assert.ok(!user.contacts.initialState || !user.contacts.initialState.find((model) => model.id === '2')); @@ -317,11 +317,11 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); assert.deepEqual( - contacts.map((c) => c.get('id')), + contacts.map((c) => c.id), ['3', '4', '5', '7', '8'], `user's contacts should have expected contacts` ); - assert.strictEqual(contacts, user.get('contacts')); + assert.strictEqual(contacts, user.contacts); }); test('hasMany + canonical vs currentState + unloadRecord', function (assert) { @@ -372,14 +372,14 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, ], }); - let contacts = user.get('contacts'); + let contacts = user.contacts; store.adapterFor('user').deleteRecord = function () { return { data: { type: 'user', id: 2 } }; }; assert.deepEqual( - contacts.map((c) => c.get('id')), + contacts.map((c) => c.id), ['2', '3', '4'], 'user should have expected contacts' ); @@ -391,7 +391,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); assert.deepEqual( - contacts.map((c) => c.get('id')), + contacts.map((c) => c.id), ['2', '3', '4', '5', '6', '7'], 'user should have expected contacts' ); @@ -400,19 +400,19 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( store.peekRecord('user', 6).unloadRecord(); assert.deepEqual( - contacts.map((c) => c.get('id')), + contacts.map((c) => c.id), ['3', '4', '5', '7'], `user's contacts should have expected contacts` ); - assert.strictEqual(contacts, user.get('contacts')); + assert.strictEqual(contacts, user.contacts); contacts.addObject(store.createRecord('user', { id: 8 })); assert.deepEqual( - contacts.map((c) => c.get('id')), + contacts.map((c) => c.id), ['3', '4', '5', '7', '8'], `user's contacts should have expected contacts` ); - assert.strictEqual(contacts, user.get('contacts')); + assert.strictEqual(contacts, user.contacts); }); test('adapter.findMany only gets unique IDs even if duplicate IDs are present in the hasMany relationship', function (assert) { @@ -458,7 +458,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); return store.findRecord('book', 1).then((book) => { - return book.get('chapters'); + return book.chapters; }); }); }); @@ -531,12 +531,12 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( return store .findRecord('post', 1) .then((post) => { - return post.get('comments'); + return post.comments; }) .then((comments) => { - assert.true(comments.get('isLoaded'), 'comments are loaded'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); - assert.strictEqual(comments.objectAt(0).get('body'), 'First', 'comment loaded successfully'); + assert.true(comments.isLoaded, 'comments are loaded'); + assert.strictEqual(comments.length, 2, 'comments have 2 length'); + assert.strictEqual(comments.objectAt(0).body, 'First', 'comment loaded successfully'); }); }); }); @@ -667,7 +667,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); return run(() => { - return post.get('comments').then(() => { + return post.comments.then(() => { store.push({ data: { type: 'comment', @@ -680,7 +680,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - return post.get('comments').then(() => { + return post.comments.then(() => { assert.ok(true, 'Promise was called'); }); }); @@ -730,16 +730,15 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( let post = store.createRecord('post', {}); store.createRecord('comment', { message: post }); - return post - .get('comments') + return post.comments .then((comments) => { - assert.strictEqual(comments.get('length'), 1, 'initially we have one comment'); + assert.strictEqual(comments.length, 1, 'initially we have one comment'); return post.save(); }) - .then(() => post.get('comments')) + .then(() => post.comments) .then((comments) => { - assert.strictEqual(comments.get('length'), 1, 'after saving, we still have one comment'); + assert.strictEqual(comments.length, 1, 'after saving, we still have one comment'); }); }); }); @@ -790,15 +789,14 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( let post = store.createRecord('post', {}); store.createRecord('comment', { message: post }); - return post - .get('comments') + return post.comments .then((comments) => { - assert.strictEqual(comments.get('length'), 1); + assert.strictEqual(comments.length, 1); return post.save(); }) - .then(() => post.get('comments')) + .then(() => post.comments) .then((comments) => { - assert.strictEqual(comments.get('length'), 2); + assert.strictEqual(comments.length, 2); }); }); }); @@ -836,11 +834,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( // relationship of post let localComment = store.createRecord('comment', { id: 'local', message: post }); - return post - .get('comments') + return post.comments .then((comments) => { - assert.strictEqual(comments.get('length'), 1); - assert.true(localComment.get('isNew')); + assert.strictEqual(comments.length, 1); + assert.true(localComment.isNew); return post.save(); }) @@ -869,10 +866,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); }) - .then(() => post.get('comments')) + .then(() => post.comments) .then((comments) => { - assert.strictEqual(comments.get('length'), 1); - assert.true(localComment.get('isNew')); + assert.strictEqual(comments.length, 1); + assert.true(localComment.isNew); }); }); }); @@ -931,8 +928,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( let post = await store.findRecord('post', 1); let comments = await post.comments; - assert.true(comments.get('isLoaded'), 'comments are loaded'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); + assert.true(comments.isLoaded, 'comments are loaded'); + assert.strictEqual(comments.length, 2, 'comments have 2 length'); adapter.findHasMany = function (store, snapshot, link, relationship) { assert.strictEqual(relationship.type, 'comment', 'findHasMany relationship type was Comment'); @@ -1000,9 +997,9 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( store .findRecord('post', '1') .then(function (post) { - let comments = post.get('comments'); - assert.true(comments.get('isLoaded'), 'comments are loaded'); - assert.strictEqual(comments.get('length'), 2, 'comments have a length of 2'); + let comments = post.comments; + assert.true(comments.isLoaded, 'comments are loaded'); + assert.strictEqual(comments.length, 2, 'comments have a length of 2'); adapter.findMany = function (store, type, ids, snapshots) { return resolve({ @@ -1016,7 +1013,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( return comments.reload(); }) .then(function (newComments) { - assert.strictEqual(newComments.get('firstObject.body'), 'FirstUpdated', 'Record body was correctly updated'); + assert.strictEqual(newComments.firstObject.body, 'FirstUpdated', 'Record body was correctly updated'); }); }); }); @@ -1076,11 +1073,11 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( store .findRecord('post', 1) .then(function (post) { - return post.get('comments'); + return post.comments; }) .then(function (comments) { - assert.true(comments.get('isLoaded'), 'comments are loaded'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); + assert.true(comments.isLoaded, 'comments are loaded'); + assert.strictEqual(comments.length, 2, 'comments have 2 length'); adapter.findMany = function (store, type, ids, snapshots) { return resolve({ @@ -1094,7 +1091,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( return comments.reload(); }) .then(function (newComments) { - assert.strictEqual(newComments.get('firstObject.body'), 'FirstUpdated', 'Record body was correctly updated'); + assert.strictEqual(newComments.firstObject.body, 'FirstUpdated', 'Record body was correctly updated'); }); }); }); @@ -1172,11 +1169,11 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( assert.ok(true, `An error was thrown on the second reload via manyArray: ${e.message}`); } - assert.true(manyArray.get('isLoaded'), 'the second reload failed, comments are still loaded though'); + assert.true(manyArray.isLoaded, 'the second reload failed, comments are still loaded though'); let reloadedManyArray = await manyArray.reload(); - assert.true(reloadedManyArray.get('isLoaded'), 'the third reload worked, comments are loaded again'); + assert.true(reloadedManyArray.isLoaded, 'the third reload worked, comments are loaded again'); assert.strictEqual(reloadedManyArray, manyArray, 'the many array stays the same'); assert.strictEqual(loadingCount, 4, 'We only fired 4 requests'); }); @@ -1233,14 +1230,11 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }; run(function () { store.findRecord('post', 1).then(function (post) { - return post - .get('comments') - .reload() - .then(function (comments) { - assert.true(comments.get('isLoaded'), 'comments are loaded'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); - assert.strictEqual(comments.get('firstObject.body'), 'FirstUpdated', 'Record body was correctly updated'); - }); + return post.comments.reload().then(function (comments) { + assert.true(comments.isLoaded, 'comments are loaded'); + assert.strictEqual(comments.length, 2, 'comments have 2 length'); + assert.strictEqual(comments.firstObject.body, 'FirstUpdated', 'Record body was correctly updated'); + }); }); }); }); @@ -1296,7 +1290,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }; run(function () { store.findRecord('post', 1).then(function (post) { - post.get('comments').then(function (comments) { + post.comments.then(function (comments) { all([comments.reload(), comments.reload(), comments.reload()]).then(function (comments) { assert.strictEqual( count, @@ -1363,14 +1357,11 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( run(function () { store.findRecord('post', 1).then(function (post) { - return post - .get('comments') - .reload() - .then(function (comments) { - assert.true(comments.get('isLoaded'), 'comments are loaded'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); - assert.strictEqual(comments.get('firstObject.body'), 'FirstUpdated', 'Record body was correctly updated'); - }); + return post.comments.reload().then(function (comments) { + assert.true(comments.isLoaded, 'comments are loaded'); + assert.strictEqual(comments.length, 2, 'comments have 2 length'); + assert.strictEqual(comments.firstObject.body, 'FirstUpdated', 'Record body was correctly updated'); + }); }); }); }); @@ -1430,7 +1421,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( run(function () { store.findRecord('post', 1).then(function (post) { - post.get('comments').then(function (comments) { + post.comments.then(function (comments) { all([comments.reload(), comments.reload(), comments.reload()]).then(function (comments) { assert.strictEqual( count, @@ -1498,13 +1489,13 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); run(function () { - post.get('comments').then(function (comments) { - assert.true(comments.get('isLoaded'), 'comments are loaded'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); + post.comments.then(function (comments) { + assert.true(comments.isLoaded, 'comments are loaded'); + assert.strictEqual(comments.length, 2, 'comments have 2 length'); - let newComment = post.get('comments').createRecord({ body: 'Third' }); - assert.strictEqual(newComment.get('body'), 'Third', 'new comment is returned'); - assert.strictEqual(comments.get('length'), 3, 'comments have 3 length, including new record'); + let newComment = post.comments.createRecord({ body: 'Third' }); + assert.strictEqual(newComment.body, 'Third', 'new comment is returned'); + assert.strictEqual(comments.length, 3, 'comments have 3 length, including new record'); }); }); } @@ -1572,10 +1563,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( post = store.peekRecord('post', 1); }); - const comments = await post.get('comments'); - assert.true(comments.get('isLoaded'), 'comments are loaded'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); - assert.strictEqual(comments.objectAt(0).get('body'), 'First', 'comment 1 successfully loaded'); + const comments = await post.comments; + assert.true(comments.isLoaded, 'comments are loaded'); + assert.strictEqual(comments.length, 2, 'comments have 2 length'); + assert.strictEqual(comments.objectAt(0).body, 'First', 'comment 1 successfully loaded'); store.push({ data: { type: 'post', @@ -1589,10 +1580,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }, }); - const newComments = await post.get('comments'); + const newComments = await post.comments; assert.strictEqual(comments, newComments, 'hasMany array was kept the same'); - assert.strictEqual(newComments.get('length'), 3, 'comments updated successfully'); - assert.strictEqual(newComments.objectAt(0).get('body'), 'Third', 'third comment loaded successfully'); + assert.strictEqual(newComments.length, 3, 'comments updated successfully'); + assert.strictEqual(newComments.objectAt(0).body, 'Third', 'third comment loaded successfully'); }); test("When a polymorphic hasMany relationship is accessed, the adapter's findMany method should not be called if all the records in the relationship are already loaded", function (assert) { @@ -1640,8 +1631,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( run(function () { store.findRecord('user', 1).then(function (user) { - let messages = user.get('messages'); - assert.strictEqual(messages.get('length'), 2, 'The messages are correctly loaded'); + let messages = user.messages; + assert.strictEqual(messages.length, 2, 'The messages are correctly loaded'); }); }); }); @@ -1687,10 +1678,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( store .findRecord('user', 1) .then(function (user) { - return user.get('messages'); + return user.messages; }) .then(function (messages) { - assert.strictEqual(messages.get('length'), 2, 'The messages are correctly loaded'); + assert.strictEqual(messages.length, 2, 'The messages are correctly loaded'); }); }); }); @@ -1706,7 +1697,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( body: 'Well I thought the title was fine', }); - igor.get('messages').addObject(comment); + igor.messages.addObject(comment); assert.strictEqual(igor.get('messages.firstObject.body'), 'Well I thought the title was fine'); }); @@ -1763,7 +1754,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( ], }); const contacts = await user.contacts; - assert.strictEqual(contacts.get('length'), 1, 'The contacts relationship is correctly set up'); + assert.strictEqual(contacts.length, 1, 'The contacts relationship is correctly set up'); }); test('Type can be inferred from the key of an async hasMany relationship', function (assert) { @@ -1815,10 +1806,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( store .findRecord('user', 1) .then(function (user) { - return user.get('contacts'); + return user.contacts; }) .then(function (contacts) { - assert.strictEqual(contacts.get('length'), 1, 'The contacts relationship is correctly set up'); + assert.strictEqual(contacts.length, 1, 'The contacts relationship is correctly set up'); }); }); }); @@ -1869,10 +1860,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( store .findRecord('user', 1) .then(function (user) { - return user.get('contacts'); + return user.contacts; }) .then(function (contacts) { - assert.strictEqual(contacts.get('length'), 2, 'The contacts relationship is correctly set up'); + assert.strictEqual(contacts.length, 2, 'The contacts relationship is correctly set up'); }); }); }); @@ -1911,7 +1902,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( contact: email, }); - assert.strictEqual(post.get('contact'), email, 'The polymorphic belongsTo is set up correctly'); + assert.strictEqual(post.contact, email, 'The polymorphic belongsTo is set up correctly'); assert.strictEqual(get(email, 'posts.length'), 1, 'The inverse has many is set up correctly on the email side.'); }); @@ -1946,7 +1937,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( run(function () { all([store.findRecord('post', 1), store.findRecord('post', 2)]).then(function (records) { assert.expectAssertion(function () { - records[0].get('comments').pushObject(records[1]); + records[0].comments.pushObject(records[1]); }, /The 'post' type does not implement 'comment' and thus cannot be assigned to the 'comments' relationship in 'post'/); }); }); @@ -2013,13 +2004,13 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( asyncRecords .then(function (records) { - records.messages = records.user.get('messages'); + records.messages = records.user.messages; return hash(records); }) .then(function (records) { records.messages.pushObject(records.post); records.messages.pushObject(records.comment); - assert.strictEqual(records.messages.get('length'), 2, 'The messages are correctly added'); + assert.strictEqual(records.messages.length, 2, 'The messages are correctly added'); assert.expectAssertion(function () { records.messages.pushObject(records.anotherUser); @@ -2058,12 +2049,12 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( const messages = await user.messages; - assert.strictEqual(messages.get('length'), 1, 'The user has 1 message'); + assert.strictEqual(messages.length, 1, 'The user has 1 message'); let removedObject = messages.popObject(); assert.strictEqual(removedObject, comment, 'The message is correctly removed'); - assert.strictEqual(messages.get('length'), 0, 'The user does not have any messages'); + assert.strictEqual(messages.length, 0, 'The user does not have any messages'); assert.strictEqual(messages.objectAt(0), undefined, "Null messages can't be fetched"); }); @@ -2194,8 +2185,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( post.set('comments', store.peekAll('comment').toArray()); }); - return post.get('comments').then((comments) => { - assert.strictEqual(comments.get('length'), 2, 'we can set async HM relationship'); + return post.comments.then((comments) => { + assert.strictEqual(comments.length, 2, 'we can set async HM relationship'); }); }); @@ -2213,7 +2204,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( return run(() => { post = store.createRecord('post'); comment = store.createRecord('comment'); - post.get('comments').pushObject(comment); + post.comments.pushObject(comment); return post.save(); }).then(() => { assert.strictEqual(get(post, 'comments.length'), 1, "The unsaved comment should be in the post's comments array"); @@ -2274,8 +2265,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( const comment = store.createRecord('comment', { post }); await comment.save(); - let commentPost = comment.get('post'); - let postComments = comment.get('post.comments'); + let commentPost = comment.post; + let postComments = comment.post.comments; let postCommentsLength = comment.get('post.comments.length'); assert.deepEqual(post, commentPost, 'expect the new comments post, to be the correct post'); @@ -2342,10 +2333,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - let fetchedComments = await post.get('comments'); + let fetchedComments = await post.comments; - assert.strictEqual(fetchedComments.get('length'), 2, 'comments fetched successfully'); - assert.strictEqual(fetchedComments.objectAt(0).get('body'), 'first', 'first comment loaded successfully'); + assert.strictEqual(fetchedComments.length, 2, 'comments fetched successfully'); + assert.strictEqual(fetchedComments.objectAt(0).body, 'first', 'first comment loaded successfully'); store.push({ data: { @@ -2363,10 +2354,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - let newlyFetchedComments = await post.get('comments'); + let newlyFetchedComments = await post.comments; - assert.strictEqual(newlyFetchedComments.get('length'), 3, 'all three comments fetched successfully'); - assert.strictEqual(newlyFetchedComments.objectAt(2).get('body'), 'third', 'third comment loaded successfully'); + assert.strictEqual(newlyFetchedComments.length, 3, 'all three comments fetched successfully'); + assert.strictEqual(newlyFetchedComments.objectAt(2).body, 'third', 'third comment loaded successfully'); }); testInDebug('A sync hasMany errors out if there are unloaded records in it', function (assert) { @@ -2391,7 +2382,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( /You looked up the 'comments' relationship on a 'post' with id 1 but some of the associated records were not loaded./; try { - post.get('comments'); + post.comments; assert.ok(false, 'expected assertion'); } catch (e) { assert.ok(assertionMessage.test(e.message), 'correct assertion'); @@ -2445,11 +2436,11 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( included: [{ type: 'comment', id: '1' }], }); const post = store.peekRecord('post', 1); - const comments = post.get('comments'); + const comments = post.comments; const comment = comments.objectAt(0); comments.removeObject(comment); store.unloadRecord(comment); - assert.strictEqual(comments.get('length'), 0); + assert.strictEqual(comments.length, 0); return post; }); @@ -2508,7 +2499,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); post = store.peekRecord('post', 1); - assert.deepEqual(post.get('comments').toArray(), [comment1, comment2], 'Initial ordering is correct'); + assert.deepEqual(post.comments.toArray(), [comment1, comment2], 'Initial ordering is correct'); }); run(() => { @@ -2527,7 +2518,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); }); - assert.deepEqual(post.get('comments').toArray(), [comment2, comment1], 'Updated ordering is correct'); + assert.deepEqual(post.comments.toArray(), [comment2, comment1], 'Updated ordering is correct'); run(() => { store.push({ @@ -2542,7 +2533,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); }); - assert.deepEqual(post.get('comments').toArray(), [comment2], 'Updated ordering is correct'); + assert.deepEqual(post.comments.toArray(), [comment2], 'Updated ordering is correct'); run(() => { store.push({ @@ -2562,11 +2553,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); }); - assert.deepEqual( - post.get('comments').toArray(), - [comment1, comment2, comment3, comment4], - 'Updated ordering is correct' - ); + assert.deepEqual(post.comments.toArray(), [comment1, comment2, comment3, comment4], 'Updated ordering is correct'); run(() => { store.push({ @@ -2584,7 +2571,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); }); - assert.deepEqual(post.get('comments').toArray(), [comment4, comment3], 'Updated ordering is correct'); + assert.deepEqual(post.comments.toArray(), [comment4, comment3], 'Updated ordering is correct'); run(() => { store.push({ @@ -2605,11 +2592,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); }); - assert.deepEqual( - post.get('comments').toArray(), - [comment4, comment2, comment3, comment1], - 'Updated ordering is correct' - ); + assert.deepEqual(post.comments.toArray(), [comment4, comment2, comment3, comment1], 'Updated ordering is correct'); }); test('Rollbacking attributes for deleted record restores implicit relationship correctly when the hasMany side has been deleted - async', async function (assert) { @@ -2724,7 +2707,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); return run(() => { - return page.get('chapter').then((fetchedChapter) => { + return page.chapter.then((fetchedChapter) => { assert.strictEqual(fetchedChapter, chapter, 'Page has a chapter after rollback attributes'); }); }); @@ -2768,7 +2751,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); run(() => { - assert.strictEqual(page.get('chapter'), chapter, 'Page has a chapter after rollback attributes'); + assert.strictEqual(page.chapter, chapter, 'Page has a chapter after rollback attributes'); }); }); @@ -2820,7 +2803,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( page2 = store.peekRecord('page', 2); chapter = store.peekRecord('chapter', 1); - chapter.get('pages').addArrayObserver(this, { + chapter.pages.addArrayObserver(this, { willChange(pages, index, removeCount, addCount) { if (observe) { assert.strictEqual(pages.objectAt(index), page2, 'page2 is passed to willChange'); @@ -2888,7 +2871,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( page2 = store.peekRecord('page', 2); chapter = store.peekRecord('chapter', 1); - chapter.get('pages').addArrayObserver(this, { + chapter.pages.addArrayObserver(this, { willChange(pages, index, removeCount, addCount) { if (observe) { assert.strictEqual(addCount, 1, 'addCount is correct'); @@ -3375,7 +3358,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( let user; run(() => { user = store.createRecord('user'); - user.get('messages'); + user.messages; assert.ok( hasRelationshipForRecord(user, 'messages'), 'Newly created record with relationships in params passed in its constructor should have relationships' @@ -3466,8 +3449,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); return run(() => { - return book.get('chapters').then((chapters) => { - let meta = chapters.get('meta'); + return book.chapters.then((chapters) => { + let meta = chapters.meta; assert.strictEqual(get(meta, 'foo'), 'bar', 'metadata is available'); }); }); @@ -3540,12 +3523,12 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); return run(() => { - return book1.get('chapters').then((chapters) => { - let meta = chapters.get('meta'); + return book1.chapters.then((chapters) => { + let meta = chapters.meta; assert.strictEqual(get(meta, 'foo'), 'bar', 'metadata should available'); - return book2.get('chapters').then((chapters) => { - let meta = chapters.get('meta'); + return book2.chapters.then((chapters) => { + let meta = chapters.meta; assert.strictEqual(meta, null, 'metadata should not be available'); }); }); @@ -3616,8 +3599,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - return post.get('comments').then((comments) => { - assert.strictEqual(comments.get('firstObject.body'), 'This is comment', 'comment body is correct'); + return post.comments.then((comments) => { + assert.strictEqual(comments.firstObject.body, 'This is comment', 'comment body is correct'); }); }); }); @@ -3687,8 +3670,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - return post.get('comments').then((comments) => { - assert.strictEqual(comments.get('firstObject.body'), 'This is comment', 'comment body is correct'); + return post.comments.then((comments) => { + assert.strictEqual(comments.firstObject.body, 'This is comment', 'comment body is correct'); }); }); }); @@ -3755,8 +3738,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( ], }); - return post.get('comments').then((comments) => { - assert.strictEqual(comments.get('firstObject.body'), 'This is comment', 'comment body is correct'); + return post.comments.then((comments) => { + assert.strictEqual(comments.firstObject.body, 'This is comment', 'comment body is correct'); }); }); }); @@ -3842,12 +3825,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( ], }); - return post.get('comments').then((comments) => { - assert.strictEqual( - comments.get('firstObject.body'), - 'This is comment fetched by link', - 'comment body is correct' - ); + return post.comments.then((comments) => { + assert.strictEqual(comments.firstObject.body, 'This is comment fetched by link', 'comment body is correct'); }); }); }); @@ -3917,8 +3896,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - return post.get('comments').then((comments) => { - assert.strictEqual(comments.get('firstObject.body'), 'This is updated comment', 'comment body is correct'); + return post.comments.then((comments) => { + assert.strictEqual(comments.firstObject.body, 'This is updated comment', 'comment body is correct'); }); }); }); @@ -3973,10 +3952,10 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - let comments = post.get('comments'); + let comments = post.comments; comments.createRecord(); return comments.then((comments) => { - assert.strictEqual(comments.get('length'), 3, 'comments have 3 length, including new record'); + assert.strictEqual(comments.length, 3, 'comments have 3 length, including new record'); }); }); } @@ -4237,7 +4216,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); run(() => { - book.get('chapters'); + book.chapters; }); assert.strictEqual(count, 0); @@ -4320,18 +4299,18 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }; const post = store.peekRecord('post', postID); - post.get('comments').then(function (comments) { - assert.true(comments.get('isLoaded'), 'comments are loaded'); + post.comments.then(function (comments) { + assert.true(comments.isLoaded, 'comments are loaded'); assert.strictEqual(hasManyCounter, 1, 'link was requested'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); + assert.strictEqual(comments.length, 2, 'comments have 2 length'); post .hasMany('comments') .reload() .then(function (comments) { - assert.true(comments.get('isLoaded'), 'comments are loaded'); + assert.true(comments.isLoaded, 'comments are loaded'); assert.strictEqual(hasManyCounter, 2, 'link was requested'); - assert.strictEqual(comments.get('length'), 2, 'comments have 2 length'); + assert.strictEqual(comments.length, 2, 'comments have 2 length'); }); }); }); @@ -4380,6 +4359,6 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }, }); - assert.strictEqual(person.get('phoneNumbers.length'), 1); + assert.strictEqual(person.phoneNumbers.length, 1); }); }); diff --git a/packages/-ember-data/tests/integration/relationships/inverse-relationship-load-test.js b/packages/-ember-data/tests/integration/relationships/inverse-relationship-load-test.js index c6423423852..3fa9feb5fd3 100644 --- a/packages/-ember-data/tests/integration/relationships/inverse-relationship-load-test.js +++ b/packages/-ember-data/tests/integration/relationships/inverse-relationship-load-test.js @@ -90,27 +90,27 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 2, 'hasMany relationship has correct number of records'); - let dog1 = dogs.get('firstObject'); - let dogPerson1 = await dog1.get('person'); + assert.strictEqual(dogs.length, 2, 'hasMany relationship has correct number of records'); + let dog1 = dogs.firstObject; + let dogPerson1 = await dog1.person; assert.strictEqual( - dogPerson1.get('id'), + dogPerson1.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); - let dogPerson2 = await dogs.objectAt(1).get('person'); + let dogPerson2 = await dogs.objectAt(1).person; assert.strictEqual( - dogPerson2.get('id'), + dogPerson2.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many (left hand async, right hand sync) - findHasMany/implicit inverse - adds parent relationship information to the payload if it is not included/added by the serializer', async function (assert) { @@ -176,27 +176,27 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 2, 'hasMany relationship has correct number of records'); - let dog1 = dogs.get('firstObject'); - let dogPerson1 = await dog1.get('person'); + assert.strictEqual(dogs.length, 2, 'hasMany relationship has correct number of records'); + let dog1 = dogs.firstObject; + let dogPerson1 = await dog1.person; assert.strictEqual( - dogPerson1.get('id'), + dogPerson1.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); - let dogPerson2 = await dogs.objectAt(1).get('person'); + let dogPerson2 = await dogs.objectAt(1).person; assert.strictEqual( - dogPerson2.get('id'), + dogPerson2.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many - findHasMany/explicit inverse - adds parent relationship information to the payload if it is not included/added by the serializer', async function (assert) { @@ -263,27 +263,27 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 2, 'hasMany relationship has correct number of records'); - let dog1 = dogs.get('firstObject'); - let dogPerson1 = await dog1.get('pal'); + assert.strictEqual(dogs.length, 2, 'hasMany relationship has correct number of records'); + let dog1 = dogs.firstObject; + let dogPerson1 = await dog1.pal; assert.strictEqual( - dogPerson1.get('id'), + dogPerson1.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); - let dogPerson2 = await dogs.objectAt(1).get('pal'); + let dogPerson2 = await dogs.objectAt(1).pal; assert.strictEqual( - dogPerson2.get('id'), + dogPerson2.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many (left hand async, right hand sync) - findHasMany/explicit inverse - adds parent relationship information to the payload if it is not included/added by the serializer', async function (assert) { @@ -351,27 +351,27 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 2, 'hasMany relationship has correct number of records'); - let dog1 = dogs.get('firstObject'); - let dogPerson1 = await dog1.get('pal'); + assert.strictEqual(dogs.length, 2, 'hasMany relationship has correct number of records'); + let dog1 = dogs.firstObject; + let dogPerson1 = await dog1.pal; assert.strictEqual( - dogPerson1.get('id'), + dogPerson1.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); - let dogPerson2 = await dogs.objectAt(1).get('pal'); + let dogPerson2 = await dogs.objectAt(1).pal; assert.strictEqual( - dogPerson2.get('id'), + dogPerson2.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many - findHasMany/null inverse - adds parent relationship information to the payload if it is not included/added by the serializer', async function (assert) { @@ -443,15 +443,15 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty); - assert.strictEqual(dogs.get('length'), 2); + assert.strictEqual(dogs.length, 2); assert.deepEqual(dogs.mapBy('id'), ['1', '2']); - let dog1 = dogs.get('firstObject'); + let dog1 = dogs.firstObject; await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1); - assert.strictEqual(dogs.get('firstObject.id'), '2'); + assert.strictEqual(dogs.length, 1); + assert.strictEqual(dogs.firstObject.id, '2'); }); test('one-to-one - findBelongsTo/implicit inverse - ensures inverse relationship is set up when payload does not return parent relationship info', async function (assert) { @@ -512,17 +512,17 @@ module('inverse relationship load test', function (hooks) { }, }); - let favoriteDog = await person.get('favoriteDog'); + let favoriteDog = await person.favoriteDog; assert.false(person.belongsTo('favoriteDog').belongsToRelationship.state.isEmpty); - assert.strictEqual(favoriteDog.get('id'), '1', 'favoriteDog id is set correctly'); - let favoriteDogPerson = await favoriteDog.get('person'); + assert.strictEqual(favoriteDog.id, '1', 'favoriteDog id is set correctly'); + let favoriteDogPerson = await favoriteDog.person; assert.strictEqual( - favoriteDogPerson.get('id'), + favoriteDogPerson.id, '1', 'favoriteDog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await favoriteDog.destroyRecord(); - favoriteDog = await person.get('favoriteDog'); + favoriteDog = await person.favoriteDog; assert.strictEqual(favoriteDog, null); }); @@ -584,17 +584,17 @@ module('inverse relationship load test', function (hooks) { }, }); - let favoriteDog = await person.get('favoriteDog'); + let favoriteDog = await person.favoriteDog; assert.false(person.belongsTo('favoriteDog').belongsToRelationship.state.isEmpty); - assert.strictEqual(favoriteDog.get('id'), '1', 'favoriteDog id is set correctly'); - let favoriteDogPerson = await favoriteDog.get('person'); + assert.strictEqual(favoriteDog.id, '1', 'favoriteDog id is set correctly'); + let favoriteDogPerson = await favoriteDog.person; assert.strictEqual( - favoriteDogPerson.get('id'), + favoriteDogPerson.id, '1', 'favoriteDog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await favoriteDog.destroyRecord(); - favoriteDog = await person.get('favoriteDog'); + favoriteDog = await person.favoriteDog; assert.strictEqual(favoriteDog, null); }); @@ -656,17 +656,17 @@ module('inverse relationship load test', function (hooks) { }, }); - let favoriteDog = await person.get('favoriteDog'); + let favoriteDog = await person.favoriteDog; assert.false(person.belongsTo('favoriteDog').belongsToRelationship.state.isEmpty); - assert.strictEqual(favoriteDog.get('id'), '1', 'favoriteDog id is set correctly'); - let favoriteDogPerson = await favoriteDog.get('pal'); + assert.strictEqual(favoriteDog.id, '1', 'favoriteDog id is set correctly'); + let favoriteDogPerson = await favoriteDog.pal; assert.strictEqual( - favoriteDogPerson.get('id'), + favoriteDogPerson.id, '1', 'favoriteDog.pal inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await favoriteDog.destroyRecord(); - favoriteDog = await person.get('favoriteDog'); + favoriteDog = await person.favoriteDog; assert.strictEqual(favoriteDog, null); }); @@ -728,17 +728,17 @@ module('inverse relationship load test', function (hooks) { }, }); - let favoriteDog = await person.get('favoriteDog'); + let favoriteDog = await person.favoriteDog; assert.false(person.belongsTo('favoriteDog').belongsToRelationship.state.isEmpty); - assert.strictEqual(favoriteDog.get('id'), '1', 'favoriteDog id is set correctly'); - let favoriteDogPerson = await favoriteDog.get('pal'); + assert.strictEqual(favoriteDog.id, '1', 'favoriteDog id is set correctly'); + let favoriteDogPerson = await favoriteDog.pal; assert.strictEqual( - favoriteDogPerson.get('id'), + favoriteDogPerson.id, '1', 'favoriteDog.pal inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await favoriteDog.destroyRecord(); - favoriteDog = await person.get('favoriteDog'); + favoriteDog = await person.favoriteDog; assert.strictEqual(favoriteDog, null); }); @@ -798,11 +798,11 @@ module('inverse relationship load test', function (hooks) { }, }); - let favoriteDog = await person.get('favoriteDog'); + let favoriteDog = await person.favoriteDog; assert.false(person.belongsTo('favoriteDog').belongsToRelationship.state.isEmpty); - assert.strictEqual(favoriteDog.get('id'), '1', 'favoriteDog id is set correctly'); + assert.strictEqual(favoriteDog.id, '1', 'favoriteDog id is set correctly'); await favoriteDog.destroyRecord(); - favoriteDog = await person.get('favoriteDog'); + favoriteDog = await person.favoriteDog; assert.strictEqual(favoriteDog, null); }); @@ -869,22 +869,22 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty); - assert.strictEqual(dogs.get('length'), 2, 'left hand side relationship is set up with correct number of records'); + assert.strictEqual(dogs.length, 2, 'left hand side relationship is set up with correct number of records'); let [dog1, dog2] = dogs.toArray(); - let dog1Walkers = await dog1.get('walkers'); + let dog1Walkers = await dog1.walkers; assert.strictEqual(dog1Walkers.length, 1, 'dog1.walkers inverse relationship includes correct number of records'); - assert.strictEqual(dog1Walkers.get('firstObject.id'), '1', 'dog1.walkers inverse relationship is set up correctly'); + assert.strictEqual(dog1Walkers.firstObject.id, '1', 'dog1.walkers inverse relationship is set up correctly'); - let dog2Walkers = await dog2.get('walkers'); + let dog2Walkers = await dog2.walkers; assert.strictEqual(dog2Walkers.length, 1, 'dog2.walkers inverse relationship includes correct number of records'); - assert.strictEqual(dog2Walkers.get('firstObject.id'), '1', 'dog2.walkers inverse relationship is set up correctly'); + assert.strictEqual(dog2Walkers.firstObject.id, '1', 'dog2.walkers inverse relationship is set up correctly'); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'person.dogs relationship was updated when record removed'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'person.dogs relationship has the correct records'); + assert.strictEqual(dogs.length, 1, 'person.dogs relationship was updated when record removed'); + assert.strictEqual(dogs.firstObject.id, '2', 'person.dogs relationship has the correct records'); }); test('many-to-many (left hand async, right hand sync) - findHasMany/implicit inverse - adds parent relationship information to the payload if it is not included/added by the serializer', async function (assert) { @@ -950,22 +950,22 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty); - assert.strictEqual(dogs.get('length'), 2, 'left hand side relationship is set up with correct number of records'); + assert.strictEqual(dogs.length, 2, 'left hand side relationship is set up with correct number of records'); let [dog1, dog2] = dogs.toArray(); - let dog1Walkers = await dog1.get('walkers'); + let dog1Walkers = await dog1.walkers; assert.strictEqual(dog1Walkers.length, 1, 'dog1.walkers inverse relationship includes correct number of records'); - assert.strictEqual(dog1Walkers.get('firstObject.id'), '1', 'dog1.walkers inverse relationship is set up correctly'); + assert.strictEqual(dog1Walkers.firstObject.id, '1', 'dog1.walkers inverse relationship is set up correctly'); - let dog2Walkers = await dog2.get('walkers'); + let dog2Walkers = await dog2.walkers; assert.strictEqual(dog2Walkers.length, 1, 'dog2.walkers inverse relationship includes correct number of records'); - assert.strictEqual(dog2Walkers.get('firstObject.id'), '1', 'dog2.walkers inverse relationship is set up correctly'); + assert.strictEqual(dog2Walkers.firstObject.id, '1', 'dog2.walkers inverse relationship is set up correctly'); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'person.dogs relationship was updated when record removed'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'person.dogs relationship has the correct records'); + assert.strictEqual(dogs.length, 1, 'person.dogs relationship was updated when record removed'); + assert.strictEqual(dogs.firstObject.id, '2', 'person.dogs relationship has the correct records'); }); test('many-to-many - findHasMany/explicit inverse - adds parent relationship information to the payload if it is not included/added by the serializer', async function (assert) { @@ -1032,22 +1032,22 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty); - assert.strictEqual(dogs.get('length'), 2, 'left hand side relationship is set up with correct number of records'); + assert.strictEqual(dogs.length, 2, 'left hand side relationship is set up with correct number of records'); let [dog1, dog2] = dogs.toArray(); - let dog1Pals = await dog1.get('pals'); + let dog1Pals = await dog1.pals; assert.strictEqual(dog1Pals.length, 1, 'dog1.pals inverse relationship includes correct number of records'); - assert.strictEqual(dog1Pals.get('firstObject.id'), '1', 'dog1.pals inverse relationship is set up correctly'); + assert.strictEqual(dog1Pals.firstObject.id, '1', 'dog1.pals inverse relationship is set up correctly'); - let dog2Pals = await dog2.get('pals'); + let dog2Pals = await dog2.pals; assert.strictEqual(dog2Pals.length, 1, 'dog2.pals inverse relationship includes correct number of records'); - assert.strictEqual(dog2Pals.get('firstObject.id'), '1', 'dog2.pals inverse relationship is set up correctly'); + assert.strictEqual(dog2Pals.firstObject.id, '1', 'dog2.pals inverse relationship is set up correctly'); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'person.dogs relationship was updated when record removed'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'person.dogs relationship has the correct records'); + assert.strictEqual(dogs.length, 1, 'person.dogs relationship was updated when record removed'); + assert.strictEqual(dogs.firstObject.id, '2', 'person.dogs relationship has the correct records'); }); test('many-to-many (left hand async, right hand sync) - findHasMany/explicit inverse - adds parent relationship information to the payload if it is not included/added by the serializer', async function (assert) { @@ -1114,22 +1114,22 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty); - assert.strictEqual(dogs.get('length'), 2, 'left hand side relationship is set up with correct number of records'); + assert.strictEqual(dogs.length, 2, 'left hand side relationship is set up with correct number of records'); let [dog1, dog2] = dogs.toArray(); - let dog1Pals = await dog1.get('pals'); + let dog1Pals = await dog1.pals; assert.strictEqual(dog1Pals.length, 1, 'dog1.pals inverse relationship includes correct number of records'); - assert.strictEqual(dog1Pals.get('firstObject.id'), '1', 'dog1.pals inverse relationship is set up correctly'); + assert.strictEqual(dog1Pals.firstObject.id, '1', 'dog1.pals inverse relationship is set up correctly'); - let dog2Pals = await dog2.get('pals'); + let dog2Pals = await dog2.pals; assert.strictEqual(dog2Pals.length, 1, 'dog2.pals inverse relationship includes correct number of records'); - assert.strictEqual(dog2Pals.get('firstObject.id'), '1', 'dog2.pals inverse relationship is set up correctly'); + assert.strictEqual(dog2Pals.firstObject.id, '1', 'dog2.pals inverse relationship is set up correctly'); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'person.dogs relationship was updated when record removed'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'person.dogs relationship has the correct records'); + assert.strictEqual(dogs.length, 1, 'person.dogs relationship was updated when record removed'); + assert.strictEqual(dogs.firstObject.id, '2', 'person.dogs relationship has the correct records'); }); test('many-to-one - findBelongsTo/implicit inverse - adds parent relationship information to the payload if it is not included/added by the serializer', async function (assert) { @@ -1186,21 +1186,21 @@ module('inverse relationship load test', function (hooks) { }, }); - let person = await dog.get('person'); + let person = await dog.person; assert.false( dog.belongsTo('person').belongsToRelationship.state.isEmpty, 'belongsTo relationship state was populated' ); - assert.strictEqual(person.get('id'), '1', 'dog.person relationship is correctly set up'); + assert.strictEqual(person.id, '1', 'dog.person relationship is correctly set up'); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; - assert.strictEqual(dogs.get('length'), 1, 'person.dogs inverse relationship includes correct number of records'); + assert.strictEqual(dogs.length, 1, 'person.dogs inverse relationship includes correct number of records'); let [dog1] = dogs.toArray(); assert.strictEqual(dog1.id, '1', 'dog1.person inverse relationship is set up correctly'); await person.destroyRecord(); - dog = await dog.get('person'); + dog = await dog.person; assert.strictEqual(dog, null, 'record deleted removed from belongsTo relationship'); }); @@ -1258,21 +1258,21 @@ module('inverse relationship load test', function (hooks) { }, }); - let person = await dog.get('person'); + let person = await dog.person; assert.false( dog.belongsTo('person').belongsToRelationship.state.isEmpty, 'belongsTo relationship state was populated' ); - assert.strictEqual(person.get('id'), '1', 'dog.person relationship is correctly set up'); + assert.strictEqual(person.id, '1', 'dog.person relationship is correctly set up'); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; - assert.strictEqual(dogs.get('length'), 1, 'person.dogs inverse relationship includes correct number of records'); + assert.strictEqual(dogs.length, 1, 'person.dogs inverse relationship includes correct number of records'); let [dog1] = dogs.toArray(); assert.strictEqual(dog1.id, '1', 'dog1.person inverse relationship is set up correctly'); await person.destroyRecord(); - dog = await dog.get('person'); + dog = await dog.person; assert.strictEqual(dog, null, 'record deleted removed from belongsTo relationship'); }); @@ -1331,21 +1331,21 @@ module('inverse relationship load test', function (hooks) { }, }); - let person = await dog.get('pal'); + let person = await dog.pal; assert.false( dog.belongsTo('pal').belongsToRelationship.state.isEmpty, 'belongsTo relationship state was populated' ); - assert.strictEqual(person.get('id'), '1', 'dog.person relationship is correctly set up'); + assert.strictEqual(person.id, '1', 'dog.person relationship is correctly set up'); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; - assert.strictEqual(dogs.get('length'), 1, 'person.dogs inverse relationship includes correct number of records'); + assert.strictEqual(dogs.length, 1, 'person.dogs inverse relationship includes correct number of records'); let [dog1] = dogs.toArray(); assert.strictEqual(dog1.id, '1', 'dog1.person inverse relationship is set up correctly'); await person.destroyRecord(); - dog = await dog.get('pal'); + dog = await dog.pal; assert.strictEqual(dog, null, 'record deleted removed from belongsTo relationship'); }); @@ -1404,21 +1404,21 @@ module('inverse relationship load test', function (hooks) { }, }); - let person = await dog.get('pal'); + let person = await dog.pal; assert.false( dog.belongsTo('pal').belongsToRelationship.state.isEmpty, 'belongsTo relationship state was populated' ); - assert.strictEqual(person.get('id'), '1', 'dog.person relationship is correctly set up'); + assert.strictEqual(person.id, '1', 'dog.person relationship is correctly set up'); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; - assert.strictEqual(dogs.get('length'), 1, 'person.dogs inverse relationship includes correct number of records'); + assert.strictEqual(dogs.length, 1, 'person.dogs inverse relationship includes correct number of records'); let [dog1] = dogs.toArray(); assert.strictEqual(dog1.id, '1', 'dog1.person inverse relationship is set up correctly'); await person.destroyRecord(); - dog = await dog.get('pal'); + dog = await dog.pal; assert.strictEqual(dog, null, 'record deleted removed from belongsTo relationship'); }); @@ -1504,7 +1504,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await person.get('dogs'); + await person.dogs; }, /The record loaded at/); } ); @@ -1591,7 +1591,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await person.get('dogs'); + await person.dogs; }, /The record loaded at/); } ); @@ -1672,7 +1672,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await person.get('dogs'); + await person.dogs; }, /The record loaded at/); } ); @@ -1753,7 +1753,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await person.get('dogs'); + await person.dogs; }, /The record loaded at/); } ); @@ -1823,7 +1823,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await person.get('dog'); + await person.dog; }, /The record loaded at/); } ); @@ -1893,7 +1893,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await person.get('dog'); + await person.dog; }, /The record loaded at/); } ); @@ -1960,7 +1960,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await person.get('dog'); + await person.dog; }, /The record loaded at/); } ); @@ -2027,7 +2027,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await person.get('dog'); + await person.dog; }, /The record loaded at/); } ); @@ -2099,7 +2099,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await dog.get('person'); + await dog.person; }, /The record loaded at/); } ); @@ -2171,7 +2171,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await dog.get('person'); + await dog.person; }, /The record loaded at/); } ); @@ -2238,7 +2238,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await dog.get('person'); + await dog.person; }, /The record loaded at/); } ); @@ -2305,7 +2305,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await dog.get('person'); + await dog.person; }, /The record loaded at/); } ); @@ -2811,7 +2811,7 @@ module('inverse relationship load test', function (hooks) { }); await assert.expectAssertion(async () => { - await person.get('dogs'); + await person.dogs; }, /The record loaded at data\[0\] in the payload specified null as its/); } ); @@ -2888,27 +2888,27 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 2, 'hasMany relationship has correct number of records'); - let dog1 = dogs.get('firstObject'); - let dogPerson1 = await dog1.get('person'); + assert.strictEqual(dogs.length, 2, 'hasMany relationship has correct number of records'); + let dog1 = dogs.firstObject; + let dogPerson1 = await dog1.person; assert.strictEqual( - dogPerson1.get('id'), + dogPerson1.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); - let dogPerson2 = await dogs.objectAt(1).get('person'); + let dogPerson2 = await dogs.objectAt(1).person; assert.strictEqual( - dogPerson2.get('id'), + dogPerson2.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many (left hand async, right hand sync) - ids/non-link/implicit inverse - ids - records loaded through ids/findRecord are linked to the parent if the response from the server does not include relationship information', async function (assert) { @@ -2983,27 +2983,27 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 2, 'hasMany relationship has correct number of records'); - let dog1 = dogs.get('firstObject'); - let dogPerson1 = await dog1.get('person'); + assert.strictEqual(dogs.length, 2, 'hasMany relationship has correct number of records'); + let dog1 = dogs.firstObject; + let dogPerson1 = await dog1.person; assert.strictEqual( - dogPerson1.get('id'), + dogPerson1.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); - let dogPerson2 = await dogs.objectAt(1).get('person'); + let dogPerson2 = await dogs.objectAt(1).person; assert.strictEqual( - dogPerson2.get('id'), + dogPerson2.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many - ids/non-link/explicit inverse - ids - records loaded through ids/findRecord are linked to the parent if the response from the server does not include relationship information', async function (assert) { @@ -3079,27 +3079,27 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 2, 'hasMany relationship has correct number of records'); - let dog1 = dogs.get('firstObject'); - let dogPerson1 = await dog1.get('pal'); + assert.strictEqual(dogs.length, 2, 'hasMany relationship has correct number of records'); + let dog1 = dogs.firstObject; + let dogPerson1 = await dog1.pal; assert.strictEqual( - dogPerson1.get('id'), + dogPerson1.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); - let dogPerson2 = await dogs.objectAt(1).get('pal'); + let dogPerson2 = await dogs.objectAt(1).pal; assert.strictEqual( - dogPerson2.get('id'), + dogPerson2.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many (left hand async, right hand sync) - ids/non-link/explicit inverse - ids - records loaded through ids/findRecord are linked to the parent if the response from the server does not include relationship information', async function (assert) { @@ -3175,27 +3175,27 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 2, 'hasMany relationship has correct number of records'); - let dog1 = dogs.get('firstObject'); - let dogPerson1 = await dog1.get('pal'); + assert.strictEqual(dogs.length, 2, 'hasMany relationship has correct number of records'); + let dog1 = dogs.firstObject; + let dogPerson1 = await dog1.pal; assert.strictEqual( - dogPerson1.get('id'), + dogPerson1.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); - let dogPerson2 = await dogs.objectAt(1).get('pal'); + let dogPerson2 = await dogs.objectAt(1).pal; assert.strictEqual( - dogPerson2.get('id'), + dogPerson2.id, '1', 'dog.person inverse relationship is set up correctly when adapter does not include parent relationships in data.relationships' ); await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many - ids/non-link/null inverse - ids - records loaded through ids/findRecord are linked to the parent if the response from the server does not include relationship information', async function (assert) { @@ -3266,15 +3266,15 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 2, 'hasMany relationship has correct number of records'); - let dog1 = dogs.get('firstObject'); + assert.strictEqual(dogs.length, 2, 'hasMany relationship has correct number of records'); + let dog1 = dogs.firstObject; await dog1.destroyRecord(); - assert.strictEqual(dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many - ids/non-link/implicit inverse - records loaded through ids/findRecord do not get associated with the parent if the server specifies another resource as the relationship value in the response', async function (assert) { @@ -3375,14 +3375,14 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 0, 'hasMany relationship for parent is empty'); + assert.strictEqual(dogs.length, 0, 'hasMany relationship for parent is empty'); - let person2Dogs = await person2.get('dogs'); + let person2Dogs = await person2.dogs; assert.strictEqual( - person2Dogs.get('length'), + person2Dogs.length, 2, 'hasMany relationship on specified record has correct number of associated records' ); @@ -3390,14 +3390,14 @@ module('inverse relationship load test', function (hooks) { let allDogs = store.peekAll('dogs').toArray(); for (let i = 0; i < allDogs.length; i++) { let dog = allDogs[i]; - let dogPerson = await dog.get('person'); - assert.strictEqual(dogPerson.get('id'), person2.get('id'), 'right hand side has correct belongsTo value'); + let dogPerson = await dog.person; + assert.strictEqual(dogPerson.id, person2.id, 'right hand side has correct belongsTo value'); } let dog1 = store.peekRecord('dog', '1'); await dog1.destroyRecord(); - assert.strictEqual(person2Dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(person2Dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(person2Dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(person2Dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many (left hand async, right hand sync) - ids/non-link/implicit inverse - records loaded through ids/findRecord do not get associated with the parent if the server specifies another resource as the relationship value in the response', async function (assert) { @@ -3498,14 +3498,14 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await person.get('dogs'); + let dogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 0, 'hasMany relationship for parent is empty'); + assert.strictEqual(dogs.length, 0, 'hasMany relationship for parent is empty'); - let person2Dogs = await person2.get('dogs'); + let person2Dogs = await person2.dogs; assert.strictEqual( - person2Dogs.get('length'), + person2Dogs.length, 2, 'hasMany relationship on specified record has correct number of associated records' ); @@ -3513,14 +3513,14 @@ module('inverse relationship load test', function (hooks) { let allDogs = store.peekAll('dogs').toArray(); for (let i = 0; i < allDogs.length; i++) { let dog = allDogs[i]; - let dogPerson = await dog.get('person'); - assert.strictEqual(dogPerson.get('id'), person2.get('id'), 'right hand side has correct belongsTo value'); + let dogPerson = await dog.person; + assert.strictEqual(dogPerson.id, person2.id, 'right hand side has correct belongsTo value'); } let dog1 = store.peekRecord('dog', '1'); await dog1.destroyRecord(); - assert.strictEqual(person2Dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(person2Dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(person2Dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(person2Dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many - ids/non-link/implicit inverse - records loaded through ids/findRecord do not get associated with the parent if the server specifies null as the relationship value in the response', async function (assert) { @@ -3605,22 +3605,22 @@ module('inverse relationship load test', function (hooks) { }, }); - let personDogs = await person.get('dogs'); + let personDogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(personDogs.get('length'), 0, 'hasMany relationship for parent is empty'); + assert.strictEqual(personDogs.length, 0, 'hasMany relationship for parent is empty'); let allDogs = store.peekAll('dogs').toArray(); for (let i = 0; i < allDogs.length; i++) { let dog = allDogs[i]; - let dogPerson = await dog.get('person'); + let dogPerson = await dog.person; assert.strictEqual(dogPerson, null, 'right hand side has correct belongsTo value'); } let dog1 = store.peekRecord('dog', '1'); await dog1.destroyRecord(); - assert.strictEqual(personDogs.get('length'), 0); + assert.strictEqual(personDogs.length, 0); }); test('one-to-many (left hand async, right hand sync) - ids/non-link/implicit inverse - records loaded through ids/findRecord do not get associated with the parent if the server specifies null as the relationship value in the response', async function (assert) { @@ -3705,22 +3705,22 @@ module('inverse relationship load test', function (hooks) { }, }); - let personDogs = await person.get('dogs'); + let personDogs = await person.dogs; assert.false(person.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(personDogs.get('length'), 0, 'hasMany relationship for parent is empty'); + assert.strictEqual(personDogs.length, 0, 'hasMany relationship for parent is empty'); let allDogs = store.peekAll('dogs').toArray(); for (let i = 0; i < allDogs.length; i++) { let dog = allDogs[i]; - let dogPerson = await dog.get('person'); + let dogPerson = await dog.person; assert.strictEqual(dogPerson, null, 'right hand side has correct belongsTo value'); } let dog1 = store.peekRecord('dog', '1'); await dog1.destroyRecord(); - assert.strictEqual(personDogs.get('length'), 0); + assert.strictEqual(personDogs.length, 0); }); test('one-to-many - ids/non-link/explicit inverse - records loaded through ids/findRecord do not get associated with the parent if the server specifies another resource as the relationship value in the response', async function (assert) { @@ -3821,14 +3821,14 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await pal.get('dogs'); + let dogs = await pal.dogs; assert.false(pal.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 0, 'hasMany relationship for parent is empty'); + assert.strictEqual(dogs.length, 0, 'hasMany relationship for parent is empty'); - let pal2Dogs = await pal2.get('dogs'); + let pal2Dogs = await pal2.dogs; assert.strictEqual( - pal2Dogs.get('length'), + pal2Dogs.length, 2, 'hasMany relationship on specified record has correct number of associated records' ); @@ -3836,14 +3836,14 @@ module('inverse relationship load test', function (hooks) { let allDogs = store.peekAll('dogs').toArray(); for (let i = 0; i < allDogs.length; i++) { let dog = allDogs[i]; - let dogPerson = await dog.get('pal'); - assert.strictEqual(dogPerson.get('id'), pal2.get('id'), 'right hand side has correct belongsTo value'); + let dogPerson = await dog.pal; + assert.strictEqual(dogPerson.id, pal2.id, 'right hand side has correct belongsTo value'); } let dog1 = store.peekRecord('dog', '1'); await dog1.destroyRecord(); - assert.strictEqual(pal2Dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(pal2Dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(pal2Dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(pal2Dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test('one-to-many (left hand async, right hand sync) - ids/non-link/explicit inverse - records loaded through ids/findRecord do not get associated with the parent if the server specifies another resource as the relationship value in the response', async function (assert) { @@ -3944,14 +3944,14 @@ module('inverse relationship load test', function (hooks) { }, }); - let dogs = await pal.get('dogs'); + let dogs = await pal.dogs; assert.false(pal.hasMany('dogs').hasManyRelationship.state.isEmpty, 'relationship state was set up correctly'); - assert.strictEqual(dogs.get('length'), 0, 'hasMany relationship for parent is empty'); + assert.strictEqual(dogs.length, 0, 'hasMany relationship for parent is empty'); - let pal2Dogs = await pal2.get('dogs'); + let pal2Dogs = await pal2.dogs; assert.strictEqual( - pal2Dogs.get('length'), + pal2Dogs.length, 2, 'hasMany relationship on specified record has correct number of associated records' ); @@ -3959,14 +3959,14 @@ module('inverse relationship load test', function (hooks) { let allDogs = store.peekAll('dogs').toArray(); for (let i = 0; i < allDogs.length; i++) { let dog = allDogs[i]; - let dogPerson = await dog.get('pal'); - assert.strictEqual(dogPerson.get('id'), pal2.get('id'), 'right hand side has correct belongsTo value'); + let dogPerson = await dog.pal; + assert.strictEqual(dogPerson.id, pal2.id, 'right hand side has correct belongsTo value'); } let dog1 = store.peekRecord('dog', '1'); await dog1.destroyRecord(); - assert.strictEqual(pal2Dogs.get('length'), 1, 'record removed from hasMany relationship after deletion'); - assert.strictEqual(pal2Dogs.get('firstObject.id'), '2', 'hasMany relationship has correct records'); + assert.strictEqual(pal2Dogs.length, 1, 'record removed from hasMany relationship after deletion'); + assert.strictEqual(pal2Dogs.firstObject.id, '2', 'hasMany relationship has correct records'); }); test("loading belongsTo doesn't remove inverse relationship for other instances", async function (assert) { @@ -4057,7 +4057,7 @@ module('inverse relationship load test', function (hooks) { assert.strictEqual(dog1.belongsTo('person').id(), '1'); assert.strictEqual(dog2.belongsTo('person').id(), '1'); - await dog1.get('person'); + await dog1.person; assert.strictEqual(dog1.belongsTo('person').id(), '1'); assert.strictEqual(dog2.belongsTo('person').id(), '1'); diff --git a/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js b/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js index 09048a6e58b..fc08fa86187 100644 --- a/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js +++ b/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js @@ -38,10 +38,10 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' const comment = store.createRecord('comment'); const post = store.createRecord('post'); - assert.strictEqual(comment.get('post'), null, 'no post has been set on the comment'); + assert.strictEqual(comment.post, null, 'no post has been set on the comment'); - post.get('comments').pushObject(comment); - assert.strictEqual(comment.get('post'), post, 'post was set on the comment'); + post.comments.pushObject(comment); + assert.strictEqual(comment.post, post, 'post was set on the comment'); }); test('Inverse relationships can be explicitly nullable', function (assert) { @@ -120,17 +120,17 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' const comment = store.createRecord('comment'); const post = store.createRecord('post'); - assert.strictEqual(comment.get('onePost'), null, 'onePost has not been set on the comment'); - assert.strictEqual(comment.get('twoPost'), null, 'twoPost has not been set on the comment'); - assert.strictEqual(comment.get('redPost'), null, 'redPost has not been set on the comment'); - assert.strictEqual(comment.get('bluePost'), null, 'bluePost has not been set on the comment'); + assert.strictEqual(comment.onePost, null, 'onePost has not been set on the comment'); + assert.strictEqual(comment.twoPost, null, 'twoPost has not been set on the comment'); + assert.strictEqual(comment.redPost, null, 'redPost has not been set on the comment'); + assert.strictEqual(comment.bluePost, null, 'bluePost has not been set on the comment'); - post.get('comments').pushObject(comment); + post.comments.pushObject(comment); - assert.strictEqual(comment.get('onePost'), null, 'onePost has not been set on the comment'); - assert.strictEqual(comment.get('twoPost'), null, 'twoPost has not been set on the comment'); - assert.strictEqual(comment.get('redPost'), post, 'redPost has been set on the comment'); - assert.strictEqual(comment.get('bluePost'), null, 'bluePost has not been set on the comment'); + assert.strictEqual(comment.onePost, null, 'onePost has not been set on the comment'); + assert.strictEqual(comment.twoPost, null, 'twoPost has not been set on the comment'); + assert.strictEqual(comment.redPost, post, 'redPost has been set on the comment'); + assert.strictEqual(comment.bluePost, null, 'bluePost has not been set on the comment'); }); test("When a record's belongsTo relationship is set, it can specify the inverse hasMany to which the new child should be added", async function (assert) { @@ -158,17 +158,17 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' comment = store.createRecord('comment'); post = store.createRecord('post'); - assert.strictEqual(post.get('meComments.length'), 0, 'meComments has no posts'); - assert.strictEqual(post.get('youComments.length'), 0, 'youComments has no posts'); - assert.strictEqual(post.get('everyoneWeKnowComments.length'), 0, 'everyoneWeKnowComments has no posts'); + assert.strictEqual(post.meComments.length, 0, 'meComments has no posts'); + assert.strictEqual(post.youComments.length, 0, 'youComments has no posts'); + assert.strictEqual(post.everyoneWeKnowComments.length, 0, 'everyoneWeKnowComments has no posts'); comment.set('post', post); - assert.strictEqual(comment.get('post'), post, 'The post that was set can be retrieved'); + assert.strictEqual(comment.post, post, 'The post that was set can be retrieved'); - assert.strictEqual(post.get('meComments.length'), 0, 'meComments has no posts'); - assert.strictEqual(post.get('youComments.length'), 1, 'youComments had the post added'); - assert.strictEqual(post.get('everyoneWeKnowComments.length'), 0, 'everyoneWeKnowComments has no posts'); + assert.strictEqual(post.meComments.length, 0, 'meComments has no posts'); + assert.strictEqual(post.youComments.length, 1, 'youComments had the post added'); + assert.strictEqual(post.everyoneWeKnowComments.length, 0, 'everyoneWeKnowComments has no posts'); }); test('When setting a belongsTo, the OneToOne invariant is respected even when other records have been previously used', async function (assert) { @@ -192,15 +192,15 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' comment.set('post', post); post2.set('bestComment', null); - assert.strictEqual(comment.get('post'), post); - assert.strictEqual(post.get('bestComment'), comment); - assert.strictEqual(post2.get('bestComment'), null); + assert.strictEqual(comment.post, post); + assert.strictEqual(post.bestComment, comment); + assert.strictEqual(post2.bestComment, null); comment.set('post', post2); - assert.strictEqual(comment.get('post'), post2); - assert.strictEqual(post.get('bestComment'), null); - assert.strictEqual(post2.get('bestComment'), comment); + assert.strictEqual(comment.post, post2); + assert.strictEqual(post.bestComment, null); + assert.strictEqual(post2.bestComment, comment); }); test('When setting a belongsTo, the OneToOne invariant is transitive', async function (assert) { @@ -223,15 +223,15 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' comment.set('post', post); - assert.strictEqual(comment.get('post'), post, 'comment post is set correctly'); - assert.strictEqual(post.get('bestComment'), comment, 'post1 comment is set correctly'); - assert.strictEqual(post2.get('bestComment'), null, 'post2 comment is not set'); + assert.strictEqual(comment.post, post, 'comment post is set correctly'); + assert.strictEqual(post.bestComment, comment, 'post1 comment is set correctly'); + assert.strictEqual(post2.bestComment, null, 'post2 comment is not set'); post2.set('bestComment', comment); - assert.strictEqual(comment.get('post'), post2, 'comment post is set correctly'); - assert.strictEqual(post.get('bestComment'), null, 'post1 comment is no longer set'); - assert.strictEqual(post2.get('bestComment'), comment, 'post2 comment is set correctly'); + assert.strictEqual(comment.post, post2, 'comment post is set correctly'); + assert.strictEqual(post.bestComment, null, 'post1 comment is no longer set'); + assert.strictEqual(post2.bestComment, comment, 'post2 comment is set correctly'); }); test('When setting a belongsTo, the OneToOne invariant is commutative', async function (assert) { @@ -254,15 +254,15 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' comment.set('post', post); - assert.strictEqual(comment.get('post'), post); - assert.strictEqual(post.get('bestComment'), comment); - assert.strictEqual(comment2.get('post'), null); + assert.strictEqual(comment.post, post); + assert.strictEqual(post.bestComment, comment); + assert.strictEqual(comment2.post, null); post.set('bestComment', comment2); - assert.strictEqual(comment.get('post'), null); - assert.strictEqual(post.get('bestComment'), comment2); - assert.strictEqual(comment2.get('post'), post); + assert.strictEqual(comment.post, null); + assert.strictEqual(post.bestComment, comment2); + assert.strictEqual(comment2.post, post); }); test('OneToNone relationship works', async function (assert) { @@ -286,13 +286,13 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' const post2 = store.createRecord('post'); comment.set('post', post1); - assert.strictEqual(comment.get('post'), post1, 'the post is set to the first one'); + assert.strictEqual(comment.post, post1, 'the post is set to the first one'); comment.set('post', post2); - assert.strictEqual(comment.get('post'), post2, 'the post is set to the second one'); + assert.strictEqual(comment.post, post2, 'the post is set to the second one'); comment.set('post', post1); - assert.strictEqual(comment.get('post'), post1, 'the post is re-set to the first one'); + assert.strictEqual(comment.post, post1, 'the post is re-set to the first one'); }); test('When a record is added to or removed from a polymorphic has-many relationship, the inverse belongsTo can be set explicitly', async function (assert) { @@ -324,24 +324,24 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' const post = store.createRecord('post'); const user = store.createRecord('user'); - assert.strictEqual(post.get('oneUser'), null, 'oneUser has not been set on the user'); - assert.strictEqual(post.get('twoUser'), null, 'twoUser has not been set on the user'); - assert.strictEqual(post.get('redUser'), null, 'redUser has not been set on the user'); - assert.strictEqual(post.get('blueUser'), null, 'blueUser has not been set on the user'); + assert.strictEqual(post.oneUser, null, 'oneUser has not been set on the user'); + assert.strictEqual(post.twoUser, null, 'twoUser has not been set on the user'); + assert.strictEqual(post.redUser, null, 'redUser has not been set on the user'); + assert.strictEqual(post.blueUser, null, 'blueUser has not been set on the user'); - user.get('messages').pushObject(post); + user.messages.pushObject(post); - assert.strictEqual(post.get('oneUser'), null, 'oneUser has not been set on the user'); - assert.strictEqual(post.get('twoUser'), null, 'twoUser has not been set on the user'); - assert.strictEqual(post.get('redUser'), user, 'redUser has been set on the user'); - assert.strictEqual(post.get('blueUser'), null, 'blueUser has not been set on the user'); + assert.strictEqual(post.oneUser, null, 'oneUser has not been set on the user'); + assert.strictEqual(post.twoUser, null, 'twoUser has not been set on the user'); + assert.strictEqual(post.redUser, user, 'redUser has been set on the user'); + assert.strictEqual(post.blueUser, null, 'blueUser has not been set on the user'); - user.get('messages').popObject(); + user.messages.popObject(); - assert.strictEqual(post.get('oneUser'), null, 'oneUser has not been set on the user'); - assert.strictEqual(post.get('twoUser'), null, 'twoUser has not been set on the user'); - assert.strictEqual(post.get('redUser'), null, 'redUser has bot been set on the user'); - assert.strictEqual(post.get('blueUser'), null, 'blueUser has not been set on the user'); + assert.strictEqual(post.oneUser, null, 'oneUser has not been set on the user'); + assert.strictEqual(post.twoUser, null, 'twoUser has not been set on the user'); + assert.strictEqual(post.redUser, null, 'redUser has bot been set on the user'); + assert.strictEqual(post.blueUser, null, 'blueUser has not been set on the user'); }); test("When a record's belongsTo relationship is set, it can specify the inverse polymorphic hasMany to which the new child should be added or removed", async function (assert) { @@ -370,21 +370,21 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' const user = store.createRecord('user'); const post = store.createRecord('post'); - assert.strictEqual(user.get('meMessages.length'), 0, 'meMessages has no posts'); - assert.strictEqual(user.get('youMessages.length'), 0, 'youMessages has no posts'); - assert.strictEqual(user.get('everyoneWeKnowMessages.length'), 0, 'everyoneWeKnowMessages has no posts'); + assert.strictEqual(user.meMessages.length, 0, 'meMessages has no posts'); + assert.strictEqual(user.youMessages.length, 0, 'youMessages has no posts'); + assert.strictEqual(user.everyoneWeKnowMessages.length, 0, 'everyoneWeKnowMessages has no posts'); post.set('user', user); - assert.strictEqual(user.get('meMessages.length'), 0, 'meMessages has no posts'); - assert.strictEqual(user.get('youMessages.length'), 1, 'youMessages had the post added'); - assert.strictEqual(user.get('everyoneWeKnowMessages.length'), 0, 'everyoneWeKnowMessages has no posts'); + assert.strictEqual(user.meMessages.length, 0, 'meMessages has no posts'); + assert.strictEqual(user.youMessages.length, 1, 'youMessages had the post added'); + assert.strictEqual(user.everyoneWeKnowMessages.length, 0, 'everyoneWeKnowMessages has no posts'); post.set('user', null); - assert.strictEqual(user.get('meMessages.length'), 0, 'meMessages has no posts'); - assert.strictEqual(user.get('youMessages.length'), 0, 'youMessages has no posts'); - assert.strictEqual(user.get('everyoneWeKnowMessages.length'), 0, 'everyoneWeKnowMessages has no posts'); + assert.strictEqual(user.meMessages.length, 0, 'meMessages has no posts'); + assert.strictEqual(user.youMessages.length, 0, 'youMessages has no posts'); + assert.strictEqual(user.everyoneWeKnowMessages.length, 0, 'everyoneWeKnowMessages has no posts'); }); test("When a record's polymorphic belongsTo relationship is set, it can specify the inverse hasMany to which the new child should be added", async function (assert) { @@ -413,21 +413,21 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' const comment = store.createRecord('comment'); const post = store.createRecord('post'); - assert.strictEqual(post.get('meMessages.length'), 0, 'meMessages has no posts'); - assert.strictEqual(post.get('youMessages.length'), 0, 'youMessages has no posts'); - assert.strictEqual(post.get('everyoneWeKnowMessages.length'), 0, 'everyoneWeKnowMessages has no posts'); + assert.strictEqual(post.meMessages.length, 0, 'meMessages has no posts'); + assert.strictEqual(post.youMessages.length, 0, 'youMessages has no posts'); + assert.strictEqual(post.everyoneWeKnowMessages.length, 0, 'everyoneWeKnowMessages has no posts'); comment.set('message', post); - assert.strictEqual(post.get('meMessages.length'), 0, 'meMessages has no posts'); - assert.strictEqual(post.get('youMessages.length'), 1, 'youMessages had the post added'); - assert.strictEqual(post.get('everyoneWeKnowMessages.length'), 0, 'everyoneWeKnowMessages has no posts'); + assert.strictEqual(post.meMessages.length, 0, 'meMessages has no posts'); + assert.strictEqual(post.youMessages.length, 1, 'youMessages had the post added'); + assert.strictEqual(post.everyoneWeKnowMessages.length, 0, 'everyoneWeKnowMessages has no posts'); comment.set('message', null); - assert.strictEqual(post.get('meMessages.length'), 0, 'meMessages has no posts'); - assert.strictEqual(post.get('youMessages.length'), 0, 'youMessages has no posts'); - assert.strictEqual(post.get('everyoneWeKnowMessages.length'), 0, 'everyoneWeKnowMessages has no posts'); + assert.strictEqual(post.meMessages.length, 0, 'meMessages has no posts'); + assert.strictEqual(post.youMessages.length, 0, 'youMessages has no posts'); + assert.strictEqual(post.everyoneWeKnowMessages.length, 0, 'everyoneWeKnowMessages has no posts'); }); testInDebug("Inverse relationships that don't exist throw a nice error for a hasMany", async function (assert) { @@ -450,7 +450,7 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' assert.expectAssertion(function () { post = store.createRecord('post'); - post.get('comments'); + post.comments; }, /We found no inverse relationships by the name of 'testPost' on the 'comment' model/); }); @@ -473,7 +473,7 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' assert.expectAssertion(function () { post = store.createRecord('post'); - post.get('user'); + post.user; }, /We found no inverse relationships by the name of 'testPost' on the 'user' model/); }); @@ -673,7 +673,7 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' const recordData = recordDataFor(comment); const post = store.createRecord('post'); - post.get('comments').pushObject(comment); + post.comments.pushObject(comment); const identifier = recordIdentifierFor(comment); await comment.destroyRecord(); diff --git a/packages/-ember-data/tests/integration/relationships/json-api-links-test.js b/packages/-ember-data/tests/integration/relationships/json-api-links-test.js index 347d0e15c9b..ebee772330c 100644 --- a/packages/-ember-data/tests/integration/relationships/json-api-links-test.js +++ b/packages/-ember-data/tests/integration/relationships/json-api-links-test.js @@ -80,7 +80,7 @@ module('integration/relationship/json-api-links | Relationship state updates', f assert.strictEqual(user1.belongsTo('organisation').remoteType(), 'id', `user's belongsTo is based on id`); assert.strictEqual(user1.belongsTo('organisation').id(), '1', `user's belongsTo has its id populated`); - return user1.get('organisation').then((orgFromUser) => { + return user1.organisation.then((orgFromUser) => { assert.false( user1.belongsTo('organisation').belongsToRelationship.state.isStale, 'user should have loaded its belongsTo relationship' @@ -714,7 +714,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi // setup user let user = run(() => store.push(deepCopy(payloads.user))); - let pets = run(() => user.get('pets')); + let pets = run(() => user.pets); assert.ok(!!pets, 'We found our pets'); @@ -804,7 +804,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi // setup user let user = run(() => store.push(deepCopy(payloads.user))); - let home = run(() => user.get('home')); + let home = run(() => user.home); if (homeRelWasEmpty) { assert.notOk(didFetchInitially, 'We did not fetch'); @@ -842,13 +842,13 @@ module('integration/relationship/json-api-links | Relationship fetching', functi // setup user let user = run(() => store.push(deepCopy(payloads.user))); - let home = run(() => user.get('home')); + let home = run(() => user.home); assert.ok(!!home, 'We found our home'); if (!homeRelWasEmpty) { run(() => home.then((h) => h.unloadRecord())); - run(() => user.get('home')); + run(() => user.home); } else { assert.ok(true, `We cant dirty a relationship we have no knowledge of`); assert.ok(true, `Nor should we have fetched it.`); @@ -1014,7 +1014,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi // setup user and pets let user = run(() => store.push(deepCopy(payloads.user))); run(() => store.push(deepCopy(payloads.pets))); - let pets = run(() => user.get('pets')); + let pets = run(() => user.pets); assert.ok(!!pets, 'We found our pets'); @@ -1081,7 +1081,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi // setup user and home let user = run(() => store.push(deepCopy(payloads.user))); run(() => store.push(deepCopy(payloads.home))); - let home = run(() => user.get('home')); + let home = run(() => user.home); assert.ok(!!home, 'We found our home'); @@ -1114,12 +1114,12 @@ module('integration/relationship/json-api-links | Relationship fetching', functi let user = run(() => store.push(deepCopy(payloads.user))); run(() => store.push(deepCopy(payloads.home))); let home; - run(() => user.get('home').then((h) => (home = h))); + run(() => user.home.then((h) => (home = h))); assert.ok(!!home, 'We found our home'); run(() => home.unloadRecord()); - run(() => user.get('home')); + run(() => user.home); }); } @@ -1374,7 +1374,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi }, }) ); - let pets = run(() => user.get('pets')); + let pets = run(() => user.pets); assert.ok(!!pets, 'We found our pets'); @@ -1495,7 +1495,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi }, }) ); - let home = run(() => user.get('home')); + let home = run(() => user.home); assert.ok(!!home, 'We found our home'); @@ -1556,12 +1556,12 @@ module('integration/relationship/json-api-links | Relationship fetching', functi }, }) ); - let home = run(() => user.get('home')); + let home = run(() => user.home); assert.ok(!!home, 'We found our home'); run(() => home.then((h) => h.unloadRecord())); - run(() => user.get('home')); + run(() => user.home); }); // missing data setup from the other side, no links @@ -1776,7 +1776,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi ], }) ); - let home = run(() => user.get('home')); + let home = run(() => user.home); assert.ok(!!home, 'We found our home'); @@ -1846,12 +1846,12 @@ module('integration/relationship/json-api-links | Relationship fetching', functi ], }) ); - let home = run(() => user.get('home')); + let home = run(() => user.home); assert.ok(!!home, 'We found our home'); run(() => home.then((h) => h.unloadRecord())); - run(() => user.get('home')); + run(() => user.home); }); // empty data, no links @@ -1892,7 +1892,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi }, }) ); - let pets = run(() => user.get('pets')); + let pets = run(() => user.pets); assert.ok(!!pets, 'We found our pets'); @@ -1975,21 +1975,21 @@ module('integration/relationship/json-api-links | Relationship fetching', functi // should not fire a request requestedUser = null; failureDescription = 'We improperly fetched the link for a known empty relationship'; - run(() => user1.get('pets')); + run(() => user1.pets); // still should not fire a request requestedUser = null; failureDescription = 'We improperly fetched the link (again) for a known empty relationship'; - run(() => user1.get('pets')); + run(() => user1.pets); // should fire a request requestedUser = user2Payload; - run(() => user2.get('pets')); + run(() => user2.pets); // should not fire a request requestedUser = null; failureDescription = 'We improperly fetched the link for a previously fetched and found to be empty relationship'; - run(() => user2.get('pets')); + run(() => user2.pets); }); test('We should not fetch a sync hasMany relationship with a link that is missing the data member', function (assert) { @@ -2033,7 +2033,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi let shen = run(() => store.push(petPayload)); // should not fire a request - run(() => shen.get('pets')); + run(() => shen.pets); assert.ok(true, 'We reached the end of the test'); }); @@ -2080,7 +2080,7 @@ module('integration/relationship/json-api-links | Relationship fetching', functi let shen = run(() => store.push(petPayload)); // should not fire a request - run(() => shen.get('owner')); + run(() => shen.owner); assert.ok(true, 'We reached the end of the test'); }); diff --git a/packages/-ember-data/tests/integration/relationships/many-to-many-test.js b/packages/-ember-data/tests/integration/relationships/many-to-many-test.js index f49f8facd87..1a8ab92da24 100644 --- a/packages/-ember-data/tests/integration/relationships/many-to-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/many-to-many-test.js @@ -87,8 +87,8 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); return run(() => { - return topic.get('users').then((fetchedUsers) => { - assert.strictEqual(fetchedUsers.get('length'), 1, 'User relationship was set up correctly'); + return topic.users.then((fetchedUsers) => { + assert.strictEqual(fetchedUsers.length, 1, 'User relationship was set up correctly'); }); }); }); @@ -129,7 +129,7 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); run(() => { - assert.strictEqual(account.get('users.length'), 1, 'User relationship was set up correctly'); + assert.strictEqual(account.users.length, 1, 'User relationship was set up correctly'); }); }); @@ -169,11 +169,11 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); return run(() => { - return user.get('topics').then((fetchedTopics) => { - assert.strictEqual(fetchedTopics.get('length'), 0, 'Topics were removed correctly'); + return user.topics.then((fetchedTopics) => { + assert.strictEqual(fetchedTopics.length, 0, 'Topics were removed correctly'); assert.strictEqual(fetchedTopics.objectAt(0), undefined, "Topics can't be fetched"); - return topic.get('users').then((fetchedUsers) => { - assert.strictEqual(fetchedUsers.get('length'), 0, 'Users were removed correctly'); + return topic.users.then((fetchedUsers) => { + assert.strictEqual(fetchedUsers.length, 0, 'Users were removed correctly'); assert.strictEqual(fetchedUsers.objectAt(0), undefined, "User can't be fetched"); }); }); @@ -230,8 +230,8 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); run(() => { - assert.strictEqual(user.get('accounts.length'), 0, 'Accounts were removed correctly'); - assert.strictEqual(account.get('users.length'), 0, 'Users were removed correctly'); + assert.strictEqual(user.accounts.length, 0, 'Accounts were removed correctly'); + assert.strictEqual(account.users.length, 0, 'Users were removed correctly'); }); }); @@ -273,10 +273,10 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); return run(() => { - return topic.get('users').then((fetchedUsers) => { + return topic.users.then((fetchedUsers) => { fetchedUsers.pushObject(user); - return user.get('topics').then((fetchedTopics) => { - assert.strictEqual(fetchedTopics.get('length'), 1, 'User relationship was set up correctly'); + return user.topics.then((fetchedTopics) => { + assert.strictEqual(fetchedTopics.length, 1, 'User relationship was set up correctly'); }); }); }); @@ -305,11 +305,11 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }, }, }); - stanley.get('accounts').pushObject(account); + stanley.accounts.pushObject(account); }); run(() => { - assert.strictEqual(account.get('users.length'), 1, 'User relationship was set up correctly'); + assert.strictEqual(account.users.length, 1, 'User relationship was set up correctly'); }); }); @@ -349,11 +349,11 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); return run(() => { - return user.get('topics').then((fetchedTopics) => { - assert.strictEqual(fetchedTopics.get('length'), 1, 'Topics were setup correctly'); + return user.topics.then((fetchedTopics) => { + assert.strictEqual(fetchedTopics.length, 1, 'Topics were setup correctly'); fetchedTopics.removeObject(topic); - return topic.get('users').then((fetchedUsers) => { - assert.strictEqual(fetchedUsers.get('length'), 0, 'Users were removed correctly'); + return topic.users.then((fetchedUsers) => { + assert.strictEqual(fetchedUsers.length, 0, 'Users were removed correctly'); assert.strictEqual(fetchedUsers.objectAt(0), undefined, "User can't be fetched"); }); }); @@ -396,10 +396,10 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); run(() => { - assert.strictEqual(account.get('users.length'), 1, 'Users were setup correctly'); - account.get('users').removeObject(user); - assert.strictEqual(user.get('accounts.length'), 0, 'Accounts were removed correctly'); - assert.strictEqual(account.get('users.length'), 0, 'Users were removed correctly'); + assert.strictEqual(account.users.length, 1, 'Users were setup correctly'); + account.users.removeObject(user); + assert.strictEqual(user.accounts.length, 0, 'Accounts were removed correctly'); + assert.strictEqual(account.users.length, 0, 'Users were removed correctly'); }); }); @@ -448,12 +448,12 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); return run(() => { - let users = topic.get('users').then((fetchedUsers) => { - assert.strictEqual(fetchedUsers.get('length'), 1, 'Users are still there'); + let users = topic.users.then((fetchedUsers) => { + assert.strictEqual(fetchedUsers.length, 1, 'Users are still there'); }); - let topics = user.get('topics').then((fetchedTopics) => { - assert.strictEqual(fetchedTopics.get('length'), 1, 'Topic got rollbacked into the user'); + let topics = user.topics.then((fetchedTopics) => { + assert.strictEqual(fetchedTopics.length, 1, 'Topic got rollbacked into the user'); }); return EmberPromise.all([users, topics]); @@ -498,8 +498,8 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', run(() => { account.deleteRecord(); account.rollbackAttributes(); - assert.strictEqual(account.get('users.length'), 1, 'Users are still there'); - assert.strictEqual(user.get('accounts.length'), 1, 'Account got rolledback correctly into the user'); + assert.strictEqual(account.users.length, 1, 'Users are still there'); + assert.strictEqual(user.accounts.length, 1, 'Account got rolledback correctly into the user'); }); }); @@ -522,17 +522,17 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); return run(() => { - return user.get('topics').then((fetchedTopics) => { + return user.topics.then((fetchedTopics) => { fetchedTopics.pushObject(topic); topic.rollbackAttributes(); - let users = topic.get('users').then((fetchedUsers) => { - assert.strictEqual(fetchedUsers.get('length'), 0, 'Users got removed'); + let users = topic.users.then((fetchedUsers) => { + assert.strictEqual(fetchedUsers.length, 0, 'Users got removed'); assert.strictEqual(fetchedUsers.objectAt(0), undefined, "User can't be fetched"); }); - let topics = user.get('topics').then((fetchedTopics) => { - assert.strictEqual(fetchedTopics.get('length'), 0, 'Topics got removed'); + let topics = user.topics.then((fetchedTopics) => { + assert.strictEqual(fetchedTopics.length, 0, 'Topics got removed'); assert.strictEqual(fetchedTopics.objectAt(0), undefined, "Topic can't be fetched"); }); @@ -560,12 +560,12 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); run(() => { - account.get('users').pushObject(user); + account.users.pushObject(user); user.rollbackAttributes(); }); - assert.strictEqual(account.get('users.length'), 0, 'Users got removed'); - assert.strictEqual(user.get('accounts.length'), 0, 'Accounts got rolledback correctly'); + assert.strictEqual(account.users.length, 0, 'Users got removed'); + assert.strictEqual(user.accounts.length, 0, 'Accounts got rolledback correctly'); }); todo( @@ -625,7 +625,7 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }, }, }); - account.get('users').removeObject(byron); + account.users.removeObject(byron); account = store.push({ data: { id: '2', @@ -652,9 +652,9 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); let state = account.hasMany('users').hasManyRelationship.canonicalState; - let users = account.get('users'); + let users = account.users; - assert.todo.equal(users.get('length'), 1, 'Accounts were updated correctly (ui state)'); + assert.todo.equal(users.length, 1, 'Accounts were updated correctly (ui state)'); assert.todo.deepEqual( users.map((r) => get(r, 'id')), ['1'], diff --git a/packages/-ember-data/tests/integration/relationships/nested-relationship-test.js b/packages/-ember-data/tests/integration/relationships/nested-relationship-test.js index 3515cdcaf58..1295c008409 100644 --- a/packages/-ember-data/tests/integration/relationships/nested-relationship-test.js +++ b/packages/-ember-data/tests/integration/relationships/nested-relationship-test.js @@ -131,14 +131,14 @@ module('integration/relationships/nested_relationships_test - Nested relationshi return run(() => { let kid = store.peekRecord('kid', '1'); - return kid.get('middleAger').then((middleAger) => { + return kid.middleAger.then((middleAger) => { assert.ok(middleAger, 'MiddleAger relationship was set up correctly'); let middleAgerName = get(middleAger, 'name'); assert.strictEqual(middleAgerName, 'Middle Ager 1', 'MiddleAger name is there'); - assert.ok(middleAger.get('kids').includes(kid)); + assert.ok(middleAger.kids.includes(kid)); - return middleAger.get('elder').then((elder) => { + return middleAger.elder.then((elder) => { assert.notEqual(elder, null, 'Elder relationship was set up correctly'); let elderName = get(elder, 'name'); assert.strictEqual(elderName, 'Elder 1', 'Elder name is there'); diff --git a/packages/-ember-data/tests/integration/relationships/one-to-many-test.js b/packages/-ember-data/tests/integration/relationships/one-to-many-test.js index 0deb19ca591..d6ff504bf1d 100644 --- a/packages/-ember-data/tests/integration/relationships/one-to-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/one-to-many-test.js @@ -81,7 +81,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); }); run(function () { - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'User relationship was set up correctly'); }); }); @@ -179,7 +179,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }, }); }); - assert.strictEqual(account.get('user'), user, 'User relationship was set up correctly'); + assert.strictEqual(account.user, user, 'User relationship was set up correctly'); }); test('Relationship is available from the hasMany side even if only loaded from the belongsTo side - async', function (assert) { @@ -215,7 +215,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { assert.strictEqual(fetchedMessages.objectAt(0), message, 'Messages relationship was set up correctly'); }); }); @@ -254,7 +254,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); }); run(function () { - assert.strictEqual(user.get('accounts').objectAt(0), account, 'Accounts relationship was set up correctly'); + assert.strictEqual(user.accounts.objectAt(0), account, 'Accounts relationship was set up correctly'); }); }); @@ -321,7 +321,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { assert.strictEqual(get(fetchedMessages, 'length'), 1, 'Messages relationship was set up correctly'); }); }); @@ -379,7 +379,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(function () { - assert.strictEqual(user.get('accounts').objectAt(0), undefined, 'Account was sucesfully removed'); + assert.strictEqual(user.accounts.objectAt(0), undefined, 'Account was sucesfully removed'); }); }); @@ -441,7 +441,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { assert.strictEqual(get(fetchedMessages, 'length'), 2, 'Messages relationship was set up correctly'); }); }); @@ -492,7 +492,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(function () { - assert.strictEqual(user.get('accounts').objectAt(0), account, 'Account was sucesfully removed'); + assert.strictEqual(user.accounts.objectAt(0), account, 'Account was sucesfully removed'); }); }); @@ -569,11 +569,11 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); }); run(function () { - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, null, 'User was removed correctly'); }); - message2.get('user').then(function (fetchedUser) { + message2.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'User was set on the second message'); }); }); @@ -648,8 +648,8 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(function () { - assert.strictEqual(account1.get('user'), null, 'User was removed correctly'); - assert.strictEqual(account2.get('user'), user, 'User was added correctly'); + assert.strictEqual(account1.user, null, 'User was removed correctly'); + assert.strictEqual(account2.user, user, 'User was added correctly'); }); }); @@ -706,7 +706,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(function () { - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'User was not removed'); }); }); @@ -774,7 +774,7 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(function () { - assert.strictEqual(account.get('user'), user, 'User was not removed'); + assert.strictEqual(account.user, user, 'User was not removed'); }); }); @@ -827,9 +827,9 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { fetchedMessages.pushObject(message2); - message2.get('user').then(function (fetchedUser) { + message2.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'user got set correctly'); }); }); @@ -887,10 +887,10 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }, }, }); - user.get('accounts').pushObject(account2); + user.accounts.pushObject(account2); }); - assert.strictEqual(account2.get('user'), user, 'user got set correctly'); + assert.strictEqual(account2.user, user, 'user got set correctly'); }); test('Removing from the hasMany side reflects the change on the belongsTo side - async', function (assert) { @@ -929,9 +929,9 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { fetchedMessages.removeObject(message); - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, null, 'user got removed correctly'); }); }); @@ -981,10 +981,10 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); }); run(function () { - user.get('accounts').removeObject(account); + user.accounts.removeObject(account); }); - assert.strictEqual(account.get('user'), null, 'user got removed correctly'); + assert.strictEqual(account.user, null, 'user got removed correctly'); }); test('Pushing to the hasMany side keeps the oneToMany invariant on the belongsTo side - async', function (assert) { @@ -1034,14 +1034,14 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(function () { - user2.get('messages').then(function (fetchedMessages) { + user2.messages.then(function (fetchedMessages) { fetchedMessages.pushObject(message); - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user2, 'user got set correctly'); }); - user.get('messages').then(function (newFetchedMessages) { + user.messages.then(function (newFetchedMessages) { assert.strictEqual(get(newFetchedMessages, 'length'), 0, 'message got removed from the old messages hasMany'); }); }); @@ -1090,11 +1090,11 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }, }, }); - user2.get('accounts').pushObject(account); + user2.accounts.pushObject(account); }); - assert.strictEqual(account.get('user'), user2, 'user got set correctly'); - assert.strictEqual(user.get('accounts.length'), 0, 'the account got removed correctly'); - assert.strictEqual(user2.get('accounts.length'), 1, 'the account got pushed correctly'); + assert.strictEqual(account.user, user2, 'user got set correctly'); + assert.strictEqual(user.accounts.length, 0, 'the account got removed correctly'); + assert.strictEqual(user2.accounts.length, 1, 'the account got pushed correctly'); }); test('Setting the belongsTo side keeps the oneToMany invariant on the hasMany- async', function (assert) { @@ -1153,12 +1153,12 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { assert.strictEqual(get(fetchedMessages, 'length'), 0, 'message got removed from the first user correctly'); }); }); run(function () { - user2.get('messages').then(function (fetchedMessages) { + user2.messages.then(function (fetchedMessages) { assert.strictEqual(get(fetchedMessages, 'length'), 1, 'message got added to the second user correctly'); }); }); @@ -1216,9 +1216,9 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); account.set('user', user2); }); - assert.strictEqual(account.get('user'), user2, 'user got set correctly'); - assert.strictEqual(user.get('accounts.length'), 0, 'the account got removed correctly'); - assert.strictEqual(user2.get('accounts.length'), 1, 'the account got pushed correctly'); + assert.strictEqual(account.user, user2, 'user got set correctly'); + assert.strictEqual(user.accounts.length, 0, 'the account got removed correctly'); + assert.strictEqual(user2.accounts.length, 1, 'the account got pushed correctly'); }); test('Setting the belongsTo side to null removes the record from the hasMany side - async', function (assert) { @@ -1267,13 +1267,13 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f message.set('user', null); }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { assert.strictEqual(get(fetchedMessages, 'length'), 0, 'message got removed from the user correctly'); }); }); run(function () { - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, null, 'user got set to null correctly'); }); }); @@ -1323,9 +1323,9 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f account.set('user', null); }); - assert.strictEqual(account.get('user'), null, 'user got set to null correctly'); + assert.strictEqual(account.user, null, 'user got set to null correctly'); - assert.strictEqual(user.get('accounts.length'), 0, 'the account got removed correctly'); + assert.strictEqual(user.accounts.length, 0, 'the account got removed correctly'); }); /* @@ -1371,10 +1371,10 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f message.rollbackAttributes(); }); run(function () { - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'Message still has the user'); }); - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { assert.strictEqual(fetchedMessages.objectAt(0), message, 'User has the message'); }); }); @@ -1417,8 +1417,8 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f run(function () { account.deleteRecord(); account.rollbackAttributes(); - assert.strictEqual(user.get('accounts.length'), 1, 'Accounts are rolled back'); - assert.strictEqual(account.get('user'), user, 'Account still has the user'); + assert.strictEqual(user.accounts.length, 1, 'Accounts are rolled back'); + assert.strictEqual(account.user, user, 'Account still has the user'); }); }); @@ -1461,11 +1461,11 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f user.rollbackAttributes(); }); run(function () { - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'Message has the user again'); }); - user.get('messages').then(function (fetchedMessages) { - assert.strictEqual(fetchedMessages.get('length'), 1, 'User still has the messages'); + user.messages.then(function (fetchedMessages) { + assert.strictEqual(fetchedMessages.length, 1, 'User still has the messages'); }); }); }); @@ -1507,8 +1507,8 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f run(function () { user.deleteRecord(); user.rollbackAttributes(); - assert.strictEqual(user.get('accounts.length'), 1, 'User still has the accounts'); - assert.strictEqual(account.get('user'), user, 'Account has the user again'); + assert.strictEqual(user.accounts.length, 1, 'User still has the accounts'); + assert.strictEqual(account.user, user, 'Account has the user again'); }); }); @@ -1536,12 +1536,12 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); run(message, 'rollbackAttributes'); run(function () { - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, null, 'Message does not have the user anymore'); }); - user.get('messages').then(function (fetchedMessages) { - assert.strictEqual(fetchedMessages.get('length'), 0, 'User does not have the message anymore'); - assert.strictEqual(fetchedMessages.get('firstObject'), undefined, "User message can't be accessed"); + user.messages.then(function (fetchedMessages) { + assert.strictEqual(fetchedMessages.length, 0, 'User does not have the message anymore'); + assert.strictEqual(fetchedMessages.firstObject, undefined, "User message can't be accessed"); }); }); }); @@ -1565,8 +1565,8 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }); }); run(account, 'rollbackAttributes'); - assert.strictEqual(user.get('accounts.length'), 0, 'Accounts are rolled back'); - assert.strictEqual(account.get('user'), null, 'Account does not have the user anymore'); + assert.strictEqual(user.accounts.length, 0, 'Accounts are rolled back'); + assert.strictEqual(account.user, null, 'Account does not have the user anymore'); }); test('Rollbacking attributes of a created record works correctly when the belongsTo side has been created - async', function (assert) { @@ -1586,15 +1586,15 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f user = store.createRecord('user'); }); run(function () { - user.get('messages').then(function (messages) { + user.messages.then(function (messages) { messages.pushObject(message); user.rollbackAttributes(); - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, null, 'Message does not have the user anymore'); }); - user.get('messages').then(function (fetchedMessages) { - assert.strictEqual(fetchedMessages.get('length'), 0, 'User does not have the message anymore'); - assert.strictEqual(fetchedMessages.get('firstObject'), undefined, "User message can't be accessed"); + user.messages.then(function (fetchedMessages) { + assert.strictEqual(fetchedMessages.length, 0, 'User does not have the message anymore'); + assert.strictEqual(fetchedMessages.firstObject, undefined, "User message can't be accessed"); }); }); }); @@ -1617,11 +1617,11 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f user = store.createRecord('user'); }); run(function () { - user.get('accounts').pushObject(account); + user.accounts.pushObject(account); }); run(user, 'rollbackAttributes'); - assert.strictEqual(user.get('accounts.length'), 0, 'User does not have the account anymore'); - assert.strictEqual(account.get('user'), null, 'Account does not have the user anymore'); + assert.strictEqual(user.accounts.length, 0, 'User does not have the account anymore'); + assert.strictEqual(account.user, null, 'Account does not have the user anymore'); }); test('createRecord updates inverse record array which has observers', async function (assert) { @@ -1643,14 +1643,14 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f }; const users = await store.findAll('user'); - assert.strictEqual(users.get('length'), 1, 'Exactly 1 user'); + assert.strictEqual(users.length, 1, 'Exactly 1 user'); - let user = users.get('firstObject'); - assert.strictEqual(user.get('messages.length'), 0, 'Record array is initially empty'); + let user = users.firstObject; + assert.strictEqual(user.messages.length, 0, 'Record array is initially empty'); // set up an observer user.addObserver('messages.@each.title', () => {}); - user.get('messages.firstObject'); + user.messages.firstObject; const messages = await user.messages; diff --git a/packages/-ember-data/tests/integration/relationships/one-to-one-test.js b/packages/-ember-data/tests/integration/relationships/one-to-one-test.js index a4b86d8e8f1..0f847b7dc26 100644 --- a/packages/-ember-data/tests/integration/relationships/one-to-one-test.js +++ b/packages/-ember-data/tests/integration/relationships/one-to-one-test.js @@ -76,7 +76,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }, }); - stanleysFriend.get('bestFriend').then(function (fetchedUser) { + stanleysFriend.bestFriend.then(function (fetchedUser) { assert.strictEqual(fetchedUser, stanley, 'User relationship was set up correctly'); }); }); @@ -114,7 +114,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }, }); }); - assert.strictEqual(job.get('user'), user, 'User relationship was set up correctly'); + assert.strictEqual(job.user, user, 'User relationship was set up correctly'); }); test('Fetching a belongsTo that is set to null removes the record from a relationship - async', function (assert) { @@ -153,7 +153,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }, }, }); - stanleysFriend.get('bestFriend').then(function (fetchedUser) { + stanleysFriend.bestFriend.then(function (fetchedUser) { assert.strictEqual(fetchedUser, null, 'User relationship was removed correctly'); }); }); @@ -207,7 +207,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }, }); }); - assert.strictEqual(job.get('user'), null, 'User relationship was removed correctly'); + assert.strictEqual(job.user, null, 'User relationship was removed correctly'); }); test('Fetching a belongsTo that is set to a different record, sets the old relationship to null - async', async function (assert) { @@ -239,7 +239,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }); let user2 = store.peekRecord('user', '2'); - let user1Friend = await user1.get('bestFriend'); + let user1Friend = await user1.bestFriend; assert.strictEqual(user1Friend, user2, '.bestFriend is '); @@ -280,9 +280,9 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }); let user3 = store.peekRecord('user', '3'); - let user1bestFriend = await user1.get('bestFriend'); - let user2bestFriend = await user2.get('bestFriend'); - let user3bestFriend = await user3.get('bestFriend'); + let user1bestFriend = await user1.bestFriend; + let user2bestFriend = await user2.bestFriend; + let user3bestFriend = await user3.bestFriend; assert.strictEqual(user3bestFriend, user2, '.bestFriend is '); assert.strictEqual(user2bestFriend, user3, '.bestFriend is '); @@ -328,7 +328,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun let job1 = store.peekRecord('job', '1'); - assert.strictEqual(user1.get('job'), job1, '.job is '); + assert.strictEqual(user1.job, job1, '.job is '); /* Now we "reload" but with a new user. While this only gives @@ -368,9 +368,9 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun let user2 = store.peekRecord('user', '2'); - assert.strictEqual(user2.get('job'), job1, '.job is '); - assert.strictEqual(job1.get('user'), user2, '.user is '); - assert.strictEqual(user1.get('job'), null, '.job is null'); + assert.strictEqual(user2.job, job1, '.job is '); + assert.strictEqual(job1.user, user2, '.user is '); + assert.strictEqual(user1.job, null, '.job is null'); let user1JobState = user1.belongsTo('job').belongsToRelationship; @@ -412,7 +412,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }); run(function () { stanley.set('bestFriend', stanleysFriend); - stanleysFriend.get('bestFriend').then(function (fetchedUser) { + stanleysFriend.bestFriend.then(function (fetchedUser) { assert.strictEqual(fetchedUser, stanley, 'User relationship was updated correctly'); }); }); @@ -445,7 +445,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun run(function () { user.set('job', job); }); - assert.strictEqual(job.get('user'), user, 'User relationship was set up correctly'); + assert.strictEqual(job.user, user, 'User relationship was set up correctly'); }); test('Setting a BelongsTo to a promise unwraps the promise before setting- async', function (assert) { @@ -490,15 +490,15 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }); }); run(function () { - newFriend.set('bestFriend', stanleysFriend.get('bestFriend')); - stanley.get('bestFriend').then(function (fetchedUser) { + newFriend.set('bestFriend', stanleysFriend.bestFriend); + stanley.bestFriend.then(function (fetchedUser) { assert.strictEqual( fetchedUser, newFriend, `Stanley's bestFriend relationship was updated correctly to newFriend` ); }); - newFriend.get('bestFriend').then(function (fetchedUser) { + newFriend.bestFriend.then(function (fetchedUser) { assert.strictEqual( fetchedUser, stanley, @@ -550,8 +550,8 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }); }); run(function () { - newFriend.set('bestFriend', igor.get('bestFriend')); - newFriend.get('bestFriend').then(function (fetchedUser) { + newFriend.set('bestFriend', igor.bestFriend); + newFriend.bestFriend.then(function (fetchedUser) { assert.strictEqual(fetchedUser, null, 'User relationship was updated correctly'); }); }); @@ -665,10 +665,10 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }; run(function () { - newFriend.set('bestFriend', stanley.get('bestFriend')); - newFriend.set('bestFriend', igor.get('bestFriend')); - newFriend.get('bestFriend').then(function (fetchedUser) { - assert.strictEqual(fetchedUser.get('name'), "Igor's friend", 'User relationship was updated correctly'); + newFriend.set('bestFriend', stanley.bestFriend); + newFriend.set('bestFriend', igor.bestFriend); + newFriend.bestFriend.then(function (fetchedUser) { + assert.strictEqual(fetchedUser.name, "Igor's friend", 'User relationship was updated correctly'); }); }); }); @@ -716,7 +716,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun run(function () { stanley.set('bestFriend', null); // :( - stanleysFriend.get('bestFriend').then(function (fetchedUser) { + stanleysFriend.bestFriend.then(function (fetchedUser) { assert.strictEqual(fetchedUser, null, 'User relationship was removed correctly'); }); }); @@ -766,7 +766,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun run(function () { user.set('job', null); }); - assert.strictEqual(job.get('user'), null, 'User relationship was removed correctly'); + assert.strictEqual(job.user, null, 'User relationship was removed correctly'); }); test('Setting a belongsTo to a different record, sets the old relationship to null - async', function (assert) { @@ -811,7 +811,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }, }); - stanleysFriend.get('bestFriend').then(function (fetchedUser) { + stanleysFriend.bestFriend.then(function (fetchedUser) { assert.strictEqual(fetchedUser, stanley, 'User relationship was initally setup correctly'); var stanleysNewFriend = store.push({ data: { @@ -827,11 +827,11 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun stanleysNewFriend.set('bestFriend', stanley); }); - stanley.get('bestFriend').then(function (fetchedNewFriend) { + stanley.bestFriend.then(function (fetchedNewFriend) { assert.strictEqual(fetchedNewFriend, stanleysNewFriend, 'User relationship was updated correctly'); }); - stanleysFriend.get('bestFriend').then(function (fetchedOldFriend) { + stanleysFriend.bestFriend.then(function (fetchedOldFriend) { assert.strictEqual(fetchedOldFriend, null, 'The old relationship was set to null correctly'); }); }); @@ -871,7 +871,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }); }); - assert.strictEqual(job.get('user'), user, 'Job and user initially setup correctly'); + assert.strictEqual(job.user, user, 'Job and user initially setup correctly'); run(function () { newBetterJob = store.push({ @@ -887,9 +887,9 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun newBetterJob.set('user', user); }); - assert.strictEqual(user.get('job'), newBetterJob, 'Job updated correctly'); - assert.strictEqual(job.get('user'), null, 'Old relationship nulled out correctly'); - assert.strictEqual(newBetterJob.get('user'), user, 'New job setup correctly'); + assert.strictEqual(user.job, newBetterJob, 'Job updated correctly'); + assert.strictEqual(job.user, null, 'Old relationship nulled out correctly'); + assert.strictEqual(newBetterJob.user, user, 'New job setup correctly'); }); /* @@ -933,10 +933,10 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }); run(function () { stanley.rollbackAttributes(); - stanleysFriend.get('bestFriend').then(function (fetchedUser) { + stanleysFriend.bestFriend.then(function (fetchedUser) { assert.strictEqual(fetchedUser, stanley, 'Stanley got rollbacked correctly'); }); - stanley.get('bestFriend').then(function (fetchedUser) { + stanley.bestFriend.then(function (fetchedUser) { assert.strictEqual(fetchedUser, stanleysFriend, 'Stanleys friend did not get removed'); }); }); @@ -978,8 +978,8 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun job.deleteRecord(); job.rollbackAttributes(); }); - assert.strictEqual(user.get('job'), job, 'Job got rollbacked correctly'); - assert.strictEqual(job.get('user'), user, 'Job still has the user'); + assert.strictEqual(user.job, job, 'Job got rollbacked correctly'); + assert.strictEqual(job.user, user, 'Job still has the user'); }); test('Rollbacking attributes of created record removes the relationship on both sides - async', async function (assert) { diff --git a/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js b/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js index b2ae81e548c..e5cafa732c5 100644 --- a/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js +++ b/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js @@ -75,9 +75,9 @@ module( video = store.peekRecord('video', 2); }); run(function () { - user.get('bestMessage').then(function (message) { + user.bestMessage.then(function (message) { assert.strictEqual(message, video, 'The message was loaded correctly'); - message.get('user').then(function (fetchedUser) { + message.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'The inverse was setup correctly'); }); }); @@ -116,10 +116,10 @@ module( run(function () { user.set('bestMessage', video); - video.get('user').then(function (fetchedUser) { + video.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'user got set correctly'); }); - user.get('bestMessage').then(function (message) { + user.bestMessage.then(function (message) { assert.strictEqual(message, video, 'The message was set correctly'); }); }); @@ -193,10 +193,10 @@ module( run(function () { user.set('bestMessage', video); - video.get('user').then(function (fetchedUser) { + video.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'user got set correctly'); }); - user.get('bestMessage').then(function (message) { + user.bestMessage.then(function (message) { assert.strictEqual(message, video, 'The message was set correctly'); }); }); diff --git a/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js b/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js index 07b3858c281..6ce689fa8fe 100644 --- a/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js @@ -77,11 +77,11 @@ module( video = store.peekRecord('video', 2); }); run(function () { - user.get('messages').then(function (messages) { + user.messages.then(function (messages) { assert.strictEqual(messages.objectAt(0), video, 'The hasMany has loaded correctly'); messages .objectAt(0) - .get('user') + .user .then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'The inverse was setup correctly'); }); @@ -125,9 +125,9 @@ module( }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { fetchedMessages.pushObject(video); - video.get('user').then(function (fetchedUser) { + video.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'user got set correctly'); }); }); @@ -171,9 +171,9 @@ module( }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { fetchedMessages.pushObject(video); - video.get('user').then(function (fetchedUser) { + video.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'user got set correctly'); }); }); @@ -218,7 +218,7 @@ module( }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { assert.expectAssertion(function () { fetchedMessages.pushObject(notMessage); }, /The 'not-message' type does not implement 'message' and thus cannot be assigned to the 'messages' relationship in 'user'. Make it a descendant of 'message/); @@ -260,9 +260,9 @@ module( }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { fetchedMessages.pushObject(video); - video.get('user').then(function (fetchedUser) { + video.user.then(function (fetchedUser) { assert.strictEqual(fetchedUser, user, 'user got set correctly'); }); }); @@ -307,7 +307,7 @@ module( }); run(function () { - user.get('messages').then(function (fetchedMessages) { + user.messages.then(function (fetchedMessages) { assert.expectAssertion(function () { fetchedMessages.pushObject(notMessage); }, /The 'not-message' type does not implement 'message' and thus cannot be assigned to the 'messages' relationship in 'user'. Make it a descendant of 'message'/); diff --git a/packages/-ember-data/tests/integration/serializers/embedded-records-mixin-test.js b/packages/-ember-data/tests/integration/serializers/embedded-records-mixin-test.js index 561b3727019..7807e457448 100644 --- a/packages/-ember-data/tests/integration/serializers/embedded-records-mixin-test.js +++ b/packages/-ember-data/tests/integration/serializers/embedded-records-mixin-test.js @@ -1597,14 +1597,14 @@ module('integration/embedded-records-mixin', function (hooks) { superVillain, }); - superVillain.get('secretWeapons').pushObject(secretWeapon); + superVillain.secretWeapons.pushObject(secretWeapon); let evilMinion = store.createRecord('evil-minion', { id: '1', name: 'Evil Minion', superVillain, }); - superVillain.get('evilMinions').pushObject(evilMinion); + superVillain.evilMinions.pushObject(evilMinion); const serializer = store.serializerFor('super-villain'); const serializedRestJson = serializer.serialize(superVillain._createSnapshot()); @@ -1687,13 +1687,13 @@ module('integration/embedded-records-mixin', function (hooks) { superVillain, }); - superVillain.get('secretWeapons').pushObject(secretWeapon); + superVillain.secretWeapons.pushObject(secretWeapon); let evilMinion = store.createRecord('evil-minion', { id: '1', name: 'Evil Minion', superVillain, }); - superVillain.get('evilMinions').pushObject(evilMinion); + superVillain.evilMinions.pushObject(evilMinion); const serializer = store.serializerFor('super-villain'); const serializedRestJson = serializer.serialize(superVillain._createSnapshot()); @@ -1936,8 +1936,8 @@ module('integration/embedded-records-mixin', function (hooks) { superVillain, }); - superVillain.get('evilMinions').pushObject(evilMinion); - superVillain.get('secretWeapons').pushObject(secretWeapon); + superVillain.evilMinions.pushObject(evilMinion); + superVillain.secretWeapons.pushObject(secretWeapon); const serializer = store.serializerFor('super-villain'); const serializedRestJson = serializer.serialize(superVillain._createSnapshot()); diff --git a/packages/-ember-data/tests/integration/serializers/json-serializer-test.js b/packages/-ember-data/tests/integration/serializers/json-serializer-test.js index 15c47964f93..011d5bb20a1 100644 --- a/packages/-ember-data/tests/integration/serializers/json-serializer-test.js +++ b/packages/-ember-data/tests/integration/serializers/json-serializer-test.js @@ -307,7 +307,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); run(function () { - post.get('comments').pushObject(comment); + post.comments.pushObject(comment); }); let json = {}; diff --git a/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js b/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js index 77c329ac3d8..576d15322d3 100644 --- a/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js +++ b/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js @@ -682,10 +682,10 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { store .findRecord('doomsday-device', 1) .then((deathRay) => { - return deathRay.get('evilMinion'); + return deathRay.evilMinion; }) .then((evilMinion) => { - assert.strictEqual(evilMinion.get('eyes'), 3); + assert.strictEqual(evilMinion.eyes, 3); }); }); }); @@ -723,10 +723,10 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { store .findRecord('doomsday-device', 1) .then((deathRay) => { - return deathRay.get('evilMinion'); + return deathRay.evilMinion; }) .then((evilMinion) => { - assert.strictEqual(evilMinion.get('eyes'), 3); + assert.strictEqual(evilMinion.eyes, 3); }); }); }); @@ -790,11 +790,11 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { store .findRecord('super-villain2', '1') .then((superVillain) => { - return superVillain.get('evilMinions'); + return superVillain.evilMinions; }) .then((evilMinions) => { - assert.ok(evilMinions.get('firstObject') instanceof YellowMinion, 'we have an instance'); - assert.strictEqual(evilMinions.get('firstObject.eyes'), 3, 'we have the right minion'); + assert.ok(evilMinions.firstObject instanceof YellowMinion, 'we have an instance'); + assert.strictEqual(evilMinions.firstObject.eyes, 3, 'we have the right minion'); }); }); }); @@ -874,13 +874,13 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { const normalRecord = store.peekRecord('basket', '1'); assert.ok(normalRecord, "payload with type that doesn't exist"); - assert.strictEqual(normalRecord.get('type'), 'bamboo'); - assert.strictEqual(normalRecord.get('size'), 10); + assert.strictEqual(normalRecord.type, 'bamboo'); + assert.strictEqual(normalRecord.size, 10); const clashingRecord = store.peekRecord('basket', '65536'); assert.ok(clashingRecord, 'payload with type that matches another model name'); - assert.strictEqual(clashingRecord.get('type'), 'yellowMinion'); - assert.strictEqual(clashingRecord.get('size'), 10); + assert.strictEqual(clashingRecord.type, 'yellowMinion'); + assert.strictEqual(clashingRecord.size, 10); }); test("don't polymorphically deserialize base on the type key in payload when a type attribute exist on a singular response", function (assert) { @@ -902,8 +902,8 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { const clashingRecord = store.peekRecord('basket', '65536'); assert.ok(clashingRecord, 'payload with type that matches another model name'); - assert.strictEqual(clashingRecord.get('type'), 'yellowMinion'); - assert.strictEqual(clashingRecord.get('size'), 10); + assert.strictEqual(clashingRecord.type, 'yellowMinion'); + assert.strictEqual(clashingRecord.size, 10); }); test("don't polymorphically deserialize based on the type key in payload when a relationship exists named type", function (assert) { @@ -921,12 +921,12 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { store .findRecord('container', 42) .then((container) => { - assert.strictEqual(container.get('volume'), '10 liters'); - return container.get('type'); + assert.strictEqual(container.volume, '10 liters'); + return container.type; }) .then((basket) => { assert.ok(basket instanceof Basket); - assert.strictEqual(basket.get('size'), 4); + assert.strictEqual(basket.size, 4); }); }); }); diff --git a/packages/-ember-data/tests/integration/snapshot-test.js b/packages/-ember-data/tests/integration/snapshot-test.js index 5353bb24772..dcb8b07b7e9 100644 --- a/packages/-ember-data/tests/integration/snapshot-test.js +++ b/packages/-ember-data/tests/integration/snapshot-test.js @@ -508,7 +508,7 @@ module('integration/snapshot - Snapshot', function (hooks) { let comment = store.peekRecord('comment', 2); assert.strictEqual(comment._createSnapshot().belongsTo('post'), undefined, 'relationship is undefined'); - await comment.get('post'); + await comment.post; assert.strictEqual(comment._createSnapshot().belongsTo('post'), undefined, 'relationship is undefined'); }); @@ -561,7 +561,7 @@ module('integration/snapshot - Snapshot', function (hooks) { }); let comment = store.peekRecord('comment', 2); - await comment.get('post').then((post) => { + await comment.post.then((post) => { store.push({ data: [ { @@ -582,7 +582,7 @@ module('integration/snapshot - Snapshot', function (hooks) { }); let comment = store.peekRecord('comment', 2); - post.get('comments').then((comments) => { + post.comments.then((comments) => { comments.addObject(comment); let postSnapshot = post._createSnapshot(); @@ -628,7 +628,7 @@ module('integration/snapshot - Snapshot', function (hooks) { let post = store.peekRecord('post', 1); let comment = store.peekRecord('comment', 2); - const comments = await post.get('comments'); + const comments = await post.comments; comments.addObject(comment); let postSnapshot = post._createSnapshot(); @@ -1036,7 +1036,7 @@ module('integration/snapshot - Snapshot', function (hooks) { let post = store.peekRecord('post', 1); - await post.get('comments').then((comments) => { + await post.comments.then((comments) => { let snapshot = post._createSnapshot(); let relationship = snapshot.hasMany('comments'); diff --git a/packages/-ember-data/tests/integration/store-test.js b/packages/-ember-data/tests/integration/store-test.js index 084c515949f..c04f15337c7 100644 --- a/packages/-ember-data/tests/integration/store-test.js +++ b/packages/-ember-data/tests/integration/store-test.js @@ -226,7 +226,7 @@ module('integration/store - destroy', function (hooks) { let personWillDestroy = tap(person, 'willDestroy'); let carWillDestroy = tap(car, 'willDestroy'); - let carsWillDestroy = tap(car.get('person.cars'), 'willDestroy'); + let carsWillDestroy = tap(car.person.cars, 'willDestroy'); adapter.query = function () { return { @@ -256,12 +256,8 @@ module('integration/store - destroy', function (hooks) { 0, 'expected adapterPopulatedPeople.willDestroy to not have been called' ); - assert.strictEqual(car.get('person'), person, "expected car's person to be the correct person"); - assert.strictEqual( - person.get('cars.firstObject'), - car, - " expected persons cars's firstRecord to be the correct car" - ); + assert.strictEqual(car.person, person, "expected car's person to be the correct person"); + assert.strictEqual(person.cars.firstObject, car, " expected persons cars's firstRecord to be the correct car"); store.destroy(); @@ -310,7 +306,7 @@ module('integration/store - findRecord', function (hooks) { let car = await store.findRecord('car', '20'); - assert.strictEqual(car.get('make'), 'BMC', 'Car with id=20 is now loaded'); + assert.strictEqual(car.make, 'BMC', 'Car with id=20 is now loaded'); }); test('store#findRecord returns cached record immediately and reloads record in the background', async function (assert) { @@ -349,13 +345,13 @@ module('integration/store - findRecord', function (hooks) { const promiseCar = store.findRecord('car', '1'); const car = await promiseCar; - assert.strictEqual(promiseCar.get('model'), 'Mini', 'promiseCar is from cache'); - assert.strictEqual(car.get('model'), 'Mini', 'car record is returned from cache'); + assert.strictEqual(promiseCar.model, 'Mini', 'promiseCar is from cache'); + assert.strictEqual(car.model, 'Mini', 'car record is returned from cache'); await settled(); - assert.strictEqual(promiseCar.get('model'), 'Princess', 'promiseCar is updated'); - assert.strictEqual(car.get('model'), 'Princess', 'Updated car record is returned'); + assert.strictEqual(promiseCar.model, 'Princess', 'promiseCar is updated'); + assert.strictEqual(car.model, 'Princess', 'Updated car record is returned'); }); test('store#findRecord { reload: true } ignores cached record and reloads record from server', async function (assert) { @@ -394,7 +390,7 @@ module('integration/store - findRecord', function (hooks) { let cachedCar = store.peekRecord('car', '1'); - assert.strictEqual(cachedCar.get('model'), 'Mini', 'cached car has expected model'); + assert.strictEqual(cachedCar.model, 'Mini', 'cached car has expected model'); let car = await store.findRecord('car', '1', { reload: true }); @@ -438,12 +434,12 @@ module('integration/store - findRecord', function (hooks) { let promiseCar = store.findRecord('car', '1', { reload: true }); - assert.strictEqual(promiseCar.get('model'), undefined, `We don't have early access to local data`); + assert.strictEqual(promiseCar.model, undefined, `We don't have early access to local data`); car = await promiseCar; assert.strictEqual(calls, 2, 'We made a second call to findRecord'); - assert.strictEqual(car.get('model'), 'Princess', 'cached record ignored, record reloaded via server'); + assert.strictEqual(car.model, 'Princess', 'cached record ignored, record reloaded via server'); }); test('store#findRecord caches the inflight requests', async function (assert) { @@ -670,11 +666,11 @@ module('integration/store - findRecord', function (hooks) { let car = store.peekRecord('car', '1'); - assert.strictEqual(car.get('model'), 'Mini', 'Car record is initially a Mini'); + assert.strictEqual(car.model, 'Mini', 'Car record is initially a Mini'); car = await store.findRecord('car', '1', { backgroundReload: false }); - assert.strictEqual(car.get('model'), 'Princess', 'Car record is reloaded immediately (not in the background)'); + assert.strictEqual(car.model, 'Princess', 'Car record is reloaded immediately (not in the background)'); }); test('store#findRecord call with `id` of type different than non-empty string or number should trigger an assertion', function (assert) { diff --git a/packages/-ember-data/tests/integration/store/query-test.js b/packages/-ember-data/tests/integration/store/query-test.js index 6a4d004c7a6..dcb912e0485 100644 --- a/packages/-ember-data/tests/integration/store/query-test.js +++ b/packages/-ember-data/tests/integration/store/query-test.js @@ -38,12 +38,12 @@ module('integration/store/query', function (hooks) { result = store.query('person', {}); }); - assert.notOk(result.get('meta.foo'), 'precond: meta is not yet set'); + assert.notOk(result.meta.foo, 'precond: meta is not yet set'); run(function () { defered.resolve({ data: [], meta: { foo: 'bar' } }); }); - assert.strictEqual(result.get('meta.foo'), 'bar'); + assert.strictEqual(result.meta.foo, 'bar'); }); }); diff --git a/packages/-ember-data/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js b/packages/-ember-data/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js index 4cd53ce2314..abe756fd3a3 100644 --- a/packages/-ember-data/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js +++ b/packages/-ember-data/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js @@ -44,7 +44,7 @@ module( .join('&'); let fullUrl = url + '?' + queryString; - maxLength = this.get('maxURLLength'); + maxLength = this.maxURLLength; lengths.push(fullUrl.length); let testRecords = options.data.ids.map((id) => ({ id })); diff --git a/packages/-ember-data/tests/unit/many-array-test.js b/packages/-ember-data/tests/unit/many-array-test.js index 60e55415539..11af4ceac4b 100644 --- a/packages/-ember-data/tests/unit/many-array-test.js +++ b/packages/-ember-data/tests/unit/many-array-test.js @@ -72,7 +72,7 @@ module('unit/many_array - ManyArray', function (hooks) { let post = store.peekRecord('post', 3); return post - .get('tags') + .tags .save() .then(() => { assert.ok(true, 'manyArray.save() promise resolved'); @@ -166,7 +166,7 @@ module('unit/many_array - ManyArray', function (hooks) { ], }); - store.peekRecord('post', 3).get('tags'); + store.peekRecord('post', 3).tags; store.push({ data: { diff --git a/packages/-ember-data/tests/unit/model-test.js b/packages/-ember-data/tests/unit/model-test.js index 163a86bd06f..de9adf56516 100644 --- a/packages/-ember-data/tests/unit/model-test.js +++ b/packages/-ember-data/tests/unit/model-test.js @@ -299,7 +299,7 @@ module('unit/model - Model', function (hooks) { test('setting the id during createRecord should correctly update the id', async function (assert) { let person = store.createRecord('person', { id: 'john' }); - assert.strictEqual(person.get('id'), 'john', 'new id should be correctly set.'); + assert.strictEqual(person.id, 'john', 'new id should be correctly set.'); let record = store.peekRecord('person', 'john'); @@ -309,11 +309,11 @@ module('unit/model - Model', function (hooks) { test('setting the id after createRecord should correctly update the id', async function (assert) { let person = store.createRecord('person'); - assert.strictEqual(person.get('id'), null, 'initial created model id should be null'); + assert.strictEqual(person.id, null, 'initial created model id should be null'); person.set('id', 'john'); - assert.strictEqual(person.get('id'), 'john', 'new id should be correctly set.'); + assert.strictEqual(person.id, 'john', 'new id should be correctly set.'); let record = store.peekRecord('person', 'john'); @@ -323,7 +323,7 @@ module('unit/model - Model', function (hooks) { testInDebug('mutating the id after createRecord but before save works', async function (assert) { let person = store.createRecord('person', { id: 'chris' }); - assert.strictEqual(person.get('id'), 'chris', 'initial created model id should be null'); + assert.strictEqual(person.id, 'chris', 'initial created model id should be null'); try { person.set('id', 'john'); @@ -343,22 +343,22 @@ module('unit/model - Model', function (hooks) { const OddPerson = Model.extend({ name: DSattr('string'), idComputed: computed('id', function () { - return this.get('id'); + return this.id; }), }); this.owner.register('model:odd-person', OddPerson); let person = store.createRecord('odd-person'); - let oddId = person.get('idComputed'); + let oddId = person.idComputed; assert.strictEqual(oddId, null, 'initial computed get is null'); // test .get access of id - assert.strictEqual(person.get('id'), null, 'initial created model id should be null'); + assert.strictEqual(person.id, null, 'initial created model id should be null'); const identifier = recordIdentifierFor(person); store._instanceCache.setRecordId('odd-person', 'john', identifier.lid); - oddId = person.get('idComputed'); + oddId = person.idComputed; assert.strictEqual(oddId, 'john', 'computed get is correct'); // test direct access of id assert.strictEqual(person.id, 'john', 'new id should be correctly set.'); @@ -379,7 +379,7 @@ module('unit/model - Model', function (hooks) { // we can locate it in the identity map let record = store.peekRecord('person', 0); - assert.strictEqual(record.get('name'), 'Tom Dale', 'found record with id 0'); + assert.strictEqual(record.name, 'Tom Dale', 'found record with id 0'); }); }); @@ -723,10 +723,10 @@ module('unit/model - Model', function (hooks) { assert.strictEqual(nameDidChange, 0, 'observer should not trigger on create'); let person = store.createRecord('legacy-person'); assert.strictEqual(nameDidChange, 0, 'observer should not trigger on create'); - assert.strictEqual(person.get('name'), 'my-name-set-in-init'); + assert.strictEqual(person.name, 'my-name-set-in-init'); person = store.createRecord('native-person'); - assert.strictEqual(person.get('name'), 'my-name-set-in-init'); + assert.strictEqual(person.name, 'my-name-set-in-init'); }); test('accessing attributes during init should not throw an error', async function (assert) { @@ -735,7 +735,7 @@ module('unit/model - Model', function (hooks) { init() { this._super(...arguments); - assert.strictEqual(this.get('name'), 'bam!', 'We all good here'); + assert.strictEqual(this.name, 'bam!', 'We all good here'); }, }); this.owner.register('model:odd-person', Person); @@ -895,13 +895,13 @@ module('unit/model - Model', function (hooks) { let person = await store.findRecord('person', '1'); - assert.false(person.get('hasDirtyAttributes'), 'precond - person record should not be dirty'); + assert.false(person.hasDirtyAttributes, 'precond - person record should not be dirty'); person.set('name', 'Peter'); person.set('isDrugAddict', true); assert.false( - person.get('hasDirtyAttributes'), + person.hasDirtyAttributes, 'record does not become dirty after setting property to old value' ); }); @@ -920,15 +920,15 @@ module('unit/model - Model', function (hooks) { let person = await store.findRecord('person', '1'); - assert.false(person.get('hasDirtyAttributes'), 'precond - person record should not be dirty'); + assert.false(person.hasDirtyAttributes, 'precond - person record should not be dirty'); person.set('isDrugAddict', false); - assert.true(person.get('hasDirtyAttributes'), 'record becomes dirty after setting property to a new value'); + assert.true(person.hasDirtyAttributes, 'record becomes dirty after setting property to a new value'); person.set('isDrugAddict', true); - assert.false(person.get('hasDirtyAttributes'), 'record becomes clean after resetting property to the old value'); + assert.false(person.hasDirtyAttributes, 'record becomes clean after resetting property to the old value'); }); test('resetting a property to the current in-flight value causes it to become clean when the save completes', async function (assert) { @@ -951,17 +951,17 @@ module('unit/model - Model', function (hooks) { let saving = person.save(); - assert.strictEqual(person.get('name'), 'Thomas'); + assert.strictEqual(person.name, 'Thomas'); person.set('name', 'Tomathy'); - assert.strictEqual(person.get('name'), 'Tomathy'); + assert.strictEqual(person.name, 'Tomathy'); person.set('name', 'Thomas'); - assert.strictEqual(person.get('name'), 'Thomas'); + assert.strictEqual(person.name, 'Thomas'); await saving; - assert.false(person.get('hasDirtyAttributes'), 'The person is now clean'); + assert.false(person.hasDirtyAttributes, 'The person is now clean'); }); test('a record becomes clean again only if all changed properties are reset', async function (assert) { @@ -978,19 +978,19 @@ module('unit/model - Model', function (hooks) { let person = await store.findRecord('person', 1); - assert.false(person.get('hasDirtyAttributes'), 'precond - person record should not be dirty'); + assert.false(person.hasDirtyAttributes, 'precond - person record should not be dirty'); person.set('isDrugAddict', false); - assert.true(person.get('hasDirtyAttributes'), 'record becomes dirty after setting one property to a new value'); + assert.true(person.hasDirtyAttributes, 'record becomes dirty after setting one property to a new value'); person.set('name', 'Mark'); - assert.true(person.get('hasDirtyAttributes'), 'record stays dirty after setting another property to a new value'); + assert.true(person.hasDirtyAttributes, 'record stays dirty after setting another property to a new value'); person.set('isDrugAddict', true); assert.true( - person.get('hasDirtyAttributes'), + person.hasDirtyAttributes, 'record stays dirty after resetting only one property to the old value' ); person.set('name', 'Peter'); assert.false( - person.get('hasDirtyAttributes'), + person.hasDirtyAttributes, 'record becomes clean after resetting both properties to the old value' ); }); @@ -1021,9 +1021,9 @@ module('unit/model - Model', function (hooks) { let person = store.peekRecord('person', 1); - assert.false(person.get('hasDirtyAttributes'), 'precond - person record should not be dirty'); + assert.false(person.hasDirtyAttributes, 'precond - person record should not be dirty'); person.set('name', 'Wolf'); - assert.true(person.get('hasDirtyAttributes'), 'record becomes dirty after setting one property to a new value'); + assert.true(person.hasDirtyAttributes, 'record becomes dirty after setting one property to a new value'); await person .save() @@ -1031,14 +1031,14 @@ module('unit/model - Model', function (hooks) { assert.ok(false, 'We should reject the save'); }) .catch(() => { - assert.false(person.get('isValid'), 'record is not valid'); - assert.true(person.get('hasDirtyAttributes'), 'record still has dirty attributes'); + assert.false(person.isValid, 'record is not valid'); + assert.true(person.hasDirtyAttributes, 'record still has dirty attributes'); person.set('name', 'Peter'); - assert.true(person.get('isValid'), 'record is valid after resetting attribute to old value'); + assert.true(person.isValid, 'record is valid after resetting attribute to old value'); assert.false( - person.get('hasDirtyAttributes'), + person.hasDirtyAttributes, 'record becomes clean after resetting property to the old value' ); }); @@ -1070,10 +1070,10 @@ module('unit/model - Model', function (hooks) { let person = store.peekRecord('person', 1); - assert.false(person.get('hasDirtyAttributes'), 'precond - person record should not be dirty'); + assert.false(person.hasDirtyAttributes, 'precond - person record should not be dirty'); person.set('name', 'Wolf'); person.set('isDrugAddict', false); - assert.true(person.get('hasDirtyAttributes'), 'record becomes dirty after setting one property to a new value'); + assert.true(person.hasDirtyAttributes, 'record becomes dirty after setting one property to a new value'); await person .save() @@ -1081,13 +1081,13 @@ module('unit/model - Model', function (hooks) { assert.ok(false, 'save should have rejected'); }) .catch(() => { - assert.false(person.get('isValid'), 'record is not valid'); - assert.true(person.get('hasDirtyAttributes'), 'record still has dirty attributes'); + assert.false(person.isValid, 'record is not valid'); + assert.true(person.hasDirtyAttributes, 'record still has dirty attributes'); person.set('name', 'Peter'); - assert.true(person.get('isValid'), 'record is valid after resetting invalid attribute to old value'); - assert.true(person.get('hasDirtyAttributes'), 'record still has dirty attributes'); + assert.true(person.isValid, 'record is valid after resetting invalid attribute to old value'); + assert.true(person.hasDirtyAttributes, 'record still has dirty attributes'); }); }); diff --git a/packages/-ember-data/tests/unit/model/errors-test.js b/packages/-ember-data/tests/unit/model/errors-test.js index 7363080691a..c31f034b2fd 100644 --- a/packages/-ember-data/tests/unit/model/errors-test.js +++ b/packages/-ember-data/tests/unit/model/errors-test.js @@ -44,35 +44,35 @@ module('unit/model/errors', function (hooks) { errors.add('firstName', 'error'); errors.trigger = assert.unexpectedSend; assert.ok(errors.has('firstName'), 'it has firstName errors'); - assert.strictEqual(errors.get('length'), 1, 'it has 1 error'); + assert.strictEqual(errors.length, 1, 'it has 1 error'); errors.add('firstName', ['error1', 'error2']); - assert.strictEqual(errors.get('length'), 3, 'it has 3 errors'); - assert.ok(!errors.get('isEmpty'), 'it is not empty'); + assert.strictEqual(errors.length, 3, 'it has 3 errors'); + assert.ok(!errors.isEmpty, 'it is not empty'); errors.add('lastName', 'error'); errors.add('lastName', 'error'); - assert.strictEqual(errors.get('length'), 4, 'it has 4 errors'); + assert.strictEqual(errors.length, 4, 'it has 4 errors'); }); testInDebug('get error', function (assert) { - assert.ok(errors.get('firstObject') === undefined, 'returns undefined'); + assert.ok(errors.firstObject === undefined, 'returns undefined'); errors.trigger = assert.becameInvalid; errors.add('firstName', 'error'); errors.trigger = assert.unexpectedSend; - assert.ok(errors.get('firstName').length === 1, 'returns errors'); - assert.deepEqual(errors.get('firstObject'), { attribute: 'firstName', message: 'error' }); + assert.ok(errors.firstName.length === 1, 'returns errors'); + assert.deepEqual(errors.firstObject, { attribute: 'firstName', message: 'error' }); errors.add('firstName', 'error2'); - assert.ok(errors.get('firstName').length === 2, 'returns errors'); + assert.ok(errors.firstName.length === 2, 'returns errors'); errors.add('lastName', 'error3'); assert.deepEqual(errors.toArray(), [ { attribute: 'firstName', message: 'error' }, { attribute: 'firstName', message: 'error2' }, { attribute: 'lastName', message: 'error3' }, ]); - assert.deepEqual(errors.get('firstName'), [ + assert.deepEqual(errors.firstName, [ { attribute: 'firstName', message: 'error' }, { attribute: 'firstName', message: 'error2' }, ]); - assert.deepEqual(errors.get('messages'), ['error', 'error2', 'error3']); + assert.deepEqual(errors.messages, ['error', 'error2', 'error3']); }); testInDebug('remove error', function (assert) { @@ -82,8 +82,8 @@ module('unit/model/errors', function (hooks) { errors.remove('firstName'); errors.trigger = assert.unexpectedSend; assert.ok(!errors.has('firstName'), 'it has no firstName errors'); - assert.strictEqual(errors.get('length'), 0, 'it has 0 error'); - assert.ok(errors.get('isEmpty'), 'it is empty'); + assert.strictEqual(errors.length, 0, 'it has 0 error'); + assert.ok(errors.isEmpty, 'it is empty'); errors.remove('firstName'); }); @@ -92,23 +92,23 @@ module('unit/model/errors', function (hooks) { errors.add('firstName', 'error'); errors.add('lastName', 'error'); errors.trigger = assert.unexpectedSend; - assert.strictEqual(errors.get('length'), 2, 'it has 2 error'); + assert.strictEqual(errors.length, 2, 'it has 2 error'); errors.remove('firstName'); - assert.strictEqual(errors.get('length'), 1, 'it has 1 error'); + assert.strictEqual(errors.length, 1, 'it has 1 error'); errors.trigger = assert.becameValid; errors.remove('lastName'); - assert.ok(errors.get('isEmpty'), 'it is empty'); + assert.ok(errors.isEmpty, 'it is empty'); }); testInDebug('clear errors', function (assert) { errors.trigger = assert.becameInvalid; errors.add('firstName', ['error', 'error1']); - assert.strictEqual(errors.get('length'), 2, 'it has 2 errors'); + assert.strictEqual(errors.length, 2, 'it has 2 errors'); errors.trigger = assert.becameValid; errors.clear(); errors.trigger = assert.unexpectedSend; assert.ok(!errors.has('firstName'), 'it has no firstName errors'); - assert.strictEqual(errors.get('length'), 0, 'it has 0 error'); + assert.strictEqual(errors.length, 0, 'it has 0 error'); errors.clear(); }); }); diff --git a/packages/-ember-data/tests/unit/model/merge-test.js b/packages/-ember-data/tests/unit/model/merge-test.js index 20146bef561..c053e81cc13 100644 --- a/packages/-ember-data/tests/unit/model/merge-test.js +++ b/packages/-ember-data/tests/unit/model/merge-test.js @@ -42,13 +42,13 @@ module('unit/model/merge - Merging', function (hooks) { return run(() => { let save = person.save(); - assert.strictEqual(person.get('name'), 'Tom Dale'); + assert.strictEqual(person.name, 'Tom Dale'); person.set('name', 'Thomas Dale'); return save.then((person) => { - assert.true(person.get('hasDirtyAttributes'), 'The person is still dirty'); - assert.strictEqual(person.get('name'), 'Thomas Dale', 'The changes made still apply'); + assert.true(person.hasDirtyAttributes, 'The person is still dirty'); + assert.strictEqual(person.name, 'Thomas Dale', 'The changes made still apply'); }); }); }); @@ -83,15 +83,15 @@ module('unit/model/merge - Merging', function (hooks) { return run(() => { let promise = person.save(); - assert.strictEqual(person.get('name'), 'Thomas Dale'); + assert.strictEqual(person.name, 'Thomas Dale'); person.set('name', 'Tomasz Dale'); - assert.strictEqual(person.get('name'), 'Tomasz Dale', 'the local changes applied on top'); + assert.strictEqual(person.name, 'Tomasz Dale', 'the local changes applied on top'); return promise.then((person) => { - assert.true(person.get('hasDirtyAttributes'), 'The person is still dirty'); - assert.strictEqual(person.get('name'), 'Tomasz Dale', 'The local changes apply'); + assert.true(person.hasDirtyAttributes, 'The person is still dirty'); + assert.strictEqual(person.name, 'Tomasz Dale', 'The local changes apply'); }); }); }); @@ -134,7 +134,7 @@ module('unit/model/merge - Merging', function (hooks) { return run(() => { var promise = person.save(); - assert.strictEqual(person.get('name'), 'Thomas Dale'); + assert.strictEqual(person.name, 'Thomas Dale'); person.set('name', 'Tomasz Dale'); @@ -149,14 +149,14 @@ module('unit/model/merge - Merging', function (hooks) { }, }); - assert.strictEqual(person.get('name'), 'Tomasz Dale', 'the local changes applied on top'); - assert.strictEqual(person.get('city'), 'PDX', 'the pushed change is available'); + assert.strictEqual(person.name, 'Tomasz Dale', 'the local changes applied on top'); + assert.strictEqual(person.city, 'PDX', 'the pushed change is available'); return promise.then((person) => { - assert.true(person.get('hasDirtyAttributes'), 'The person is still dirty'); - assert.strictEqual(person.get('name'), 'Tomasz Dale', 'The local changes apply'); + assert.true(person.hasDirtyAttributes, 'The person is still dirty'); + assert.strictEqual(person.name, 'Tomasz Dale', 'The local changes apply'); assert.strictEqual( - person.get('city'), + person.city, 'Portland', 'The updates from the server apply on top of the previous pushes' ); @@ -181,9 +181,9 @@ module('unit/model/merge - Merging', function (hooks) { person.set('name', 'Tomasz Dale'); }); - assert.true(person.get('hasDirtyAttributes'), 'the person is currently dirty'); - assert.strictEqual(person.get('name'), 'Tomasz Dale', 'the update was effective'); - assert.strictEqual(person.get('city'), 'San Francisco', 'the original data applies'); + assert.true(person.hasDirtyAttributes, 'the person is currently dirty'); + assert.strictEqual(person.name, 'Tomasz Dale', 'the update was effective'); + assert.strictEqual(person.city, 'San Francisco', 'the original data applies'); run(() => { this.store.push({ @@ -198,9 +198,9 @@ module('unit/model/merge - Merging', function (hooks) { }); }); - assert.true(person.get('hasDirtyAttributes'), 'the local changes are reapplied'); - assert.strictEqual(person.get('name'), 'Tomasz Dale', 'the local changes are reapplied'); - assert.strictEqual(person.get('city'), 'Portland', 'if there are no local changes, the new data applied'); + assert.true(person.hasDirtyAttributes, 'the local changes are reapplied'); + assert.strictEqual(person.name, 'Tomasz Dale', 'the local changes are reapplied'); + assert.strictEqual(person.city, 'Portland', 'if there are no local changes, the new data applied'); }); test('When a record is invalid, pushes are overridden by local changes', async function (assert) { @@ -235,10 +235,10 @@ module('unit/model/merge - Merging', function (hooks) { } catch (e) { assert.ok(true, 'We rejected the save'); } - assert.false(person.get('isValid'), 'the person is currently invalid'); - assert.true(person.get('hasDirtyAttributes'), 'the person is currently dirty'); - assert.strictEqual(person.get('name'), 'Brondan McLoughlin', 'the update was effective'); - assert.strictEqual(person.get('city'), 'Boston', 'the original data applies'); + assert.false(person.isValid, 'the person is currently invalid'); + assert.true(person.hasDirtyAttributes, 'the person is currently dirty'); + assert.strictEqual(person.name, 'Brondan McLoughlin', 'the update was effective'); + assert.strictEqual(person.city, 'Boston', 'the original data applies'); run(() => { this.store.push({ @@ -253,10 +253,10 @@ module('unit/model/merge - Merging', function (hooks) { }); }); - assert.true(person.get('hasDirtyAttributes'), 'the local changes are reapplied'); - assert.false(person.get('isValid'), 'record is still invalid'); - assert.strictEqual(person.get('name'), 'Brondan McLoughlin', 'the local changes are reapplied'); - assert.strictEqual(person.get('city'), 'Prague', 'if there are no local changes, the new data applied'); + assert.true(person.hasDirtyAttributes, 'the local changes are reapplied'); + assert.false(person.isValid, 'record is still invalid'); + assert.strictEqual(person.name, 'Brondan McLoughlin', 'the local changes are reapplied'); + assert.strictEqual(person.city, 'Prague', 'if there are no local changes, the new data applied'); }); test('A record with no changes can still be saved', function (assert) { @@ -284,7 +284,7 @@ module('unit/model/merge - Merging', function (hooks) { return run(() => { return person.save().then((foo) => { - assert.strictEqual(person.get('name'), 'Thomas Dale', 'the updates occurred'); + assert.strictEqual(person.name, 'Thomas Dale', 'the updates occurred'); }); }); }); @@ -319,9 +319,9 @@ module('unit/model/merge - Merging', function (hooks) { return run(() => { return person.reload().then(() => { - assert.true(person.get('hasDirtyAttributes'), 'the person is dirty'); - assert.strictEqual(person.get('name'), 'Tomasz Dale', 'the local changes remain'); - assert.strictEqual(person.get('city'), 'Portland', 'the new changes apply'); + assert.true(person.hasDirtyAttributes, 'the person is dirty'); + assert.strictEqual(person.name, 'Tomasz Dale', 'the local changes remain'); + assert.strictEqual(person.city, 'Portland', 'the new changes apply'); }); }); }); diff --git a/packages/-ember-data/tests/unit/model/relationships/belongs-to-test.js b/packages/-ember-data/tests/unit/model/relationships/belongs-to-test.js index 1098eb46c29..a93379e4217 100644 --- a/packages/-ember-data/tests/unit/model/relationships/belongs-to-test.js +++ b/packages/-ember-data/tests/unit/model/relationships/belongs-to-test.js @@ -157,7 +157,7 @@ module('unit/model/relationships - belongsTo', function (hooks) { person.addObserver('tag', tagDidChange); - assert.strictEqual(person.get('tag.name'), 'whatever', 'relationship is correct'); + assert.strictEqual(person.tag.name, 'whatever', 'relationship is correct'); // This needs to be removed so it is not triggered when test context is torn down person.removeObserver('tag', tagDidChange); @@ -525,7 +525,7 @@ module('unit/model/relationships - belongsTo', function (hooks) { return run(() => { return store.findRecord('person', '1').then((person) => { - assert.strictEqual(person.get('tag'), null, 'undefined values should return null relationships'); + assert.strictEqual(person.tag, null, 'undefined values should return null relationships'); }); }); }); @@ -640,7 +640,7 @@ module('unit/model/relationships - belongsTo', function (hooks) { }); }); - run(() => store.peekRecord('person', 1).get('occupation')); + run(() => store.peekRecord('person', 1).occupation); }); test('belongsTo supports relationships to models with id 0', function (assert) { @@ -859,6 +859,6 @@ module('unit/model/relationships - belongsTo', function (hooks) { let person = store.createRecord('person'); - assert.ok(person.get('tag') instanceof DS.PromiseObject, 'tag should be an async relationship'); + assert.ok(person.tag instanceof DS.PromiseObject, 'tag should be an async relationship'); }); }); diff --git a/packages/-ember-data/tests/unit/model/relationships/has-many-test.js b/packages/-ember-data/tests/unit/model/relationships/has-many-test.js index a1f6f311008..0ee6e9fb916 100644 --- a/packages/-ember-data/tests/unit/model/relationships/has-many-test.js +++ b/packages/-ember-data/tests/unit/model/relationships/has-many-test.js @@ -302,7 +302,7 @@ module('unit/model/relationships - hasMany', function (hooks) { assert.ok(false, 'observer is not called'); }); - assert.deepEqual(tag.get('people').mapBy('name'), ['David J. Hamilton'], 'relationship is correct'); + assert.deepEqual(tag.people.mapBy('name'), ['David J. Hamilton'], 'relationship is correct'); }); }); @@ -347,7 +347,7 @@ module('unit/model/relationships - hasMany', function (hooks) { return run(() => { let tag = store.peekRecord('tag', 1); - assert.strictEqual(tag.get('people.length'), 0, 'relationship is correct'); + assert.strictEqual(tag.people.length, 0, 'relationship is correct'); }); }); @@ -432,8 +432,8 @@ module('unit/model/relationships - hasMany', function (hooks) { let tag = store.peekRecord('tag', 1); let person = store.peekRecord('person', 1); - assert.strictEqual(person.get('tag'), null, 'relationship is empty'); - assert.strictEqual(tag.get('people.length'), 0, 'relationship is correct'); + assert.strictEqual(person.tag, null, 'relationship is empty'); + assert.strictEqual(tag.people.length, 0, 'relationship is correct'); }); }); @@ -509,7 +509,7 @@ module('unit/model/relationships - hasMany', function (hooks) { run(() => { let tag = store.peekRecord('tag', 1); - assert.strictEqual(tag.get('people.length'), 1, 'relationship does not contain duplicates'); + assert.strictEqual(tag.people.length, 1, 'relationship does not contain duplicates'); }); }); @@ -637,11 +637,11 @@ module('unit/model/relationships - hasMany', function (hooks) { run(() => { let tag = store.peekRecord('tag', 1); - assert.strictEqual(tag.get('people.length'), 2, 'relationship does contain all data'); + assert.strictEqual(tag.people.length, 2, 'relationship does contain all data'); let person1 = store.peekRecord('person', 1); - assert.strictEqual(person1.get('tags.length'), 2, 'relationship does contain all data'); + assert.strictEqual(person1.tags.length, 2, 'relationship does contain all data'); let person2 = store.peekRecord('person', 2); - assert.strictEqual(person2.get('tags.length'), 2, 'relationship does contain all data'); + assert.strictEqual(person2.tags.length, 2, 'relationship does contain all data'); }); }); @@ -710,7 +710,7 @@ module('unit/model/relationships - hasMany', function (hooks) { let person = store.peekRecord('person', 1); let tag = store.peekRecord('tag', 1); - assert.strictEqual(person.get('tag'), tag, 'relationship is not empty'); + assert.strictEqual(person.tag, tag, 'relationship is not empty'); }); run(() => { @@ -735,8 +735,8 @@ module('unit/model/relationships - hasMany', function (hooks) { let person = store.peekRecord('person', 1); let tag = store.peekRecord('tag', 1); - assert.strictEqual(person.get('tag'), null, 'relationship is now empty'); - assert.strictEqual(tag.get('people.length'), 0, 'relationship is correct'); + assert.strictEqual(person.tag, null, 'relationship is now empty'); + assert.strictEqual(tag.people.length, 0, 'relationship is correct'); }); }); @@ -778,11 +778,7 @@ module('unit/model/relationships - hasMany', function (hooks) { }); let eddy = store.peekRecord('person', 1); - assert.deepEqual( - eddy.get('trueFriends').mapBy('name'), - ['Edward II'], - 'hasMany supports reflexive self-relationships' - ); + assert.deepEqual(eddy.trueFriends.mapBy('name'), ['Edward II'], 'hasMany supports reflexive self-relationships'); }); test('hasMany lazily loads async relationships', function (assert) { @@ -897,7 +893,7 @@ module('unit/model/relationships - hasMany', function (hooks) { return hash({ wycats, - tags: wycats.get('tags'), + tags: wycats.tags, }); }) .then((records) => { @@ -1132,7 +1128,7 @@ module('unit/model/relationships - hasMany', function (hooks) { .then((person) => { assert.strictEqual(get(person, 'name'), 'Tom Dale', 'The person is now populated'); - return run(() => person.get('tags')); + return run(() => person.tags); }) .then((tags) => { assert.strictEqual(get(tags, 'length'), 2, 'the tags object still exists'); @@ -1268,7 +1264,7 @@ module('unit/model/relationships - hasMany', function (hooks) { }); const person = store.peekRecord('person', '1'); - const pets = run(() => person.get('pets')); + const pets = run(() => person.pets); const shen = pets.objectAt(0); const rambo = store.peekRecord('pet', '2'); @@ -1367,7 +1363,7 @@ module('unit/model/relationships - hasMany', function (hooks) { }); const person = store.peekRecord('person', '1'); - const pets = run(() => person.get('pets')); + const pets = run(() => person.pets); const shen = pets.objectAt(0); const rebel = store.peekRecord('pet', '3'); @@ -1485,7 +1481,7 @@ module('unit/model/relationships - hasMany', function (hooks) { }); const person = store.peekRecord('person', '1'); - const pets = run(() => person.get('pets')); + const pets = run(() => person.pets); const shen = pets.objectAt(0); const rebel = store.peekRecord('pet', '3'); @@ -1604,7 +1600,7 @@ module('unit/model/relationships - hasMany', function (hooks) { return run(() => { const person = store.peekRecord('person', '1'); - const petsProxy = run(() => person.get('pets')); + const petsProxy = run(() => person.pets); return petsProxy.then((pets) => { const shen = pets.objectAt(0); @@ -1697,7 +1693,7 @@ module('unit/model/relationships - hasMany', function (hooks) { }); const person = store.peekRecord('person', '1'); - let dog = run(() => person.get('dog')); + let dog = run(() => person.dog); const shen = store.peekRecord('dog', '1'); const rambo = store.peekRecord('dog', '2'); @@ -1708,12 +1704,12 @@ module('unit/model/relationships - hasMany', function (hooks) { person.set('dog', rambo); }); - dog = person.get('dog'); + dog = person.dog; assert.strictEqual(dog, rambo, 'precond2 - relationship was updated'); return run(() => { return shen.destroyRecord({}).then(() => { - dog = person.get('dog'); + dog = person.dog; assert.strictEqual(dog, rambo, 'The currentState of the belongsTo was preserved after the delete'); }); }); @@ -1780,18 +1776,18 @@ module('unit/model/relationships - hasMany', function (hooks) { const shen = store.peekRecord('dog', '1'); const rambo = store.peekRecord('dog', '2'); - return person.get('dog').then((dog) => { + return person.dog.then((dog) => { assert.strictEqual(dog, shen, 'precond - the belongsTo points to the correct dog'); assert.strictEqual(get(dog, 'name'), 'Shenanigans', 'precond - relationships work'); person.set('dog', rambo); - dog = person.get('dog.content'); + dog = person.dog.content; assert.strictEqual(dog, rambo, 'precond2 - relationship was updated'); return shen.destroyRecord({}).then(() => { - dog = person.get('dog.content'); + dog = person.dog.content; assert.strictEqual(dog, rambo, 'The currentState of the belongsTo was preserved after the delete'); }); }); @@ -1855,7 +1851,7 @@ module('unit/model/relationships - hasMany', function (hooks) { }); const person = store.peekRecord('person', '1'); - let dog = run(() => person.get('dog')); + let dog = run(() => person.dog); const shen = store.peekRecord('dog', '1'); const rambo = store.peekRecord('dog', '2'); @@ -1866,12 +1862,12 @@ module('unit/model/relationships - hasMany', function (hooks) { person.set('dog', rambo); }); - dog = person.get('dog'); + dog = person.dog; assert.strictEqual(dog, rambo, 'precond2 - relationship was updated'); return run(() => { return rambo.destroyRecord({}).then(() => { - dog = person.get('dog'); + dog = person.dog; assert.strictEqual(dog, null, 'The current state of the belongsTo was clearer'); }); }); @@ -1925,18 +1921,18 @@ module('unit/model/relationships - hasMany', function (hooks) { }); let person = store.peekRecord('person', 1); - let cars = person.get('cars'); + let cars = person.cars; - assert.strictEqual(cars.get('length'), 2); + assert.strictEqual(cars.length, 2); run(() => { - cars.get('firstObject').unloadRecord(); - assert.strictEqual(cars.get('length'), 1); // unload now.. - assert.strictEqual(person.get('cars.length'), 1); // unload now.. + cars.firstObject.unloadRecord(); + assert.strictEqual(cars.length, 1); // unload now.. + assert.strictEqual(person.cars.length, 1); // unload now.. }); - assert.strictEqual(cars.get('length'), 1); // unload now.. - assert.strictEqual(person.get('cars.length'), 1); // unload now.. + assert.strictEqual(cars.length, 1); // unload now.. + assert.strictEqual(person.cars.length, 1); // unload now.. }); /* @@ -2032,7 +2028,7 @@ module('unit/model/relationships - hasMany', function (hooks) { }); const person = store.peekRecord('person', '1'); - const pets = run(() => person.get('pets')); + const pets = run(() => person.pets); const shen = store.peekRecord('pet', '1'); const rebel = store.peekRecord('pet', '3'); @@ -2134,13 +2130,13 @@ module('unit/model/relationships - hasMany', function (hooks) { run(() => { tom = store.peekRecord('person', '1'); sylvain = store.peekRecord('person', '2'); - // Test that since sylvain.get('tags') instanceof ManyArray, + // Test that since sylvain.tags instanceof ManyArray, // adding records on Relationship iterates correctly. - tom.get('tags').setObjects(sylvain.get('tags')); + tom.tags.setObjects(sylvain.tags); }); - assert.strictEqual(tom.get('tags.length'), 1); - assert.strictEqual(tom.get('tags.firstObject'), store.peekRecord('tag', 2)); + assert.strictEqual(tom.tags.length, 1); + assert.strictEqual(tom.tags.firstObject, store.peekRecord('tag', 2)); }); test('Replacing `has-many` with non-array will throw assertion', function (assert) { @@ -2199,7 +2195,7 @@ module('unit/model/relationships - hasMany', function (hooks) { run(() => { tom = store.peekRecord('person', '1'); assert.expectAssertion(() => { - tom.get('tags').setObjects(store.peekRecord('tag', '2')); + tom.tags.setObjects(store.peekRecord('tag', '2')); }, /The third argument to replace needs to be an array./); }); }); @@ -2322,7 +2318,7 @@ module('unit/model/relationships - hasMany', function (hooks) { let store = this.owner.lookup('service:store'); let tag = store.createRecord('tag'); - assert.ok(tag.get('people') instanceof PromiseManyArray, 'people should be an async relationship'); + assert.ok(tag.people instanceof PromiseManyArray, 'people should be an async relationship'); }); test('hasMany is stable', function (assert) { @@ -2341,13 +2337,13 @@ module('unit/model/relationships - hasMany', function (hooks) { let store = this.owner.lookup('service:store'); let tag = store.createRecord('tag'); - let people = tag.get('people'); - let peopleCached = tag.get('people'); + let people = tag.people; + let peopleCached = tag.people; assert.strictEqual(people, peopleCached); tag.notifyPropertyChange('people'); - let notifiedPeople = tag.get('people'); + let notifiedPeople = tag.people; assert.strictEqual(people, notifiedPeople); @@ -2730,7 +2726,7 @@ module('unit/model/relationships - hasMany', function (hooks) { assert.strictEqual(get(tag, 'name'), 'second', 'The tag is now loaded'); return run(() => - person.get('tags').then((tags) => { + person.tags.then((tags) => { assert.strictEqual(get(tags, 'length'), 3, 'the tags are all loaded'); }) ); diff --git a/packages/-ember-data/tests/unit/model/relationships/record-array-test.js b/packages/-ember-data/tests/unit/model/relationships/record-array-test.js index 512f4a6dbd0..4efd7aba314 100644 --- a/packages/-ember-data/tests/unit/model/relationships/record-array-test.js +++ b/packages/-ember-data/tests/unit/model/relationships/record-array-test.js @@ -93,10 +93,10 @@ module('unit/model/relationships - RecordArray', function (hooks) { }); let person = await store.findRecord('person', 1); - person.get('tags').createRecord({ name: 'cool' }); + person.tags.createRecord({ name: 'cool' }); assert.strictEqual(get(person, 'name'), 'Tom Dale', 'precond - retrieves person record from store'); assert.strictEqual(get(person, 'tags.length'), 1, 'tag is added to the parent record'); - assert.strictEqual(get(person, 'tags').objectAt(0).get('name'), 'cool', 'tag values are passed along'); + assert.strictEqual(get(person, 'tags').objectAt(0).name, 'cool', 'tag values are passed along'); }); }); diff --git a/packages/-ember-data/tests/unit/model/rollback-attributes-test.js b/packages/-ember-data/tests/unit/model/rollback-attributes-test.js index 0ceaf1e48a5..688066ef765 100644 --- a/packages/-ember-data/tests/unit/model/rollback-attributes-test.js +++ b/packages/-ember-data/tests/unit/model/rollback-attributes-test.js @@ -56,12 +56,12 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h return person; }); - assert.strictEqual(person.get('firstName'), 'Thomas', 'PreCond: we mutated firstName'); + assert.strictEqual(person.firstName, 'Thomas', 'PreCond: we mutated firstName'); run(() => person.rollbackAttributes()); - assert.strictEqual(person.get('firstName'), 'Tom', 'We rolled back firstName'); - assert.false(person.get('hasDirtyAttributes'), 'We expect the record to be clean'); + assert.strictEqual(person.firstName, 'Tom', 'We rolled back firstName'); + assert.false(person.hasDirtyAttributes, 'We expect the record to be clean'); }); test('changes to unassigned attributes can be rolled back', function (assert) { @@ -84,12 +84,12 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h return person; }); - assert.strictEqual(person.get('firstName'), 'Thomas'); + assert.strictEqual(person.firstName, 'Thomas'); run(() => person.rollbackAttributes()); - assert.strictEqual(person.get('firstName'), undefined); - assert.false(person.get('hasDirtyAttributes')); + assert.strictEqual(person.firstName, undefined); + assert.false(person.hasDirtyAttributes); }); test('changes to attributes made after a record is in-flight only rolls back the local changes', function (assert) { @@ -122,20 +122,20 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h return run(() => { let saving = person.save(); - assert.strictEqual(person.get('firstName'), 'Thomas'); + assert.strictEqual(person.firstName, 'Thomas'); person.set('lastName', 'Dolly'); - assert.strictEqual(person.get('lastName'), 'Dolly'); + assert.strictEqual(person.lastName, 'Dolly'); person.rollbackAttributes(); - assert.strictEqual(person.get('firstName'), 'Thomas'); - assert.strictEqual(person.get('lastName'), 'Dale'); - assert.true(person.get('isSaving')); + assert.strictEqual(person.firstName, 'Thomas'); + assert.strictEqual(person.lastName, 'Dale'); + assert.true(person.isSaving); return saving.then(() => { - assert.false(person.get('hasDirtyAttributes'), 'The person is now clean'); + assert.false(person.hasDirtyAttributes, 'The person is now clean'); }); }); }); @@ -170,14 +170,14 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h run(function () { person.save().then(null, function () { - assert.true(person.get('isError')); + assert.true(person.isError); assert.deepEqual(person.changedAttributes().firstName, ['Tom', 'Thomas']); run(function () { person.rollbackAttributes(); }); - assert.strictEqual(person.get('firstName'), 'Tom'); - assert.false(person.get('isError')); + assert.strictEqual(person.firstName, 'Tom'); + assert.false(person.isError); assert.strictEqual(Object.keys(person.changedAttributes()).length, 0); }); }); @@ -210,25 +210,25 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h run(() => person.deleteRecord()); - assert.strictEqual(people.get('length'), 1, 'a deleted record appears in record array until it is saved'); + assert.strictEqual(people.length, 1, 'a deleted record appears in record array until it is saved'); assert.strictEqual(people.objectAt(0), person, 'a deleted record appears in record array until it is saved'); return run(() => { return person .save() .catch(() => { - assert.true(person.get('isError')); - assert.true(person.get('isDeleted')); + assert.true(person.isError); + assert.true(person.isDeleted); run(() => person.rollbackAttributes()); - assert.false(person.get('isDeleted')); - assert.false(person.get('isError')); - assert.false(person.get('hasDirtyAttributes'), 'must be not dirty'); + assert.false(person.isDeleted); + assert.false(person.isError); + assert.false(person.hasDirtyAttributes, 'must be not dirty'); }) .then(() => { assert.strictEqual( - people.get('length'), + people.length, 1, 'the underlying record array is updated accordingly in an asynchronous way' ); @@ -240,14 +240,14 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h let store = this.owner.lookup('service:store'); let person = store.createRecord('person', { id: 1 }); - assert.true(person.get('isNew'), 'must be new'); - assert.true(person.get('hasDirtyAttributes'), 'must be dirty'); + assert.true(person.isNew, 'must be new'); + assert.true(person.hasDirtyAttributes, 'must be dirty'); run(person, 'rollbackAttributes'); - assert.false(person.get('isNew'), 'must not be new'); - assert.false(person.get('hasDirtyAttributes'), 'must not be dirty'); - assert.true(person.get('isDeleted'), 'must be deleted'); + assert.false(person.isNew, 'must not be new'); + assert.false(person.hasDirtyAttributes, 'must not be dirty'); + assert.true(person.isDeleted, 'must be deleted'); }); test(`invalid new record's attributes can be rollbacked`, function (assert) { @@ -270,19 +270,19 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h let store = this.owner.lookup('service:store'); let person = store.createRecord('person', { id: 1 }); - assert.true(person.get('isNew'), 'must be new'); - assert.true(person.get('hasDirtyAttributes'), 'must be dirty'); + assert.true(person.isNew, 'must be new'); + assert.true(person.hasDirtyAttributes, 'must be dirty'); return run(() => { return person.save().catch((reason) => { assert.strictEqual(error, reason); - assert.false(person.get('isValid')); + assert.false(person.isValid); run(() => person.rollbackAttributes()); - assert.false(person.get('isNew'), 'must not be new'); - assert.false(person.get('hasDirtyAttributes'), 'must not be dirty'); - assert.true(person.get('isDeleted'), 'must be deleted'); + assert.false(person.isNew, 'must not be new'); + assert.false(person.hasDirtyAttributes, 'must not be dirty'); + assert.true(person.isDeleted, 'must be deleted'); }); }); }); @@ -316,22 +316,22 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h }); return run(() => { - assert.strictEqual(person.get('firstName'), 'updated name', 'precondition: firstName is changed'); + assert.strictEqual(person.firstName, 'updated name', 'precondition: firstName is changed'); return person .save() .catch(() => { - assert.true(person.get('hasDirtyAttributes'), 'has dirty attributes'); - assert.strictEqual(person.get('firstName'), 'updated name', 'firstName is still changed'); + assert.true(person.hasDirtyAttributes, 'has dirty attributes'); + assert.strictEqual(person.firstName, 'updated name', 'firstName is still changed'); return person.save(); }) .catch(() => { run(() => person.rollbackAttributes()); - assert.false(person.get('hasDirtyAttributes'), 'has no dirty attributes'); + assert.false(person.hasDirtyAttributes, 'has no dirty attributes'); assert.strictEqual( - person.get('firstName'), + person.firstName, 'original name', 'after rollbackAttributes() firstName has the original value' ); @@ -356,16 +356,16 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h person.deleteRecord(); }); - assert.strictEqual(people.get('length'), 1, 'a deleted record appears in the record array until it is saved'); + assert.strictEqual(people.length, 1, 'a deleted record appears in the record array until it is saved'); assert.strictEqual(people.objectAt(0), person, 'a deleted record appears in the record array until it is saved'); - assert.true(person.get('isDeleted'), 'must be deleted'); + assert.true(person.isDeleted, 'must be deleted'); run(() => person.rollbackAttributes()); - assert.strictEqual(people.get('length'), 1, 'the rollbacked record should appear again in the record array'); - assert.false(person.get('isDeleted'), 'must not be deleted'); - assert.false(person.get('hasDirtyAttributes'), 'must not be dirty'); + assert.strictEqual(people.length, 1, 'the rollbacked record should appear again in the record array'); + assert.false(person.isDeleted, 'must not be deleted'); + assert.false(person.hasDirtyAttributes, 'must not be dirty'); }); test("invalid record's attributes can be rollbacked", async function (assert) { @@ -406,7 +406,7 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h }); if (!gte('4.0.0')) { - dog.get('errors').addArrayObserver( + dog.errors.addArrayObserver( {}, { willChange() { @@ -428,10 +428,10 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h dog.rollbackAttributes(); await settled(); - assert.false(dog.get('hasDirtyAttributes'), 'must not be dirty'); - assert.strictEqual(dog.get('name'), 'Pluto', 'Name is rolled back'); - assert.notOk(dog.get('errors.name'), 'We have no errors for name anymore'); - assert.ok(dog.get('isValid'), 'We are now in a valid state'); + assert.false(dog.hasDirtyAttributes, 'must not be dirty'); + assert.strictEqual(dog.name, 'Pluto', 'Name is rolled back'); + assert.notOk(dog.errors.name, 'We have no errors for name anymore'); + assert.ok(dog.isValid, 'We are now in a valid state'); } if (!gte('4.0.0')) { @@ -484,25 +484,25 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h await dog.save(); } catch (reason) { assert.strictEqual(reason, thrownAdapterError); - assert.strictEqual(dog.get('name'), 'is a dwarf planet'); - assert.strictEqual(dog.get('breed'), 'planet'); - assert.ok(isPresent(dog.get('errors.name'))); + assert.strictEqual(dog.name, 'is a dwarf planet'); + assert.strictEqual(dog.breed, 'planet'); + assert.ok(isPresent(dog.errors.name)); assert.strictEqual(dog.get('errors.name.length'), 1); dog.set('name', 'Seymour Asses'); await settled(); - assert.strictEqual(dog.get('name'), 'Seymour Asses'); - assert.strictEqual(dog.get('breed'), 'planet'); + assert.strictEqual(dog.name, 'Seymour Asses'); + assert.strictEqual(dog.breed, 'planet'); dog.rollbackAttributes(); await settled(); - assert.strictEqual(dog.get('name'), 'Pluto'); - assert.strictEqual(dog.get('breed'), 'Disney'); - assert.false(dog.get('hasDirtyAttributes'), 'must not be dirty'); - assert.notOk(dog.get('errors.name')); - assert.ok(dog.get('isValid')); + assert.strictEqual(dog.name, 'Pluto'); + assert.strictEqual(dog.breed, 'Disney'); + assert.false(dog.hasDirtyAttributes, 'must not be dirty'); + assert.notOk(dog.errors.name); + assert.ok(dog.isValid); } }); @@ -547,17 +547,17 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h return dog.destroyRecord().catch((reason) => { assert.strictEqual(reason, error); - assert.false(dog.get('isError'), 'must not be error'); - assert.true(dog.get('isDeleted'), 'must be deleted'); - assert.false(dog.get('isValid'), 'must not be valid'); - assert.ok(dog.get('errors.length') > 0, 'must have errors'); + assert.false(dog.isError, 'must not be error'); + assert.true(dog.isDeleted, 'must be deleted'); + assert.false(dog.isValid, 'must not be valid'); + assert.ok(dog.errors.length > 0, 'must have errors'); dog.rollbackAttributes(); - assert.false(dog.get('isError'), 'must not be error after `rollbackAttributes`'); - assert.false(dog.get('isDeleted'), 'must not be deleted after `rollbackAttributes`'); - assert.true(dog.get('isValid'), 'must be valid after `rollbackAttributes`'); - assert.strictEqual(dog.get('errors.length'), 0, 'must not have errors'); + assert.false(dog.isError, 'must not be error after `rollbackAttributes`'); + assert.false(dog.isDeleted, 'must not be deleted after `rollbackAttributes`'); + assert.true(dog.isValid, 'must be valid after `rollbackAttributes`'); + assert.strictEqual(dog.errors.length, 0, 'must not have errors'); }); }); }); diff --git a/packages/-ember-data/tests/unit/promise-proxies-test.js b/packages/-ember-data/tests/unit/promise-proxies-test.js index 374a028a218..19edc249f06 100644 --- a/packages/-ember-data/tests/unit/promise-proxies-test.js +++ b/packages/-ember-data/tests/unit/promise-proxies-test.js @@ -175,7 +175,7 @@ module('unit/PromiseBelongsTo', function (hooks) { const belongsToProxy = parent.child; assert.expectAssertion(() => { - belongsToProxy.get('meta'); + belongsToProxy.meta; }, 'You attempted to access meta on the promise for the async belongsTo relationship ' + `child:child'.` + '\nUse `record.belongsTo(relationshipName).meta()` instead.'); assert.strictEqual(parent.belongsTo('child').meta(), meta); }); diff --git a/packages/-ember-data/tests/unit/record-arrays/adapter-populated-record-array-test.js b/packages/-ember-data/tests/unit/record-arrays/adapter-populated-record-array-test.js index b3aff671f3d..09ecb063c48 100644 --- a/packages/-ember-data/tests/unit/record-arrays/adapter-populated-record-array-test.js +++ b/packages/-ember-data/tests/unit/record-arrays/adapter-populated-record-array-test.js @@ -29,12 +29,12 @@ module('unit/record-arrays/adapter-populated-record-array - DS.AdapterPopulatedR store: null, }); - assert.false(recordArray.get('isLoaded'), 'expected isLoaded to be false'); - assert.strictEqual(recordArray.get('modelName'), 'recordType', 'has modelName'); - assert.deepEqual(recordArray.get('content'), [], 'has no content'); - assert.strictEqual(recordArray.get('query'), null, 'no query'); - assert.strictEqual(recordArray.get('store'), null, 'no store'); - assert.strictEqual(recordArray.get('links'), null, 'no links'); + assert.false(recordArray.isLoaded, 'expected isLoaded to be false'); + assert.strictEqual(recordArray.modelName, 'recordType', 'has modelName'); + assert.deepEqual(recordArray.content, [], 'has no content'); + assert.strictEqual(recordArray.query, null, 'no query'); + assert.strictEqual(recordArray.store, null, 'no store'); + assert.strictEqual(recordArray.links, null, 'no links'); }); test('custom initial state', async function (assert) { @@ -48,13 +48,13 @@ module('unit/record-arrays/adapter-populated-record-array - DS.AdapterPopulatedR query: 'some-query', links: 'foo', }); - assert.true(recordArray.get('isLoaded')); - assert.false(recordArray.get('isUpdating')); - assert.strictEqual(recordArray.get('modelName'), 'apple'); - assert.deepEqual(recordArray.get('content'), content); - assert.strictEqual(recordArray.get('store'), store); - assert.strictEqual(recordArray.get('query'), 'some-query'); - assert.strictEqual(recordArray.get('links'), 'foo'); + assert.true(recordArray.isLoaded); + assert.false(recordArray.isUpdating); + assert.strictEqual(recordArray.modelName, 'apple'); + assert.deepEqual(recordArray.content, content); + assert.strictEqual(recordArray.store, store); + assert.strictEqual(recordArray.query, 'some-query'); + assert.strictEqual(recordArray.links, 'foo'); }); test('#replace() throws error', function (assert) { @@ -92,7 +92,7 @@ module('unit/record-arrays/adapter-populated-record-array - DS.AdapterPopulatedR query: 'some-query', }); - assert.false(recordArray.get('isUpdating'), 'should not yet be updating'); + assert.false(recordArray.isUpdating, 'should not yet be updating'); assert.strictEqual(queryCalled, 0); @@ -102,11 +102,11 @@ module('unit/record-arrays/adapter-populated-record-array - DS.AdapterPopulatedR const expectedResult = A(); deferred.resolve(expectedResult); - assert.true(recordArray.get('isUpdating'), 'should be updating'); + assert.true(recordArray.isUpdating, 'should be updating'); const result = await updateResult; assert.strictEqual(result, expectedResult); - assert.false(recordArray.get('isUpdating'), 'should no longer be updating'); + assert.false(recordArray.isUpdating, 'should no longer be updating'); }); // TODO: is this method required, i suspect store._query should be refactor so this is not needed @@ -167,8 +167,8 @@ module('unit/record-arrays/adapter-populated-record-array - DS.AdapterPopulatedR assert.strictEqual(didAddRecord, 2, 'two records should have been added'); assert.deepEqual(recordArray.toArray(), [record1, record2], 'should now contain the loaded records by identifier'); - assert.strictEqual(recordArray.get('links').foo, 1, 'has links'); - assert.strictEqual(recordArray.get('meta').bar, 2, 'has meta'); + assert.strictEqual(recordArray.links.foo, 1, 'has links'); + assert.strictEqual(recordArray.meta.bar, 2, 'has meta'); await settled(); }); @@ -252,8 +252,8 @@ module('unit/record-arrays/adapter-populated-record-array - DS.AdapterPopulatedR assert.strictEqual(addAmt, 2, 'expected addAmt'); }); - assert.true(recordArray.get('isLoaded'), 'should be considered loaded'); - assert.false(recordArray.get('isUpdating'), 'should not yet be updating'); + assert.true(recordArray.isLoaded, 'should be considered loaded'); + assert.false(recordArray.isUpdating, 'should not yet be updating'); assert.strictEqual(arrayDidChange, 0); assert.strictEqual(contentDidChange, 0, 'recordArray.content should not have changed'); @@ -284,8 +284,8 @@ module('unit/record-arrays/adapter-populated-record-array - DS.AdapterPopulatedR recordArray._setIdentifiers([recordIdentifierFor(record3), recordIdentifierFor(record4)], {}); assert.strictEqual(didAddRecord, 2, 'expected 2 didAddRecords'); - assert.true(recordArray.get('isLoaded'), 'should be considered loaded'); - assert.false(recordArray.get('isUpdating'), 'should no longer be updating'); + assert.true(recordArray.isLoaded, 'should be considered loaded'); + assert.false(recordArray.isUpdating, 'should no longer be updating'); assert.strictEqual(arrayDidChange, 1, 'record array should have omitted ONE change event'); assert.strictEqual(contentDidChange, 0, 'recordArray.content should not have changed'); @@ -309,8 +309,8 @@ module('unit/record-arrays/adapter-populated-record-array - DS.AdapterPopulatedR }); // re-query - assert.true(recordArray.get('isLoaded'), 'should be considered loaded'); - assert.false(recordArray.get('isUpdating'), 'should not yet be updating'); + assert.true(recordArray.isLoaded, 'should be considered loaded'); + assert.false(recordArray.isUpdating, 'should not yet be updating'); assert.strictEqual(arrayDidChange, 0, 'record array should not yet have omitted a change event'); assert.strictEqual(contentDidChange, 0, 'recordArray.content should not have changed'); @@ -331,8 +331,8 @@ module('unit/record-arrays/adapter-populated-record-array - DS.AdapterPopulatedR assert.strictEqual(didAddRecord, 1, 'expected 0 didAddRecord'); - assert.true(recordArray.get('isLoaded'), 'should be considered loaded'); - assert.false(recordArray.get('isUpdating'), 'should not longer be updating'); + assert.true(recordArray.isLoaded, 'should be considered loaded'); + assert.false(recordArray.isUpdating, 'should not longer be updating'); assert.strictEqual(arrayDidChange, 1, 'record array should have emitted one change event'); assert.strictEqual(contentDidChange, 0, 'recordArray.content should not have changed'); diff --git a/packages/-ember-data/tests/unit/record-arrays/record-array-test.js b/packages/-ember-data/tests/unit/record-arrays/record-array-test.js index 15773d990a3..dd9aba05111 100644 --- a/packages/-ember-data/tests/unit/record-arrays/record-array-test.js +++ b/packages/-ember-data/tests/unit/record-arrays/record-array-test.js @@ -178,11 +178,11 @@ module('unit/record-arrays/record-array - DS.RecordArray', function (hooks) { let model3 = { lid: '@lid:model-3' }; assert.strictEqual(recordArray._pushIdentifiers([model1]), undefined, '_pushIdentifiers has no return value'); - assert.deepEqual(recordArray.get('content'), [model1], 'now contains model1'); + assert.deepEqual(recordArray.content, [model1], 'now contains model1'); recordArray._pushIdentifiers([model1]); assert.deepEqual( - recordArray.get('content'), + recordArray.content, [model1, model1], 'allows duplicates, because record-array-manager ensures no duplicates, this layer should not double check' ); @@ -192,7 +192,7 @@ module('unit/record-arrays/record-array - DS.RecordArray', function (hooks) { // can add multiple models at once recordArray._pushIdentifiers([model2, model3]); - assert.deepEqual(recordArray.get('content'), [model1, model2, model3], 'now contains model1, model2, model3'); + assert.deepEqual(recordArray.content, [model1, model2, model3], 'now contains model1, model2, model3'); }); test('#_removeIdentifiers', async function (assert) { @@ -205,17 +205,17 @@ module('unit/record-arrays/record-array - DS.RecordArray', function (hooks) { let model2 = { lid: '@lid:model-2' }; let model3 = { lid: '@lid:model-3' }; - assert.strictEqual(recordArray.get('content').length, 0); + assert.strictEqual(recordArray.content.length, 0); assert.strictEqual(recordArray._removeIdentifiers([model1]), undefined, '_removeIdentifiers has no return value'); - assert.deepEqual(recordArray.get('content'), [], 'now contains no models'); + assert.deepEqual(recordArray.content, [], 'now contains no models'); recordArray._pushIdentifiers([model1, model2]); - assert.deepEqual(recordArray.get('content'), [model1, model2], 'now contains model1, model2,'); + assert.deepEqual(recordArray.content, [model1, model2], 'now contains model1, model2,'); assert.strictEqual(recordArray._removeIdentifiers([model1]), undefined, '_removeIdentifiers has no return value'); - assert.deepEqual(recordArray.get('content'), [model2], 'now only contains model2'); + assert.deepEqual(recordArray.content, [model2], 'now only contains model2'); assert.strictEqual(recordArray._removeIdentifiers([model2]), undefined, '_removeIdentifiers has no return value'); - assert.deepEqual(recordArray.get('content'), [], 'now contains no models'); + assert.deepEqual(recordArray.content, [], 'now contains no models'); recordArray._pushIdentifiers([model1, model2, model3]); @@ -225,9 +225,9 @@ module('unit/record-arrays/record-array - DS.RecordArray', function (hooks) { '_removeIdentifiers has no return value' ); - assert.deepEqual(recordArray.get('content'), [model2], 'now contains model2'); + assert.deepEqual(recordArray.content, [model2], 'now contains model2'); assert.strictEqual(recordArray._removeIdentifiers([model2]), undefined, '_removeIdentifiers has no return value'); - assert.deepEqual(recordArray.get('content'), [], 'now contains no models'); + assert.deepEqual(recordArray.content, [], 'now contains no models'); }); test('#save', async function (assert) { diff --git a/packages/-ember-data/tests/unit/store/adapter-interop-test.js b/packages/-ember-data/tests/unit/store/adapter-interop-test.js index 3289c9b869c..22e7cfa57db 100644 --- a/packages/-ember-data/tests/unit/store/adapter-interop-test.js +++ b/packages/-ember-data/tests/unit/store/adapter-interop-test.js @@ -185,7 +185,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho return store .findRecord('test', 1) .then((object) => { - assert.strictEqual(typeof object.get('id'), 'string', 'id was coerced to a string'); + assert.strictEqual(typeof object.id, 'string', 'id was coerced to a string'); run(() => { store.push({ data: { @@ -203,7 +203,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho .then((object) => { assert.ok(object, 'object was found'); assert.strictEqual( - typeof object.get('id'), + typeof object.id, 'string', 'id is a string despite being supplied and searched for as a number' ); @@ -525,9 +525,9 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho return store.findRecord('person', 1, { preload: { friend: 2 } }).then(() => { return store .peekRecord('person', 1) - .get('friend') + .friend .then((friend) => { - assert.strictEqual(friend.get('id'), '2', 'Preloaded belongsTo set'); + assert.strictEqual(friend.id, '2', 'Preloaded belongsTo set'); }); }); }); @@ -679,7 +679,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho return run(() => { return all([tom.save(), yehuda.save()]).then(() => { people.forEach((person, index) => { - assert.strictEqual(person.get('id'), String(index + 1), `The record's id should be correct.`); + assert.strictEqual(person.id, String(index + 1), `The record's id should be correct.`); }); }); }); @@ -991,7 +991,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho }); return store.findRecord('person', 1).then((record) => { - assert.strictEqual(record.get('name'), 'Tom'); + assert.strictEqual(record.name, 'Tom'); }); }); }); @@ -1031,7 +1031,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho }); return store.findRecord('person', 1).then((record) => { - assert.strictEqual(record.get('name'), 'Tom'); + assert.strictEqual(record.name, 'Tom'); }); }); }); @@ -1065,7 +1065,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho }); return store.findRecord('person', 1).then((record) => { - assert.strictEqual(record.get('name'), undefined); + assert.strictEqual(record.name, undefined); }); }); }); @@ -1099,11 +1099,11 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho }); return store.findRecord('person', 1).then((record) => { - assert.strictEqual(record.get('name'), undefined); + assert.strictEqual(record.name, undefined); }); }); - assert.strictEqual(store.peekRecord('person', 1).get('name'), 'Tom'); + assert.strictEqual(store.peekRecord('person', 1).name, 'Tom'); return done; }); @@ -1159,7 +1159,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho return run(() => { return store.findAll('person').then((records) => { - assert.strictEqual(records.get('firstObject.name'), 'Tom'); + assert.strictEqual(records.firstObject.name, 'Tom'); }); }); }); @@ -1192,7 +1192,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho return run(() => { return store.findAll('person').then((records) => { - assert.strictEqual(records.get('firstObject.name'), 'Tom'); + assert.strictEqual(records.firstObject.name, 'Tom'); }); }); }); @@ -1223,7 +1223,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho return run(() => { return store.findAll('person').then((records) => { - assert.strictEqual(records.get('firstObject'), undefined); + assert.strictEqual(records.firstObject, undefined); }); }); }); @@ -1258,11 +1258,11 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho let done = run(() => { return store.findAll('person').then((records) => { - assert.strictEqual(records.get('firstObject.name'), undefined); + assert.strictEqual(records.firstObject.name, undefined); }); }); - assert.strictEqual(store.peekRecord('person', 1).get('name'), 'Tom'); + assert.strictEqual(store.peekRecord('person', 1).name, 'Tom'); return done; }); diff --git a/packages/-ember-data/tests/unit/store/create-record-test.js b/packages/-ember-data/tests/unit/store/create-record-test.js index ae9ef6c08b0..b1181e9c9b7 100644 --- a/packages/-ember-data/tests/unit/store/create-record-test.js +++ b/packages/-ember-data/tests/unit/store/create-record-test.js @@ -103,14 +103,14 @@ module('unit/store/createRecord - Store creating records', function (hooks) { let records = store.peekAll('record').toArray(); let storage = store.createRecord('storage', { name: 'Great store', records: records }); - assert.strictEqual(storage.get('name'), 'Great store', 'The attribute is well defined'); + assert.strictEqual(storage.name, 'Great store', 'The attribute is well defined'); assert.strictEqual( - storage.get('records').findBy('id', '1'), + storage.records.findBy('id', '1'), records.find((r) => r.id === '1'), 'Defined relationships are allowed in createRecord' ); assert.strictEqual( - storage.get('records').findBy('id', '2'), + storage.records.findBy('id', '2'), records.find((r) => r.id === '2'), 'Defined relationships are allowed in createRecord' ); @@ -131,7 +131,7 @@ module('unit/store/createRecord - Store with models by dash', function (hooks) { let attributes = { foo: 'bar' }; let record = store.createRecord('some-thing', attributes); - assert.strictEqual(record.get('foo'), attributes.foo, 'The record is created'); + assert.strictEqual(record.foo, attributes.foo, 'The record is created'); assert.strictEqual(store.modelFor('some-thing').modelName, 'some-thing'); }); }); diff --git a/packages/-ember-data/tests/unit/store/finders-test.js b/packages/-ember-data/tests/unit/store/finders-test.js index d7e5551e595..ca8f0393758 100644 --- a/packages/-ember-data/tests/unit/store/finders-test.js +++ b/packages/-ember-data/tests/unit/store/finders-test.js @@ -141,7 +141,7 @@ module('unit/store/finders', function (hooks) { }, }); - let storePromise = this.store.peekRecord('person', 1).get('dogs'); + let storePromise = this.store.peekRecord('person', 1).dogs; assert.false(serializerLoaded, 'serializer is not eagerly loaded'); deferedFind.resolve({ @@ -194,7 +194,7 @@ module('unit/store/finders', function (hooks) { }, }); - let storePromise = this.store.peekRecord('person', 1).get('favoriteDog'); + let storePromise = this.store.peekRecord('person', 1).favoriteDog; assert.false(serializerLoaded, 'serializer is not eagerly loaded'); diff --git a/packages/-ember-data/tests/unit/store/push-test.js b/packages/-ember-data/tests/unit/store/push-test.js index 910f3e15111..4f7144f911c 100644 --- a/packages/-ember-data/tests/unit/store/push-test.js +++ b/packages/-ember-data/tests/unit/store/push-test.js @@ -45,12 +45,12 @@ module('unit/store/push - Store#push', function (hooks) { }, }); - assert.strictEqual(person.get('firstName'), 'original first name', 'initial first name is correct'); - assert.strictEqual(person.get('currentState.stateName'), 'root.loaded.saved', 'initial state name is correct'); + assert.strictEqual(person.firstName, 'original first name', 'initial first name is correct'); + assert.strictEqual(person.currentState.stateName, 'root.loaded.saved', 'initial state name is correct'); person.set('firstName', 'updated first name'); - assert.strictEqual(person.get('firstName'), 'updated first name', 'mutated first name is correct'); + assert.strictEqual(person.firstName, 'updated first name', 'mutated first name is correct'); assert.strictEqual( person.currentState.stateName, 'root.loaded.updated.uncommitted', @@ -73,8 +73,8 @@ module('unit/store/push - Store#push', function (hooks) { }, }); - assert.strictEqual(person.get('firstName'), 'updated first name'); - assert.strictEqual(person.get('currentState.stateName'), 'root.loaded.saved'); + assert.strictEqual(person.firstName, 'updated first name'); + assert.strictEqual(person.currentState.stateName, 'root.loaded.saved'); assert.false(person.currentState.isDirty, 'currentState is not Dirty after push'); assert.notOk(person.changedAttributes().firstName); }); @@ -186,8 +186,8 @@ module('unit/store/push - Store#push', function (hooks) { ); }); - assert.strictEqual(person.get('firstName'), 'Jacquie', 'you can push raw JSON into the store'); - assert.strictEqual(person.get('lastName'), 'Jackson', 'existing fields are untouched'); + assert.strictEqual(person.firstName, 'Jacquie', 'you can push raw JSON into the store'); + assert.strictEqual(person.lastName, 'Jackson', 'existing fields are untouched'); }); test('Calling push with a normalized hash containing IDs of related records returns a record', function (assert) { @@ -255,7 +255,7 @@ module('unit/store/push - Store#push', function (hooks) { }); let person = store.push(normalized); - return person.get('phoneNumbers').then((phoneNumbers) => { + return person.phoneNumbers.then((phoneNumbers) => { let items = phoneNumbers.map((item) => { return item ? item.getProperties('id', 'number', 'person') : null; }); @@ -370,7 +370,7 @@ module('unit/store/push - Store#push', function (hooks) { let person = store.peekRecord('person', 1); - assert.strictEqual(person.get('phoneNumbers.length'), 1); + assert.strictEqual(person.phoneNumbers.length, 1); assert.strictEqual(person.get('phoneNumbers.firstObject.number'), '1-800-DATA'); // GET /persons/1 @@ -390,7 +390,7 @@ module('unit/store/push - Store#push', function (hooks) { }); }); - assert.strictEqual(person.get('phoneNumbers.length'), 1); + assert.strictEqual(person.phoneNumbers.length, 1); assert.strictEqual(person.get('phoneNumbers.firstObject.number'), '1-800-DATA'); } ); @@ -438,7 +438,7 @@ module('unit/store/push - Store#push', function (hooks) { let person = store.peekRecord('person', 1); - assert.strictEqual(person.get('firstName'), 'Tan', 'you can use links containing an object'); + assert.strictEqual(person.firstName, 'Tan', 'you can use links containing an object'); }); test('Calling push with a link containing the value null', function (assert) { @@ -464,7 +464,7 @@ module('unit/store/push - Store#push', function (hooks) { let person = store.peekRecord('person', 1); - assert.strictEqual(person.get('firstName'), 'Tan', 'you can use links that contain null as a value'); + assert.strictEqual(person.firstName, 'Tan', 'you can use links that contain null as a value'); }); testInDebug('calling push with hasMany relationship the value must be an array', function (assert) { @@ -633,7 +633,7 @@ module('unit/store/push - Store#pushPayload', function (hooks) { let post = store.peekRecord('post', 1); - assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); + assert.strictEqual(post.postTitle, 'Ember rocks', 'you can push raw JSON into the store'); run(() => { store.pushPayload('post', { @@ -646,7 +646,7 @@ module('unit/store/push - Store#pushPayload', function (hooks) { }); }); - assert.strictEqual(post.get('postTitle'), 'Ember rocks (updated)', 'You can update data in the store'); + assert.strictEqual(post.postTitle, 'Ember rocks (updated)', 'You can update data in the store'); }); test('Calling pushPayload allows pushing singular payload properties', function (assert) { @@ -663,7 +663,7 @@ module('unit/store/push - Store#pushPayload', function (hooks) { let post = store.peekRecord('post', 1); - assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); + assert.strictEqual(post.postTitle, 'Ember rocks', 'you can push raw JSON into the store'); run(() => { store.pushPayload('post', { @@ -674,7 +674,7 @@ module('unit/store/push - Store#pushPayload', function (hooks) { }); }); - assert.strictEqual(post.get('postTitle'), 'Ember rocks (updated)', 'You can update data in the store'); + assert.strictEqual(post.postTitle, 'Ember rocks (updated)', 'You can update data in the store'); }); test(`Calling pushPayload should use the type's serializer for normalizing`, function (assert) { @@ -726,11 +726,11 @@ module('unit/store/push - Store#pushPayload', function (hooks) { let post = store.peekRecord('post', '1'); - assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); + assert.strictEqual(post.postTitle, 'Ember rocks', 'you can push raw JSON into the store'); let person = store.peekRecord('person', '2'); - assert.strictEqual(person.get('firstName'), 'Yehuda', 'you can push raw JSON into the store'); + assert.strictEqual(person.firstName, 'Yehuda', 'you can push raw JSON into the store'); }); test(`Calling pushPayload without a type uses application serializer's pushPayload method`, function (assert) { @@ -803,11 +803,11 @@ module('unit/store/push - Store#pushPayload', function (hooks) { var post = store.peekRecord('post', 1); - assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); + assert.strictEqual(post.postTitle, 'Ember rocks', 'you can push raw JSON into the store'); var person = store.peekRecord('person', 2); - assert.strictEqual(person.get('firstName'), 'Yehuda', 'you can push raw JSON into the store'); + assert.strictEqual(person.firstName, 'Yehuda', 'you can push raw JSON into the store'); }); test('Calling pushPayload allows partial updates with raw JSON', function (assert) { @@ -835,8 +835,8 @@ module('unit/store/push - Store#pushPayload', function (hooks) { let person = store.peekRecord('person', 1); - assert.strictEqual(person.get('firstName'), 'Robert', 'you can push raw JSON into the store'); - assert.strictEqual(person.get('lastName'), 'Jackson', 'you can push raw JSON into the store'); + assert.strictEqual(person.firstName, 'Robert', 'you can push raw JSON into the store'); + assert.strictEqual(person.lastName, 'Jackson', 'you can push raw JSON into the store'); run(() => { store.pushPayload('person', { @@ -849,8 +849,8 @@ module('unit/store/push - Store#pushPayload', function (hooks) { }); }); - assert.strictEqual(person.get('firstName'), 'Jacquie', 'you can push raw JSON into the store'); - assert.strictEqual(person.get('lastName'), 'Jackson', 'existing fields are untouched'); + assert.strictEqual(person.firstName, 'Jacquie', 'you can push raw JSON into the store'); + assert.strictEqual(person.lastName, 'Jackson', 'existing fields are untouched'); }); testInDebug( @@ -979,10 +979,10 @@ module('unit/store/push - Store#push with JSON-API', function (hooks) { }); let tom = store.peekRecord('person', 1); - assert.strictEqual(tom.get('name'), 'Tom Dale', 'Tom should be in the store'); + assert.strictEqual(tom.name, 'Tom Dale', 'Tom should be in the store'); let tomster = store.peekRecord('person', 2); - assert.strictEqual(tomster.get('name'), 'Tomster', 'Tomster should be in the store'); + assert.strictEqual(tomster.name, 'Tomster', 'Tomster should be in the store'); }); test('Should support pushing included models into the store', function (assert) { @@ -1032,9 +1032,9 @@ module('unit/store/push - Store#push with JSON-API', function (hooks) { }); let tomster = store.peekRecord('person', 1); - assert.strictEqual(tomster.get('name'), 'Tomster', 'Tomster should be in the store'); + assert.strictEqual(tomster.name, 'Tomster', 'Tomster should be in the store'); let car = store.peekRecord('car', 1); - assert.strictEqual(car.get('model'), 'Neon', "Tomster's car should be in the store"); + assert.strictEqual(car.model, 'Neon', "Tomster's car should be in the store"); }); }); diff --git a/packages/-ember-data/tests/unit/store/unload-test.js b/packages/-ember-data/tests/unit/store/unload-test.js index e7bcf6ae673..f1929498d5b 100644 --- a/packages/-ember-data/tests/unit/store/unload-test.js +++ b/packages/-ember-data/tests/unit/store/unload-test.js @@ -213,7 +213,7 @@ module('Store - unload record with relationships', function (hooks) { }) .then((product) => { assert.strictEqual( - product.get('description'), + product.description, 'cuisinart', "The record was unloaded and the adapter's `findRecord` was called" ); diff --git a/packages/-ember-data/tests/unit/system/relationships/polymorphic-relationship-payloads-test.js b/packages/-ember-data/tests/unit/system/relationships/polymorphic-relationship-payloads-test.js index 90a8056e8a3..7479697b0c4 100644 --- a/packages/-ember-data/tests/unit/system/relationships/polymorphic-relationship-payloads-test.js +++ b/packages/-ember-data/tests/unit/system/relationships/polymorphic-relationship-payloads-test.js @@ -64,7 +64,7 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio const user = run(() => this.store.push(userData)); - const finalResult = user.get('hats').mapBy('type'); + const finalResult = user.hats.mapBy('type'); assert.deepEqual(finalResult, ['hat', 'big-hat', 'small-hat'], 'We got all our hats!'); }); @@ -118,7 +118,7 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio }; const user = run(() => this.store.push(userData)), - finalResult = user.get('hats').mapBy('type'), + finalResult = user.hats.mapBy('type'), expectedResults = included.map((m) => m.type); assert.deepEqual(finalResult, expectedResults, 'We got all our hats!'); @@ -174,7 +174,7 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio }; const user = run(() => this.store.push(userData)), - finalResult = user.get('hats').mapBy('type'), + finalResult = user.hats.mapBy('type'), expectedResults = included.map((m) => m.type); assert.deepEqual(finalResult, expectedResults, 'We got all our hats!'); @@ -230,7 +230,7 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio const expectedAlienResults = alienIncluded.map((m) => m.type), alien = run(() => this.store.push(alienData)), - alienFinalHats = alien.get('hats').mapBy('type'); + alienFinalHats = alien.hats.mapBy('type'); assert.deepEqual(alienFinalHats, expectedAlienResults, 'We got all alien hats!'); }); @@ -309,8 +309,8 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio return this.store.push(smallPersonData); }); - const finalBigResult = bigPerson.get('hats').toArray(); - const finalSmallResult = smallPerson.get('hats').toArray(); + const finalBigResult = bigPerson.hats.toArray(); + const finalSmallResult = smallPerson.hats.toArray(); assert.strictEqual(finalBigResult.length, 4, 'We got all our hats!'); assert.strictEqual(finalSmallResult.length, 2, 'We got all our hats!'); @@ -404,12 +404,12 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio }); const familyResultReferences = boyInstance - .get('family') + .family .toArray() .map((i) => { return { type: i.constructor.modelName, id: i.id }; }); - const twinResult = boyInstance.get('twin'); + const twinResult = boyInstance.twin; const twinResultReference = { type: twinResult.constructor.modelName, id: twinResult.id }; assert.deepEqual(familyResultReferences, expectedFamilyReferences, 'We linked family correctly'); @@ -507,12 +507,12 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio }); const familyResultReferences = boyInstance - .get('family') + .family .toArray() .map((i) => { return { type: i.constructor.modelName, id: i.id }; }); - const twinResult = boyInstance.get('twin'); + const twinResult = boyInstance.twin; const twinResultReference = twinResult && { type: twinResult.constructor.modelName, id: twinResult.id, @@ -559,12 +559,12 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio const expectedHatsReferences = [{ id: '1', type: 'big-hat' }]; const finalHatsReferences = hat2 - .get('hats') + .hats .toArray() .map((i) => { return { type: i.constructor.modelName, id: i.id }; }); - const hatResult = hat1.get('hat'); + const hatResult = hat1.hat; const finalHatReference = hatResult && { type: hatResult.constructor.modelName, id: hatResult.id, @@ -606,12 +606,12 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio const expectedHatsReferences = [{ id: '1', type: 'big-hat' }]; const finalHatsReferences = hat - .get('hats') + .hats .toArray() .map((i) => { return { type: i.constructor.modelName, id: i.id }; }); - const hatResult = hat.get('hat'); + const hatResult = hat.hat; const finalHatReference = hatResult && { type: hatResult.constructor.modelName, id: hatResult.id, @@ -666,7 +666,7 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio }) ); - const hats = user.get('hats'); + const hats = user.hats; assert.deepEqual( hats.map((h) => h.constructor.modelName), @@ -722,7 +722,7 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio }) ); - const hats = user.get('hats'); + const hats = user.hats; assert.deepEqual( hats.map((h) => h.constructor.modelName), @@ -810,11 +810,11 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio return this.store.push(smallPersonData); }); - const finalBigResult = bigPerson.get('hats').toArray(); - const finalSmallResult = smallPerson.get('hats').toArray(); + const finalBigResult = bigPerson.hats.toArray(); + const finalSmallResult = smallPerson.hats.toArray(); assert.deepEqual( - finalBigResult.map((h) => ({ type: h.constructor.modelName, id: h.get('id') })), + finalBigResult.map((h) => ({ type: h.constructor.modelName, id: h.id })), [ { type: 'big-hat', id: '1' }, { type: 'small-hat', id: '1' }, @@ -825,7 +825,7 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio ); assert.deepEqual( - finalSmallResult.map((h) => ({ type: h.constructor.modelName, id: h.get('id') })), + finalSmallResult.map((h) => ({ type: h.constructor.modelName, id: h.id })), [ { type: 'big-hat', id: '3' }, { type: 'small-hat', id: '3' }, diff --git a/packages/adapter/addon/error.js b/packages/adapter/addon/error.js index 9dedfe0a64f..8f88239ad9f 100644 --- a/packages/adapter/addon/error.js +++ b/packages/adapter/addon/error.js @@ -298,7 +298,7 @@ ForbiddenError.prototype.code = 'ForbiddenError'; export default class PostRoute extends Route { @service store; model(params) { - return this.get('store').findRecord('post', params.post_id); + return this.store.findRecord('post', params.post_id); } @action error(error, transition) { diff --git a/packages/adapter/addon/json-api.ts b/packages/adapter/addon/json-api.ts index 8c3057b57ef..5a61d9f6dac 100644 --- a/packages/adapter/addon/json-api.ts +++ b/packages/adapter/addon/json-api.ts @@ -200,7 +200,7 @@ class JSONAPIAdapter extends RESTAdapter { } ``` - By default calling `post.get('comments')` will trigger the following requests(assuming the + By default calling `post.comments` will trigger the following requests(assuming the comments haven't been loaded before): ``` diff --git a/packages/adapter/addon/rest.ts b/packages/adapter/addon/rest.ts index d35e4241c99..dac51b0e821 100644 --- a/packages/adapter/addon/rest.ts +++ b/packages/adapter/addon/rest.ts @@ -295,7 +295,7 @@ declare const jQuery: JQueryStatic | undefined; export default class ApplicationAdapter extends RESTAdapter { headers: computed('session.authToken', function() { return { - 'API_KEY': this.get('session.authToken'), + 'API_KEY': this.session.authToken, 'ANOTHER_HEADER': 'Some header value' }; }) @@ -438,7 +438,7 @@ class RESTAdapter extends Adapter.extend(BuildURLMixin) { } ``` - By default calling `post.get('comments')` will trigger the following requests(assuming the + By default calling `post.comments` will trigger the following requests(assuming the comments haven't been loaded before): ``` diff --git a/packages/debug/addon/index.js b/packages/debug/addon/index.js index 666b7795ecc..0308aa19559 100644 --- a/packages/debug/addon/index.js +++ b/packages/debug/addon/index.js @@ -199,7 +199,7 @@ export default DataAdapter.extend({ } } assert('Cannot find model name. Please upgrade to Ember.js >= 1.13 for Ember Inspector support', !!modelName); - return this.get('store').peekAll(modelName); + return this.store.peekAll(modelName); }, /** @@ -251,9 +251,9 @@ export default DataAdapter.extend({ */ getRecordFilterValues(record) { return { - isNew: record.get('isNew'), - isModified: record.get('hasDirtyAttributes') && !record.get('isNew'), - isClean: !record.get('hasDirtyAttributes'), + isNew: record.isNew, + isModified: record.hasDirtyAttributes && !record.isNew, + isClean: !record.hasDirtyAttributes, }; }, @@ -268,9 +268,9 @@ export default DataAdapter.extend({ */ getRecordColor(record) { let color = 'black'; - if (record.get('isNew')) { + if (record.isNew) { color = 'green'; - } else if (record.get('hasDirtyAttributes')) { + } else if (record.hasDirtyAttributes) { color = 'blue'; } return color; diff --git a/packages/model/addon/-private/belongs-to.js b/packages/model/addon/-private/belongs-to.js index 29f7f6ebac4..3e2e4c32c5f 100644 --- a/packages/model/addon/-private/belongs-to.js +++ b/packages/model/addon/-private/belongs-to.js @@ -98,7 +98,7 @@ import { computedMacroWithOptionalParams } from './util'; a related resource is known to exist and it has not been loaded. ``` - let post = comment.get('post'); + let post = comment.post; ``` diff --git a/packages/model/addon/-private/errors.ts b/packages/model/addon/-private/errors.ts index 6ac3f451c3a..89e88b68f0c 100644 --- a/packages/model/addon/-private/errors.ts +++ b/packages/model/addon/-private/errors.ts @@ -123,7 +123,7 @@ export default class Errors extends ArrayProxyWithCustomOverrides undefined - errors.get('messages') + errors.messages // => [] ``` @method clear @@ -406,7 +406,7 @@ export default class Errors extends ArrayProxyWithCustomOverrides { + post.comments.forEach((comment) => { }); @@ -189,9 +189,6 @@ function hasMany(type, options) { ); } } - if (this.isDestroyed || this.isDestroying) { - return []; - } return LEGACY_SUPPORT.lookup(this).getHasMany(key); }, set(key, records) { diff --git a/packages/model/addon/-private/legacy-relationships-support.ts b/packages/model/addon/-private/legacy-relationships-support.ts index 0f1ac067a6a..aa36a576cd9 100644 --- a/packages/model/addon/-private/legacy-relationships-support.ts +++ b/packages/model/addon/-private/legacy-relationships-support.ts @@ -643,7 +643,7 @@ function extractRecordDataFromRecord(recordOrPromiseRecord: PromiseProxyRecord | } if (isPromiseRecord(recordOrPromiseRecord)) { - let content = recordOrPromiseRecord.get && recordOrPromiseRecord.get('content'); + let content = recordOrPromiseRecord.get && recordOrPromiseRecord.content; assert( 'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.', content !== undefined diff --git a/packages/model/addon/-private/many-array.ts b/packages/model/addon/-private/many-array.ts index 65c94026cfe..12ce591e639 100644 --- a/packages/model/addon/-private/many-array.ts +++ b/packages/model/addon/-private/many-array.ts @@ -348,6 +348,12 @@ export default class ManyArray extends MutableArrayWithObject [ { name: 'users', kind: 'hasMany' }, // { name: 'owner', kind: 'belongsTo' } ] - relationships.get('post'); + relationships.post; //=> [ { name: 'posts', kind: 'hasMany' } ] ``` @@ -1724,9 +1724,9 @@ class Model extends EmberObject { import Blog from 'app/models/blog'; let relationshipsByName = Blog.relationshipsByName; - relationshipsByName.get('users'); + relationshipsByName.users; //=> { key: 'users', kind: 'hasMany', type: 'user', options: Object, isRelationship: true } - relationshipsByName.get('owner'); + relationshipsByName.owner; //=> { key: 'owner', kind: 'belongsTo', type: 'user', options: Object, isRelationship: true } ``` diff --git a/packages/model/addon/-private/references/belongs-to.ts b/packages/model/addon/-private/references/belongs-to.ts index 965d1645fed..9b0827e1db6 100644 --- a/packages/model/addon/-private/references/belongs-to.ts +++ b/packages/model/addon/-private/references/belongs-to.ts @@ -125,7 +125,7 @@ export default class BelongsToReference { `type()` and `id()` methods form a composite key for the identity map. This can be used to access the id of an async relationship without triggering a fetch that would normally happen if you - attempted to use `record.get('relationship.id')`. + attempted to use `record.relationship.id`. Example @@ -404,7 +404,7 @@ export default class BelongsToReference { /** `value()` synchronously returns the current value of the belongs-to - relationship. Unlike `record.get('relationshipName')`, calling + relationship. Unlike `record.relationshipName`, calling `value()` on a reference does not trigger a fetch if the async relationship is not yet loaded. If the relationship is not loaded it will always return `null`. diff --git a/packages/model/addon/-private/references/has-many.ts b/packages/model/addon/-private/references/has-many.ts index 69f10b38616..3686c7f7ec8 100644 --- a/packages/model/addon/-private/references/has-many.ts +++ b/packages/model/addon/-private/references/has-many.ts @@ -442,7 +442,7 @@ export default class HasManyReference { /** `value()` synchronously returns the current value of the has-many - relationship. Unlike `record.get('relationshipName')`, calling + relationship. Unlike `record.relationshipName`, calling `value()` on a reference does not trigger a fetch if the async relationship is not yet loaded. If the relationship is not loaded it will always return `null`. @@ -472,7 +472,7 @@ export default class HasManyReference { let commentsRef = post.hasMany('comments'); - post.get('comments').then(function(comments) { + post.comments.then(function(comments) { commentsRef.value() === comments }) ``` diff --git a/packages/serializer/addon/-private/embedded-records-mixin.js b/packages/serializer/addon/-private/embedded-records-mixin.js index afe2bd864e8..937add31ef3 100644 --- a/packages/serializer/addon/-private/embedded-records-mixin.js +++ b/packages/serializer/addon/-private/embedded-records-mixin.js @@ -544,7 +544,7 @@ export default Mixin.create({ }, attrsOption(attr) { - let attrs = this.get('attrs'); + let attrs = this.attrs; return attrs && (attrs[camelize(attr)] || attrs[attr]); }, diff --git a/packages/serializer/addon/json-api.js b/packages/serializer/addon/json-api.js index f503c839b6c..5c225dd6b1d 100644 --- a/packages/serializer/addon/json-api.js +++ b/packages/serializer/addon/json-api.js @@ -710,7 +710,7 @@ const JSONAPISerializer = JSONSerializer.extend({ } // only serialize has many relationships that are not new - let nonNewHasMany = hasMany.filter((item) => item.record && !item.record.get('isNew')); + let nonNewHasMany = hasMany.filter((item) => item.record && !item.record.isNew); let data = new Array(nonNewHasMany.length); for (let i = 0; i < nonNewHasMany.length; i++) { diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index 572089b3481..e70ee1bac2d 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -144,7 +144,18 @@ export class InstanceCache { // we should consider allowing for something to be loaded that is simply "not empty". // which is how RecordState currently handles this case; however, RecordState is buggy // in that it does not account for unloading. - return !isEmpty; + // return !isEmpty; + + const req = this.store.getRequestStateService(); + const fulfilled = req.getLastRequestForRecord(identifier); + const isLoading = + fulfilled !== null && req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query'); + + if (isEmpty || recordData.isDeletionCommitted?.() || isLoading) { + return false; + } + + return true; } constructor(store: Store) { @@ -597,30 +608,6 @@ function normalizeProperties( return properties; } -export function isHiddenFromRecordArrays(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { - // During dematerialization we don't want to rematerialize the record. - // recordWasDeleted can cause other records to rematerialize because it - // removes the internal model from the array and Ember arrays will always - // `objectAt(0)` and `objectAt(len -1)` to check whether `firstObject` or - // `lastObject` have changed. When this happens we don't want those - // models to rematerialize their records. - - // eager checks to avoid instantiating record data if we are empty or loading - let recordData = cache.peek({ identifier, bucket: 'recordData' }); - if (!recordData) { - return true; - } - - if (!_isEmpty(cache, identifier) || _isLoading(cache, identifier)) { - return false; - } - if (recordData.isDeletionCommitted?.() || (recordData.isNew?.() && recordData.isDeleted?.())) { - return true; - } else { - return false; - } -} - function _recordDataIsFullDeleted(recordData: RecordData): boolean { if (recordData.isDeletionCommitted?.() || (recordData.isNew?.() && recordData.isDeleted?.())) { return true; @@ -664,7 +651,7 @@ function extractRecordDataFromRecord(recordOrPromiseRecord: PromiseProxyRecord | } if (isPromiseRecord(recordOrPromiseRecord)) { - let content = recordOrPromiseRecord.get && recordOrPromiseRecord.get('content'); + let content = recordOrPromiseRecord.get && recordOrPromiseRecord.content; assert( 'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.', content !== undefined @@ -746,7 +733,7 @@ function _convertPreloadRelationshipToJSON(value: RecordInstance | string, type: return recordIdentifierFor(value); } -export function _isEmpty(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { +function _isEmpty(cache: InstanceCache, identifier: StableRecordIdentifier): boolean { const recordData = cache.peek({ identifier: identifier, bucket: 'recordData' }); if (!recordData) { return true; diff --git a/packages/store/addon/-private/managers/record-array-manager.ts b/packages/store/addon/-private/managers/record-array-manager.ts index e3970cc2dd2..929d835311e 100644 --- a/packages/store/addon/-private/managers/record-array-manager.ts +++ b/packages/store/addon/-private/managers/record-array-manager.ts @@ -12,7 +12,6 @@ import type { CollectionResourceDocument, Meta } from '@ember-data/types/q/ember import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; import type { Dict } from '@ember-data/types/q/utils'; -import { isHiddenFromRecordArrays } from '../caches/instance-cache'; import AdapterPopulatedRecordArray from '../record-arrays/adapter-populated-record-array'; import RecordArray from '../record-arrays/record-array'; import type Store from '../store-service'; @@ -62,6 +61,7 @@ class RecordArrayManager { return; } let identifiersToRemove: StableRecordIdentifier[] = []; + let cache = this.store._instanceCache; for (let j = 0; j < identifiers.length; j++) { let i = identifiers[j]; @@ -69,8 +69,7 @@ class RecordArrayManager { // recordArrayManager pendingForIdentifier.delete(i); // build up a set of models to ensure we have purged correctly; - let isIncluded = !isHiddenFromRecordArrays(this.store._instanceCache, i); - if (!isIncluded) { + if (!cache.recordIsLoaded(i)) { identifiersToRemove.push(i); } } @@ -182,14 +181,14 @@ class RecordArrayManager { } _visibleIdentifiersByType(modelName: string) { - const list = this.store._instanceCache.peekList[modelName]; + const cache = this.store._instanceCache; + const list = cache.peekList[modelName]; let all = list ? [...list.values()] : []; let visible: StableRecordIdentifier[] = []; for (let i = 0; i < all.length; i++) { let identifier = all[i]; - let shouldInclude = !isHiddenFromRecordArrays(this.store._instanceCache, identifier); - if (shouldInclude) { + if (cache.recordIsLoaded(identifier)) { visible.push(identifier); } } @@ -374,20 +373,18 @@ function removeFromArray(array: RecordArray[], item: RecordArray): boolean { function updateLiveRecordArray(store: Store, recordArray: RecordArray, identifiers: StableRecordIdentifier[]): void { let identifiersToAdd: StableRecordIdentifier[] = []; let identifiersToRemove: StableRecordIdentifier[] = []; + const cache = store._instanceCache; for (let i = 0; i < identifiers.length; i++) { let identifier = identifiers[i]; - let shouldInclude = !isHiddenFromRecordArrays(store._instanceCache, identifier); let recordArrays = recordArraysForIdentifier(identifier); - if (shouldInclude) { + if (cache.recordIsLoaded(identifier)) { if (!recordArrays.has(recordArray)) { identifiersToAdd.push(identifier); recordArrays.add(recordArray); } - } - - if (!shouldInclude) { + } else { identifiersToRemove.push(identifier); recordArrays.delete(recordArray); } diff --git a/packages/store/addon/-private/network/finders.js b/packages/store/addon/-private/network/finders.js index bb5f504a067..ef5f6fd8807 100644 --- a/packages/store/addon/-private/network/finders.js +++ b/packages/store/addon/-private/network/finders.js @@ -48,6 +48,7 @@ export function _findAll(adapter, store, modelName, options) { export function _query(adapter, store, modelName, query, recordArray, options) { let modelClass = store.modelFor(modelName); // adapter.query needs the class + // TODO @deprecate RecordArrays being passed to Adapters recordArray = recordArray || store.recordArrayManager.createAdapterPopulatedRecordArray(modelName, query); let promise = Promise.resolve().then(() => adapter.query(store, modelClass, query, recordArray, options)); @@ -64,16 +65,7 @@ export function _query(adapter, store, modelName, query, recordArray, options) { 'The response to store.query is expected to be an array but it was a single record. Please wrap your response in an array or use `store.queryRecord` to query for a single record.', Array.isArray(identifiers) ); - if (recordArray) { - recordArray._setIdentifiers(identifiers, payload); - } else { - recordArray = store.recordArrayManager.createAdapterPopulatedRecordArray( - modelName, - query, - identifiers, - payload - ); - } + recordArray._setIdentifiers(identifiers, payload); return recordArray; }, diff --git a/packages/store/addon/-private/network/snapshot-record-array.ts b/packages/store/addon/-private/network/snapshot-record-array.ts index e1441aea93c..16b04516de4 100644 --- a/packages/store/addon/-private/network/snapshot-record-array.ts +++ b/packages/store/addon/-private/network/snapshot-record-array.ts @@ -77,7 +77,7 @@ export default class SnapshotRecordArray { @public @type {Number} */ - this.length = recordArray.get('length'); + this.length = recordArray.length; /** Meta objects for the record array. @@ -220,7 +220,7 @@ if (DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS) { since: { available: '4.5.0', enabled: '4.5.0' }, } ); - return this._recordArray.get('type'); + return this._recordArray.type; }, }); } diff --git a/packages/store/addon/-private/proxies/promise-proxies.ts b/packages/store/addon/-private/proxies/promise-proxies.ts index edc5ebd49d7..9896835b125 100644 --- a/packages/store/addon/-private/proxies/promise-proxies.ts +++ b/packages/store/addon/-private/proxies/promise-proxies.ts @@ -31,10 +31,10 @@ import { PromiseArrayProxy, PromiseObjectProxy } from './promise-proxy-base'; promise: $.getJSON('/some/remote/data.json') }); - promiseArray.get('length'); // 0 + promiseArray.length; // 0 promiseArray.then(function() { - promiseArray.get('length'); // 100 + promiseArray.length; // 100 }); ``` @@ -77,10 +77,10 @@ export class PromiseArray> extends PromiseArrayPr promise: $.getJSON('/some/remote/data.json') }); - promiseObject.get('name'); // null + promiseObject.name; // null promiseObject.then(function() { - promiseObject.get('name'); // 'Tomster' + promiseObject.name; // 'Tomster' }); ``` diff --git a/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts b/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts index 98a643ccc76..6145279da53 100644 --- a/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts +++ b/packages/store/addon/-private/record-arrays/adapter-populated-record-array.ts @@ -55,11 +55,11 @@ export interface AdapterPopulatedRecordArrayCreateArgs { // // GET /users?isAdmin=true admins.update().then(function() { - admins.get('isUpdating'); // false + admins.isUpdating; // false admins.get("length"); // 123 }); - admins.get('isUpdating'); // true + admins.isUpdating; // true } ``` diff --git a/packages/store/addon/-private/record-arrays/record-array.ts b/packages/store/addon/-private/record-arrays/record-array.ts index 147f5b1563b..9e8d7b5e539 100644 --- a/packages/store/addon/-private/record-arrays/record-array.ts +++ b/packages/store/addon/-private/record-arrays/record-array.ts @@ -67,7 +67,7 @@ export default class RecordArray extends ArrayProxy comment.serialize().data), }; @@ -232,7 +232,7 @@ module('integration/has-many - Has Many Tests', function (hooks) { let post = store.push(initialRecord); await assert.expectAssertion(async function () { - await post.get('comments'); + await post.comments; }, /You tried to load a hasMany relationship from a specified 'link' in the original payload but your adapter does not implement 'findHasMany'/); }); @@ -302,7 +302,7 @@ module('integration/has-many - Has Many Tests', function (hooks) { owner.register('adapter:application', TestFindRecordAdapter); let post = store.push(initialRecord); - let comments = await post.get('comments'); + let comments = await post.comments; let serializedComments = { data: comments.toArray().map((comment) => comment.serialize().data), }; @@ -374,7 +374,7 @@ module('integration/has-many - Has Many Tests', function (hooks) { owner.register('adapter:application', TestFindRecordAdapter); let post = store.push(initialRecord); - let comments = await post.get('comments'); + let comments = await post.comments; let serializedComments = { data: comments.toArray().map((comment) => comment.serialize().data), }; @@ -457,7 +457,7 @@ module('integration/has-many - Has Many Tests', function (hooks) { owner.register('adapter:application', TestFindManyAdapter); let post = store.push(initialRecord); - let comments = await post.get('comments'); + let comments = await post.comments; let serializedComments = { data: comments.toArray().map((comment) => comment.serialize().data), }; @@ -536,7 +536,7 @@ module('integration/has-many - Has Many Tests', function (hooks) { owner.register('adapter:application', TestFindManyAdapter); let post = store.push(initialRecord); - let comments = await post.get('comments'); + let comments = await post.comments; let serializedComments = { data: comments.toArray().map((comment) => comment.serialize().data), }; @@ -615,7 +615,7 @@ module('integration/has-many - Has Many Tests', function (hooks) { let post = store.push(initialRecord); - let comments = await post.get('comments'); + let comments = await post.comments; let serializedComments = { data: comments.toArray().map((comment) => comment.serialize().data), }; @@ -697,7 +697,7 @@ module('integration/has-many - Has Many Tests', function (hooks) { owner.register('adapter:application', TestFindManyAdapter); let post = store.push(initialRecord); - let comments = await post.get('comments'); + let comments = await post.comments; let serializedComments = { data: comments.toArray().map((comment) => comment.serialize().data), }; @@ -768,7 +768,7 @@ module('integration/has-many - Has Many Tests', function (hooks) { owner.register('adapter:application', TestFindRecordAdapter); let post = store.push(initialRecord); - let comments = await post.get('comments'); + let comments = await post.comments; let serializedComments = { data: comments.toArray().map((comment) => comment.serialize().data), }; diff --git a/packages/unpublished-fastboot-test-app/app/models/person.js b/packages/unpublished-fastboot-test-app/app/models/person.js index d353542d03e..79444014e3a 100644 --- a/packages/unpublished-fastboot-test-app/app/models/person.js +++ b/packages/unpublished-fastboot-test-app/app/models/person.js @@ -11,7 +11,7 @@ export default class Person extends Model { parent; get parentId() { - return this.parent.get('id'); + return this.parent.id; } toNode() { diff --git a/packages/unpublished-serializer-encapsulation-test-app/tests/integration/relationships-test.js b/packages/unpublished-serializer-encapsulation-test-app/tests/integration/relationships-test.js index a6186c6079c..8892341919e 100644 --- a/packages/unpublished-serializer-encapsulation-test-app/tests/integration/relationships-test.js +++ b/packages/unpublished-serializer-encapsulation-test-app/tests/integration/relationships-test.js @@ -114,7 +114,7 @@ module('integration/relationships - running requests for async relatonships with }, }, }); - let comments = await post.get('comments'); + let comments = await post.comments; assert.strictEqual(normalizeResponseCalled, 1, 'normalizeResponse is called once'); assert.deepEqual(comments.mapBy('message'), ['Message 1', 'Message 2'], 'response is expected response'); @@ -177,7 +177,7 @@ module('integration/relationships - running requests for async relatonships with }, }, }); - let comments = await post.get('comments'); + let comments = await post.comments; assert.strictEqual(normalizeResponseCalled, 1, 'normalizeResponse is called once'); assert.deepEqual(comments.mapBy('message'), ['Message 1', 'Message 2'], 'response is expected response'); @@ -247,7 +247,7 @@ module('integration/relationships - running requests for async relatonships with }, }, }); - let post = await comment.get('post'); + let post = await comment.post; assert.strictEqual(normalizeResponseCalled, 1, 'normalizeResponse is called once'); assert.deepEqual(post.title, 'Chris', 'response is expected response'); diff --git a/packages/unpublished-test-infra/addon-test-support/legacy.js b/packages/unpublished-test-infra/addon-test-support/legacy.js index e472c442fbb..9210f7a5ca5 100644 --- a/packages/unpublished-test-infra/addon-test-support/legacy.js +++ b/packages/unpublished-test-infra/addon-test-support/legacy.js @@ -12,7 +12,7 @@ export default function additionalLegacyAsserts() { assert.assertClean = function (promise) { return promise.then( this.wait((record) => { - this.equal(record.get('hasDirtyAttributes'), false, 'The record is now clean'); + this.equal(record.hasDirtyAttributes, false, 'The record is now clean'); return record; }) ); From 63832739210296805b568953eca7bb3b5f91649b Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 8 Aug 2022 13:51:35 -0700 Subject: [PATCH 21/29] All the cleanup --- .../adapter/json-api-adapter-test.js | 7 +- .../integration/adapter/store-adapter-test.js | 12 +- .../integration/records/delete-record-test.js | 3 +- .../tests/integration/records/unload-test.js | 270 ++++++++---------- .../relationships/nested-relationship-test.js | 149 +++++----- .../relationships/one-to-many-test.js | 67 +++-- .../tests/integration/store-test.js | 7 +- .../tests/integration/store/query-test.js | 25 +- packages/-ember-data/tests/test-helper.js | 2 - .../tests/unit/model/errors-test.js | 6 +- .../model/relationships/belongs-to-test.js | 179 ++++++------ .../unit/model/rollback-attributes-test.js | 10 +- .../tests/unit/store/adapter-interop-test.js | 34 +-- packages/adapter/addon/rest.ts | 3 +- packages/debug/addon/index.js | 13 +- packages/model/addon/-private/errors.ts | 6 +- .../-private/legacy-relationships-support.ts | 7 +- packages/record-data/tests/test-helper.js | 2 - .../addon/-private/embedded-records-mixin.js | 9 +- packages/serializer/addon/-private/index.js | 1 - packages/serializer/addon/-private/utils.js | 10 - packages/serializer/addon/json.js | 27 +- packages/serializer/addon/rest.js | 4 +- .../addon/-private/caches/instance-cache.ts | 4 +- .../schema-definition-service.ts | 5 +- .../-private/managers/record-array-manager.ts | 3 +- .../-private/record-arrays/record-array.ts | 11 +- packages/store/addon/-private/utils/common.js | 3 +- .../addon-test-support/async.js | 42 --- .../addon-test-support/legacy.js | 28 -- 30 files changed, 413 insertions(+), 536 deletions(-) delete mode 100644 packages/serializer/addon/-private/utils.js delete mode 100644 packages/unpublished-test-infra/addon-test-support/async.js delete mode 100644 packages/unpublished-test-infra/addon-test-support/legacy.js diff --git a/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js b/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js index fcfd50dd601..3fd16a2aa70 100644 --- a/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js +++ b/packages/-ember-data/tests/integration/adapter/json-api-adapter-test.js @@ -189,13 +189,16 @@ module('integration/adapter/json-api-adapter - JSONAPIAdapter', function (hooks) assert.strictEqual(posts.firstObject.title, 'Ember.js rocks', 'The title for the first post is correct'); assert.strictEqual(posts.lastObject.title, 'Tomster rules', 'The title for the second post is correct'); + const firstPostAuthor = await posts.firstObject.author; + const lastPostAuthor = await posts.lastObject.author; + assert.strictEqual( - posts.firstObject.author.firstName, + firstPostAuthor.firstName, 'Yehuda', 'The author for the first post is loaded and has the correct first name' ); assert.strictEqual( - posts.lastObject.author.lastName, + lastPostAuthor.lastName, 'Katz', 'The author for the last post is loaded and has the correct last name' ); diff --git a/packages/-ember-data/tests/integration/adapter/store-adapter-test.js b/packages/-ember-data/tests/integration/adapter/store-adapter-test.js index 5b853d8cbf5..9eec2afcca5 100644 --- a/packages/-ember-data/tests/integration/adapter/store-adapter-test.js +++ b/packages/-ember-data/tests/integration/adapter/store-adapter-test.js @@ -127,8 +127,16 @@ module('integration/adapter/store-adapter - DS.Store and DS.Adapter integration tom = records.tom; yehuda = records.yehuda; - assert.asyncEqual(tom, store.findRecord('person', '1'), 'Once an ID is in, findRecord returns the same object'); - assert.asyncEqual(yehuda, store.findRecord('person', '2'), 'Once an ID is in, findRecord returns the same object'); + assert.strictEqual( + tom, + await store.findRecord('person', '1'), + 'Once an ID is in, findRecord returns the same object' + ); + assert.strictEqual( + yehuda, + await store.findRecord('person', '2'), + 'Once an ID is in, findRecord returns the same object' + ); assert.strictEqual(tom.updatedAt, 'now', 'The new information is received'); assert.strictEqual(yehuda.updatedAt, 'now', 'The new information is received'); }); diff --git a/packages/-ember-data/tests/integration/records/delete-record-test.js b/packages/-ember-data/tests/integration/records/delete-record-test.js index 0561de410e6..132e6398fda 100644 --- a/packages/-ember-data/tests/integration/records/delete-record-test.js +++ b/packages/-ember-data/tests/integration/records/delete-record-test.js @@ -425,10 +425,11 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { store.push(jsonGroup); group = store.peekRecord('group', '1'); + const groupCompany = await group.company; // Sanity Check assert.ok(group, 'expected group to be found'); - assert.strictEqual(group.company.name, 'Inc.', 'group belongs to our company'); + assert.strictEqual(groupCompany.name, 'Inc.', 'group belongs to our company'); assert.strictEqual(group.employees.length, 1, 'expected 1 related record before delete'); const employees = await group.employees; employee = employees.objectAt(0); diff --git a/packages/-ember-data/tests/integration/records/unload-test.js b/packages/-ember-data/tests/integration/records/unload-test.js index 516d69fbe24..a9c48eadf97 100644 --- a/packages/-ember-data/tests/integration/records/unload-test.js +++ b/packages/-ember-data/tests/integration/records/unload-test.js @@ -1719,79 +1719,70 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(findManyCalls, 2, 'findMany called as expected'); }); - test('1 sync : 1 async unload sync side', function (assert) { - run(() => - store.push({ - data: { - id: 1, - type: 'person', - relationships: { - favoriteBook: { - data: { - id: 2, - type: 'book', - }, + test('1 sync : 1 async unload sync side', async function (assert) { + let person = store.push({ + data: { + id: '1', + type: 'person', + relationships: { + favoriteBook: { + data: { + id: '2', + type: 'book', }, }, }, - included: [ - { - id: 2, - type: 'book', - }, - ], - }) - ); + }, + included: [ + { + id: '2', + type: 'book', + }, + ], + }); - let person = store.peekRecord('person', 1); - let book = store.peekRecord('book', 2); + let book = store.peekRecord('book', '2'); + await book.person; - return book.person.then(() => { - assert.strictEqual(person.favoriteBook.id, '2', 'initially relationship established lhs'); - assert.strictEqual(book.belongsTo('person').id(), '1', 'initially relationship established rhs'); + assert.strictEqual(person.favoriteBook.id, '2', 'initially relationship established lhs'); + assert.strictEqual(book.belongsTo('person').id(), '1', 'initially relationship established rhs'); - run(() => book.unloadRecord()); + book.unloadRecord(); + await settled(); - assert.strictEqual(person.book, undefined, 'unloading acts as a delete for sync relationships'); - assert.strictEqual(store.peekRecord('book', '2'), null, 'unloaded record gone from store'); + assert.strictEqual(person.book, undefined, 'unloading acts as a delete for sync relationships'); + assert.strictEqual(store.peekRecord('book', '2'), null, 'unloaded record gone from store'); - book = run(() => - store.push({ - data: { - id: 2, - type: 'book', - }, - }) - ); - - assert.notStrictEqual(store.peekRecord('book', '2'), null, 'unloaded record can be restored'); - assert.strictEqual(person.book, undefined, 'restoring unloaded record does not restore relationship'); - assert.strictEqual( - book.belongsTo('person').id(), - null, - 'restoring unloaded record does not restore relationship' - ); - - run(() => - store.push({ - data: { - id: 2, - type: 'book', - relationships: { - person: { - data: { - id: 1, - type: 'person', - }, - }, + store.push({ + data: { + id: '2', + type: 'book', + }, + }); + + book = store.peekRecord('book', '2'); + assert.notStrictEqual(book, null, 'unloaded record can be restored'); + assert.strictEqual(person.book, undefined, 'restoring unloaded record does not restore relationship'); + assert.strictEqual(book.belongsTo('person').id(), null, 'restoring unloaded record does not restore relationship'); + + store.push({ + data: { + id: '2', + type: 'book', + relationships: { + person: { + data: { + id: 1, + type: 'person', }, }, - }) - ); - - assert.strictEqual(person.favoriteBook.id, '2', 'after unloading, relationship can be restored'); - assert.strictEqual(book.person.id, '1', 'after unloading, relationship can be restored'); + }, + }, }); + + const bookPerson = await book.person; + assert.strictEqual(person.favoriteBook.id, '2', 'after unloading, relationship can be restored'); + assert.strictEqual(bookPerson?.id, '1', 'after unloading, relationship can be restored'); }); test('1 sync : 1 async unload async side', function (assert) { @@ -2110,7 +2101,7 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(findManyCalls, 2, 'findMany called as expected'); }); - test('1 sync : many async unload sync side', function (assert) { + test('1 sync : many async unload sync side', async function (assert) { let findManyCalls = 0; adapter.coalesceFindRequests = true; @@ -2134,102 +2125,91 @@ module('integration/unload - Unloading Records', function (hooks) { }; }; - let person = run(() => - store.push({ - data: { - id: 1, - type: 'person', - relationships: { - favoriteShows: { - data: [ - { - id: 2, - type: 'show', - }, - { - id: 3, - type: 'show', - }, - ], - }, + let person = store.push({ + data: { + id: '1', + type: 'person', + relationships: { + favoriteShows: { + data: [ + { + id: '2', + type: 'show', + }, + { + id: '3', + type: 'show', + }, + ], }, }, - }) - ); - + }, + }); let shows, show2, show3; - return run(() => - person.favoriteShows - .then((asyncRecords) => { - shows = asyncRecords; - [show2, show3] = shows.toArray(); + const asyncRecords = await person.favoriteShows; + shows = asyncRecords; + [show2, show3] = shows.toArray(); - assert.deepEqual(person.hasMany('favoriteShows').ids(), ['2', '3'], 'initially relationship established lhs'); - assert.strictEqual(show2.person.id, '1', 'initially relationship established rhs'); - assert.strictEqual(show3.person.id, '1', 'initially relationship established rhs'); - assert.deepEqual(shows.mapBy('id'), ['2', '3'], 'many array is initially set up correctly'); + assert.deepEqual(person.hasMany('favoriteShows').ids(), ['2', '3'], 'initially relationship established lhs'); + assert.strictEqual(show2.person.id, '1', 'initially relationship established rhs'); + assert.strictEqual(show3.person.id, '1', 'initially relationship established rhs'); + assert.deepEqual(shows.mapBy('id'), ['2', '3'], 'many array is initially set up correctly'); - run(() => person.unloadRecord()); + person.unloadRecord(); + await settled(); - assert.strictEqual(store.peekRecord('person', '1'), null, 'unloaded record gone from store'); + assert.strictEqual(store.peekRecord('person', '1'), null, 'unloaded record gone from store'); - assert.true(shows.isDestroyed, 'previous manyarray immediately destroyed'); - assert.strictEqual(show2.person.id, undefined, 'unloading acts as delete for sync relationships'); - assert.strictEqual(show3.person.id, undefined, 'unloading acts as delete for sync relationships'); + assert.true(shows.isDestroyed, 'previous manyarray immediately destroyed'); + assert.strictEqual(show2.person?.id, undefined, 'unloading acts as delete for sync relationships'); + assert.strictEqual(show3.person?.id, undefined, 'unloading acts as delete for sync relationships'); - person = run(() => - store.push({ - data: { - id: 1, - type: 'person', + person = store.push({ + data: { + id: '1', + type: 'person', + }, + }); + + assert.notStrictEqual(store.peekRecord('person', '1'), null, 'unloaded record can be restored'); + assert.deepEqual( + person.hasMany('favoriteShows').ids(), + [], + 'restoring unloaded record does not restore relationship' + ); + assert.strictEqual(show2.person?.id, undefined, 'restoring unloaded record does not restore relationship'); + assert.strictEqual(show3.person?.id, undefined, 'restoring unloaded record does not restore relationship'); + + store.push({ + data: { + id: '1', + type: 'person', + relationships: { + favoriteShows: { + data: [ + { + id: '2', + type: 'show', }, - }) - ); - - assert.notStrictEqual(store.peekRecord('person', '1'), null, 'unloaded record can be restored'); - assert.deepEqual( - person.hasMany('favoriteShows').ids(), - [], - 'restoring unloaded record does not restore relationship' - ); - assert.strictEqual(show2.person.id, undefined, 'restoring unloaded record does not restore relationship'); - assert.strictEqual(show3.person.id, undefined, 'restoring unloaded record does not restore relationship'); - - run(() => - store.push({ - data: { - id: 1, - type: 'person', - relationships: { - favoriteShows: { - data: [ - { - id: 2, - type: 'show', - }, - { - id: 3, - type: 'show', - }, - ], - }, - }, + { + id: '3', + type: 'show', }, - }) - ); + ], + }, + }, + }, + }); - assert.deepEqual(person.hasMany('favoriteShows').ids(), ['2', '3'], 'relationship can be restored'); + assert.deepEqual(person.hasMany('favoriteShows').ids(), ['2', '3'], 'relationship can be restored'); - return person.favoriteShows; - }) - .then((refetchedShows) => { - assert.notEqual(refetchedShows, shows, 'ManyArray not reused'); - assert.deepEqual(refetchedShows.mapBy('id'), ['2', '3'], 'unload async not treated as a delete'); + const refetchedShows = await person.favoriteShows; - assert.strictEqual(findManyCalls, 1, 'findMany calls as expected'); - }) - ); + assert.notEqual(refetchedShows, shows, 'ManyArray not reused'); + assert.deepEqual(refetchedShows.mapBy('id'), ['2', '3'], 'unload async not treated as a delete'); + + assert.strictEqual(findManyCalls, 1, 'findMany calls as expected'); }); test('unload invalidates link promises', function (assert) { diff --git a/packages/-ember-data/tests/integration/relationships/nested-relationship-test.js b/packages/-ember-data/tests/integration/relationships/nested-relationship-test.js index 1295c008409..28d248da690 100644 --- a/packages/-ember-data/tests/integration/relationships/nested-relationship-test.js +++ b/packages/-ember-data/tests/integration/relationships/nested-relationship-test.js @@ -1,6 +1,3 @@ -import { get } from '@ember/object'; -import { run } from '@ember/runloop'; - import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; @@ -38,7 +35,7 @@ module('integration/relationships/nested_relationships_test - Nested relationshi Server loading tests */ - test('Sideloaded nested relationships load correctly', function (assert) { + test('Sideloaded nested relationships load correctly', async function (assert) { let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -46,104 +43,98 @@ module('integration/relationships/nested_relationships_test - Nested relationshi return false; }; - run(() => { - store.push({ - data: { + store.push({ + data: { + id: '1', + type: 'kid', + links: { + self: '/kids/1', + }, + attributes: { + name: 'Kid 1', + }, + relationships: { + middleAger: { + links: { + self: '/kids/1/relationships/middle-ager', + related: '/kids/1/middle-ager', + }, + data: { + type: 'middle-ager', + id: '1', + }, + }, + }, + }, + included: [ + { id: '1', - type: 'kid', + type: 'middle-ager', links: { - self: '/kids/1', + self: '/middle-ager/1', }, attributes: { - name: 'Kid 1', + name: 'Middle Ager 1', }, relationships: { - middleAger: { + elder: { links: { - self: '/kids/1/relationships/middle-ager', - related: '/kids/1/middle-ager', + self: '/middle-agers/1/relationships/elder', + related: '/middle-agers/1/elder', }, data: { - type: 'middle-ager', + type: 'elder', id: '1', }, }, - }, - }, - included: [ - { - id: '1', - type: 'middle-ager', - links: { - self: '/middle-ager/1', - }, - attributes: { - name: 'Middle Ager 1', - }, - relationships: { - elder: { - links: { - self: '/middle-agers/1/relationships/elder', - related: '/middle-agers/1/elder', - }, - data: { - type: 'elder', - id: '1', - }, + kids: { + links: { + self: '/middle-agers/1/relationships/kids', + related: '/middle-agers/1/kids', }, - kids: { - links: { - self: '/middle-agers/1/relationships/kids', - related: '/middle-agers/1/kids', + data: [ + { + type: 'kid', + id: '1', }, - data: [ - { - type: 'kid', - id: '1', - }, - ], - }, + ], }, }, + }, - { - id: '1', - type: 'elder', - links: { - self: '/elders/1', - }, - attributes: { - name: 'Elder 1', - }, - relationships: { - middleAger: { - links: { - self: '/elders/1/relationships/middle-agers', - related: '/elders/1/middle-agers', - }, + { + id: '1', + type: 'elder', + links: { + self: '/elders/1', + }, + attributes: { + name: 'Elder 1', + }, + relationships: { + middleAger: { + links: { + self: '/elders/1/relationships/middle-agers', + related: '/elders/1/middle-agers', }, }, }, - ], - }); + }, + ], }); - return run(() => { - let kid = store.peekRecord('kid', '1'); + let kid = store.peekRecord('kid', '1'); + const middleAger = await kid.middleAger; + assert.ok(middleAger, 'MiddleAger relationship was set up correctly'); - return kid.middleAger.then((middleAger) => { - assert.ok(middleAger, 'MiddleAger relationship was set up correctly'); + let middleAgerName = middleAger.name; + let kids = await middleAger.kids; + assert.strictEqual(middleAgerName, 'Middle Ager 1', 'MiddleAger name is there'); + assert.ok(kids.includes(kid)); - let middleAgerName = get(middleAger, 'name'); - assert.strictEqual(middleAgerName, 'Middle Ager 1', 'MiddleAger name is there'); - assert.ok(middleAger.kids.includes(kid)); - - return middleAger.elder.then((elder) => { - assert.notEqual(elder, null, 'Elder relationship was set up correctly'); - let elderName = get(elder, 'name'); - assert.strictEqual(elderName, 'Elder 1', 'Elder name is there'); - }); - }); - }); + const elder = await middleAger.elder; + assert.notEqual(elder, null, 'Elder relationship was set up correctly'); + let elderName = elder.name; + assert.strictEqual(elderName, 'Elder 1', 'Elder name is there'); }); }); diff --git a/packages/-ember-data/tests/integration/relationships/one-to-many-test.js b/packages/-ember-data/tests/integration/relationships/one-to-many-test.js index d6ff504bf1d..5b1bfd90b03 100644 --- a/packages/-ember-data/tests/integration/relationships/one-to-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/one-to-many-test.js @@ -9,6 +9,7 @@ import { setupTest } from 'ember-qunit'; import Adapter from '@ember-data/adapter'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; +import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; module('integration/relationships/one_to_many_test - OneToMany relationships', function (hooks) { setupTest(hooks); @@ -1624,44 +1625,48 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f assert.strictEqual(account.user, null, 'Account does not have the user anymore'); }); - test('createRecord updates inverse record array which has observers', async function (assert) { - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); - - adapter.findAll = () => { - return { - data: [ - { - id: '2', - type: 'user', - attributes: { - name: 'Stanley', + deprecatedTest( + 'createRecord updates inverse record array which has observers', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 5 }, + async function (assert) { + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); + + adapter.findAll = () => { + return { + data: [ + { + id: '2', + type: 'user', + attributes: { + name: 'Stanley', + }, }, - }, - ], + ], + }; }; - }; - const users = await store.findAll('user'); - assert.strictEqual(users.length, 1, 'Exactly 1 user'); + const users = await store.findAll('user'); + assert.strictEqual(users.length, 1, 'Exactly 1 user'); - let user = users.firstObject; - assert.strictEqual(user.messages.length, 0, 'Record array is initially empty'); + let user = users.firstObject; + assert.strictEqual(user.messages.length, 0, 'Record array is initially empty'); - // set up an observer - user.addObserver('messages.@each.title', () => {}); - user.messages.firstObject; + // set up an observer + user.addObserver('messages.@each.title', () => {}); + user.messages.firstObject; - const messages = await user.messages; + const messages = await user.messages; - assert.strictEqual(messages.length, 0, 'we have no messages'); - assert.strictEqual(user.messages.length, 0, 'we have no messages'); + assert.strictEqual(messages.length, 0, 'we have no messages'); + assert.strictEqual(user.messages.length, 0, 'we have no messages'); - let message = store.createRecord('message', { user, title: 'EmberFest was great' }); - assert.strictEqual(messages.length, 1, 'The message is added to the record array'); - assert.strictEqual(user.messages.length, 1, 'The message is added to the record array'); + let message = store.createRecord('message', { user, title: 'EmberFest was great' }); + assert.strictEqual(messages.length, 1, 'The message is added to the record array'); + assert.strictEqual(user.messages.length, 1, 'The message is added to the record array'); - let messageFromArray = user.messages.firstObject; - assert.strictEqual(message, messageFromArray, 'Only one message record instance should be created'); - }); + let messageFromArray = user.messages.firstObject; + assert.strictEqual(message, messageFromArray, 'Only one message record instance should be created'); + } + ); }); diff --git a/packages/-ember-data/tests/integration/store-test.js b/packages/-ember-data/tests/integration/store-test.js index c04f15337c7..17447121868 100644 --- a/packages/-ember-data/tests/integration/store-test.js +++ b/packages/-ember-data/tests/integration/store-test.js @@ -310,7 +310,7 @@ module('integration/store - findRecord', function (hooks) { }); test('store#findRecord returns cached record immediately and reloads record in the background', async function (assert) { - assert.expect(4); + assert.expect(2); let adapter = store.adapterFor('application'); @@ -342,15 +342,12 @@ module('integration/store - findRecord', function (hooks) { }; }; - const promiseCar = store.findRecord('car', '1'); - const car = await promiseCar; + const car = await store.findRecord('car', '1'); - assert.strictEqual(promiseCar.model, 'Mini', 'promiseCar is from cache'); assert.strictEqual(car.model, 'Mini', 'car record is returned from cache'); await settled(); - assert.strictEqual(promiseCar.model, 'Princess', 'promiseCar is updated'); assert.strictEqual(car.model, 'Princess', 'Updated car record is returned'); }); diff --git a/packages/-ember-data/tests/integration/store/query-test.js b/packages/-ember-data/tests/integration/store/query-test.js index dcb912e0485..1e44f30d51c 100644 --- a/packages/-ember-data/tests/integration/store/query-test.js +++ b/packages/-ember-data/tests/integration/store/query-test.js @@ -1,48 +1,43 @@ -import { run } from '@ember/runloop'; - import { module, test } from 'qunit'; import RSVP from 'rsvp'; -import DS from 'ember-data'; import { setupTest } from 'ember-qunit'; +import Adapter from '@ember-data/adapter'; +import Model from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; module('integration/store/query', function (hooks) { setupTest(hooks); hooks.beforeEach(function () { - const Person = DS.Model.extend(); + const Person = Model.extend(); this.owner.register('model:person', Person); - this.owner.register('adapter:application', DS.Adapter.extend()); + this.owner.register('adapter:application', Adapter.extend()); this.owner.register('serializer:application', class extends JSONAPISerializer {}); }); - test('meta is proxied correctly on the PromiseArray', function (assert) { + test('meta is proxied correctly on the PromiseArray', async function (assert) { let store = this.owner.lookup('service:store'); let defered = RSVP.defer(); this.owner.register( 'adapter:person', - DS.Adapter.extend({ + Adapter.extend({ query(store, type, query) { return defered.promise; }, }) ); - let result; - run(function () { - result = store.query('person', {}); - }); + let result = store.query('person', {}); - assert.notOk(result.meta.foo, 'precond: meta is not yet set'); + assert.notOk(result.meta?.foo, 'precond: meta is not yet set'); - run(function () { - defered.resolve({ data: [], meta: { foo: 'bar' } }); - }); + defered.resolve({ data: [], meta: { foo: 'bar' } }); + await result; assert.strictEqual(result.meta.foo, 'bar'); }); diff --git a/packages/-ember-data/tests/test-helper.js b/packages/-ember-data/tests/test-helper.js index 057ae6a25c6..147cf2a3d4d 100644 --- a/packages/-ember-data/tests/test-helper.js +++ b/packages/-ember-data/tests/test-helper.js @@ -7,7 +7,6 @@ import RSVP from 'rsvp'; import { start } from 'ember-qunit'; import assertAllDeprecations from '@ember-data/unpublished-test-infra/test-support/assert-all-deprecations'; -import additionalLegacyAsserts from '@ember-data/unpublished-test-infra/test-support/legacy'; import configureAsserts from '@ember-data/unpublished-test-infra/test-support/qunit-asserts'; import customQUnitAdapter from '@ember-data/unpublished-test-infra/test-support/testem/custom-qunit-adapter'; @@ -26,7 +25,6 @@ if (QUnit.urlParams.enableoptionalfeatures) { setup(QUnit.assert); configureAsserts(); -additionalLegacyAsserts(); setApplication(Application.create(config.APP)); diff --git a/packages/-ember-data/tests/unit/model/errors-test.js b/packages/-ember-data/tests/unit/model/errors-test.js index c31f034b2fd..24688eca4b4 100644 --- a/packages/-ember-data/tests/unit/model/errors-test.js +++ b/packages/-ember-data/tests/unit/model/errors-test.js @@ -58,17 +58,17 @@ module('unit/model/errors', function (hooks) { errors.trigger = assert.becameInvalid; errors.add('firstName', 'error'); errors.trigger = assert.unexpectedSend; - assert.ok(errors.firstName.length === 1, 'returns errors'); + assert.ok(errors.get('firstName').length === 1, 'returns errors'); assert.deepEqual(errors.firstObject, { attribute: 'firstName', message: 'error' }); errors.add('firstName', 'error2'); - assert.ok(errors.firstName.length === 2, 'returns errors'); + assert.ok(errors.get('firstName').length === 2, 'returns errors'); errors.add('lastName', 'error3'); assert.deepEqual(errors.toArray(), [ { attribute: 'firstName', message: 'error' }, { attribute: 'firstName', message: 'error2' }, { attribute: 'lastName', message: 'error3' }, ]); - assert.deepEqual(errors.firstName, [ + assert.deepEqual(errors.get('firstName'), [ { attribute: 'firstName', message: 'error' }, { attribute: 'firstName', message: 'error2' }, ]); diff --git a/packages/-ember-data/tests/unit/model/relationships/belongs-to-test.js b/packages/-ember-data/tests/unit/model/relationships/belongs-to-test.js index a93379e4217..8b73d401a54 100644 --- a/packages/-ember-data/tests/unit/model/relationships/belongs-to-test.js +++ b/packages/-ember-data/tests/unit/model/relationships/belongs-to-test.js @@ -20,7 +20,7 @@ module('unit/model/relationships - belongsTo', function (hooks) { this.owner.register('serializer:application', class extends JSONAPISerializer {}); }); - test('belongsTo lazily loads relationships as needed', function (assert) { + test('belongsTo lazily loads relationships as needed', async function (assert) { assert.expect(5); const Tag = Model.extend({ @@ -41,61 +41,55 @@ module('unit/model/relationships - belongsTo', function (hooks) { adapter.shouldBackgroundReloadRecord = () => false; - run(() => { - store.push({ - data: [ - { - type: 'tag', - id: '5', - attributes: { - name: 'friendly', - }, + store.push({ + data: [ + { + type: 'tag', + id: '5', + attributes: { + name: 'friendly', }, - { - type: 'tag', - id: '2', - attributes: { - name: 'smarmy', - }, + }, + { + type: 'tag', + id: '2', + attributes: { + name: 'smarmy', }, - { - type: 'tag', - id: '12', - attributes: { - name: 'oohlala', - }, + }, + { + type: 'tag', + id: '12', + attributes: { + name: 'oohlala', }, - { - type: 'person', - id: '1', - attributes: { - name: 'Tom Dale', - }, - relationships: { - tag: { - data: { type: 'tag', id: '5' }, - }, + }, + { + type: 'person', + id: '1', + attributes: { + name: 'Tom Dale', + }, + relationships: { + tag: { + data: { type: 'tag', id: '5' }, }, }, - ], - }); + }, + ], }); + const person = await store.findRecord('person', '1'); + assert.strictEqual(get(person, 'name'), 'Tom Dale', 'precond - retrieves person record from store'); - return run(() => { - return store.findRecord('person', 1).then((person) => { - assert.strictEqual(get(person, 'name'), 'Tom Dale', 'precond - retrieves person record from store'); - - assert.true(get(person, 'tag') instanceof Tag, 'the tag property should return a tag'); - assert.strictEqual(get(person, 'tag.name'), 'friendly', 'the tag shuld have name'); + assert.true(person.tag instanceof Tag, 'the tag property should return a tag'); + assert.strictEqual(person.tag.name, 'friendly', 'the tag shuld have name'); - assert.strictEqual(get(person, 'tag'), get(person, 'tag'), 'the returned object is always the same'); - assert.asyncEqual( - get(person, 'tag'), - store.findRecord('tag', 5), - 'relationship object is the same as object retrieved directly' - ); - }); - }); + assert.strictEqual(person.tag, person.tag, 'the returned object is always the same'); + assert.strictEqual( + person.tag, + await store.findRecord('tag', 5), + 'relationship object is the same as object retrieved directly' + ); }); test('belongsTo does not notify when it is initially reified', function (assert) { @@ -643,7 +637,7 @@ module('unit/model/relationships - belongsTo', function (hooks) { run(() => store.peekRecord('person', 1).occupation); }); - test('belongsTo supports relationships to models with id 0', function (assert) { + test('belongsTo supports relationships to models with id 0', async function (assert) { assert.expect(5); const Tag = Model.extend({ @@ -664,61 +658,56 @@ module('unit/model/relationships - belongsTo', function (hooks) { adapter.shouldBackgroundReloadRecord = () => false; - run(() => { - store.push({ - data: [ - { - type: 'tag', - id: '0', - attributes: { - name: 'friendly', - }, + store.push({ + data: [ + { + type: 'tag', + id: '0', + attributes: { + name: 'friendly', }, - { - type: 'tag', - id: '2', - attributes: { - name: 'smarmy', - }, + }, + { + type: 'tag', + id: '2', + attributes: { + name: 'smarmy', }, - { - type: 'tag', - id: '12', - attributes: { - name: 'oohlala', - }, + }, + { + type: 'tag', + id: '12', + attributes: { + name: 'oohlala', }, - { - type: 'person', - id: '1', - attributes: { - name: 'Tom Dale', - }, - relationships: { - tag: { - data: { type: 'tag', id: '0' }, - }, + }, + { + type: 'person', + id: '1', + attributes: { + name: 'Tom Dale', + }, + relationships: { + tag: { + data: { type: 'tag', id: '0' }, }, }, - ], - }); + }, + ], }); - return run(() => { - return store.findRecord('person', 1).then((person) => { - assert.strictEqual(get(person, 'name'), 'Tom Dale', 'precond - retrieves person record from store'); + const person = await store.findRecord('person', '1'); + assert.strictEqual(person.name, 'Tom Dale', 'precond - retrieves person record from store'); - assert.true(get(person, 'tag') instanceof Tag, 'the tag property should return a tag'); - assert.strictEqual(get(person, 'tag.name'), 'friendly', 'the tag should have name'); + assert.true(person.tag instanceof Tag, 'the tag property should return a tag'); + assert.strictEqual(person.tag.name, 'friendly', 'the tag should have name'); - assert.strictEqual(get(person, 'tag'), get(person, 'tag'), 'the returned object is always the same'); - assert.asyncEqual( - get(person, 'tag'), - store.findRecord('tag', 0), - 'relationship object is the same as object retrieved directly' - ); - }); - }); + assert.strictEqual(person.tag, person.tag, 'the returned object is always the same'); + assert.strictEqual( + person.tag, + await store.findRecord('tag', 0), + 'relationship object is the same as object retrieved directly' + ); }); testInDebug('belongsTo gives a warning when provided with a serialize option', function (assert) { diff --git a/packages/-ember-data/tests/unit/model/rollback-attributes-test.js b/packages/-ember-data/tests/unit/model/rollback-attributes-test.js index 688066ef765..b68ea1e617d 100644 --- a/packages/-ember-data/tests/unit/model/rollback-attributes-test.js +++ b/packages/-ember-data/tests/unit/model/rollback-attributes-test.js @@ -473,8 +473,8 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h }, }); - dog.set('name', 'is a dwarf planet'); - dog.set('breed', 'planet'); + dog.name = 'is a dwarf planet'; + dog.breed = 'planet'; addObserver(dog, 'errors.name', function () { assert.ok(true, 'errors.name did change'); @@ -486,8 +486,8 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h assert.strictEqual(reason, thrownAdapterError); assert.strictEqual(dog.name, 'is a dwarf planet'); assert.strictEqual(dog.breed, 'planet'); - assert.ok(isPresent(dog.errors.name)); - assert.strictEqual(dog.get('errors.name.length'), 1); + assert.ok(isPresent(dog.errors.get('name'))); + assert.strictEqual(dog.errors.get('name.length'), 1); dog.set('name', 'Seymour Asses'); await settled(); @@ -501,7 +501,7 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h assert.strictEqual(dog.name, 'Pluto'); assert.strictEqual(dog.breed, 'Disney'); assert.false(dog.hasDirtyAttributes, 'must not be dirty'); - assert.notOk(dog.errors.name); + assert.notOk(dog.errors.get('name')); assert.ok(dog.isValid); } }); diff --git a/packages/-ember-data/tests/unit/store/adapter-interop-test.js b/packages/-ember-data/tests/unit/store/adapter-interop-test.js index 22e7cfa57db..1e6f5c53acd 100644 --- a/packages/-ember-data/tests/unit/store/adapter-interop-test.js +++ b/packages/-ember-data/tests/unit/store/adapter-interop-test.js @@ -1,5 +1,6 @@ import { get, set } from '@ember/object'; import { later, run } from '@ember/runloop'; +import { settled } from '@ember/test-helpers'; import { module, test } from 'qunit'; import { all, Promise as EmberPromise, resolve } from 'rsvp'; @@ -523,12 +524,9 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho return run(() => { return store.findRecord('person', 1, { preload: { friend: 2 } }).then(() => { - return store - .peekRecord('person', 1) - .friend - .then((friend) => { - assert.strictEqual(friend.id, '2', 'Preloaded belongsTo set'); - }); + return store.peekRecord('person', 1).friend.then((friend) => { + assert.strictEqual(friend.id, '2', 'Preloaded belongsTo set'); + }); }); }); }); @@ -1228,8 +1226,9 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho }); }); - test('store should reload all records in the background when `shouldBackgroundReloadAll` is true', function (assert) { + test('store should reload all records in the background when `shouldBackgroundReloadAll` is true', async function (assert) { assert.expect(5); + let hasPerformedInitialFind = false; const Person = Model.extend({ name: attr(), @@ -1245,8 +1244,12 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho return true; }, findAll() { - assert.ok(true, 'find should not be called'); - return { data: [{ id: 1, type: 'person', attributes: { name: 'Tom' } }] }; + assert.ok(true, 'findAll should be called'); + return new Promise((resolve) => setTimeout(resolve, 1)).then(() => { + return { + data: [{ id: 1, type: 'person', attributes: { name: 'Tom' } }], + }; + }); }, }); @@ -1256,15 +1259,14 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho let store = this.owner.lookup('service:store'); - let done = run(() => { - return store.findAll('person').then((records) => { - assert.strictEqual(records.firstObject.name, undefined); - }); - }); + store.push({ data: [{ id: '1', type: 'person', attributes: { name: 'John' } }] }); - assert.strictEqual(store.peekRecord('person', 1).name, 'Tom'); + const records = await store.findAll('person'); - return done; + assert.strictEqual(records.firstObject.name, 'John', 'on initial load name is stale'); + + await settled(); + assert.strictEqual(store.peekRecord('person', 1).name, 'Tom', 'after background reload name is loaded'); }); testInDebug('Calling adapterFor with a model class should assert', function (assert) { diff --git a/packages/adapter/addon/rest.ts b/packages/adapter/addon/rest.ts index dac51b0e821..a45441e03ce 100644 --- a/packages/adapter/addon/rest.ts +++ b/packages/adapter/addon/rest.ts @@ -311,13 +311,12 @@ declare const jQuery: JQueryStatic | undefined; ```app/adapters/application.js import RESTAdapter from '@ember-data/adapter/rest'; - import { get } from '@ember/object'; import { computed } from '@ember/object'; export default class ApplicationAdapter extends RESTAdapter { headers: computed(function() { return { - 'API_KEY': get(document.cookie.match(/apiKey\=([^;]*)/), '1'), + 'API_KEY': document.cookie.match(/apiKey\=([^;]*)/)['1'], 'ANOTHER_HEADER': 'Some header value' }; }).volatile() diff --git a/packages/debug/addon/index.js b/packages/debug/addon/index.js index 0308aa19559..d259f1f563a 100644 --- a/packages/debug/addon/index.js +++ b/packages/debug/addon/index.js @@ -26,7 +26,6 @@ import { A } from '@ember/array'; import { assert } from '@ember/debug'; import DataAdapter from '@ember/debug/data-adapter'; -import { get } from '@ember/object'; import { addObserver, removeObserver } from '@ember/object/observers'; import { inject as service } from '@ember/service'; import { capitalize, underscore } from '@ember/string'; @@ -64,7 +63,7 @@ export default DataAdapter.extend({ }, _nameToClass(type) { - return get(this, 'store').modelFor(type); + return this.store.modelFor(type); }, /** @@ -80,7 +79,7 @@ export default DataAdapter.extend({ @return {Function} Method to call to remove all observers */ watchModelTypes(typesAdded, typesUpdated) { - const store = get(this, 'store'); + const { store } = this; const __getRecordData = store._instanceCache.getRecordData; const _releaseMethods = []; const discoveredTypes = typesMapFor(store); @@ -166,7 +165,7 @@ export default DataAdapter.extend({ ]; let count = 0; let self = this; - get(typeClass, 'attributes').forEach((meta, name) => { + typeClass.attributes.forEach((meta, name) => { if (count++ > self.attributeLimit) { return false; } @@ -213,13 +212,13 @@ export default DataAdapter.extend({ */ getRecordColumnValues(record) { let count = 0; - let columnValues = { id: get(record, 'id') }; + let columnValues = { id: record.id }; record.eachAttribute((key) => { if (count++ > this.attributeLimit) { return false; } - columnValues[key] = get(record, key); + columnValues[key] = record[key]; }); return columnValues; }, @@ -236,7 +235,7 @@ export default DataAdapter.extend({ let keywords = []; let keys = A(['id']); record.eachAttribute((key) => keys.push(key)); - keys.forEach((key) => keywords.push(get(record, key))); + keys.forEach((key) => keywords.push(record[key])); return keywords; }, diff --git a/packages/model/addon/-private/errors.ts b/packages/model/addon/-private/errors.ts index 89e88b68f0c..4b2d9ad056d 100644 --- a/packages/model/addon/-private/errors.ts +++ b/packages/model/addon/-private/errors.ts @@ -219,7 +219,7 @@ export default class Errors extends ArrayProxyWithCustomOverrides { if (data[key] === undefined) { @@ -576,7 +573,7 @@ const JSONSerializer = Serializer.extend({ export default class ApplicationSerializer extends JSONSerializer { normalize(typeClass, hash) { - let fields = get(typeClass, 'fields'); + let fields = typeClass.fields; fields.forEach(function(type, field) { let payloadField = underscore(field); @@ -629,7 +626,7 @@ const JSONSerializer = Serializer.extend({ @return {String} */ extractId(modelClass, resourceHash) { - let primaryKey = get(this, 'primaryKey'); + let primaryKey = this.primaryKey; let id = resourceHash[primaryKey]; return coerceId(id); }, @@ -685,7 +682,7 @@ const JSONSerializer = Serializer.extend({ } let modelClass = this.store.modelFor(relationshipModelName); - if (relationshipHash.type && !modelHasAttributeOrRelationshipNamedType(modelClass)) { + if (relationshipHash.type && !modelClass.fields.has('type')) { relationshipHash.type = this.modelNameFromPayloadKey(relationshipHash.type); } @@ -830,7 +827,7 @@ const JSONSerializer = Serializer.extend({ @private */ normalizeUsingDeclaredMapping(modelClass, hash) { - let attrs = get(this, 'attrs'); + let attrs = this.attrs; let normalizedKey; let payloadKey; @@ -842,11 +839,11 @@ const JSONSerializer = Serializer.extend({ continue; } - if (get(modelClass, 'attributes').has(key)) { + if (modelClass.attributes.has(key)) { normalizedKey = this.keyForAttribute(key, 'deserialize'); } - if (get(modelClass, 'relationshipsByName').has(key)) { + if (modelClass.relationshipsByName.has(key)) { normalizedKey = this.keyForRelationship(key, modelClass, 'deserialize'); } @@ -874,13 +871,13 @@ const JSONSerializer = Serializer.extend({ '` on `' + modelClass.modelName + '`. Check your serializers attrs hash.', - get(modelClass, 'attributes').has(key) || get(modelClass, 'relationshipsByName').has(key), + modelClass.attributes.has(key) || modelClass.relationshipsByName.has(key), { id: 'ds.serializer.no-mapped-attrs-key', } ); - let attrs = get(this, 'attrs'); + let attrs = this.attrs; let mappedKey; if (attrs && attrs[key]) { mappedKey = attrs[key]; @@ -907,7 +904,7 @@ const JSONSerializer = Serializer.extend({ @return {boolean} true if the key can be serialized */ _canSerialize(key) { - let attrs = get(this, 'attrs'); + let attrs = this.attrs; return !attrs || !attrs[key] || attrs[key].serialize !== false; }, @@ -923,7 +920,7 @@ const JSONSerializer = Serializer.extend({ @return {boolean} true if the key must be serialized */ _mustSerialize(key) { - let attrs = get(this, 'attrs'); + let attrs = this.attrs; return attrs && attrs[key] && attrs[key].serialize === true; }, @@ -1110,7 +1107,7 @@ const JSONSerializer = Serializer.extend({ if (options && options.includeId) { const id = snapshot.id; if (id) { - json[get(this, 'primaryKey')] = id; + json[this.primaryKey] = id; } } diff --git a/packages/serializer/addon/rest.js b/packages/serializer/addon/rest.js index e107f9fd7cb..56c1c63a23f 100644 --- a/packages/serializer/addon/rest.js +++ b/packages/serializer/addon/rest.js @@ -11,8 +11,6 @@ import { singularize } from 'ember-inflector'; import JSONSerializer from '@ember-data/serializer/json'; import { coerceId } from '@ember-data/store/-private'; -import { modelHasAttributeOrRelationshipNamedType } from './-private'; - function makeArray(value) { return Array.isArray(value) ? value : [value]; } @@ -198,7 +196,7 @@ const RESTSerializer = JSONSerializer.extend({ let serializer = primarySerializer; let modelClass = primaryModelClass; - let primaryHasTypeAttribute = modelHasAttributeOrRelationshipNamedType(primaryModelClass); + let primaryHasTypeAttribute = primaryModelClass.fields.has('type'); if (!primaryHasTypeAttribute && hash.type) { // Support polymorphic records in async relationships diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index e70ee1bac2d..feca8e25140 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -643,7 +643,7 @@ function assertRecordsPassedToHasMany(records: RecordInstance[]) { function extractRecordDatasFromRecords(records: RecordInstance[]): RecordData[] { return records.map(extractRecordDataFromRecord) as RecordData[]; } -type PromiseProxyRecord = { then(): void; get(str: 'content'): RecordInstance | null | undefined }; +type PromiseProxyRecord = { then(): void; content: RecordInstance | null | undefined }; function extractRecordDataFromRecord(recordOrPromiseRecord: PromiseProxyRecord | RecordInstance | null) { if (!recordOrPromiseRecord) { @@ -651,7 +651,7 @@ function extractRecordDataFromRecord(recordOrPromiseRecord: PromiseProxyRecord | } if (isPromiseRecord(recordOrPromiseRecord)) { - let content = recordOrPromiseRecord.get && recordOrPromiseRecord.content; + let content = recordOrPromiseRecord.content; assert( 'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.', content !== undefined diff --git a/packages/store/addon/-private/legacy-model-support/schema-definition-service.ts b/packages/store/addon/-private/legacy-model-support/schema-definition-service.ts index d462b0c0fce..0120a6f8f98 100644 --- a/packages/store/addon/-private/legacy-model-support/schema-definition-service.ts +++ b/packages/store/addon/-private/legacy-model-support/schema-definition-service.ts @@ -1,6 +1,5 @@ import { getOwner } from '@ember/application'; import { deprecate } from '@ember/debug'; -import { get } from '@ember/object'; import { importSync } from '@embroider/macros'; @@ -55,7 +54,7 @@ export class DSModelSchemaDefinitionService { if (attributes === undefined) { let modelClass = this.store.modelFor(modelName); - let attributeMap = get(modelClass, 'attributes'); + let attributeMap = modelClass.attributes; attributes = Object.create(null); attributeMap.forEach((meta, name) => (attributes[name] = meta)); @@ -88,7 +87,7 @@ export class DSModelSchemaDefinitionService { if (relationships === undefined) { let modelClass = this.store.modelFor(modelName); - relationships = get(modelClass, 'relationshipsObject') || null; + relationships = modelClass.relationshipsObject || null; this._relationshipsDefCache[modelName] = relationships; } diff --git a/packages/store/addon/-private/managers/record-array-manager.ts b/packages/store/addon/-private/managers/record-array-manager.ts index 929d835311e..083f77d1409 100644 --- a/packages/store/addon/-private/managers/record-array-manager.ts +++ b/packages/store/addon/-private/managers/record-array-manager.ts @@ -4,7 +4,6 @@ import { A } from '@ember/array'; import { assert } from '@ember/debug'; -import { set } from '@ember/object'; import { _backburner as emberBackburner } from '@ember/runloop'; import { DEBUG } from '@glimmer/env'; @@ -142,7 +141,7 @@ class RecordArrayManager { _didUpdateAll(modelName: string): void { let recordArray = this._liveRecordArrays[modelName]; if (recordArray) { - set(recordArray, 'isUpdating', false); + recordArray.isUpdating = false; // TODO potentially we should sync here, currently // this occurs as a side-effect of individual records updating // this._syncLiveRecordArray(recordArray, modelName); diff --git a/packages/store/addon/-private/record-arrays/record-array.ts b/packages/store/addon/-private/record-arrays/record-array.ts index 9e8d7b5e539..7f57573f51e 100644 --- a/packages/store/addon/-private/record-arrays/record-array.ts +++ b/packages/store/addon/-private/record-arrays/record-array.ts @@ -4,7 +4,6 @@ import type NativeArray from '@ember/array/-private/native-array'; import ArrayProxy from '@ember/array/proxy'; import { assert, deprecate } from '@ember/debug'; -import { get, set } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { Promise } from 'rsvp'; @@ -107,7 +106,7 @@ export default class RecordArray extends ArrayProxy); - set(this, 'length', 0); + this.content = null as unknown as NativeArray; + this.length = 0; super.willDestroy(); } diff --git a/packages/store/addon/-private/utils/common.js b/packages/store/addon/-private/utils/common.js index 3162d912e04..1d63f75289f 100644 --- a/packages/store/addon/-private/utils/common.js +++ b/packages/store/addon/-private/utils/common.js @@ -1,5 +1,4 @@ import { deprecate } from '@ember/debug'; -import { get } from '@ember/object'; import { DEBUG } from '@glimmer/env'; import { resolve } from 'rsvp'; @@ -27,7 +26,7 @@ export function _guard(promise, test) { } export function _objectIsAlive(object) { - return !(get(object, 'isDestroyed') || get(object, 'isDestroying')); + return !(object.isDestroyed || object.isDestroying); } export function guardDestroyedStore(promise, store, label) { diff --git a/packages/unpublished-test-infra/addon-test-support/async.js b/packages/unpublished-test-infra/addon-test-support/async.js deleted file mode 100644 index 811a4044f50..00000000000 --- a/packages/unpublished-test-infra/addon-test-support/async.js +++ /dev/null @@ -1,42 +0,0 @@ -import { run } from '@ember/runloop'; - -import { all, resolve } from 'rsvp'; - -// Should not use these going forward, use async/await instead. -export function wait(callback, timeout) { - let done = this.async(); - - let timer = setTimeout(() => { - this.ok(false, 'Timeout was reached'); - done(); - }, timeout || 200); - - return function () { - window.clearTimeout(timer); - - let args = arguments; - let result; - try { - result = run(() => callback.apply(this, args)); - } finally { - done(); - } - return result; - }; -} - -export function asyncEqual(a, b, message) { - return all([resolve(a), resolve(b)]).then( - this.wait((array) => { - let actual = array[0]; - let expected = array[1]; - let result = actual === expected; - - this.pushResult({ result, actual, expected, message }); - }) - ); -} - -export function invokeAsync(callback, timeout = 1) { - setTimeout(this.wait(callback, timeout + 100), timeout); -} diff --git a/packages/unpublished-test-infra/addon-test-support/legacy.js b/packages/unpublished-test-infra/addon-test-support/legacy.js deleted file mode 100644 index 9210f7a5ca5..00000000000 --- a/packages/unpublished-test-infra/addon-test-support/legacy.js +++ /dev/null @@ -1,28 +0,0 @@ -import QUnit from 'qunit'; - -import { asyncEqual, invokeAsync, wait } from './async'; - -const { assert } = QUnit; - -export default function additionalLegacyAsserts() { - assert.wait = wait; - assert.asyncEqual = asyncEqual; - assert.invokeAsync = invokeAsync; - - assert.assertClean = function (promise) { - return promise.then( - this.wait((record) => { - this.equal(record.hasDirtyAttributes, false, 'The record is now clean'); - return record; - }) - ); - }; - - assert.contains = function (array, item) { - this.ok(array.indexOf(item) !== -1, `array contains ${item}`); - }; - - assert.without = function (array, item) { - this.ok(array.indexOf(item) === -1, `array doesn't contain ${item}`); - }; -} From d0532a30f59e01377878238b7ba4f3824ecfd228 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 8 Aug 2022 13:54:44 -0700 Subject: [PATCH 22/29] fix lint --- .../record-data/record-data-state-test.ts | 1 - .../integration/records/create-record-test.js | 5 +-- .../integration/records/edit-record-test.js | 40 ++++--------------- .../polymorphic-mixins-has-many-test.js | 9 ++--- .../-ember-data/tests/unit/many-array-test.js | 9 ++--- packages/-ember-data/tests/unit/model-test.js | 20 ++-------- .../tests/unit/model/merge-test.js | 6 +-- .../tests/unit/store/adapter-interop-test.js | 1 - .../polymorphic-relationship-payloads-test.js | 36 ++++++----------- .../-private/network/snapshot-record-array.ts | 2 +- 10 files changed, 33 insertions(+), 96 deletions(-) diff --git a/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts b/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts index c985628dd77..ef8475f6a44 100644 --- a/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts +++ b/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts @@ -1,5 +1,4 @@ import EmberObject from '@ember/object'; -import Ember from 'ember'; import { module, test } from 'qunit'; import { Promise } from 'rsvp'; diff --git a/packages/-ember-data/tests/integration/records/create-record-test.js b/packages/-ember-data/tests/integration/records/create-record-test.js index 7c9ab555f70..cd98b404a19 100644 --- a/packages/-ember-data/tests/integration/records/create-record-test.js +++ b/packages/-ember-data/tests/integration/records/create-record-test.js @@ -81,10 +81,7 @@ module('Store.createRecord() coverage', function (hooks) { // check that we are properly configured assert.strictEqual(pet.owner, chris, 'Precondition: Our owner is Chris'); - let pets = chris - .pets - .toArray() - .map((pet) => pet.name); + let pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Precondition: Chris has Shen as a pet'); pet.unloadRecord(); diff --git a/packages/-ember-data/tests/integration/records/edit-record-test.js b/packages/-ember-data/tests/integration/records/edit-record-test.js index 102c9ad496b..69e2777f5bf 100644 --- a/packages/-ember-data/tests/integration/records/edit-record-test.js +++ b/packages/-ember-data/tests/integration/records/edit-record-test.js @@ -67,10 +67,7 @@ module('Editing a Record', function (hooks) { // check that we are properly configured assert.strictEqual(pet.owner, null, 'Precondition: Our owner is null'); - let pets = chris - .pets - .toArray() - .map((pet) => pet.name); + let pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, [], 'Precondition: Chris has no pets'); pet.set('owner', chris); @@ -78,10 +75,7 @@ module('Editing a Record', function (hooks) { assert.strictEqual(pet.owner, chris, 'Shen has Chris as an owner'); // check that the relationship has been established - pets = chris - .pets - .toArray() - .map((pet) => pet.name); + pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Chris has Shen as a pet'); }); @@ -107,10 +101,7 @@ module('Editing a Record', function (hooks) { // check that we are properly configured assert.strictEqual(pet.owner, null, 'Precondition: Our owner is null'); - let pets = chris - .pets - .toArray() - .map((pet) => pet.name); + let pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, [], 'Precondition: Chris has no pets'); pet.set('owner', chris); @@ -118,10 +109,7 @@ module('Editing a Record', function (hooks) { assert.strictEqual(pet.owner, chris, 'Shen has Chris as an owner'); // check that the relationship has been established - pets = chris - .pets - .toArray() - .map((pet) => pet.name); + pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Chris has Shen as a pet'); }); @@ -139,10 +127,7 @@ module('Editing a Record', function (hooks) { // check that we are properly configured assert.strictEqual(pet.owner, null, 'Precondition: Our owner is null'); - let pets = chris - .pets - .toArray() - .map((pet) => pet.name); + let pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, [], 'Precondition: Chris has no pets'); pet.set('owner', chris); @@ -150,10 +135,7 @@ module('Editing a Record', function (hooks) { assert.strictEqual(pet.owner, chris, 'Shen has Chris as an owner'); // check that the relationship has been established - pets = chris - .pets - .toArray() - .map((pet) => pet.name); + pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Chris has Shen as a pet'); }); @@ -179,10 +161,7 @@ module('Editing a Record', function (hooks) { // check that we are properly configured assert.strictEqual(pet.owner, null, 'Precondition: Our owner is null'); - let pets = chris - .pets - .toArray() - .map((pet) => pet.name); + let pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, [], 'Precondition: Chris has no pets'); pet.set('owner', chris); @@ -190,10 +169,7 @@ module('Editing a Record', function (hooks) { assert.strictEqual(pet.owner, chris, 'Shen has Chris as an owner'); // check that the relationship has been established - pets = chris - .pets - .toArray() - .map((pet) => pet.name); + pets = chris.pets.toArray().map((pet) => pet.name); assert.deepEqual(pets, ['Shen'], 'Chris has Shen as a pet'); }); diff --git a/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js b/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js index 6ce689fa8fe..b6cf2ed899f 100644 --- a/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/polymorphic-mixins-has-many-test.js @@ -79,12 +79,9 @@ module( run(function () { user.messages.then(function (messages) { assert.strictEqual(messages.objectAt(0), video, 'The hasMany has loaded correctly'); - messages - .objectAt(0) - .user - .then(function (fetchedUser) { - assert.strictEqual(fetchedUser, user, 'The inverse was setup correctly'); - }); + messages.objectAt(0).user.then(function (fetchedUser) { + assert.strictEqual(fetchedUser, user, 'The inverse was setup correctly'); + }); }); }); }); diff --git a/packages/-ember-data/tests/unit/many-array-test.js b/packages/-ember-data/tests/unit/many-array-test.js index 11af4ceac4b..a5df7929224 100644 --- a/packages/-ember-data/tests/unit/many-array-test.js +++ b/packages/-ember-data/tests/unit/many-array-test.js @@ -71,12 +71,9 @@ module('unit/many_array - ManyArray', function (hooks) { let post = store.peekRecord('post', 3); - return post - .tags - .save() - .then(() => { - assert.ok(true, 'manyArray.save() promise resolved'); - }); + return post.tags.save().then(() => { + assert.ok(true, 'manyArray.save() promise resolved'); + }); }); }); diff --git a/packages/-ember-data/tests/unit/model-test.js b/packages/-ember-data/tests/unit/model-test.js index de9adf56516..bdd0e582035 100644 --- a/packages/-ember-data/tests/unit/model-test.js +++ b/packages/-ember-data/tests/unit/model-test.js @@ -900,10 +900,7 @@ module('unit/model - Model', function (hooks) { person.set('name', 'Peter'); person.set('isDrugAddict', true); - assert.false( - person.hasDirtyAttributes, - 'record does not become dirty after setting property to old value' - ); + assert.false(person.hasDirtyAttributes, 'record does not become dirty after setting property to old value'); }); test('resetting a property on a record cause it to become clean again', async function (assert) { @@ -984,15 +981,9 @@ module('unit/model - Model', function (hooks) { person.set('name', 'Mark'); assert.true(person.hasDirtyAttributes, 'record stays dirty after setting another property to a new value'); person.set('isDrugAddict', true); - assert.true( - person.hasDirtyAttributes, - 'record stays dirty after resetting only one property to the old value' - ); + assert.true(person.hasDirtyAttributes, 'record stays dirty after resetting only one property to the old value'); person.set('name', 'Peter'); - assert.false( - person.hasDirtyAttributes, - 'record becomes clean after resetting both properties to the old value' - ); + assert.false(person.hasDirtyAttributes, 'record becomes clean after resetting both properties to the old value'); }); test('an invalid record becomes clean again if changed property is reset', async function (assert) { @@ -1037,10 +1028,7 @@ module('unit/model - Model', function (hooks) { person.set('name', 'Peter'); assert.true(person.isValid, 'record is valid after resetting attribute to old value'); - assert.false( - person.hasDirtyAttributes, - 'record becomes clean after resetting property to the old value' - ); + assert.false(person.hasDirtyAttributes, 'record becomes clean after resetting property to the old value'); }); }); diff --git a/packages/-ember-data/tests/unit/model/merge-test.js b/packages/-ember-data/tests/unit/model/merge-test.js index c053e81cc13..5ac1ded0e0c 100644 --- a/packages/-ember-data/tests/unit/model/merge-test.js +++ b/packages/-ember-data/tests/unit/model/merge-test.js @@ -155,11 +155,7 @@ module('unit/model/merge - Merging', function (hooks) { return promise.then((person) => { assert.true(person.hasDirtyAttributes, 'The person is still dirty'); assert.strictEqual(person.name, 'Tomasz Dale', 'The local changes apply'); - assert.strictEqual( - person.city, - 'Portland', - 'The updates from the server apply on top of the previous pushes' - ); + assert.strictEqual(person.city, 'Portland', 'The updates from the server apply on top of the previous pushes'); }); }); }); diff --git a/packages/-ember-data/tests/unit/store/adapter-interop-test.js b/packages/-ember-data/tests/unit/store/adapter-interop-test.js index 1e6f5c53acd..6136d9867f8 100644 --- a/packages/-ember-data/tests/unit/store/adapter-interop-test.js +++ b/packages/-ember-data/tests/unit/store/adapter-interop-test.js @@ -1228,7 +1228,6 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho test('store should reload all records in the background when `shouldBackgroundReloadAll` is true', async function (assert) { assert.expect(5); - let hasPerformedInitialFind = false; const Person = Model.extend({ name: attr(), diff --git a/packages/-ember-data/tests/unit/system/relationships/polymorphic-relationship-payloads-test.js b/packages/-ember-data/tests/unit/system/relationships/polymorphic-relationship-payloads-test.js index 7479697b0c4..8d3dfffd1ad 100644 --- a/packages/-ember-data/tests/unit/system/relationships/polymorphic-relationship-payloads-test.js +++ b/packages/-ember-data/tests/unit/system/relationships/polymorphic-relationship-payloads-test.js @@ -403,12 +403,9 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio return this.store.push(payload); }); - const familyResultReferences = boyInstance - .family - .toArray() - .map((i) => { - return { type: i.constructor.modelName, id: i.id }; - }); + const familyResultReferences = boyInstance.family.toArray().map((i) => { + return { type: i.constructor.modelName, id: i.id }; + }); const twinResult = boyInstance.twin; const twinResultReference = { type: twinResult.constructor.modelName, id: twinResult.id }; @@ -506,12 +503,9 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio return this.store.push(payload); }); - const familyResultReferences = boyInstance - .family - .toArray() - .map((i) => { - return { type: i.constructor.modelName, id: i.id }; - }); + const familyResultReferences = boyInstance.family.toArray().map((i) => { + return { type: i.constructor.modelName, id: i.id }; + }); const twinResult = boyInstance.twin; const twinResultReference = twinResult && { type: twinResult.constructor.modelName, @@ -558,12 +552,9 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio const expectedHatReference = { id: '2', type: 'big-hat' }; const expectedHatsReferences = [{ id: '1', type: 'big-hat' }]; - const finalHatsReferences = hat2 - .hats - .toArray() - .map((i) => { - return { type: i.constructor.modelName, id: i.id }; - }); + const finalHatsReferences = hat2.hats.toArray().map((i) => { + return { type: i.constructor.modelName, id: i.id }; + }); const hatResult = hat1.hat; const finalHatReference = hatResult && { type: hatResult.constructor.modelName, @@ -605,12 +596,9 @@ module('unit/relationships/relationship-payloads-manager (polymorphic)', functio const expectedHatReference = { id: '1', type: 'big-hat' }; const expectedHatsReferences = [{ id: '1', type: 'big-hat' }]; - const finalHatsReferences = hat - .hats - .toArray() - .map((i) => { - return { type: i.constructor.modelName, id: i.id }; - }); + const finalHatsReferences = hat.hats.toArray().map((i) => { + return { type: i.constructor.modelName, id: i.id }; + }); const hatResult = hat.hat; const finalHatReference = hatResult && { type: hatResult.constructor.modelName, diff --git a/packages/store/addon/-private/network/snapshot-record-array.ts b/packages/store/addon/-private/network/snapshot-record-array.ts index 16b04516de4..db0285973e8 100644 --- a/packages/store/addon/-private/network/snapshot-record-array.ts +++ b/packages/store/addon/-private/network/snapshot-record-array.ts @@ -77,7 +77,7 @@ export default class SnapshotRecordArray { @public @type {Number} */ - this.length = recordArray.length; + this.length = recordArray.length as unknown as number; // deal with computedProperty shennanigans /** Meta objects for the record array. From bd332aecdb5822517e507a68a73f398b6d3375dc Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 8 Aug 2022 17:13:27 -0700 Subject: [PATCH 23/29] all the main tests fixed --- .../record-data/record-data-state-test.ts | 19 +- .../tests/integration/records/load-test.js | 6 +- .../integration/records/rematerialize-test.js | 2 +- .../tests/integration/records/save-test.js | 7 +- .../tests/integration/records/unload-test.js | 305 +++++++----------- .../relationships/belongs-to-test.js | 70 ++-- .../inverse-relationships-test.js | 4 +- .../relationships/many-to-many-test.js | 47 ++- .../relationships/one-to-many-test.js | 87 +++-- packages/-ember-data/tests/unit/utils-test.js | 29 +- packages/model/addon/-private/has-many.js | 4 + .../-private/legacy-relationships-support.ts | 4 +- packages/model/addon/-private/many-array.ts | 2 +- .../addon/-private/references/has-many.ts | 2 +- packages/record-data/ember-cli-build.js | 4 +- .../addon/-private/caches/instance-cache.ts | 5 +- .../-private/managers/record-array-manager.ts | 6 +- .../addon/-private/network/fetch-manager.ts | 4 + .../-private/record-arrays/record-array.ts | 7 +- .../store/addon/-private/store-service.ts | 3 +- .../ember-cli-build.js | 4 +- .../unpublished-test-infra/ember-cli-build.js | 4 +- 22 files changed, 272 insertions(+), 353 deletions(-) diff --git a/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts b/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts index ef8475f6a44..0ff1b53fd19 100644 --- a/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts +++ b/packages/-ember-data/tests/integration/record-data/record-data-state-test.ts @@ -1,4 +1,5 @@ import EmberObject from '@ember/object'; +import { settled } from '@ember/test-helpers'; import { module, test } from 'qunit'; import { Promise } from 'rsvp'; @@ -216,7 +217,7 @@ module('integration/record-data - Record Data State', function (hooks) { }); test('Record Data state record flags', async function (assert) { - assert.expect(9); + assert.expect(14); let isDeleted, isNew, isDeletionCommitted; let calledSetIsDeleted = false; let storeWrapper; @@ -274,32 +275,42 @@ module('integration/record-data - Record Data State', function (hooks) { let person = store.peekRecord('person', '1'); let people = store.peekAll('person'); - isNew = true; + assert.strictEqual(people.length, 1, 'live array starting length is 1'); + isNew = true; storeWrapper.notifyStateChange('person', '1', null, 'isNew'); + await settled(); assert.true(person.isNew, 'person is new'); + assert.strictEqual(people.length, 1, 'live array starting length is 1'); isNew = false; isDeleted = true; storeWrapper.notifyStateChange('person', '1', null, 'isDeleted'); storeWrapper.notifyStateChange('person', '1', null, 'isNew'); - + await settled(); assert.false(person.isNew, 'person is not new'); assert.true(person.isDeleted, 'person is deleted'); + assert.strictEqual(people.length, 1, 'live array starting length is 1'); isNew = false; isDeleted = false; storeWrapper.notifyStateChange('person', '1', null, 'isDeleted'); + await settled(); assert.false(person.isNew, 'person is not new'); assert.false(person.isDeleted, 'person is not deleted'); - + assert.strictEqual(people.length, 1, 'live array starting length is 1'); person.deleteRecord(); + await settled(); + assert.strictEqual(people.length, 1, 'live array starting length is 1 after deleteRecord'); assert.false(person.isDeleted, 'calling deleteRecord does not automatically set isDeleted flag to true'); assert.true(calledSetIsDeleted, 'called setIsDeleted'); + storeWrapper.notifyStateChange('person', '1', null); assert.strictEqual(people.length, 1, 'live array starting length is 1'); + isDeletionCommitted = true; storeWrapper.notifyStateChange('person', '1', null, 'isDeletionCommitted'); + await settled(); assert.strictEqual(people.length, 0, 'commiting a deletion updates the live array'); }); }); diff --git a/packages/-ember-data/tests/integration/records/load-test.js b/packages/-ember-data/tests/integration/records/load-test.js index 6c0cf503aeb..cfdfa7c98bc 100644 --- a/packages/-ember-data/tests/integration/records/load-test.js +++ b/packages/-ember-data/tests/integration/records/load-test.js @@ -13,13 +13,11 @@ import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in function _isLoading(cache, identifier) { const req = cache.store.getRequestStateService(); - // const fulfilled = req.getLastRequestForRecord(identifier); + const fulfilled = req.getLastRequestForRecord(identifier); const isLoaded = cache.recordIsLoaded(identifier); return ( - !isLoaded && - // fulfilled === null && - req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query') + !isLoaded && fulfilled === null && req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query') ); } diff --git a/packages/-ember-data/tests/integration/records/rematerialize-test.js b/packages/-ember-data/tests/integration/records/rematerialize-test.js index 4a98370c759..1327d69ea48 100644 --- a/packages/-ember-data/tests/integration/records/rematerialize-test.js +++ b/packages/-ember-data/tests/integration/records/rematerialize-test.js @@ -235,7 +235,7 @@ module('integration/unload - Rematerializing Unloaded Records', function (hooks) ); // cause a rematerialization, this should also cause us to fetch boat '1' again - boats = run(() => adam.boats); + boats = await adam.boats; let rematerializedBoaty = boats.objectAt(1); assert.ok(!!rematerializedBoaty, 'We have a boat!'); diff --git a/packages/-ember-data/tests/integration/records/save-test.js b/packages/-ember-data/tests/integration/records/save-test.js index fa39d027a1c..51a5cb40176 100644 --- a/packages/-ember-data/tests/integration/records/save-test.js +++ b/packages/-ember-data/tests/integration/records/save-test.js @@ -38,6 +38,7 @@ module('integration/records/save - Save Record', function (hooks) { if (DEPRECATE_SAVE_PROMISE_ACCESS) { // `save` returns a PromiseObject which allows to call get on it + assert.strictEqual(saved.get('id'), undefined); assert.strictEqual(saved.id, undefined); } @@ -45,10 +46,12 @@ module('integration/records/save - Save Record', function (hooks) { let model = await saved; assert.ok(true, 'save operation was resolved'); if (DEPRECATE_SAVE_PROMISE_ACCESS) { - assert.strictEqual(saved.id, '123'); + assert.strictEqual(saved.get('id'), '123'); assert.strictEqual(saved.id, undefined); + assert.strictEqual(model.id, '123'); } else { assert.strictEqual(saved.id, undefined); + assert.strictEqual(model.id, '123'); } assert.strictEqual(model, post, 'resolves with the model'); if (DEPRECATE_SAVE_PROMISE_ACCESS) { @@ -56,7 +59,7 @@ module('integration/records/save - Save Record', function (hooks) { // should not throw an error and only show a deprecation. assert.strictEqual(saved.__ec_cancel__, undefined); - assert.expectDeprecation({ id: 'ember-data:model-save-promise', count: 4 }); + assert.expectDeprecation({ id: 'ember-data:model-save-promise', count: 5 }); } }); diff --git a/packages/-ember-data/tests/integration/records/unload-test.js b/packages/-ember-data/tests/integration/records/unload-test.js index a9c48eadf97..5aa9fa176a8 100644 --- a/packages/-ember-data/tests/integration/records/unload-test.js +++ b/packages/-ember-data/tests/integration/records/unload-test.js @@ -621,129 +621,74 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(relatedPerson, newPerson, 'we have a new related record'); }); - test('Unloading a record twice only schedules destroy once', function (assert) { - let record; - - // populate initial record - run(function () { - record = store.push({ - data: { - type: 'person', - id: '1', - attributes: { - name: 'Adam Sunderland', - }, - }, + test('after unloading a record, the record can be fetched again immediately', async function (assert) { + let resolver; + // stub findRecord + adapter.findRecord = () => { + return new Promise((resolve) => { + resolver = resolve; }); - }); - - const internalModel = record._internalModel; - - run(function () { - store.unloadRecord(record); - store.unloadRecord(record); - internalModel.cancelDestroy(); - }); - - assert.false(internalModel.isDestroyed, 'We cancelled destroy'); - }); - - test('Cancelling destroy leaves the record in the empty state', function (assert) { - let record; + }; // populate initial record - run(function () { - record = store.push({ - data: { - type: 'person', - id: '1', - attributes: { - name: 'Adam Sunderland', - }, + let record = store.push({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Adam Sunderland', }, - }); - }); - - const internalModel = record._internalModel; - assert.strictEqual(record.currentState.stateName, 'root.loaded.saved', 'We are loaded initially'); - - run(function () { - store.unloadRecord(record); - assert.true(record.isDestroying, 'the record is destroying'); - assert.false(internalModel.isDestroyed, 'the internal model is not destroyed'); - assert.true(internalModel._isDematerializing, 'the internal model is dematerializing'); - internalModel.cancelDestroy(); - assert.true(internalModel.isEmpty, 'We are unloaded after unloadRecord'); - }); - - assert.false(internalModel.isDestroyed, 'the internal model was not destroyed'); - assert.false(internalModel._isDematerializing, 'the internal model is no longer dematerializing'); - assert.true(internalModel.isEmpty, 'We are still unloaded after unloadRecord'); - }); - - test('after unloading a record, the record can be fetched again immediately', function (assert) { - // stub findRecord - adapter.findRecord = () => { - return { - data: { - type: 'person', - id: '1', - attributes: { - name: 'Adam Sunderland', + relationships: { + cars: { + data: [ + { + id: 1, + type: 'car', + }, + ], }, }, - }; - }; - - // populate initial record - let record = run(() => { - return store.push({ - data: { - type: 'person', - id: '1', + }, + included: [ + { + type: 'car', + id: 1, attributes: { - name: 'Adam Sunderland', - }, - relationships: { - cars: { - data: [ - { - id: 1, - type: 'car', - }, - ], - }, + make: 'jeep', + model: 'wrangler', }, }, - included: [ - { - type: 'car', - id: 1, - attributes: { - make: 'jeep', - model: 'wrangler', - }, - }, - ], - }); + ], }); + store.DISABLE_WAITER = true; - const internalModel = record._internalModel; assert.strictEqual(record.currentState.stateName, 'root.loaded.saved', 'We are loaded initially'); // we test that we can sync call unloadRecord followed by findRecord - return run(() => { - store.unloadRecord(record); - assert.true(record.isDestroying, 'the record is destroying'); - assert.true(internalModel.isEmpty, 'We are unloaded after unloadRecord'); - return store.findRecord('person', '1').then((newRecord) => { - assert.strictEqual(internalModel, newRecord._internalModel, 'the old internalModel is reused'); - assert.strictEqual(newRecord.currentState.stateName, 'root.loaded.saved', 'We are loaded after findRecord'); - }); + const identifier = recordIdentifierFor(record); + store.unloadRecord(record); + const promise = store.findRecord('person', '1'); + assert.true(record.isDestroying, 'the record is destroying'); + + await settled(); + assert.strictEqual(store.peekRecord('person', '1'), null, 'We are unloaded after unloadRecord'); + + resolver({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Adam Sunderland', + }, + }, }); + const newRecord = await promise; + const newIdentifier = recordIdentifierFor(newRecord); + assert.notStrictEqual(identifier, newIdentifier, 'the identifier is not reused'); + assert.strictEqual(newRecord.currentState.stateName, 'root.loaded.saved', 'We are loaded after findRecord'); }); - test('after unloading a record, the record can be fetched again immediately (purge relationship)', function (assert) { + test('after unloading a record, the record can be fetched again immediately (purge relationship)', async function (assert) { // stub findRecord adapter.findRecord = () => { return { @@ -763,36 +708,34 @@ module('integration/unload - Unloading Records', function (hooks) { }; // populate initial record - let record = run(() => { - return store.push({ - data: { - type: 'person', + let record = store.push({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Adam Sunderland', + }, + relationships: { + cars: { + data: [ + { + id: '1', + type: 'car', + }, + ], + }, + }, + }, + included: [ + { + type: 'car', id: '1', attributes: { - name: 'Adam Sunderland', - }, - relationships: { - cars: { - data: [ - { - id: '1', - type: 'car', - }, - ], - }, + make: 'jeep', + model: 'wrangler', }, }, - included: [ - { - type: 'car', - id: '1', - attributes: { - make: 'jeep', - model: 'wrangler', - }, - }, - ], - }); + ], }); let identifier = recordIdentifierFor(record); @@ -800,22 +743,19 @@ module('integration/unload - Unloading Records', function (hooks) { assert.strictEqual(record.currentState.stateName, 'root.loaded.saved', 'We are loaded initially'); // we test that we can sync call unloadRecord followed by findRecord - return run(() => { - assert.strictEqual(record.get('cars.firstObject.make'), 'jeep'); - store.unloadRecord(record); - assert.true(record.isDestroying, 'the record is destroying'); - assert.true(recordData.isEmpty(), 'Expected the previous data to be unloaded'); - - return store.findRecord('person', '1').then((record) => { - assert.strictEqual(record.cars.length, 0, 'Expected relationship to be cleared by the new push'); - assert.strictEqual(identifier, recordIdentifierFor(record), 'the old identifier is reused'); - assert.strictEqual( - record.currentState.stateName, - 'root.loaded.saved', - 'Expected the NEW internal model to be loaded' - ); - }); - }); + assert.strictEqual(record.get('cars.firstObject.make'), 'jeep'); + store.unloadRecord(record); + assert.true(record.isDestroying, 'the record is destroying'); + assert.true(recordData.isEmpty(), 'Expected the previous data to be unloaded'); + + const recordAgain = await store.findRecord('person', '1'); + assert.strictEqual(recordAgain.cars.length, 0, 'Expected relationship to be cleared by the new push'); + assert.notStrictEqual(identifier, recordIdentifierFor(recordAgain), 'the old identifier is not reused'); + assert.strictEqual( + record.currentState.stateName, + 'root.loaded.saved', + 'Expected the NEW internal model to be loaded' + ); }); test('after unloading a record, the record can be fetched again immediately (with relationships)', function (assert) { @@ -1965,46 +1905,44 @@ module('integration/unload - Unloading Records', function (hooks) { return { data: { - id: 1, + id: '1', type: 'person', }, }; }; - let person = run(() => - store.push({ - data: { - id: 1, - type: 'person', - relationships: { - favoriteSpoons: { - data: [ - { - id: 2, - type: 'spoon', - }, - { - id: 3, - type: 'spoon', - }, - ], - }, + let person = store.push({ + data: { + id: 1, + type: 'person', + relationships: { + favoriteSpoons: { + data: [ + { + id: '2', + type: 'spoon', + }, + { + id: '3', + type: 'spoon', + }, + ], }, }, - included: [ - { - id: 2, - type: 'spoon', - }, - { - id: 3, - type: 'spoon', - }, - ], - }) - ); - let spoon2 = store.peekRecord('spoon', 2); - let spoon3 = store.peekRecord('spoon', 3); + }, + included: [ + { + id: '2', + type: 'spoon', + }, + { + id: '3', + type: 'spoon', + }, + ], + }); + let spoon2 = store.peekRecord('spoon', '2'); + let spoon3 = store.peekRecord('spoon', '3'); let spoons = person.favoriteSpoons; assert.deepEqual(person.favoriteSpoons.mapBy('id'), ['2', '3'], 'initially relationship established lhs'); @@ -2013,7 +1951,8 @@ module('integration/unload - Unloading Records', function (hooks) { assert.false(spoons.isDestroyed, 'ManyArray is not destroyed'); - run(() => person.unloadRecord()); + person.unloadRecord(); + await settled(); assert.true(spoons.isDestroyed, 'ManyArray is destroyed when 1 side is unloaded'); assert.strictEqual(spoon2.belongsTo('person').id(), '1', 'unload async is not treated as delete'); @@ -2023,7 +1962,7 @@ module('integration/unload - Unloading Records', function (hooks) { assert.notEqual(person, refetchedPerson, 'the previously loaded record is not reused'); - assert.deepEqual(person.favoriteSpoons.mapBy('id'), ['2', '3'], 'unload async is not treated as delete'); + assert.deepEqual(refetchedPerson.favoriteSpoons.mapBy('id'), ['2', '3'], 'unload async is not treated as delete'); assert.strictEqual(spoon2.belongsTo('person').id(), '1', 'unload async is not treated as delete'); assert.strictEqual(spoon3.belongsTo('person').id(), '1', 'unload async is not treated as delete'); diff --git a/packages/-ember-data/tests/integration/relationships/belongs-to-test.js b/packages/-ember-data/tests/integration/relationships/belongs-to-test.js index 8757e5d8801..90706e1c922 100644 --- a/packages/-ember-data/tests/integration/relationships/belongs-to-test.js +++ b/packages/-ember-data/tests/integration/relationships/belongs-to-test.js @@ -336,7 +336,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function ); }); - test('returning a null relationship from payload sets the relationship to null on both sides', function (assert) { + test('returning a null relationship from payload sets the relationship to null on both sides', async function (assert) { this.owner.register( 'model:app', Model.extend({ @@ -354,43 +354,43 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - run(() => { - store.push({ - data: { - id: '1', - type: 'app', - relationships: { - team: { - data: { - id: '1', - type: 'team', - }, + store.push({ + data: { + id: '1', + type: 'app', + relationships: { + team: { + data: { + id: '1', + type: 'team', }, }, }, - included: [ - { - id: '1', - type: 'team', - relationships: { - apps: { - data: [ - { - id: '1', - type: 'app', - }, - ], - }, + }, + included: [ + { + id: '1', + type: 'team', + relationships: { + apps: { + data: [ + { + id: '1', + type: 'app', + }, + ], }, }, - ], - }); + }, + ], }); const app = store.peekRecord('app', '1'); const team = store.peekRecord('team', '1'); - assert.strictEqual(app.team.id, team.id, 'sets team correctly on app'); - assert.deepEqual(team.apps.toArray().mapBy('id'), ['1'], 'sets apps correctly on team'); + let appTeam = await app.team; + assert.strictEqual(appTeam.id, team.id, 'sets team correctly on app'); + const apps = await team.apps; + assert.deepEqual(apps.toArray().mapBy('id'), ['1'], 'sets apps correctly on team'); adapter.shouldBackgroundReloadRecord = () => false; adapter.updateRecord = (store, type, snapshot) => { @@ -410,13 +410,11 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }); }; - return run(() => { - app.set('name', 'Hello'); - return app.save().then(() => { - assert.strictEqual(app.team.id, undefined, 'team removed from app relationship'); - assert.deepEqual(team.apps.toArray().mapBy('id'), [], 'app removed from team apps relationship'); - }); - }); + app.set('name', 'Hello'); + await app.save(); + appTeam = await app.team; + assert.strictEqual(appTeam?.id, undefined, 'team removed from app relationship'); + assert.deepEqual(apps.toArray().mapBy('id'), [], 'app removed from team apps relationship'); }); test('The store can materialize a non loaded monomorphic belongsTo association', function (assert) { diff --git a/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js b/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js index fc08fa86187..8f7736a25cf 100644 --- a/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js +++ b/packages/-ember-data/tests/integration/relationships/inverse-relationships-test.js @@ -672,8 +672,8 @@ module('integration/relationships/inverse_relationships - Inverse Relationships' const comment = store.createRecord('comment'); const recordData = recordDataFor(comment); const post = store.createRecord('post'); - - post.comments.pushObject(comment); + const comments = await post.comments; + comments.pushObject(comment); const identifier = recordIdentifierFor(comment); await comment.destroyRecord(); diff --git a/packages/-ember-data/tests/integration/relationships/many-to-many-test.js b/packages/-ember-data/tests/integration/relationships/many-to-many-test.js index 1a8ab92da24..58eecc48bf9 100644 --- a/packages/-ember-data/tests/integration/relationships/many-to-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/many-to-many-test.js @@ -503,42 +503,31 @@ module('integration/relationships/many_to_many_test - ManyToMany relationships', }); }); - test('Rollbacking attributes for a created record that has a ManyToMany relationship works correctly - async', function (assert) { + test('Rollbacking attributes for a created record that has a ManyToMany relationship works correctly - async', async function (assert) { let store = this.owner.lookup('service:store'); - let user, topic; - run(() => { - user = store.push({ - data: { - id: '1', - type: 'user', - attributes: { - name: 'Stanley', - }, + let user = store.push({ + data: { + id: '1', + type: 'user', + attributes: { + name: 'Stanley', }, - }); - - topic = store.createRecord('topic'); + }, }); + let topic = store.createRecord('topic'); - return run(() => { - return user.topics.then((fetchedTopics) => { - fetchedTopics.pushObject(topic); - topic.rollbackAttributes(); + let fetchedTopics = await user.topics; + fetchedTopics.pushObject(topic); + topic.rollbackAttributes(); - let users = topic.users.then((fetchedUsers) => { - assert.strictEqual(fetchedUsers.length, 0, 'Users got removed'); - assert.strictEqual(fetchedUsers.objectAt(0), undefined, "User can't be fetched"); - }); + let fetchedUsers = await topic.users; + assert.strictEqual(fetchedUsers.length, 0, 'Users got removed'); + assert.strictEqual(fetchedUsers.objectAt(0), undefined, "User can't be fetched"); - let topics = user.topics.then((fetchedTopics) => { - assert.strictEqual(fetchedTopics.length, 0, 'Topics got removed'); - assert.strictEqual(fetchedTopics.objectAt(0), undefined, "Topic can't be fetched"); - }); - - return EmberPromise.all([users, topics]); - }); - }); + fetchedTopics = await user.topics; + assert.strictEqual(fetchedTopics.length, 0, 'Topics got removed'); + assert.strictEqual(fetchedTopics.objectAt(0), undefined, "Topic can't be fetched"); }); test('Deleting an unpersisted record via rollbackAttributes that has a hasMany relationship removes it from the otherMany array but does not remove the other record from itself - sync', function (assert) { diff --git a/packages/-ember-data/tests/integration/relationships/one-to-many-test.js b/packages/-ember-data/tests/integration/relationships/one-to-many-test.js index 5b1bfd90b03..0a4c1a4facb 100644 --- a/packages/-ember-data/tests/integration/relationships/one-to-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/one-to-many-test.js @@ -1517,34 +1517,29 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f Rollback attributes from created state */ - test('Rollbacking attributes of a created record works correctly when the hasMany side has been created - async', function (assert) { + test('Rollbacking attributes of a created record works correctly when the hasMany side has been created - async', async function (assert) { let store = this.owner.lookup('service:store'); - - var user, message; - run(function () { - user = store.push({ - data: { - id: '1', - type: 'user', - attributes: { - name: 'Stanley', - }, + let user = store.push({ + data: { + id: '1', + type: 'user', + attributes: { + name: 'Stanley', }, - }); - message = store.createRecord('message', { - user: user, - }); + }, }); - run(message, 'rollbackAttributes'); - run(function () { - message.user.then(function (fetchedUser) { - assert.strictEqual(fetchedUser, null, 'Message does not have the user anymore'); - }); - user.messages.then(function (fetchedMessages) { - assert.strictEqual(fetchedMessages.length, 0, 'User does not have the message anymore'); - assert.strictEqual(fetchedMessages.firstObject, undefined, "User message can't be accessed"); - }); + let message = store.createRecord('message', { + user: user, }); + + message.rollbackAttributes(); + + let fetchedUser = await message.user; + assert.strictEqual(fetchedUser, null, 'Message does not have the user anymore'); + let fetchedMessages = await user.messages; + + assert.strictEqual(fetchedMessages.length, 0, 'User does not have the message anymore'); + assert.strictEqual(fetchedMessages.firstObject, undefined, "User message can't be accessed"); }); test('Rollbacking attributes of a created record works correctly when the hasMany side has been created - sync', function (assert) { @@ -1570,35 +1565,27 @@ module('integration/relationships/one_to_many_test - OneToMany relationships', f assert.strictEqual(account.user, null, 'Account does not have the user anymore'); }); - test('Rollbacking attributes of a created record works correctly when the belongsTo side has been created - async', function (assert) { + test('Rollbacking attributes of a created record works correctly when the belongsTo side has been created - async', async function (assert) { let store = this.owner.lookup('service:store'); - - var message, user; - run(function () { - message = store.push({ - data: { - id: '2', - type: 'message', - attributes: { - title: 'EmberFest was great', - }, + let message = store.push({ + data: { + id: '2', + type: 'message', + attributes: { + title: 'EmberFest was great', }, - }); - user = store.createRecord('user'); - }); - run(function () { - user.messages.then(function (messages) { - messages.pushObject(message); - user.rollbackAttributes(); - message.user.then(function (fetchedUser) { - assert.strictEqual(fetchedUser, null, 'Message does not have the user anymore'); - }); - user.messages.then(function (fetchedMessages) { - assert.strictEqual(fetchedMessages.length, 0, 'User does not have the message anymore'); - assert.strictEqual(fetchedMessages.firstObject, undefined, "User message can't be accessed"); - }); - }); + }, }); + let user = store.createRecord('user'); + let messages = await user.messages; + messages.pushObject(message); + user.rollbackAttributes(); + let fetchedUser = await message.user; + assert.strictEqual(fetchedUser, null, 'Message does not have the user anymore'); + + let fetchedMessages = await user.messages; + assert.strictEqual(fetchedMessages.length, 0, 'User does not have the message anymore'); + assert.strictEqual(fetchedMessages.firstObject, undefined, "User message can't be accessed"); }); test('Rollbacking attributes of a created record works correctly when the belongsTo side has been created - sync', function (assert) { diff --git a/packages/-ember-data/tests/unit/utils-test.js b/packages/-ember-data/tests/unit/utils-test.js index 6314aa8628d..e3d8ce15d3a 100644 --- a/packages/-ember-data/tests/unit/utils-test.js +++ b/packages/-ember-data/tests/unit/utils-test.js @@ -1,11 +1,10 @@ import Mixin from '@ember/object/mixin'; -import { module, test } from 'qunit'; +import { module } from 'qunit'; import { setupTest } from 'ember-qunit'; -import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; -import { modelHasAttributeOrRelationshipNamedType } from '@ember-data/serializer/-private'; +import Model, { hasMany } from '@ember-data/model'; import { recordIdentifierFor } from '@ember-data/store'; import { assertPolymorphicType } from '@ember-data/store/-debug'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; @@ -76,30 +75,6 @@ module('unit/utils', function (hooks) { }, "The 'person' type does not implement 'message' and thus cannot be assigned to the 'messages' relationship in 'user'. Make it a descendant of 'message' or use a mixin of the same name."); }); - test('modelHasAttributeOrRelationshipNamedType', function (assert) { - class Blank extends Model {} - class ModelWithTypeAttribute extends Model { - @attr type; - } - class ModelWithTypeBelongsTo extends Model { - @belongsTo type; - } - class ModelWithTypeHasMany extends Model { - @hasMany type; - } - this.owner.register('model:blank', Blank); - this.owner.register('model:with-attr', ModelWithTypeAttribute); - this.owner.register('model:with-belongs-to', ModelWithTypeBelongsTo); - this.owner.register('model:with-has-many', ModelWithTypeHasMany); - const store = this.owner.lookup('service:store'); - - assert.false(modelHasAttributeOrRelationshipNamedType(store.modelFor('blank'))); - - assert.true(modelHasAttributeOrRelationshipNamedType(store.modelFor('with-attr'))); - assert.true(modelHasAttributeOrRelationshipNamedType(store.modelFor('with-belongs-to'))); - assert.true(modelHasAttributeOrRelationshipNamedType(store.modelFor('with-has-many'))); - }); - testInDebug('assertPolymorphicType works for mixins', function (assert) { let store = this.owner.lookup('service:store'); let [post, video, person] = store.push({ diff --git a/packages/model/addon/-private/has-many.js b/packages/model/addon/-private/has-many.js index d5695fe3c28..c104eac20a2 100644 --- a/packages/model/addon/-private/has-many.js +++ b/packages/model/addon/-private/has-many.js @@ -1,6 +1,7 @@ /** @module @ember-data/model */ +import { A } from '@ember/array'; import { assert, inspect } from '@ember/debug'; import { computed } from '@ember/object'; import { DEBUG } from '@glimmer/env'; @@ -189,6 +190,9 @@ function hasMany(type, options) { ); } } + if (this.isDestroying || this.isDestroyed) { + return A(); + } return LEGACY_SUPPORT.lookup(this).getHasMany(key); }, set(key, records) { diff --git a/packages/model/addon/-private/legacy-relationships-support.ts b/packages/model/addon/-private/legacy-relationships-support.ts index 3e2f09cf6b8..4e8fcc4e420 100644 --- a/packages/model/addon/-private/legacy-relationships-support.ts +++ b/packages/model/addon/-private/legacy-relationships-support.ts @@ -140,7 +140,7 @@ export class LegacySupport { `You looked up the '${key}' relationship on a '${identifier.type}' with id ${ identifier.id || 'null' } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (\`belongsTo({ async: true })\`)`, - toReturn === null || store._instanceCache.recordIsLoaded(relatedIdentifier) + toReturn === null || store._instanceCache.recordIsLoaded(relatedIdentifier, true) ); return toReturn; } @@ -665,7 +665,7 @@ function anyUnloaded(store: Store, relationship: ManyRelationship) { let state = relationship.currentState; const cache = store._instanceCache; const unloaded = state.find((s) => { - let isLoaded = cache.recordIsLoaded(s); + let isLoaded = cache.recordIsLoaded(s, true); return !isLoaded; }); diff --git a/packages/model/addon/-private/many-array.ts b/packages/model/addon/-private/many-array.ts index 12ce591e639..877a860cd08 100644 --- a/packages/model/addon/-private/many-array.ts +++ b/packages/model/addon/-private/many-array.ts @@ -314,7 +314,7 @@ export default class ManyArray extends MutableArrayWithObject { - return this.store._instanceCache.recordIsLoaded(identifier) === true; + return this.store._instanceCache.recordIsLoaded(identifier, true) === true; }); } diff --git a/packages/record-data/ember-cli-build.js b/packages/record-data/ember-cli-build.js index 42bfee0ed33..563d528c6e7 100644 --- a/packages/record-data/ember-cli-build.js +++ b/packages/record-data/ember-cli-build.js @@ -12,7 +12,9 @@ module.exports = function (defaults) { babel: { // this ensures that the same build-time code stripping that is done // for library packages is also done for our tests and dummy app - plugins: [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, { compatWith })], + plugins: [ + ...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, { compatWith, debug: {} }), + ], }, 'ember-cli-babel': { throwUnlessParallelizable: true, diff --git a/packages/store/addon/-private/caches/instance-cache.ts b/packages/store/addon/-private/caches/instance-cache.ts index feca8e25140..a3340537d99 100644 --- a/packages/store/addon/-private/caches/instance-cache.ts +++ b/packages/store/addon/-private/caches/instance-cache.ts @@ -125,7 +125,7 @@ export class InstanceCache { reference: new WeakCache(DEBUG ? 'reference' : ''), }; - recordIsLoaded(identifier: StableRecordIdentifier) { + recordIsLoaded(identifier: StableRecordIdentifier, filterDeleted: boolean = false) { const recordData = this.peek({ identifier, bucket: 'recordData' }); if (!recordData) { return false; @@ -151,7 +151,7 @@ export class InstanceCache { const isLoading = fulfilled !== null && req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query'); - if (isEmpty || recordData.isDeletionCommitted?.() || isLoading) { + if (isEmpty || (filterDeleted && recordData.isDeletionCommitted?.()) || isLoading) { return false; } @@ -399,6 +399,7 @@ export class InstanceCache { this.disconnect(identifier); } + this.store._fetchManager.clearEntries(identifier); this.store.recordArrayManager.recordDidChange(identifier); if (LOG_INSTANCE_CACHE) { // eslint-disable-next-line no-console diff --git a/packages/store/addon/-private/managers/record-array-manager.ts b/packages/store/addon/-private/managers/record-array-manager.ts index 083f77d1409..314d5c1da7f 100644 --- a/packages/store/addon/-private/managers/record-array-manager.ts +++ b/packages/store/addon/-private/managers/record-array-manager.ts @@ -68,7 +68,7 @@ class RecordArrayManager { // recordArrayManager pendingForIdentifier.delete(i); // build up a set of models to ensure we have purged correctly; - if (!cache.recordIsLoaded(i)) { + if (!cache.recordIsLoaded(i, true)) { identifiersToRemove.push(i); } } @@ -187,7 +187,7 @@ class RecordArrayManager { for (let i = 0; i < all.length; i++) { let identifier = all[i]; - if (cache.recordIsLoaded(identifier)) { + if (cache.recordIsLoaded(identifier, true)) { visible.push(identifier); } } @@ -378,7 +378,7 @@ function updateLiveRecordArray(store: Store, recordArray: RecordArray, identifie let identifier = identifiers[i]; let recordArrays = recordArraysForIdentifier(identifier); - if (cache.recordIsLoaded(identifier)) { + if (cache.recordIsLoaded(identifier, true)) { if (!recordArrays.has(recordArray)) { identifiersToAdd.push(identifier); recordArrays.add(recordArray); diff --git a/packages/store/addon/-private/network/fetch-manager.ts b/packages/store/addon/-private/network/fetch-manager.ts index dc831ffb5e1..db236d8073d 100644 --- a/packages/store/addon/-private/network/fetch-manager.ts +++ b/packages/store/addon/-private/network/fetch-manager.ts @@ -84,6 +84,10 @@ export default class FetchManager { this.isDestroyed = false; } + clearEntries(identifier: StableRecordIdentifier) { + delete this.requestCache._done[identifier.lid]; + } + /** This method is called by `record.save`, and gets passed a resolver for the promise that `record.save` returns. diff --git a/packages/store/addon/-private/record-arrays/record-array.ts b/packages/store/addon/-private/record-arrays/record-array.ts index 7f57573f51e..ce7250a85c9 100644 --- a/packages/store/addon/-private/record-arrays/record-array.ts +++ b/packages/store/addon/-private/record-arrays/record-array.ts @@ -4,6 +4,7 @@ import type NativeArray from '@ember/array/-private/native-array'; import ArrayProxy from '@ember/array/proxy'; import { assert, deprecate } from '@ember/debug'; +import { set } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { Promise } from 'rsvp'; @@ -233,8 +234,10 @@ export default class RecordArray extends ArrayProxy; - this.length = 0; + // TODO we have to use set here vs dot notation because computed properties don't clear + // otherwise for destroyed records and will not update their value. + set(this, 'content', null as unknown as NativeArray); + set(this, 'length', 0); super.willDestroy(); } diff --git a/packages/store/addon/-private/store-service.ts b/packages/store/addon/-private/store-service.ts index ca5179e7199..6948bddd6bf 100644 --- a/packages/store/addon/-private/store-service.ts +++ b/packages/store/addon/-private/store-service.ts @@ -193,6 +193,7 @@ class Store extends Service { declare _trackAsyncRequestStart: (str: string) => void; declare _trackAsyncRequestEnd: (token: AsyncTrackingToken) => void; declare __asyncWaiter: () => boolean; + declare DISABLE_WAITER?: boolean; /** @method init @@ -270,7 +271,7 @@ class Store extends Service { this.__asyncWaiter = () => { let tracked = this._trackedAsyncRequests; - return tracked.length === 0; + return this.DISABLE_WAITER || tracked.length === 0; }; registerWaiter(this.__asyncWaiter); diff --git a/packages/unpublished-model-encapsulation-test-app/ember-cli-build.js b/packages/unpublished-model-encapsulation-test-app/ember-cli-build.js index 2c69cf51e89..ac483a09b06 100644 --- a/packages/unpublished-model-encapsulation-test-app/ember-cli-build.js +++ b/packages/unpublished-model-encapsulation-test-app/ember-cli-build.js @@ -8,7 +8,9 @@ module.exports = function (defaults) { const isProd = process.env.EMBER_ENV === 'production'; // allows testing with env config for stripping all deprecations const compatWith = process.env.EMBER_DATA_FULL_COMPAT ? '99.0' : null; - const plugins = [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, { compatWith })]; + const plugins = [ + ...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, { compatWith, debug: {} }), + ]; let app = new EmberApp(defaults, { emberData: { diff --git a/packages/unpublished-test-infra/ember-cli-build.js b/packages/unpublished-test-infra/ember-cli-build.js index be27252724e..8ed73a75377 100644 --- a/packages/unpublished-test-infra/ember-cli-build.js +++ b/packages/unpublished-test-infra/ember-cli-build.js @@ -15,7 +15,9 @@ module.exports = function (defaults) { babel: { // this ensures that the same build-time code stripping that is done // for library packages is also done for our tests and dummy app - plugins: [...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, { compatWith })], + plugins: [ + ...require('@ember-data/private-build-infra/src/debug-macros')(null, isProd, { compatWith, debug: {} }), + ], }, 'ember-cli-babel': { throwUnlessParallelizable: true, From 25a8d057dee52f6874c9cba182aa330da4a8942d Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 8 Aug 2022 17:36:28 -0700 Subject: [PATCH 24/29] bump some deps --- packages/-ember-data/config/ember-try.js | 4 ++-- .../unpublished-fastboot-test-app/app/models/person.js | 2 +- .../app/templates/index.hbs | 9 ++------- .../unpublished-fastboot-test-app/config/ember-try.js | 2 +- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/-ember-data/config/ember-try.js b/packages/-ember-data/config/ember-try.js index 7afb04cddac..87c8c208c0a 100644 --- a/packages/-ember-data/config/ember-try.js +++ b/packages/-ember-data/config/ember-try.js @@ -14,7 +14,7 @@ module.exports = function () { }, npm: { devDependencies: { - 'ember-fetch': '*', + 'ember-fetch': '^8.1.1', '@ember/jquery': null, }, }, @@ -23,7 +23,7 @@ module.exports = function () { name: 'with-ember-fetch-and-jquery', npm: { devDependencies: { - 'ember-fetch': '*', + 'ember-fetch': '^8.1.1', '@ember/jquery': '^2.0.0', }, }, diff --git a/packages/unpublished-fastboot-test-app/app/models/person.js b/packages/unpublished-fastboot-test-app/app/models/person.js index 79444014e3a..8255ebebbe3 100644 --- a/packages/unpublished-fastboot-test-app/app/models/person.js +++ b/packages/unpublished-fastboot-test-app/app/models/person.js @@ -11,7 +11,7 @@ export default class Person extends Model { parent; get parentId() { - return this.parent.id; + return this.belongsTo('parent').id(); } toNode() { diff --git a/packages/unpublished-fastboot-test-app/app/templates/index.hbs b/packages/unpublished-fastboot-test-app/app/templates/index.hbs index 53706222ffa..6d6eab1a509 100644 --- a/packages/unpublished-fastboot-test-app/app/templates/index.hbs +++ b/packages/unpublished-fastboot-test-app/app/templates/index.hbs @@ -1,10 +1,5 @@ -{{#x-tree - model=this.model - checkable=true - expandDepth=-1 - as |node| -}} + {{node.toggle}} {{node.checkbox}} {{node.model.name}} -{{/x-tree}} \ No newline at end of file + \ No newline at end of file diff --git a/packages/unpublished-fastboot-test-app/config/ember-try.js b/packages/unpublished-fastboot-test-app/config/ember-try.js index 5905ff6359d..a6dd4c25584 100644 --- a/packages/unpublished-fastboot-test-app/config/ember-try.js +++ b/packages/unpublished-fastboot-test-app/config/ember-try.js @@ -8,7 +8,7 @@ module.exports = function () { name: 'fastboot-with-ember-fetch', npm: { devDependencies: { - 'ember-fetch': '*', + 'ember-fetch': '^8.1.1', }, }, }, From 980fb72f5530d92cf512a3134ce8fad501f17dfa Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 8 Aug 2022 17:45:46 -0700 Subject: [PATCH 25/29] make nice for deprecation stripping --- .../relationships/promise-many-array-test.js | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/-ember-data/tests/integration/relationships/promise-many-array-test.js b/packages/-ember-data/tests/integration/relationships/promise-many-array-test.js index ec6a2945134..4c81810f393 100644 --- a/packages/-ember-data/tests/integration/relationships/promise-many-array-test.js +++ b/packages/-ember-data/tests/integration/relationships/promise-many-array-test.js @@ -8,6 +8,7 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import Model, { attr, hasMany } from '@ember-data/model'; +import { DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS } from '@ember-data/private-build-infra/deprecations'; import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; module('PromiseManyArray', (hooks) => { @@ -27,19 +28,24 @@ module('PromiseManyArray', (hooks) => { const members = ['Bob', 'John', 'Michael', 'Larry', 'Lucy'].map((name) => store.createRecord('person', { name })); const group = store.createRecord('group', { members }); - const replaceFn = group.members.replace; + const forEachFn = group.members.forEach; assert.strictEqual(group.members.length, 5, 'initial length is correct'); - group.members.replace(0, 1); - assert.strictEqual(group.members.length, 4, 'updated length is correct'); + if (DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS) { + group.members.replace(0, 1); + assert.strictEqual(group.members.length, 4, 'updated length is correct'); + } A(group.members); - assert.strictEqual(replaceFn, group.members.replace, 'we have the same function for replace'); - group.members.replace(0, 1); - assert.strictEqual(group.members.length, 3, 'updated length is correct'); - // we'll want to use a different test for this but will want to still ensure we are not side-affected - assert.expectDeprecation({ id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 2 }); + assert.strictEqual(forEachFn, group.members.forEach, 'we have the same function for forEach'); + + if (DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS) { + group.members.replace(0, 1); + assert.strictEqual(group.members.length, 3, 'updated length is correct'); + // we'll want to use a different test for this but will want to still ensure we are not side-affected + assert.expectDeprecation({ id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 2 }); + } }); deprecatedTest( From 81a74b21490033cc012234ceb6bd714f38e5ba35 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 8 Aug 2022 17:55:09 -0700 Subject: [PATCH 26/29] some minor cleanup --- ember-data-types/q/record-data-schemas.ts | 2 +- .../integration/adapter/rest-adapter-test.js | 76 ++++++++++-------- .../serializers/rest-serializer-test.js | 77 ++++++++++--------- 3 files changed, 82 insertions(+), 73 deletions(-) diff --git a/ember-data-types/q/record-data-schemas.ts b/ember-data-types/q/record-data-schemas.ts index e7f8095e6ca..d9dcae701bf 100644 --- a/ember-data-types/q/record-data-schemas.ts +++ b/ember-data-types/q/record-data-schemas.ts @@ -14,7 +14,7 @@ export interface RelationshipSchema { key: string; // TODO @runspired remove our uses // TODO @runspired should RFC be updated to make this optional? // TODO @runspired sohuld RFC be update to enforce async and inverse are set? else internals need to know - // that meta came from DS.Model vs not from DS.Model as defaults should switch. + // that meta came from @ember-data/model vs not from @ember-data/model as defaults should switch. options: { async?: boolean; // controls inverse unloading "client side delete semantics" so we should replace that with a real flag polymorphic?: boolean; diff --git a/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js b/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js index b8d3ed0ec53..893df575366 100644 --- a/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js +++ b/packages/-ember-data/tests/integration/adapter/rest-adapter-test.js @@ -5,14 +5,22 @@ import Pretender from 'pretender'; import { module, test } from 'qunit'; import { reject, resolve } from 'rsvp'; -import DS from 'ember-data'; import { singularize } from 'ember-inflector'; import { setupTest } from 'ember-qunit'; +import AdapterError, { + AbortError, + ConflictError, + ForbiddenError, + NotFoundError, + ServerError, + UnauthorizedError, +} from '@ember-data/adapter/error'; import RESTAdapter from '@ember-data/adapter/rest'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import RESTSerializer from '@ember-data/serializer/rest'; import { recordIdentifierFor } from '@ember-data/store'; +import { Snapshot } from '@ember-data/store/-private'; import deepCopy from '@ember-data/unpublished-test-infra/test-support/deep-copy'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; @@ -342,13 +350,13 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { adapter.shouldBackgroundReloadRecord = () => false; this.owner.register( 'serializer:post', - DS.RESTSerializer.extend({ - primaryKey: '_id_', + class extends RESTSerializer { + primaryKey = '_id_'; - attrs: { + attrs = { name: '_name_', - }, - }) + }; + } ); store.push({ @@ -837,10 +845,10 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { this.owner.register( 'serializer:post', - DS.RESTSerializer.extend({ - primaryKey: '_ID_', - attrs: { name: '_NAME_' }, - }) + class extends RESTSerializer { + primaryKey = '_ID_'; + attrs = { name: '_NAME_' }; + } ); ajaxResponse({ @@ -1096,10 +1104,10 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { this.owner.register( 'serializer:post', - DS.RESTSerializer.extend({ - primaryKey: '_ID_', - attrs: { name: '_NAME_' }, - }) + class extends RESTSerializer { + primaryKey = '_ID_'; + attrs = { name: '_NAME_' }; + } ); ajaxResponse({ @@ -1266,7 +1274,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { this.owner.register( 'serializer:post', - DS.RESTSerializer.extend({ + RESTSerializer.extend({ primaryKey: '_ID_', attrs: { name: '_NAME_' }, }) @@ -1563,7 +1571,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { adapter.shouldBackgroundReloadRecord = () => false; this.owner.register( 'serializer:post', - DS.RESTSerializer.extend({ + RESTSerializer.extend({ primaryKey: '_ID_', attrs: { name: '_NAME_' }, }) @@ -1571,7 +1579,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { this.owner.register( 'serializer:comment', - DS.RESTSerializer.extend({ + RESTSerializer.extend({ primaryKey: '_ID_', attrs: { name: '_NAME_' }, }) @@ -1689,7 +1697,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.expect(2); adapter.shouldBackgroundReloadRecord = () => false; adapter.buildURL = function (type, id, snapshot, requestType) { - assert.ok(snapshot instanceof DS.Snapshot); + assert.ok(snapshot instanceof Snapshot); assert.strictEqual(requestType, 'findHasMany'); }; @@ -1792,7 +1800,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { adapter.shouldBackgroundReloadRecord = () => false; this.owner.register( 'serializer:post', - DS.RESTSerializer.extend({ + RESTSerializer.extend({ primaryKey: '_ID_', attrs: { name: '_NAME_' }, }) @@ -1800,7 +1808,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { this.owner.register( 'serializer:comment', - DS.RESTSerializer.extend({ + RESTSerializer.extend({ primaryKey: '_ID_', attrs: { name: '_NAME_' }, }) @@ -1877,7 +1885,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.expect(2); adapter.shouldBackgroundReloadRecord = () => false; adapter.buildURL = function (type, id, snapshot, requestType) { - assert.ok(snapshot instanceof DS.Snapshot); + assert.ok(snapshot instanceof Snapshot); assert.strictEqual(requestType, 'findBelongsTo'); }; @@ -2078,7 +2086,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { this.owner.register( 'serializer:application', - DS.RESTSerializer.extend({ + RESTSerializer.extend({ keyForAttribute(attr) { return underscore(attr); }, @@ -2305,7 +2313,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { assert.deepEqual(status, 400); assert.deepEqual(json, responseText); assert.deepEqual(requestData, expectedRequestData); - return new DS.AdapterError('nope!'); + return new AdapterError('nope!'); }; try { @@ -2315,7 +2323,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { } }); - test('rejects promise if DS.AdapterError is returned from adapter.handleResponse', async function (assert) { + test('rejects promise if AdapterError is returned from adapter.handleResponse', async function (assert) { class Post extends Model { @attr name; @hasMany('comment', { async: true, inverse: 'post' }) comments; @@ -2339,14 +2347,14 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { adapter.handleResponse = function (status, headers, json) { assert.ok(true, 'handleResponse should be called'); - return new DS.AdapterError(json); + return new AdapterError(json); }; try { await store.findRecord('post', '1'); } catch (reason) { assert.ok(true, 'promise should be rejected'); - assert.ok(reason instanceof DS.AdapterError, 'reason should be an instance of DS.AdapterError'); + assert.ok(reason instanceof AdapterError, 'reason should be an instance of AdapterError'); } }); @@ -2430,7 +2438,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { try { await store.findRecord('post', '1'); } catch (err) { - assert.ok(err instanceof DS.AbortError, 'reason should be an instance of DS.AbortError'); + assert.ok(err instanceof AbortError, 'reason should be an instance of AbortError'); assert.strictEqual(err.errors.length, 1, 'AbortError includes errors with request/response details'); let expectedError = { title: 'Adapter Error', @@ -2483,7 +2491,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { } }); - test('on error wraps the error string in an DS.AdapterError object', async function (assert) { + test('on error wraps the error string in an AdapterError object', async function (assert) { assert.expect(2); class Post extends Model { @attr name; @@ -2518,7 +2526,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { } }); - test('rejects promise with a specialized subclass of DS.AdapterError if ajax responds with http error codes', async function (assert) { + test('rejects promise with a specialized subclass of AdapterError if ajax responds with http error codes', async function (assert) { class Post extends Model { @attr name; @hasMany('comment', { async: true, inverse: 'post' }) comments; @@ -2537,7 +2545,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { await store.findRecord('post', '1'); } catch (reason) { assert.ok(true, 'promise should be rejected'); - assert.ok(reason instanceof DS.UnauthorizedError, 'reason should be an instance of DS.UnauthorizedError'); + assert.ok(reason instanceof UnauthorizedError, 'reason should be an instance of UnauthorizedError'); } ajaxError('error', 403); @@ -2546,7 +2554,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { await store.findRecord('post', '1'); } catch (reason) { assert.ok(true, 'promise should be rejected'); - assert.ok(reason instanceof DS.ForbiddenError, 'reason should be an instance of DS.ForbiddenError'); + assert.ok(reason instanceof ForbiddenError, 'reason should be an instance of ForbiddenError'); } ajaxError('error', 404); @@ -2555,7 +2563,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { await store.findRecord('post', '1'); } catch (reason) { assert.ok(true, 'promise should be rejected'); - assert.ok(reason instanceof DS.NotFoundError, 'reason should be an instance of DS.NotFoundError'); + assert.ok(reason instanceof NotFoundError, 'reason should be an instance of NotFoundError'); } ajaxError('error', 409); @@ -2564,7 +2572,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { await store.findRecord('post', '1'); } catch (reason) { assert.ok(true, 'promise should be rejected'); - assert.ok(reason instanceof DS.ConflictError, 'reason should be an instance of DS.ConflictError'); + assert.ok(reason instanceof ConflictError, 'reason should be an instance of ConflictError'); } ajaxError('error', 500); @@ -2573,7 +2581,7 @@ module('integration/adapter/rest_adapter - REST Adapter', function (hooks) { await store.findRecord('post', '1'); } catch (reason) { assert.ok(true, 'promise should be rejected'); - assert.ok(reason instanceof DS.ServerError, 'reason should be an instance of DS.ServerError'); + assert.ok(reason instanceof ServerError, 'reason should be an instance of ServerError'); } }); diff --git a/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js b/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js index 576d15322d3..b615be1301a 100644 --- a/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js +++ b/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js @@ -7,6 +7,7 @@ import DS from 'ember-data'; import Inflector, { singularize } from 'ember-inflector'; import { setupTest } from 'ember-qunit'; +import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import RESTSerializer from '@ember-data/serializer/rest'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; @@ -16,40 +17,40 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { setupTest(hooks); hooks.beforeEach(function () { - HomePlanet = DS.Model.extend({ - name: DS.attr('string'), - superVillains: DS.hasMany('super-villain', { async: false }), + HomePlanet = Model.extend({ + name: attr('string'), + superVillains: hasMany('super-villain', { async: false }), }); - SuperVillain = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - homePlanet: DS.belongsTo('home-planet', { async: false }), - evilMinions: DS.hasMany('evil-minion', { async: false }), + SuperVillain = Model.extend({ + firstName: attr('string'), + lastName: attr('string'), + homePlanet: belongsTo('home-planet', { async: false }), + evilMinions: hasMany('evil-minion', { async: false }), }); - EvilMinion = DS.Model.extend({ - superVillain: DS.belongsTo('super-villain', { async: false }), - name: DS.attr('string'), - doomsdayDevice: DS.belongsTo('doomsday-device', { async: false }), + EvilMinion = Model.extend({ + superVillain: belongsTo('super-villain', { async: false }), + name: attr('string'), + doomsdayDevice: belongsTo('doomsday-device', { async: false }), }); YellowMinion = EvilMinion.extend({ - eyes: DS.attr('number'), + eyes: attr('number'), }); - DoomsdayDevice = DS.Model.extend({ - name: DS.attr('string'), - evilMinion: DS.belongsTo('evil-minion', { polymorphic: true, async: true }), + DoomsdayDevice = Model.extend({ + name: attr('string'), + evilMinion: belongsTo('evil-minion', { polymorphic: true, async: true }), }); - Comment = DS.Model.extend({ - body: DS.attr('string'), - root: DS.attr('boolean'), - children: DS.hasMany('comment', { inverse: null, async: false }), + Comment = Model.extend({ + body: attr('string'), + root: attr('boolean'), + children: hasMany('comment', { inverse: null, async: false }), }); - Basket = DS.Model.extend({ - type: DS.attr('string'), - size: DS.attr('number'), + Basket = Model.extend({ + type: attr('string'), + size: attr('number'), }); - Container = DS.Model.extend({ - type: DS.belongsTo('basket', { async: true }), - volume: DS.attr('string'), + Container = Model.extend({ + type: belongsTo('basket', { async: true }), + volume: attr('string'), }); this.owner.register('model:super-villain', SuperVillain); @@ -732,22 +733,22 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { }); test('normalizeResponse with async polymorphic hasMany', function (assert) { - const HomePlanet = DS.Model.extend({ - name: DS.attr('string'), - superVillains: DS.hasMany('super-villain2', { async: false }), + const HomePlanet = Model.extend({ + name: attr('string'), + superVillains: hasMany('super-villain2', { async: false }), }); - const SuperVillain = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - homePlanet: DS.belongsTo('home-planet2', { async: false }), - evilMinions: DS.hasMany('evil-minion2', { async: true, polymorphic: true }), + const SuperVillain = Model.extend({ + firstName: attr('string'), + lastName: attr('string'), + homePlanet: belongsTo('home-planet2', { async: false }), + evilMinions: hasMany('evil-minion2', { async: true, polymorphic: true }), }); - const EvilMinion = DS.Model.extend({ - superVillain: DS.belongsTo('super-villain2', { async: false }), - name: DS.attr('string'), + const EvilMinion = Model.extend({ + superVillain: belongsTo('super-villain2', { async: false }), + name: attr('string'), }); const YellowMinion = EvilMinion.extend({ - eyes: DS.attr('number'), + eyes: attr('number'), }); this.owner.register('model:super-villain2', SuperVillain); From 8c06bce016d29bc497ac4bf5d016f88cfb6c1930 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 9 Aug 2022 00:22:22 -0700 Subject: [PATCH 27/29] fix some scenarios --- .../records/relationship-changes-test.js | 612 +++++++++--------- .../src/utilities/require-module.js | 9 +- .../app/templates/index.hbs | 2 +- yarn.lock | 3 +- 4 files changed, 312 insertions(+), 314 deletions(-) diff --git a/packages/-ember-data/tests/integration/records/relationship-changes-test.js b/packages/-ember-data/tests/integration/records/relationship-changes-test.js index 3bfbc79dc30..bf37c550a48 100644 --- a/packages/-ember-data/tests/integration/records/relationship-changes-test.js +++ b/packages/-ember-data/tests/integration/records/relationship-changes-test.js @@ -109,64 +109,52 @@ module('integration/records/relationship-changes - Relationship changes', functi }); if (!gte('4.0.0')) { - deprecatedTest( - 'Calling push with relationship triggers observers once if the relationship was empty and is added to', - { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 3 }, - function (assert) { - assert.expect(1); + test('Calling push with relationship triggers observers once if the relationship was empty and is added to', async function (assert) { + assert.expect(1); - let store = this.owner.lookup('service:store'); - let person = null; - let observerCount = 0; + let store = this.owner.lookup('service:store'); + let person = null; + let observerCount = 0; - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: { - firstName: 'Yehuda', - lastName: 'Katz', - }, - relationships: { - siblings: { - data: [], - }, - }, + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: { + firstName: 'Yehuda', + lastName: 'Katz', + }, + relationships: { + siblings: { + data: [], }, - }); - person = store.peekRecord('person', 'wat'); - }); + }, + }, + }); + person = store.peekRecord('person', 'wat'); - run(() => { - person.addObserver('siblings.[]', function () { - observerCount++; - }); - // prime the pump - person.siblings; - }); + person.addObserver('siblings.[]', function () { + observerCount++; + }); + // prime the pump + await person.siblings; - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: {}, - relationships: { - siblings: { - data: [sibling1Ref], - }, - }, + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: {}, + relationships: { + siblings: { + data: [sibling1Ref], }, - included: [sibling1], - }); - }); + }, + }, + included: [sibling1], + }); - run(() => { - assert.ok(observerCount >= 1, 'siblings observer should be triggered at least once'); - }); - } - ); + assert.ok(observerCount >= 1, 'siblings observer should be triggered at least once'); + }); } deprecatedTest( @@ -589,323 +577,325 @@ module('integration/records/relationship-changes - Relationship changes', functi { id: 'array-observers', count: 2, when: { ember: '>=3.26.0' } } ); }); - test('Calling push with relationship triggers willChange and didChange with detail when truncating', async function (assert) { - assert.expectDeprecation( - async () => { - let store = this.owner.lookup('service:store'); + deprecatedTest( + 'Calling push with relationship triggers willChange and didChange with detail when truncating', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 2 }, + async function (assert) { + let store = this.owner.lookup('service:store'); - let willChangeCount = 0; - let didChangeCount = 0; + let willChangeCount = 0; + let didChangeCount = 0; - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: { - firstName: 'Yehuda', - lastName: 'Katz', - }, - relationships: { - siblings: { - data: [sibling1Ref, sibling2Ref], - }, + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: { + firstName: 'Yehuda', + lastName: 'Katz', + }, + relationships: { + siblings: { + data: [sibling1Ref, sibling2Ref], }, }, - included: [sibling1, sibling2], - }); + }, + included: [sibling1, sibling2], + }); - let person = store.peekRecord('person', 'wat'); - let siblingsPromise = person.siblings; + let person = store.peekRecord('person', 'wat'); + let siblingsPromise = person.siblings; - await siblingsPromise; + await siblingsPromise; - // flush initial state since - // nothing is consuming us. - // else the test will fail because we will - // (correctly) not notify the array observer - // as there is still a pending notification - siblingsPromise.length; + // flush initial state since + // nothing is consuming us. + // else the test will fail because we will + // (correctly) not notify the array observer + // as there is still a pending notification + siblingsPromise.length; - let observer = { - arrayWillChange(array, start, removing, adding) { - willChangeCount++; - assert.strictEqual(start, 1); - assert.strictEqual(removing, 1); - assert.strictEqual(adding, 0); - }, + let observer = { + arrayWillChange(array, start, removing, adding) { + willChangeCount++; + assert.strictEqual(start, 1); + assert.strictEqual(removing, 1); + assert.strictEqual(adding, 0); + }, - arrayDidChange(array, start, removed, added) { - didChangeCount++; - assert.strictEqual(start, 1); - assert.strictEqual(removed, 1); - assert.strictEqual(added, 0); - }, - }; + arrayDidChange(array, start, removed, added) { + didChangeCount++; + assert.strictEqual(start, 1); + assert.strictEqual(removed, 1); + assert.strictEqual(added, 0); + }, + }; - siblingsPromise.addArrayObserver(observer); + siblingsPromise.addArrayObserver(observer); - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: {}, - relationships: { - siblings: { - data: [sibling1Ref], - }, + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: {}, + relationships: { + siblings: { + data: [sibling1Ref], }, }, - included: [], - }); - - assert.strictEqual(willChangeCount, 1, 'willChange observer should be triggered once'); - assert.strictEqual(didChangeCount, 1, 'didChange observer should be triggered once'); - - siblingsPromise.removeArrayObserver(observer); - }, - { id: 'array-observers', count: 2, when: { ember: '>=3.26.0' } } - ); - }); - - test('Calling push with relationship triggers willChange and didChange with detail when inserting at front', async function (assert) { - assert.expectDeprecation( - async () => { - let store = this.owner.lookup('service:store'); - - let willChangeCount = 0; - let didChangeCount = 0; - - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: { - firstName: 'Yehuda', - lastName: 'Katz', - }, - relationships: { - siblings: { - data: [sibling2Ref], - }, - }, - }, - included: [sibling2], - }); - }); - let person = store.peekRecord('person', 'wat'); + }, + included: [], + }); - let observer = { - arrayWillChange(array, start, removing, adding) { - willChangeCount++; - assert.strictEqual(start, 0, 'change will start at the beginning'); - assert.strictEqual(removing, 0, 'we have no removals'); - assert.strictEqual(adding, 1, 'we have one insertion'); - }, + assert.strictEqual(willChangeCount, 1, 'willChange observer should be triggered once'); + assert.strictEqual(didChangeCount, 1, 'didChange observer should be triggered once'); - arrayDidChange(array, start, removed, added) { - didChangeCount++; - assert.strictEqual(start, 0, 'change did start at the beginning'); - assert.strictEqual(removed, 0, 'change had no removals'); - assert.strictEqual(added, 1, 'change had one insertion'); - }, - }; + siblingsPromise.removeArrayObserver(observer); - const siblingsProxy = person.siblings; - const siblings = await siblingsProxy; + assert.expectDeprecation({ id: 'array-observers', count: 2, when: { ember: '>=3.26.0' } }); + } + ); - // flush initial state since - // nothing is consuming us. - // else the test will fail because we will - // (correctly) not notify the array observer - // as there is still a pending notification - siblingsProxy.length; + deprecatedTest( + 'Calling push with relationship triggers willChange and didChange with detail when inserting at front', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 2 }, + async function (assert) { + let store = this.owner.lookup('service:store'); - siblingsProxy.addArrayObserver(observer); + let willChangeCount = 0; + let didChangeCount = 0; + run(() => { store.push({ data: { type: 'person', id: 'wat', - attributes: {}, + attributes: { + firstName: 'Yehuda', + lastName: 'Katz', + }, relationships: { siblings: { - data: [sibling1Ref, sibling2Ref], + data: [sibling2Ref], }, }, }, - included: [sibling1], + included: [sibling2], }); + }); + let person = store.peekRecord('person', 'wat'); + + let observer = { + arrayWillChange(array, start, removing, adding) { + willChangeCount++; + assert.strictEqual(start, 0, 'change will start at the beginning'); + assert.strictEqual(removing, 0, 'we have no removals'); + assert.strictEqual(adding, 1, 'we have one insertion'); + }, - assert.strictEqual(willChangeCount, 1, 'willChange observer should be triggered once'); - assert.strictEqual(didChangeCount, 1, 'didChange observer should be triggered once'); - assert.deepEqual( - siblings.map((i) => i.id), - ['1', '2'], - 'We have the correct siblings' - ); + arrayDidChange(array, start, removed, added) { + didChangeCount++; + assert.strictEqual(start, 0, 'change did start at the beginning'); + assert.strictEqual(removed, 0, 'change had no removals'); + assert.strictEqual(added, 1, 'change had one insertion'); + }, + }; - siblingsProxy.removeArrayObserver(observer); - }, - { id: 'array-observers', count: 2, when: { ember: '>=3.26.0' } } - ); - }); + const siblingsProxy = person.siblings; + const siblings = await siblingsProxy; - test('Calling push with relationship triggers willChange and didChange with detail when inserting in middle', function (assert) { - assert.expectDeprecation( - async () => { - let store = this.owner.lookup('service:store'); + // flush initial state since + // nothing is consuming us. + // else the test will fail because we will + // (correctly) not notify the array observer + // as there is still a pending notification + siblingsProxy.length; - let willChangeCount = 0; - let didChangeCount = 0; + siblingsProxy.addArrayObserver(observer); - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: { - firstName: 'Yehuda', - lastName: 'Katz', - }, - relationships: { - siblings: { - data: [sibling1Ref, sibling3Ref], - }, - }, + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: {}, + relationships: { + siblings: { + data: [sibling1Ref, sibling2Ref], }, - included: [sibling1, sibling3], - }); - }); - let person = store.peekRecord('person', 'wat'); - let observer = { - arrayWillChange(array, start, removing, adding) { - willChangeCount++; - assert.strictEqual(start, 1); - assert.strictEqual(removing, 0); - assert.strictEqual(adding, 1); }, - arrayDidChange(array, start, removed, added) { - didChangeCount++; - assert.strictEqual(start, 1); - assert.strictEqual(removed, 0); - assert.strictEqual(added, 1); - }, - }; + }, + included: [sibling1], + }); - let siblings = run(() => person.siblings); + assert.strictEqual(willChangeCount, 1, 'willChange observer should be triggered once'); + assert.strictEqual(didChangeCount, 1, 'didChange observer should be triggered once'); + assert.deepEqual( + siblings.map((i) => i.id), + ['1', '2'], + 'We have the correct siblings' + ); - // flush initial state since - // nothing is consuming us. - // else the test will fail because we will - // (correctly) not notify the array observer - // as there is still a pending notification - siblings.length; + siblingsProxy.removeArrayObserver(observer); - siblings.addArrayObserver(observer); + assert.expectDeprecation({ id: 'array-observers', count: 2, when: { ember: '>=3.26.0' } }); + } + ); - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: {}, - relationships: { - siblings: { - data: [sibling1Ref, sibling2Ref, sibling3Ref], + deprecatedTest( + 'Calling push with relationship triggers willChange and didChange with detail when inserting in middle', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 2 }, + function (assert) { + assert.expectDeprecation( + async () => { + let store = this.owner.lookup('service:store'); + + let willChangeCount = 0; + let didChangeCount = 0; + + run(() => { + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: { + firstName: 'Yehuda', + lastName: 'Katz', + }, + relationships: { + siblings: { + data: [sibling1Ref, sibling3Ref], + }, }, }, + included: [sibling1, sibling3], + }); + }); + let person = store.peekRecord('person', 'wat'); + let observer = { + arrayWillChange(array, start, removing, adding) { + willChangeCount++; + assert.strictEqual(start, 1); + assert.strictEqual(removing, 0); + assert.strictEqual(adding, 1); }, - included: [sibling2], + arrayDidChange(array, start, removed, added) { + didChangeCount++; + assert.strictEqual(start, 1); + assert.strictEqual(removed, 0); + assert.strictEqual(added, 1); + }, + }; + + let siblings = run(() => person.siblings); + + // flush initial state since + // nothing is consuming us. + // else the test will fail because we will + // (correctly) not notify the array observer + // as there is still a pending notification + siblings.length; + + siblings.addArrayObserver(observer); + + run(() => { + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: {}, + relationships: { + siblings: { + data: [sibling1Ref, sibling2Ref, sibling3Ref], + }, + }, + }, + included: [sibling2], + }); }); - }); - assert.strictEqual(willChangeCount, 1, 'willChange observer should be triggered once'); - assert.strictEqual(didChangeCount, 1, 'didChange observer should be triggered once'); + assert.strictEqual(willChangeCount, 1, 'willChange observer should be triggered once'); + assert.strictEqual(didChangeCount, 1, 'didChange observer should be triggered once'); - siblings.removeArrayObserver(observer); - }, - { id: 'array-observers', count: 2, when: { ember: '>=3.26.0' } } - ); - }); + siblings.removeArrayObserver(observer); + }, + { id: 'array-observers', count: 2, when: { ember: '>=3.26.0' } } + ); + } + ); - test('Calling push with relationship triggers willChange and didChange with detail when replacing different length in middle', function (assert) { - assert.expectDeprecation( - async () => { - let store = this.owner.lookup('service:store'); + deprecatedTest( + 'Calling push with relationship triggers willChange and didChange with detail when replacing different length in middle', + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 2 }, + async function (assert) { + let store = this.owner.lookup('service:store'); - let willChangeCount = 0; - let didChangeCount = 0; + let willChangeCount = 0; + let didChangeCount = 0; - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: { - firstName: 'Yehuda', - lastName: 'Katz', - }, - relationships: { - siblings: { - data: [sibling1Ref, sibling2Ref, sibling3Ref], - }, - }, + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: { + firstName: 'Yehuda', + lastName: 'Katz', + }, + relationships: { + siblings: { + data: [sibling1Ref, sibling2Ref, sibling3Ref], }, - included: [sibling1, sibling2, sibling3], - }); - }); - - let person = store.peekRecord('person', 'wat'); - let observer = { - arrayWillChange(array, start, removing, adding) { - willChangeCount++; - assert.strictEqual(start, 1); - assert.strictEqual(removing, 1); - assert.strictEqual(adding, 2); }, + }, + included: [sibling1, sibling2, sibling3], + }); - arrayDidChange(array, start, removed, added) { - didChangeCount++; - assert.strictEqual(start, 1); - assert.strictEqual(removed, 1); - assert.strictEqual(added, 2); - }, - }; + let person = store.peekRecord('person', 'wat'); + let observer = { + arrayWillChange(array, start, removing, adding) { + willChangeCount++; + assert.strictEqual(start, 1); + assert.strictEqual(removing, 1); + assert.strictEqual(adding, 2); + }, - let siblings = run(() => person.siblings); - // flush initial state since - // nothing is consuming us. - // else the test will fail because we will - // (correctly) not notify the array observer - // as there is still a pending notification - siblings.length; - siblings.addArrayObserver(observer); + arrayDidChange(array, start, removed, added) { + didChangeCount++; + assert.strictEqual(start, 1); + assert.strictEqual(removed, 1); + assert.strictEqual(added, 2); + }, + }; - run(() => { - store.push({ - data: { - type: 'person', - id: 'wat', - attributes: {}, - relationships: { - siblings: { - data: [sibling1Ref, sibling4Ref, sibling5Ref, sibling3Ref], - }, - }, + let siblings = run(() => person.siblings); + // flush initial state since + // nothing is consuming us. + // else the test will fail because we will + // (correctly) not notify the array observer + // as there is still a pending notification + siblings.length; + siblings.addArrayObserver(observer); + + store.push({ + data: { + type: 'person', + id: 'wat', + attributes: {}, + relationships: { + siblings: { + data: [sibling1Ref, sibling4Ref, sibling5Ref, sibling3Ref], }, - included: [sibling4, sibling5], - }); - }); + }, + }, + included: [sibling4, sibling5], + }); - assert.strictEqual(willChangeCount, 1, 'willChange observer should be triggered once'); - assert.strictEqual(didChangeCount, 1, 'didChange observer should be triggered once'); + assert.strictEqual(willChangeCount, 1, 'willChange observer should be triggered once'); + assert.strictEqual(didChangeCount, 1, 'didChange observer should be triggered once'); - siblings.removeArrayObserver(observer); - }, - { id: 'array-observers', count: 2, when: { ember: '>=3.26.0' } } - ); - }); + siblings.removeArrayObserver(observer); + assert.expectDeprecation({ id: 'array-observers', count: 2, when: { ember: '>=3.26.0' } }); + } + ); test('Calling push with updated belongsTo relationship trigger observer', function (assert) { assert.expect(1); diff --git a/packages/private-build-infra/src/utilities/require-module.js b/packages/private-build-infra/src/utilities/require-module.js index 2be6af39775..d96584c17ef 100644 --- a/packages/private-build-infra/src/utilities/require-module.js +++ b/packages/private-build-infra/src/utilities/require-module.js @@ -8,7 +8,7 @@ module.exports = function requireModule(modulePath) { if (fileContents.includes('export default')) { newContents = fileContents.replace('export default ', 'return '); } else { - newContents = fileContents.replaceAll('export const ', 'module.exports.'); + newContents = replaceAll(fileContents, 'export const ', 'module.exports.'); newContents = `const module = { exports: {} };\n${newContents}\nreturn module.exports;`; } try { @@ -19,3 +19,10 @@ module.exports = function requireModule(modulePath) { console.log(e); } }; + +function replaceAll(str, pattern, replacement) { + if (str.replaceAll) { + return str.replaceAll(pattern, replacement); + } + return str.replace(new RegExp((pattern, 'g'), replacement)); +} diff --git a/packages/unpublished-fastboot-test-app/app/templates/index.hbs b/packages/unpublished-fastboot-test-app/app/templates/index.hbs index 6d6eab1a509..e0c9b62fd3e 100644 --- a/packages/unpublished-fastboot-test-app/app/templates/index.hbs +++ b/packages/unpublished-fastboot-test-app/app/templates/index.hbs @@ -1,4 +1,4 @@ - + {{node.toggle}} {{node.checkbox}} {{node.model.name}} diff --git a/yarn.lock b/yarn.lock index 5712ed1fb92..2b39c02ddfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7188,7 +7188,8 @@ eslint-module-utils@^2.7.3: find-up "^2.1.0" "eslint-plugin-ember-data@link:./packages/unpublished-eslint-rules": - version "4.8.0-alpha.1" + version "0.0.0" + uid "" eslint-plugin-ember@^11.0.4: version "11.0.4" From 51603bad425d458a6ec4d00217e39a1eb4836567 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 9 Aug 2022 00:28:05 -0700 Subject: [PATCH 28/29] fix lock --- yarn.lock | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2b39c02ddfa..5712ed1fb92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7188,8 +7188,7 @@ eslint-module-utils@^2.7.3: find-up "^2.1.0" "eslint-plugin-ember-data@link:./packages/unpublished-eslint-rules": - version "0.0.0" - uid "" + version "4.8.0-alpha.1" eslint-plugin-ember@^11.0.4: version "11.0.4" From 89106968893bedd6d70b2e4531fea70c571da94d Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 9 Aug 2022 00:51:09 -0700 Subject: [PATCH 29/29] fix node14 --- packages/private-build-infra/src/utilities/require-module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/private-build-infra/src/utilities/require-module.js b/packages/private-build-infra/src/utilities/require-module.js index d96584c17ef..80164eb811f 100644 --- a/packages/private-build-infra/src/utilities/require-module.js +++ b/packages/private-build-infra/src/utilities/require-module.js @@ -24,5 +24,5 @@ function replaceAll(str, pattern, replacement) { if (str.replaceAll) { return str.replaceAll(pattern, replacement); } - return str.replace(new RegExp((pattern, 'g'), replacement)); + return str.replace(new RegExp(pattern, 'g'), replacement); }