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.
- package/.release-plan.json +16 -4
- package/CHANGELOG.md +29 -0
- package/README.md +95 -31
- package/addon/array/fragment.js +19 -0
- package/addon/array/stateful.js +9 -45
- package/addon/attributes/array.js +55 -46
- package/addon/attributes/fragment-array.js +58 -49
- package/addon/attributes/fragment-owner.js +2 -1
- package/addon/attributes/fragment.js +61 -49
- package/addon/cache/fragment-cache.js +323 -24
- package/addon/cache/fragment-record-data-proxy.js +3 -1
- package/addon/cache/fragment-state-manager.js +283 -70
- package/addon/ext.js +52 -216
- package/addon/fragment.js +122 -48
- package/addon/index.js +15 -4
- package/addon/schema-service.js +190 -0
- package/addon/serializer.js +21 -0
- package/addon/serializers/fragment.js +85 -0
- package/addon/serializers/json-api.js +85 -0
- package/addon/serializers/rest.js +83 -0
- package/addon/serializers/utils.js +253 -0
- package/addon/store.js +301 -0
- package/addon/transforms/fragment.js +3 -7
- package/addon/util/fragment-cache.js +59 -0
- package/package.json +76 -65
- package/addon/record-data.js +0 -1131
- package/app/initializers/model-fragments.js +0 -11
- package/record-data.d.ts +0 -4
package/addon/ext.js
CHANGED
|
@@ -1,151 +1,11 @@
|
|
|
1
|
-
import { assert } from '@ember/debug';
|
|
2
|
-
import Store from '@ember-data/store';
|
|
3
1
|
import Model from '@ember-data/model';
|
|
4
|
-
|
|
5
|
-
import { Snapshot } from 'ember-data/-private';
|
|
6
|
-
import
|
|
7
|
-
import JSONSerializer from '@ember-data/serializer/json';
|
|
8
|
-
import FragmentCache from './cache/fragment-cache';
|
|
9
|
-
import { default as Fragment } from './fragment';
|
|
10
|
-
import { isPresent } from '@ember/utils';
|
|
11
|
-
import { getOwner } from '@ember/application';
|
|
12
|
-
|
|
13
|
-
function serializerForFragment(owner, normalizedModelName) {
|
|
14
|
-
let serializer = owner.lookup(`serializer:${normalizedModelName}`);
|
|
15
|
-
|
|
16
|
-
if (serializer !== undefined) {
|
|
17
|
-
return serializer;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// no serializer found for the specific model, fallback and check for application serializer
|
|
21
|
-
serializer = owner.lookup('serializer:-fragment');
|
|
22
|
-
if (serializer !== undefined) {
|
|
23
|
-
return serializer;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// final fallback, no model specific serializer, no application serializer, no
|
|
27
|
-
// `serializer` property on store: use json-api serializer
|
|
28
|
-
serializer = owner.lookup('serializer:-default');
|
|
29
|
-
|
|
30
|
-
return serializer;
|
|
31
|
-
}
|
|
2
|
+
import { dependencySatisfies, macroCondition } from '@embroider/macros';
|
|
3
|
+
import { Snapshot } from '@ember-data/legacy-compat/-private';
|
|
4
|
+
import fragmentCacheFor from './util/fragment-cache';
|
|
32
5
|
/**
|
|
33
6
|
@module ember-data-model-fragments
|
|
34
7
|
*/
|
|
35
8
|
|
|
36
|
-
/**
|
|
37
|
-
@class Store
|
|
38
|
-
@namespace DS
|
|
39
|
-
*/
|
|
40
|
-
Store.reopen({
|
|
41
|
-
/**
|
|
42
|
-
* Override createCache to return our FragmentCache
|
|
43
|
-
* This is the V2 Cache hook introduced in ember-data 4.7+
|
|
44
|
-
*/
|
|
45
|
-
createCache(storeWrapper) {
|
|
46
|
-
return new FragmentCache(storeWrapper);
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Override teardownRecord to handle fragments in a disconnected state.
|
|
51
|
-
* In ember-data 4.12+, fragments can end up disconnected during unload,
|
|
52
|
-
* and the default teardownRecord fails when trying to destroy them.
|
|
53
|
-
*/
|
|
54
|
-
teardownRecord(record) {
|
|
55
|
-
// Check if record is a fragment (by checking if it has no id or by model type)
|
|
56
|
-
// We need to handle the case where the fragment's store is disconnected
|
|
57
|
-
if (record.isDestroyed || record.isDestroying) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
try {
|
|
61
|
-
record.destroy();
|
|
62
|
-
} catch (e) {
|
|
63
|
-
// If the error is about disconnected state, just let it go
|
|
64
|
-
// The fragment will be cleaned up by ember's garbage collection
|
|
65
|
-
if (
|
|
66
|
-
e?.message?.includes?.('disconnected state') ||
|
|
67
|
-
e?.message?.includes?.('cannot utilize the store')
|
|
68
|
-
) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
throw e;
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
Create a new fragment that does not yet have an owner record.
|
|
77
|
-
The properties passed to this method are set on the newly created
|
|
78
|
-
fragment.
|
|
79
|
-
|
|
80
|
-
To create a new instance of the `name` fragment:
|
|
81
|
-
|
|
82
|
-
```js
|
|
83
|
-
store.createFragment('name', {
|
|
84
|
-
first: 'Alex',
|
|
85
|
-
last: 'Routé'
|
|
86
|
-
});
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
@method createFragment
|
|
90
|
-
@param {String} type
|
|
91
|
-
@param {Object} properties a hash of properties to set on the
|
|
92
|
-
newly created fragment.
|
|
93
|
-
@return {MF.Fragment} fragment
|
|
94
|
-
*/
|
|
95
|
-
createFragment(modelName, props) {
|
|
96
|
-
assert(
|
|
97
|
-
`The '${modelName}' model must be a subclass of MF.Fragment`,
|
|
98
|
-
this.isFragment(modelName),
|
|
99
|
-
);
|
|
100
|
-
// Create a new identifier for the fragment
|
|
101
|
-
const identifier = this.identifierCache.createIdentifierForNewRecord({
|
|
102
|
-
type: modelName,
|
|
103
|
-
});
|
|
104
|
-
// Signal to cache that this is a new record
|
|
105
|
-
this.cache.clientDidCreate(identifier, props || {});
|
|
106
|
-
// Get the record instance
|
|
107
|
-
return this._instanceCache.getRecord(identifier, props);
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
Returns true if the modelName is a fragment, false if not
|
|
112
|
-
|
|
113
|
-
@method isFragment
|
|
114
|
-
@private
|
|
115
|
-
@param {String} the modelName to check if a fragment
|
|
116
|
-
@return {boolean}
|
|
117
|
-
*/
|
|
118
|
-
isFragment(modelName) {
|
|
119
|
-
if (modelName === 'application' || modelName === '-default') {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const type = this.modelFor(modelName);
|
|
124
|
-
return Fragment.detect(type);
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
serializerFor(modelName) {
|
|
128
|
-
// this assertion is cargo-culted from ember-data TODO: update comment
|
|
129
|
-
assert(
|
|
130
|
-
"You need to pass a model name to the store's serializerFor method",
|
|
131
|
-
isPresent(modelName),
|
|
132
|
-
);
|
|
133
|
-
assert(
|
|
134
|
-
`Passing classes to store.serializerFor has been removed. Please pass a dasherized string instead of ${modelName}`,
|
|
135
|
-
typeof modelName === 'string',
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const owner = getOwner(this);
|
|
139
|
-
const normalizedModelName = dasherize(modelName);
|
|
140
|
-
|
|
141
|
-
if (this.isFragment(normalizedModelName)) {
|
|
142
|
-
return serializerForFragment(owner, normalizedModelName);
|
|
143
|
-
} else {
|
|
144
|
-
return this._super(...arguments);
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
|
|
149
9
|
/**
|
|
150
10
|
Override `Snapshot._attributes` to snapshot fragment attributes before they are
|
|
151
11
|
passed to the `DS.Model#serialize`.
|
|
@@ -157,89 +17,65 @@ const oldSnapshotAttributes = Object.getOwnPropertyDescriptor(
|
|
|
157
17
|
'_attributes',
|
|
158
18
|
);
|
|
159
19
|
|
|
20
|
+
// Symbol to store our converted attributes cache
|
|
21
|
+
const FRAGMENT_ATTRS = Symbol('fragmentAttrs');
|
|
22
|
+
|
|
23
|
+
function convertSnapshotValue(value) {
|
|
24
|
+
if (value && typeof value._createSnapshot === 'function') {
|
|
25
|
+
return value._createSnapshot();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (Array.isArray(value)) {
|
|
29
|
+
return value.map((item) => convertSnapshotValue(item));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isFragmentDefinition(definition) {
|
|
36
|
+
return definition?.isFragment || definition?.options?.isFragment;
|
|
37
|
+
}
|
|
38
|
+
|
|
160
39
|
Object.defineProperty(Snapshot.prototype, '_attributes', {
|
|
161
40
|
get() {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
return item;
|
|
177
|
-
});
|
|
178
|
-
}
|
|
41
|
+
// Return cached converted attrs if available
|
|
42
|
+
if (this[FRAGMENT_ATTRS]) {
|
|
43
|
+
return this[FRAGMENT_ATTRS];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const cachedAttrs = oldSnapshotAttributes.get.call(this);
|
|
47
|
+
|
|
48
|
+
// Create a new object to avoid modifying the cached __attributes in place
|
|
49
|
+
// This is needed because ember-data caches __attributes and reuses it
|
|
50
|
+
const attrs = Object.create(null);
|
|
51
|
+
|
|
52
|
+
Object.keys(cachedAttrs).forEach((key) => {
|
|
53
|
+
attrs[key] = convertSnapshotValue(cachedAttrs[key]);
|
|
179
54
|
});
|
|
180
|
-
return attrs;
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
55
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
@namespace DS
|
|
187
|
-
*/
|
|
188
|
-
JSONSerializer.reopen({
|
|
189
|
-
/**
|
|
190
|
-
Enables fragment properties to have custom transforms based on the fragment
|
|
191
|
-
type, so that deserialization does not have to happen on the fly
|
|
192
|
-
|
|
193
|
-
@method transformFor
|
|
194
|
-
@private
|
|
195
|
-
*/
|
|
196
|
-
transformFor(attributeType) {
|
|
197
|
-
if (attributeType.indexOf('-mf-') !== 0) {
|
|
198
|
-
return this._super(...arguments);
|
|
199
|
-
}
|
|
56
|
+
if (macroCondition(dependencySatisfies('ember-data', '>=5.8.0'))) {
|
|
57
|
+
const schema = this._store.getSchemaDefinitionService?.();
|
|
200
58
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (!owner.hasRegistration(containerKey)) {
|
|
205
|
-
const match = attributeType.match(
|
|
206
|
-
/^-mf-(fragment|fragment-array|array)(?:\$([^$]+))?(?:\$(.+))?$/,
|
|
207
|
-
);
|
|
208
|
-
assert(
|
|
209
|
-
`Failed parsing ember-data-model-fragments attribute type ${attributeType}`,
|
|
210
|
-
match != null,
|
|
211
|
-
);
|
|
212
|
-
const transformName = match[1];
|
|
213
|
-
const type = match[2];
|
|
214
|
-
const polymorphicTypeProp = match[3];
|
|
215
|
-
let transformClass = owner.factoryFor(`transform:${transformName}`);
|
|
216
|
-
transformClass = transformClass && transformClass.class;
|
|
217
|
-
transformClass = transformClass.extend({
|
|
218
|
-
type,
|
|
219
|
-
polymorphicTypeProp,
|
|
220
|
-
store: this.store,
|
|
221
|
-
});
|
|
222
|
-
owner.register(containerKey, transformClass);
|
|
223
|
-
}
|
|
224
|
-
return owner.lookup(containerKey);
|
|
225
|
-
},
|
|
59
|
+
if (schema) {
|
|
60
|
+
const definitions = schema.attributesDefinitionFor(this.identifier);
|
|
226
61
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
62
|
+
Object.entries(definitions).forEach(([key, definition]) => {
|
|
63
|
+
if (key in attrs || !isFragmentDefinition(definition)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
230
66
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
67
|
+
attrs[key] = convertSnapshotValue(
|
|
68
|
+
fragmentCacheFor(this._store).getAttr(this.identifier, key),
|
|
69
|
+
);
|
|
70
|
+
});
|
|
234
71
|
}
|
|
72
|
+
}
|
|
235
73
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
data[key] = transform.deserialize(data[key], transformMeta.options, data);
|
|
239
|
-
});
|
|
74
|
+
// Cache the converted attrs
|
|
75
|
+
this[FRAGMENT_ATTRS] = attrs;
|
|
240
76
|
|
|
241
|
-
return
|
|
77
|
+
return attrs;
|
|
242
78
|
},
|
|
243
79
|
});
|
|
244
80
|
|
|
245
|
-
export {
|
|
81
|
+
export { Model };
|
package/addon/fragment.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { get, computed } from '@ember/object';
|
|
2
|
-
import Ember from 'ember';
|
|
3
2
|
import { isDestroying, isDestroyed } from '@ember/destroyable';
|
|
3
|
+
import { Comparable } from '@ember/-internals/runtime';
|
|
4
4
|
// DS.Model gets munged to add fragment support, which must be included first
|
|
5
5
|
import { Model } from './ext';
|
|
6
6
|
import { copy } from './util/copy';
|
|
7
|
+
import fragmentCacheFor from './util/fragment-cache';
|
|
7
8
|
import { recordIdentifierFor } from '@ember-data/store';
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -16,7 +17,7 @@ import { recordIdentifierFor } from '@ember-data/store';
|
|
|
16
17
|
*/
|
|
17
18
|
export function fragmentRecordDataFor(fragment) {
|
|
18
19
|
const identifier = recordIdentifierFor(fragment);
|
|
19
|
-
return fragment.store.
|
|
20
|
+
return fragmentCacheFor(fragment.store).createFragmentRecordData(identifier);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -31,14 +32,17 @@ export function fragmentRecordDataFor(fragment) {
|
|
|
31
32
|
Example:
|
|
32
33
|
|
|
33
34
|
```javascript
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
35
|
+
import Model from '@ember-data/model';
|
|
36
|
+
import MF from 'ember-data-model-fragments';
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
class Person extends Model {
|
|
39
|
+
@MF.fragment('name') name;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class Name extends MF.Fragment {
|
|
43
|
+
@attr('string') first;
|
|
44
|
+
@attr('string') last;
|
|
45
|
+
}
|
|
42
46
|
```
|
|
43
47
|
|
|
44
48
|
With JSON response:
|
|
@@ -54,37 +58,39 @@ export function fragmentRecordDataFor(fragment) {
|
|
|
54
58
|
```
|
|
55
59
|
|
|
56
60
|
```javascript
|
|
57
|
-
let person = store.
|
|
58
|
-
let name = person.
|
|
61
|
+
let person = store.peekRecord('person', '1');
|
|
62
|
+
let name = person.name;
|
|
59
63
|
|
|
60
|
-
person.
|
|
61
|
-
name.
|
|
62
|
-
name.
|
|
64
|
+
person.hasDirtyAttributes; // false
|
|
65
|
+
name.hasDirtyAttributes; // false
|
|
66
|
+
name.first; // 'Robert'
|
|
63
67
|
|
|
64
|
-
name.
|
|
65
|
-
name.
|
|
66
|
-
person.
|
|
68
|
+
name.first = 'The Animal';
|
|
69
|
+
name.hasDirtyAttributes; // true
|
|
70
|
+
person.hasDirtyAttributes; // true
|
|
67
71
|
|
|
68
72
|
person.rollbackAttributes();
|
|
69
|
-
name.
|
|
70
|
-
person.
|
|
71
|
-
person.get('hasDirtyAttributes'); // false
|
|
73
|
+
name.first; // 'Robert'
|
|
74
|
+
person.hasDirtyAttributes; // false
|
|
72
75
|
```
|
|
73
76
|
|
|
74
77
|
@class Fragment
|
|
75
78
|
@namespace MF
|
|
76
|
-
@extends
|
|
77
|
-
@uses
|
|
78
|
-
@
|
|
79
|
+
@extends Model
|
|
80
|
+
@uses Comparable
|
|
81
|
+
@public
|
|
79
82
|
*/
|
|
80
|
-
|
|
83
|
+
// Note: We use Model.extend() with Comparable mixin for now
|
|
84
|
+
// as mixins are being phased out but still work in ember-data 4.12+
|
|
85
|
+
const Fragment = Model.extend(Comparable, {
|
|
81
86
|
/**
|
|
82
87
|
Compare two fragments by identity to allow `FragmentArray` to diff arrays.
|
|
83
88
|
|
|
84
89
|
@method compare
|
|
85
|
-
@param
|
|
86
|
-
@param
|
|
87
|
-
@return {Integer}
|
|
90
|
+
@param {Fragment} f1 - The first fragment to compare
|
|
91
|
+
@param {Fragment} f2 - The second fragment to compare
|
|
92
|
+
@return {Integer} The result of the comparison (0 if equal, 1 if not)
|
|
93
|
+
@public
|
|
88
94
|
*/
|
|
89
95
|
compare(f1, f2) {
|
|
90
96
|
return f1 === f2 ? 0 : 1;
|
|
@@ -96,37 +102,81 @@ const Fragment = Model.extend(Ember.Comparable, {
|
|
|
96
102
|
to other records safely.
|
|
97
103
|
|
|
98
104
|
@method copy
|
|
99
|
-
@return {
|
|
105
|
+
@return {Fragment} The newly created fragment
|
|
106
|
+
@public
|
|
100
107
|
*/
|
|
101
108
|
copy() {
|
|
102
109
|
const type = this.constructor;
|
|
103
110
|
const props = Object.create(null);
|
|
104
111
|
const modelName = type.modelName || this._internalModel.modelName;
|
|
112
|
+
const identifier = recordIdentifierFor(this);
|
|
105
113
|
|
|
106
|
-
//
|
|
107
|
-
|
|
114
|
+
// Use schema service to get all attributes including fragment attributes
|
|
115
|
+
// eachAttribute only iterates standard @attr() properties, not fragment properties
|
|
116
|
+
const schemaService = this.store
|
|
117
|
+
.getSchemaDefinitionService()
|
|
118
|
+
.attributesDefinitionFor(identifier);
|
|
108
119
|
|
|
109
120
|
// Loop over each attribute and copy individually to ensure nested fragments
|
|
110
|
-
// are also copied
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
// are also copied. For fragment attributes, we need to serialize to raw data
|
|
122
|
+
// since createFragment expects raw data, not fragment instances.
|
|
123
|
+
for (const name of Object.keys(schemaService)) {
|
|
124
|
+
const value = get(this, name);
|
|
125
|
+
const definition = schemaService[name];
|
|
126
|
+
const isFragmentAttr =
|
|
127
|
+
definition?.isFragment || definition?.options?.isFragment;
|
|
128
|
+
|
|
129
|
+
if (isFragmentAttr) {
|
|
130
|
+
// For fragment attributes, serialize to get raw data that can be used to create new fragments
|
|
131
|
+
if (value === null || value === undefined) {
|
|
132
|
+
props[name] = value;
|
|
133
|
+
} else if (typeof value.serialize === 'function') {
|
|
134
|
+
// Single fragment - serialize it
|
|
135
|
+
props[name] = value.serialize();
|
|
136
|
+
} else if (
|
|
137
|
+
Array.isArray(value) ||
|
|
138
|
+
typeof value.toArray === 'function'
|
|
139
|
+
) {
|
|
140
|
+
// Fragment array or array - serialize each element
|
|
141
|
+
const arr =
|
|
142
|
+
typeof value.toArray === 'function' ? value.toArray() : value;
|
|
143
|
+
props[name] = arr.map((item) =>
|
|
144
|
+
typeof item.serialize === 'function' ? item.serialize() : item,
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
// Fallback - use as-is
|
|
148
|
+
props[name] = value;
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
// Regular attribute - just copy the value
|
|
152
|
+
props[name] = copy(value);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
114
155
|
|
|
115
156
|
return this.store.createFragment(modelName, props);
|
|
116
157
|
},
|
|
117
158
|
|
|
159
|
+
/**
|
|
160
|
+
@method toStringExtension
|
|
161
|
+
@return {String}
|
|
162
|
+
@public
|
|
163
|
+
*/
|
|
118
164
|
toStringExtension() {
|
|
119
165
|
if (isDestroying(this) || isDestroyed(this)) {
|
|
120
166
|
return '';
|
|
121
167
|
}
|
|
122
168
|
const identifier = recordIdentifierFor(this);
|
|
123
|
-
const owner = this.store.
|
|
169
|
+
const owner = fragmentCacheFor(this.store).getFragmentOwner(identifier);
|
|
124
170
|
return owner ? `owner(${owner.ownerIdentifier?.id})` : '';
|
|
125
171
|
},
|
|
126
172
|
|
|
127
173
|
/**
|
|
128
174
|
Override toString to include the toStringExtension output.
|
|
129
175
|
ember-data 4.12+ doesn't call toStringExtension in Model.toString().
|
|
176
|
+
|
|
177
|
+
@method toString
|
|
178
|
+
@return {String}
|
|
179
|
+
@public
|
|
130
180
|
*/
|
|
131
181
|
toString() {
|
|
132
182
|
if (isDestroying(this) || isDestroyed(this)) {
|
|
@@ -137,18 +187,32 @@ const Fragment = Model.extend(Ember.Comparable, {
|
|
|
137
187
|
const extensionStr = extension ? `:${extension}` : '';
|
|
138
188
|
return `<${identifier.type}:${identifier.id}${extensionStr}>`;
|
|
139
189
|
},
|
|
140
|
-
})
|
|
141
|
-
fragmentOwnerProperties: computed(function () {
|
|
142
|
-
const props = [];
|
|
190
|
+
});
|
|
143
191
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
192
|
+
// Add static property using native class syntax approach
|
|
193
|
+
// This replaces reopenClass which is deprecated
|
|
194
|
+
Object.defineProperty(Fragment, 'fragmentOwnerProperties', {
|
|
195
|
+
get() {
|
|
196
|
+
return computed(function () {
|
|
197
|
+
const props = [];
|
|
198
|
+
|
|
199
|
+
this.eachComputedProperty((name, meta) => {
|
|
200
|
+
if (meta.isFragmentOwner) {
|
|
201
|
+
props.push(name);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return props;
|
|
206
|
+
}).readOnly();
|
|
207
|
+
},
|
|
208
|
+
configurable: true,
|
|
209
|
+
});
|
|
149
210
|
|
|
150
|
-
|
|
151
|
-
|
|
211
|
+
Object.defineProperty(Fragment, 'toString', {
|
|
212
|
+
value() {
|
|
213
|
+
return `model:${this.modelName || 'fragment'}`;
|
|
214
|
+
},
|
|
215
|
+
configurable: true,
|
|
152
216
|
});
|
|
153
217
|
|
|
154
218
|
/**
|
|
@@ -178,7 +242,7 @@ export function setFragmentOwner(fragment, ownerRecordDataOrIdentifier, key) {
|
|
|
178
242
|
const fragmentIdentifier = recordIdentifierFor(fragment);
|
|
179
243
|
const ownerIdentifier =
|
|
180
244
|
ownerRecordDataOrIdentifier.identifier || ownerRecordDataOrIdentifier;
|
|
181
|
-
fragment.store.
|
|
245
|
+
fragmentCacheFor(fragment.store).setFragmentOwner(
|
|
182
246
|
fragmentIdentifier,
|
|
183
247
|
ownerIdentifier,
|
|
184
248
|
key,
|
|
@@ -187,7 +251,17 @@ export function setFragmentOwner(fragment, ownerRecordDataOrIdentifier, key) {
|
|
|
187
251
|
// Notify any observers of `fragmentOwner` properties
|
|
188
252
|
// Look up model via store to avoid schema access deprecation in ember-data 4.12+
|
|
189
253
|
const modelClass = fragment.store.modelFor(fragment.constructor.modelName);
|
|
190
|
-
|
|
254
|
+
|
|
255
|
+
// Get the fragment owner properties array
|
|
256
|
+
// In 4.13+, we need to iterate computed properties directly since static property access may not work
|
|
257
|
+
const ownerProps = [];
|
|
258
|
+
modelClass.eachComputedProperty((name, meta) => {
|
|
259
|
+
if (meta.isFragmentOwner) {
|
|
260
|
+
ownerProps.push(name);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
ownerProps.forEach((name) => {
|
|
191
265
|
fragment.notifyPropertyChange(name);
|
|
192
266
|
});
|
|
193
267
|
|
|
@@ -207,7 +281,7 @@ export function isFragment(obj) {
|
|
|
207
281
|
Object.defineProperty(Fragment.prototype, 'hasDirtyAttributes', {
|
|
208
282
|
get() {
|
|
209
283
|
const identifier = recordIdentifierFor(this);
|
|
210
|
-
return this.store.
|
|
284
|
+
return fragmentCacheFor(this.store).hasChangedAttrs(identifier);
|
|
211
285
|
},
|
|
212
286
|
configurable: true,
|
|
213
287
|
});
|
package/addon/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Namespace from '@ember/application/namespace';
|
|
2
|
-
import
|
|
2
|
+
import { importSync } from '@embroider/macros';
|
|
3
3
|
import VERSION from './version';
|
|
4
4
|
import Fragment from './fragment';
|
|
5
5
|
import FragmentArray from './array/fragment';
|
|
@@ -7,6 +7,11 @@ import FragmentTransform from './transforms/fragment';
|
|
|
7
7
|
import FragmentArrayTransform from './transforms/fragment-array';
|
|
8
8
|
import ArrayTransform from './transforms/array';
|
|
9
9
|
import { fragment, fragmentArray, array, fragmentOwner } from './attributes';
|
|
10
|
+
import FragmentStore from './store';
|
|
11
|
+
import FragmentSerializer, {
|
|
12
|
+
FragmentRESTSerializer,
|
|
13
|
+
FragmentJSONAPISerializer,
|
|
14
|
+
} from './serializer';
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
Ember Data Model Fragments
|
|
@@ -21,14 +26,20 @@ const MF = Namespace.create({
|
|
|
21
26
|
FragmentTransform: FragmentTransform,
|
|
22
27
|
FragmentArrayTransform: FragmentArrayTransform,
|
|
23
28
|
ArrayTransform: ArrayTransform,
|
|
29
|
+
FragmentStore: FragmentStore,
|
|
30
|
+
FragmentSerializer: FragmentSerializer,
|
|
31
|
+
FragmentRESTSerializer: FragmentRESTSerializer,
|
|
32
|
+
FragmentJSONAPISerializer: FragmentJSONAPISerializer,
|
|
24
33
|
fragment: fragment,
|
|
25
34
|
fragmentArray: fragmentArray,
|
|
26
35
|
array: array,
|
|
27
36
|
fragmentOwner: fragmentOwner,
|
|
28
37
|
});
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
Object.defineProperty(MF, 'FragmentSchemaService', {
|
|
40
|
+
get() {
|
|
41
|
+
return importSync('./schema-service').default;
|
|
42
|
+
},
|
|
43
|
+
});
|
|
33
44
|
|
|
34
45
|
export default MF;
|