ember-data-model-fragments 7.0.3 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,18 +1,30 @@
1
1
  {
2
2
  "solution": {
3
3
  "ember-data-model-fragments": {
4
- "impact": "patch",
5
- "oldVersion": "7.0.2",
6
- "newVersion": "7.0.3",
4
+ "impact": "major",
5
+ "oldVersion": "7.0.3",
6
+ "newVersion": "8.0.0",
7
7
  "tagName": "latest",
8
8
  "constraints": [
9
+ {
10
+ "impact": "major",
11
+ "reason": "Appears in changelog section :boom: Breaking Change"
12
+ },
13
+ {
14
+ "impact": "minor",
15
+ "reason": "Appears in changelog section :rocket: Enhancement"
16
+ },
9
17
  {
10
18
  "impact": "patch",
11
19
  "reason": "Appears in changelog section :bug: Bug Fix"
20
+ },
21
+ {
22
+ "impact": "patch",
23
+ "reason": "Appears in changelog section :house: Internal"
12
24
  }
13
25
  ],
14
26
  "pkgJSONPath": "./package.json"
15
27
  }
16
28
  },
17
- "description": "## Release (2026-02-27)\n\n* ember-data-model-fragments 7.0.3 (patch)\n\n#### :bug: Bug Fix\n* `ember-data-model-fragments`\n * [#514](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/514) Guard fragment access on destroyed/unloaded records ([@deanmarano](https://github.com/deanmarano))\n\n#### Committers: 1\n- Dean Marano ([@deanmarano](https://github.com/deanmarano))\n"
29
+ "description": "## Release (2026-04-28)\n\n* ember-data-model-fragments 8.0.0 (major)\n\n#### :boom: Breaking Change\n* `ember-data-model-fragments`\n * [#507](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/507) Support ember-data 4.13.x and require extending FragmentStore/FragmentSerializer ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n\n#### :rocket: Enhancement\n* `ember-data-model-fragments`\n * [#523](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/523) Add ember-data 5.x support ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#521](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/521) Import Comparable directly in fragments ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n\n#### :bug: Bug Fix\n* `ember-data-model-fragments`\n * [#526](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/526) Fix fragmentSerialize writing to wrong location for fragment-only models ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#525](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/525) Fix createRecord crash when initializing fragment attrs with fragment instances ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#524](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/524) Fix fragment serializer resolution falling back to application serializer ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n\n#### :house: Internal\n* `ember-data-model-fragments`\n * [#522](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/522) Update release-plan ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#520](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/520) Remove unused legacy record-data implementation ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#519](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/519) Remove old ember-data version checks ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#517](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/517) Various tech debt cleanup ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n\n#### Committers: 1\n- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n"
18
30
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## Release (2026-04-28)
4
+
5
+ * ember-data-model-fragments 8.0.0 (major)
6
+
7
+ #### :boom: Breaking Change
8
+ * `ember-data-model-fragments`
9
+ * [#507](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/507) Support ember-data 4.13.x and require extending FragmentStore/FragmentSerializer ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
10
+
11
+ #### :rocket: Enhancement
12
+ * `ember-data-model-fragments`
13
+ * [#523](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/523) Add ember-data 5.x support ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
14
+ * [#521](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/521) Import Comparable directly in fragments ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
15
+
16
+ #### :bug: Bug Fix
17
+ * `ember-data-model-fragments`
18
+ * [#526](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/526) Fix fragmentSerialize writing to wrong location for fragment-only models ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
19
+ * [#525](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/525) Fix createRecord crash when initializing fragment attrs with fragment instances ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
20
+ * [#524](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/524) Fix fragment serializer resolution falling back to application serializer ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
21
+
22
+ #### :house: Internal
23
+ * `ember-data-model-fragments`
24
+ * [#522](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/522) Update release-plan ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
25
+ * [#520](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/520) Remove unused legacy record-data implementation ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
26
+ * [#519](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/519) Remove old ember-data version checks ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
27
+ * [#517](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/517) Various tech debt cleanup ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
28
+
29
+ #### Committers: 1
30
+ - Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
31
+
3
32
  ## Release (2026-02-27)
4
33
 
5
34
  * ember-data-model-fragments 7.0.3 (patch)
package/README.md CHANGED
@@ -20,20 +20,7 @@ Use the following table to decide which version of this project to use with your
20
20
  | >= v3.13.x < v3.27.x | v5.x | 12+ |
21
21
  | >= v3.28.x < v4.7.x | v6.0.x | 14+ |
22
22
  | v4.12.x | v7.x | 18+ |
23
-
24
- ### Upgrading to ember-data 4.12
25
-
26
- When using ember-data 4.12, you must add the following entry to your app's deprecation workflow to allow the app to boot. This addon uses `reopenClass` internally, which ember-data 4.12 deprecates:
27
-
28
- ```javascript
29
- // config/deprecation-workflow.js
30
- self.deprecationWorkflow = self.deprecationWorkflow || {};
31
- self.deprecationWorkflow.config = {
32
- workflow: [
33
- { handler: "silence", matchId: "ember-data:deprecate-model-reopenclass" },
34
- ],
35
- };
36
- ```
23
+ | v4.12.x < v6.x | v8.x | 20.19+ |
37
24
 
38
25
  ## Installation
39
26
 
@@ -51,6 +38,36 @@ $ ember generate fragment foo someAttr:string anotherAttr:boolean
51
38
 
52
39
  Which will create the module `app/models/foo.js` which exports a `Fragment` class with the given attributes.
53
40
 
41
+ ## Setup
42
+
43
+ This addon requires you to extend the provided `FragmentStore` and a fragment-aware serializer in your application.
44
+
45
+ ### Store
46
+
47
+ Create or update your application's store service to extend `FragmentStore`:
48
+
49
+ ```javascript
50
+ // app/services/store.js
51
+
52
+ import FragmentStore from "ember-data-model-fragments/store";
53
+
54
+ export default class Store extends FragmentStore {}
55
+ ```
56
+
57
+ ### Serializer
58
+
59
+ Create or update your application serializer to extend one of the fragment-aware serializers:
60
+
61
+ ```javascript
62
+ // app/serializers/application.js
63
+
64
+ import FragmentSerializer from "ember-data-model-fragments/serializer";
65
+
66
+ export default class ApplicationSerializer extends FragmentSerializer {}
67
+ ```
68
+
69
+ See the [Serialization](#serializing) section for more options if you're using `RESTSerializer` or `JSONAPISerializer`.
70
+
54
71
  ## Example
55
72
 
56
73
  ```javascript
@@ -297,42 +314,89 @@ export default class NameSerializer extends JSONSerializer {
297
314
  }
298
315
  ```
299
316
 
300
- Since fragment deserialization uses the value of a single attribute in the parent model, the `normalizeResponse` method of the serializer is never used. And since the attribute value is not a full-fledged [JSON API](http://jsonapi.org/) response, `JSONAPISerializer` cannot be used with fragments. Because of this, auto-generated fragment serializers **do not use the application serializer** and instead use `JSONSerializer`.
317
+ Since fragment deserialization uses the value of a single attribute in the parent model, the `normalizeResponse` method of the serializer is never used. The fragment value is not a full-fledged [JSON:API](http://jsonapi.org/) resource, so a `JSONAPISerializer` cannot be used to normalize a fragment directly. The addon takes care of this for you (see [Fragment serializer resolution](#fragment-serializer-resolution) below) — your application serializer can still be `FragmentJSONAPISerializer` or `FragmentRESTSerializer`.
301
318
 
302
- If common logic must be added to auto-generated fragment serializers, apps can register a custom `serializer:-fragment` with the application in an initializer.
319
+ Your application serializer should extend one of the fragment-aware serializers provided by this addon:
303
320
 
304
321
  ```javascript
305
- // app/serializers/fragment.js
322
+ // app/serializers/application.js
306
323
 
307
- import JSONSerializer from "@ember-data/serializer/json";
324
+ import FragmentSerializer from "ember-data-model-fragments/serializer";
308
325
 
309
- export default class FragmentSerializer extends JSONSerializer {}
326
+ export default class ApplicationSerializer extends FragmentSerializer {}
310
327
  ```
311
328
 
329
+ If you're using `RESTSerializer`, use `FragmentRESTSerializer` instead:
330
+
312
331
  ```javascript
313
- // app/initializers/fragment-serializer.js
332
+ // app/serializers/application.js
314
333
 
315
- import FragmentSerializer from "../serializers/fragment";
334
+ import { FragmentRESTSerializer } from "ember-data-model-fragments/serializer";
335
+
336
+ export default class ApplicationSerializer extends FragmentRESTSerializer {}
337
+ ```
338
+
339
+ If you're using `JSONAPISerializer`, use `FragmentJSONAPISerializer`:
340
+
341
+ ```javascript
342
+ // app/serializers/application.js
316
343
 
317
- export function initialize(application) {
318
- application.register("serializer:-fragment", FragmentSerializer);
344
+ import { FragmentJSONAPISerializer } from "ember-data-model-fragments/serializer";
345
+
346
+ export default class ApplicationSerializer extends FragmentJSONAPISerializer {}
347
+ ```
348
+
349
+ ### Fragment serializer resolution
350
+
351
+ When this addon needs to (de)serialize a fragment, it looks up a serializer for that fragment's model name in this order:
352
+
353
+ 1. **A serializer registered for the specific fragment type** — e.g. `app/serializers/name.js` for an `MF.fragment("name")` attribute. Use this to customize how a single fragment type is (de)serialized (see the `NameSerializer` example above).
354
+ 2. **An app-wide fragment serializer registered as `serializer:-fragment`** — use this if you want every fragment in your app to share custom behavior without registering a separate serializer for each fragment type.
355
+ 3. **The bundled `FragmentSerializer`** (a `JSONSerializer`-based serializer) — registered automatically the first time it is needed. This is what powers the "just works" behavior for apps that don't customize fragment serialization.
356
+
357
+ Fragment lookups never fall back to `serializer:application`. This is intentional: a typical application serializer is a `RESTSerializer` or `JSONAPISerializer`, neither of which can normalize a raw fragment hash. Your application serializer therefore remains free to be `FragmentRESTSerializer` or `FragmentJSONAPISerializer`, while fragments themselves are always handled by a JSON serializer.
358
+
359
+ #### Customizing serialization for one fragment type
360
+
361
+ ```javascript
362
+ // app/serializers/name.js
363
+
364
+ import FragmentSerializer from "ember-data-model-fragments/serializer";
365
+
366
+ export default class NameSerializer extends FragmentSerializer {
367
+ attrs = {
368
+ given: "first",
369
+ family: "last",
370
+ };
319
371
  }
372
+ ```
320
373
 
321
- export default {
322
- name: "fragment-serializer",
323
- initialize: initialize,
324
- };
374
+ #### Customizing serialization for every fragment
375
+
376
+ ```javascript
377
+ // app/serializers/-fragment.js
378
+
379
+ import FragmentSerializer from "ember-data-model-fragments/serializer";
380
+
381
+ export default class AppFragmentSerializer extends FragmentSerializer {
382
+ keyForAttribute(key) {
383
+ // e.g. snake_case fragment attribute keys app-wide
384
+ return key.replace(/([A-Z])/g, "_$1").toLowerCase();
385
+ }
386
+ }
325
387
  ```
326
388
 
389
+ Per-fragment-type serializers (`app/serializers/<fragment-name>.js`) take precedence over `serializer:-fragment`.
390
+
327
391
  If custom serialization of the owner record is needed, fragment [snapshots](http://emberjs.com/api/data/classes/DS.Snapshot.html) can be accessed using the [`Snapshot#attr`](http://emberjs.com/api/data/classes/DS.Snapshot.html#method_attr) method. Note that this differs from how relationships are accessed on snapshots (using `belongsTo`/`hasMany` methods):
328
392
 
329
393
  ```javascript
330
394
  // apps/serializers/person.js
331
395
  // Fragment snapshots are accessed using `snapshot.attr()`
332
396
 
333
- import JSONSerializer from "@ember-data/serializer/json";
397
+ import FragmentSerializer from "ember-data-model-fragments/serializer";
334
398
 
335
- export default JSONSerializer.extend({
399
+ export default class PersonSerializer extends FragmentSerializer {
336
400
  serialize(snapshot, options) {
337
401
  const json = super.serialize(...arguments);
338
402
 
@@ -355,8 +419,8 @@ export default JSONSerializer.extend({
355
419
  json.title_count = titlesSnapshot.length;
356
420
 
357
421
  return json;
358
- },
359
- });
422
+ }
423
+ }
360
424
  ```
361
425
 
362
426
  ## Nesting
@@ -1,5 +1,6 @@
1
1
  import { assert } from '@ember/debug';
2
2
  import { typeOf } from '@ember/utils';
3
+ import { dependencySatisfies, macroCondition } from '@embroider/macros';
3
4
  import StatefulArray from './stateful';
4
5
  import { isFragment, setFragmentOwner } from '../fragment';
5
6
  import isInstanceOfType from '../util/instance-of-type';
@@ -166,10 +167,28 @@ const FragmentArray = StatefulArray.extend({
166
167
  this.key,
167
168
  props,
168
169
  );
170
+ if (macroCondition(dependencySatisfies('ember-data', '>=5.8.0'))) {
171
+ const fragment = this.store._instanceCache.getRecord(fragmentIdentifier);
172
+ const definitions = this.store
173
+ .getSchemaDefinitionService()
174
+ .fields(fragmentIdentifier);
175
+
176
+ if (props) {
177
+ for (const [key, value] of Object.entries(props)) {
178
+ if (!definitions.has(key)) {
179
+ fragment.set(key, value);
180
+ }
181
+ }
182
+ }
183
+
184
+ return this.pushObject(fragment);
185
+ }
186
+
169
187
  const fragment = this.store._instanceCache.getRecord(
170
188
  fragmentIdentifier,
171
189
  props,
172
190
  );
191
+
173
192
  return this.pushObject(fragment);
174
193
  },
175
194
  });
@@ -2,24 +2,13 @@ import EmberObject, { get } from '@ember/object';
2
2
  import { isArray } from '@ember/array';
3
3
  import MutableArray from '@ember/array/mutable';
4
4
  import { assert } from '@ember/debug';
5
- import { diffArray } from '@ember-data/model/-private';
6
5
  import { copy } from '../util/copy';
7
- import { gte } from 'ember-compatibility-helpers';
6
+ import fragmentCacheFor from '../util/fragment-cache';
8
7
 
9
8
  /**
10
9
  @module ember-data-model-fragments
11
10
  */
12
11
 
13
- /**
14
- * Whether the current version of ember supports array observers.
15
- * Array observers were deprecated in ember 3.26 and removed in 4.0.
16
- * @see https://deprecations.emberjs.com/v3.x#toc_array-observers
17
- * @see https://github.com/emberjs/ember.js/pull/19833
18
- * @type {boolean}
19
- * @private
20
- */
21
- export const HAS_ARRAY_OBSERVERS = !gte('4.0.0');
22
-
23
12
  /**
24
13
  A state-aware array that is tied to an attribute of a `DS.Model` instance.
25
14
 
@@ -73,7 +62,7 @@ const StatefulArray = EmberObject.extend(MutableArray, {
73
62
  @private
74
63
  */
75
64
  get cache() {
76
- return this.store.cache;
65
+ return fragmentCacheFor(this.store);
77
66
  },
78
67
 
79
68
  init() {
@@ -88,14 +77,10 @@ const StatefulArray = EmberObject.extend(MutableArray, {
88
77
 
89
78
  notify() {
90
79
  this._isDirty = true;
91
- if (HAS_ARRAY_OBSERVERS && this.hasArrayObservers && !this._hasNotified) {
92
- this.retrieveLatest();
93
- } else {
94
- this._hasNotified = true;
95
- this.notifyPropertyChange('[]');
96
- this.notifyPropertyChange('firstObject');
97
- this.notifyPropertyChange('lastObject');
98
- }
80
+ this._hasNotified = true;
81
+ this.notifyPropertyChange('[]');
82
+ this.notifyPropertyChange('firstObject');
83
+ this.notifyPropertyChange('lastObject');
99
84
  },
100
85
 
101
86
  get length() {
@@ -181,30 +166,9 @@ const StatefulArray = EmberObject.extend(MutableArray, {
181
166
 
182
167
  this._isDirty = false;
183
168
  this._isUpdating = true;
184
- if (HAS_ARRAY_OBSERVERS && this.hasArrayObservers && !this._hasNotified) {
185
- // diff to find changes
186
- const diff = diffArray(this.currentState, currentState);
187
- // it's null if no change found
188
- if (diff.firstChangeIndex !== null) {
189
- // we found a change
190
- this.arrayContentWillChange(
191
- diff.firstChangeIndex,
192
- diff.removedCount,
193
- diff.addedCount,
194
- );
195
- this._length = currentState.length;
196
- this.currentState = currentState;
197
- this.arrayContentDidChange(
198
- diff.firstChangeIndex,
199
- diff.removedCount,
200
- diff.addedCount,
201
- );
202
- }
203
- } else {
204
- this._hasNotified = false;
205
- this._length = currentState.length;
206
- this.currentState = currentState;
207
- }
169
+ this._hasNotified = false;
170
+ this._length = currentState.length;
171
+ this.currentState = currentState;
208
172
  this._isUpdating = false;
209
173
  },
210
174
 
@@ -3,6 +3,7 @@ import { isArray } from '@ember/array';
3
3
  import { assert } from '@ember/debug';
4
4
  import { recordIdentifierFor } from '@ember-data/store';
5
5
  import metaTypeFor from '../util/meta-type-for';
6
+ import fragmentCacheFor from '../util/fragment-cache';
6
7
  import StatefulArray from '../array/stateful';
7
8
 
8
9
  /**
@@ -52,51 +53,59 @@ export default function array(type, options) {
52
53
  options,
53
54
  };
54
55
 
55
- // eslint-disable-next-line ember/require-computed-property-dependencies
56
- return computed({
57
- get(key) {
58
- if (this.isDestroying || this.isDestroyed) {
59
- return null;
60
- }
61
- const identifier = recordIdentifierFor(this);
62
- const cache = this.store.cache;
63
- if (cache.getFragment(identifier, key) === null) {
64
- return null;
65
- }
66
- let array = cache.getFragmentArrayCache(identifier, key);
67
- if (!array) {
68
- array = StatefulArray.create({
69
- store: this.store,
70
- identifier,
71
- key,
72
- });
73
- cache.setFragmentArrayCache(identifier, key, array);
74
- }
75
- return array;
56
+ // Use computed with a dependency on hasDirtyAttributes which changes on rollback
57
+ // This ensures the computed property is re-evaluated when dirty state changes
58
+ return computed(
59
+ 'currentState',
60
+ 'hasDirtyAttributes',
61
+ 'isDestroyed',
62
+ 'isDestroying',
63
+ 'store.cache',
64
+ {
65
+ get(key) {
66
+ if (this.isDestroying || this.isDestroyed) {
67
+ return null;
68
+ }
69
+ const identifier = recordIdentifierFor(this);
70
+ const cache = fragmentCacheFor(this.store);
71
+ if (cache.getFragment(identifier, key) === null) {
72
+ return null;
73
+ }
74
+ let array = cache.getFragmentArrayCache(identifier, key);
75
+ if (!array) {
76
+ array = StatefulArray.create({
77
+ store: this.store,
78
+ identifier,
79
+ key,
80
+ });
81
+ cache.setFragmentArrayCache(identifier, key, array);
82
+ }
83
+ return array;
84
+ },
85
+ set(key, value) {
86
+ assert(
87
+ 'You must pass an array or null to set an array',
88
+ value === null || isArray(value),
89
+ );
90
+ const identifier = recordIdentifierFor(this);
91
+ const cache = fragmentCacheFor(this.store);
92
+ if (value === null) {
93
+ cache.setDirtyFragment(identifier, key, null);
94
+ return null;
95
+ }
96
+ cache.setDirtyFragment(identifier, key, value.slice());
97
+ let array = cache.getFragmentArrayCache(identifier, key);
98
+ if (!array) {
99
+ array = StatefulArray.create({
100
+ store: this.store,
101
+ identifier,
102
+ key,
103
+ });
104
+ cache.setFragmentArrayCache(identifier, key, array);
105
+ }
106
+ array._setFragments(value);
107
+ return array;
108
+ },
76
109
  },
77
- set(key, value) {
78
- assert(
79
- 'You must pass an array or null to set an array',
80
- value === null || isArray(value),
81
- );
82
- const identifier = recordIdentifierFor(this);
83
- const cache = this.store.cache;
84
- if (value === null) {
85
- cache.setDirtyFragment(identifier, key, null);
86
- return null;
87
- }
88
- cache.setDirtyFragment(identifier, key, value.slice());
89
- let array = cache.getFragmentArrayCache(identifier, key);
90
- if (!array) {
91
- array = StatefulArray.create({
92
- store: this.store,
93
- identifier,
94
- key,
95
- });
96
- cache.setFragmentArrayCache(identifier, key, array);
97
- }
98
- array._setFragments(value);
99
- return array;
100
- },
101
- }).meta(meta);
110
+ ).meta(meta);
102
111
  }
@@ -4,6 +4,7 @@ import { typeOf } from '@ember/utils';
4
4
  import { isArray } from '@ember/array';
5
5
  import { recordIdentifierFor } from '@ember-data/store';
6
6
  import { isFragment } from '../fragment';
7
+ import fragmentCacheFor from '../util/fragment-cache';
7
8
  import metaTypeFor from '../util/meta-type-for';
8
9
  import FragmentArray from '../array/fragment';
9
10
 
@@ -61,54 +62,62 @@ export default function fragmentArray(type, options) {
61
62
  options,
62
63
  };
63
64
 
64
- // eslint-disable-next-line ember/require-computed-property-dependencies
65
- return computed({
66
- get(key) {
67
- if (this.isDestroying || this.isDestroyed) {
68
- return null;
69
- }
70
- const identifier = recordIdentifierFor(this);
71
- const cache = this.store.cache;
72
- if (cache.getFragment(identifier, key) === null) {
73
- return null;
74
- }
75
- let fragmentArray = cache.getFragmentArrayCache(identifier, key);
76
- if (!fragmentArray) {
77
- fragmentArray = FragmentArray.create({
78
- modelName: type,
79
- store: this.store,
80
- identifier,
81
- key,
82
- });
83
- cache.setFragmentArrayCache(identifier, key, fragmentArray);
84
- }
85
- return fragmentArray;
65
+ // Use computed with a dependency on hasDirtyAttributes which changes on rollback
66
+ // This ensures the computed property is re-evaluated when dirty state changes
67
+ return computed(
68
+ 'currentState',
69
+ 'hasDirtyAttributes',
70
+ 'isDestroyed',
71
+ 'isDestroying',
72
+ 'store.cache',
73
+ {
74
+ get(key) {
75
+ if (this.isDestroying || this.isDestroyed) {
76
+ return null;
77
+ }
78
+ const identifier = recordIdentifierFor(this);
79
+ const cache = fragmentCacheFor(this.store);
80
+ if (cache.getFragment(identifier, key) === null) {
81
+ return null;
82
+ }
83
+ let fragmentArray = cache.getFragmentArrayCache(identifier, key);
84
+ if (!fragmentArray) {
85
+ fragmentArray = FragmentArray.create({
86
+ modelName: type,
87
+ store: this.store,
88
+ identifier,
89
+ key,
90
+ });
91
+ cache.setFragmentArrayCache(identifier, key, fragmentArray);
92
+ }
93
+ return fragmentArray;
94
+ },
95
+ set(key, value) {
96
+ assert(
97
+ 'You must pass an array of fragments, or null to set a fragmentArray',
98
+ value === null ||
99
+ (isArray(value) &&
100
+ value.every((v) => isFragment(v) || typeOf(v) === 'object')),
101
+ );
102
+ const identifier = recordIdentifierFor(this);
103
+ const cache = fragmentCacheFor(this.store);
104
+ if (value === null) {
105
+ cache.setDirtyFragment(identifier, key, null);
106
+ return null;
107
+ }
108
+ let fragmentArray = cache.getFragmentArrayCache(identifier, key);
109
+ if (!fragmentArray) {
110
+ fragmentArray = FragmentArray.create({
111
+ modelName: type,
112
+ store: this.store,
113
+ identifier,
114
+ key,
115
+ });
116
+ cache.setFragmentArrayCache(identifier, key, fragmentArray);
117
+ }
118
+ fragmentArray._setFragments(value);
119
+ return fragmentArray;
120
+ },
86
121
  },
87
- set(key, value) {
88
- assert(
89
- 'You must pass an array of fragments, or null to set a fragmentArray',
90
- value === null ||
91
- (isArray(value) &&
92
- value.every((v) => isFragment(v) || typeOf(v) === 'object')),
93
- );
94
- const identifier = recordIdentifierFor(this);
95
- const cache = this.store.cache;
96
- if (value === null) {
97
- cache.setDirtyFragment(identifier, key, null);
98
- return null;
99
- }
100
- let fragmentArray = cache.getFragmentArrayCache(identifier, key);
101
- if (!fragmentArray) {
102
- fragmentArray = FragmentArray.create({
103
- modelName: type,
104
- store: this.store,
105
- identifier,
106
- key,
107
- });
108
- cache.setFragmentArrayCache(identifier, key, fragmentArray);
109
- }
110
- fragmentArray._setFragments(value);
111
- return fragmentArray;
112
- },
113
- }).meta(meta);
122
+ ).meta(meta);
114
123
  }
@@ -3,6 +3,7 @@ import { computed } from '@ember/object';
3
3
  import { isDestroying, isDestroyed } from '@ember/destroyable';
4
4
  import { isFragment } from '../fragment';
5
5
  import { recordIdentifierFor } from '@ember-data/store';
6
+ import fragmentCacheFor from '../util/fragment-cache';
6
7
 
7
8
  /**
8
9
  `MF.fragmentOwner` defines a read-only attribute on a `MF.Fragment`
@@ -42,7 +43,7 @@ export default function fragmentOwner() {
42
43
  isFragment(this),
43
44
  );
44
45
  const identifier = recordIdentifierFor(this);
45
- const owner = this.store.cache.getFragmentOwner(identifier);
46
+ const owner = fragmentCacheFor(this.store).getFragmentOwner(identifier);
46
47
  if (!owner) {
47
48
  return null;
48
49
  }