ember-data-model-fragments 7.0.2 → 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 +40 -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 -43
- package/addon/attributes/fragment-array.js +58 -46
- package/addon/attributes/fragment-owner.js +12 -2
- package/addon/attributes/fragment.js +61 -45
- 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 +129 -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,8 +1,10 @@
|
|
|
1
1
|
import { get, computed } from '@ember/object';
|
|
2
|
-
import
|
|
2
|
+
import { isDestroying, isDestroyed } from '@ember/destroyable';
|
|
3
|
+
import { Comparable } from '@ember/-internals/runtime';
|
|
3
4
|
// DS.Model gets munged to add fragment support, which must be included first
|
|
4
5
|
import { Model } from './ext';
|
|
5
6
|
import { copy } from './util/copy';
|
|
7
|
+
import fragmentCacheFor from './util/fragment-cache';
|
|
6
8
|
import { recordIdentifierFor } from '@ember-data/store';
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -15,7 +17,7 @@ import { recordIdentifierFor } from '@ember-data/store';
|
|
|
15
17
|
*/
|
|
16
18
|
export function fragmentRecordDataFor(fragment) {
|
|
17
19
|
const identifier = recordIdentifierFor(fragment);
|
|
18
|
-
return fragment.store.
|
|
20
|
+
return fragmentCacheFor(fragment.store).createFragmentRecordData(identifier);
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -30,14 +32,17 @@ export function fragmentRecordDataFor(fragment) {
|
|
|
30
32
|
Example:
|
|
31
33
|
|
|
32
34
|
```javascript
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
35
|
+
import Model from '@ember-data/model';
|
|
36
|
+
import MF from 'ember-data-model-fragments';
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
+
}
|
|
41
46
|
```
|
|
42
47
|
|
|
43
48
|
With JSON response:
|
|
@@ -53,37 +58,39 @@ export function fragmentRecordDataFor(fragment) {
|
|
|
53
58
|
```
|
|
54
59
|
|
|
55
60
|
```javascript
|
|
56
|
-
let person = store.
|
|
57
|
-
let name = person.
|
|
61
|
+
let person = store.peekRecord('person', '1');
|
|
62
|
+
let name = person.name;
|
|
58
63
|
|
|
59
|
-
person.
|
|
60
|
-
name.
|
|
61
|
-
name.
|
|
64
|
+
person.hasDirtyAttributes; // false
|
|
65
|
+
name.hasDirtyAttributes; // false
|
|
66
|
+
name.first; // 'Robert'
|
|
62
67
|
|
|
63
|
-
name.
|
|
64
|
-
name.
|
|
65
|
-
person.
|
|
68
|
+
name.first = 'The Animal';
|
|
69
|
+
name.hasDirtyAttributes; // true
|
|
70
|
+
person.hasDirtyAttributes; // true
|
|
66
71
|
|
|
67
72
|
person.rollbackAttributes();
|
|
68
|
-
name.
|
|
69
|
-
person.
|
|
70
|
-
person.get('hasDirtyAttributes'); // false
|
|
73
|
+
name.first; // 'Robert'
|
|
74
|
+
person.hasDirtyAttributes; // false
|
|
71
75
|
```
|
|
72
76
|
|
|
73
77
|
@class Fragment
|
|
74
78
|
@namespace MF
|
|
75
|
-
@extends
|
|
76
|
-
@uses
|
|
77
|
-
@
|
|
79
|
+
@extends Model
|
|
80
|
+
@uses Comparable
|
|
81
|
+
@public
|
|
78
82
|
*/
|
|
79
|
-
|
|
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, {
|
|
80
86
|
/**
|
|
81
87
|
Compare two fragments by identity to allow `FragmentArray` to diff arrays.
|
|
82
88
|
|
|
83
89
|
@method compare
|
|
84
|
-
@param
|
|
85
|
-
@param
|
|
86
|
-
@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
|
|
87
94
|
*/
|
|
88
95
|
compare(f1, f2) {
|
|
89
96
|
return f1 === f2 ? 0 : 1;
|
|
@@ -95,53 +102,117 @@ const Fragment = Model.extend(Ember.Comparable, {
|
|
|
95
102
|
to other records safely.
|
|
96
103
|
|
|
97
104
|
@method copy
|
|
98
|
-
@return {
|
|
105
|
+
@return {Fragment} The newly created fragment
|
|
106
|
+
@public
|
|
99
107
|
*/
|
|
100
108
|
copy() {
|
|
101
109
|
const type = this.constructor;
|
|
102
110
|
const props = Object.create(null);
|
|
103
111
|
const modelName = type.modelName || this._internalModel.modelName;
|
|
112
|
+
const identifier = recordIdentifierFor(this);
|
|
104
113
|
|
|
105
|
-
//
|
|
106
|
-
|
|
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);
|
|
107
119
|
|
|
108
120
|
// Loop over each attribute and copy individually to ensure nested fragments
|
|
109
|
-
// are also copied
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
+
}
|
|
113
155
|
|
|
114
156
|
return this.store.createFragment(modelName, props);
|
|
115
157
|
},
|
|
116
158
|
|
|
159
|
+
/**
|
|
160
|
+
@method toStringExtension
|
|
161
|
+
@return {String}
|
|
162
|
+
@public
|
|
163
|
+
*/
|
|
117
164
|
toStringExtension() {
|
|
165
|
+
if (isDestroying(this) || isDestroyed(this)) {
|
|
166
|
+
return '';
|
|
167
|
+
}
|
|
118
168
|
const identifier = recordIdentifierFor(this);
|
|
119
|
-
const owner = this.store.
|
|
169
|
+
const owner = fragmentCacheFor(this.store).getFragmentOwner(identifier);
|
|
120
170
|
return owner ? `owner(${owner.ownerIdentifier?.id})` : '';
|
|
121
171
|
},
|
|
122
172
|
|
|
123
173
|
/**
|
|
124
174
|
Override toString to include the toStringExtension output.
|
|
125
175
|
ember-data 4.12+ doesn't call toStringExtension in Model.toString().
|
|
176
|
+
|
|
177
|
+
@method toString
|
|
178
|
+
@return {String}
|
|
179
|
+
@public
|
|
126
180
|
*/
|
|
127
181
|
toString() {
|
|
182
|
+
if (isDestroying(this) || isDestroyed(this)) {
|
|
183
|
+
return `<fragment(destroyed)>`;
|
|
184
|
+
}
|
|
128
185
|
const identifier = recordIdentifierFor(this);
|
|
129
186
|
const extension = this.toStringExtension();
|
|
130
187
|
const extensionStr = extension ? `:${extension}` : '';
|
|
131
188
|
return `<${identifier.type}:${identifier.id}${extensionStr}>`;
|
|
132
189
|
},
|
|
133
|
-
})
|
|
134
|
-
fragmentOwnerProperties: computed(function () {
|
|
135
|
-
const props = [];
|
|
190
|
+
});
|
|
136
191
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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 = [];
|
|
142
198
|
|
|
143
|
-
|
|
144
|
-
|
|
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
|
+
});
|
|
210
|
+
|
|
211
|
+
Object.defineProperty(Fragment, 'toString', {
|
|
212
|
+
value() {
|
|
213
|
+
return `model:${this.modelName || 'fragment'}`;
|
|
214
|
+
},
|
|
215
|
+
configurable: true,
|
|
145
216
|
});
|
|
146
217
|
|
|
147
218
|
/**
|
|
@@ -171,7 +242,7 @@ export function setFragmentOwner(fragment, ownerRecordDataOrIdentifier, key) {
|
|
|
171
242
|
const fragmentIdentifier = recordIdentifierFor(fragment);
|
|
172
243
|
const ownerIdentifier =
|
|
173
244
|
ownerRecordDataOrIdentifier.identifier || ownerRecordDataOrIdentifier;
|
|
174
|
-
fragment.store.
|
|
245
|
+
fragmentCacheFor(fragment.store).setFragmentOwner(
|
|
175
246
|
fragmentIdentifier,
|
|
176
247
|
ownerIdentifier,
|
|
177
248
|
key,
|
|
@@ -180,7 +251,17 @@ export function setFragmentOwner(fragment, ownerRecordDataOrIdentifier, key) {
|
|
|
180
251
|
// Notify any observers of `fragmentOwner` properties
|
|
181
252
|
// Look up model via store to avoid schema access deprecation in ember-data 4.12+
|
|
182
253
|
const modelClass = fragment.store.modelFor(fragment.constructor.modelName);
|
|
183
|
-
|
|
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) => {
|
|
184
265
|
fragment.notifyPropertyChange(name);
|
|
185
266
|
});
|
|
186
267
|
|
|
@@ -200,7 +281,7 @@ export function isFragment(obj) {
|
|
|
200
281
|
Object.defineProperty(Fragment.prototype, 'hasDirtyAttributes', {
|
|
201
282
|
get() {
|
|
202
283
|
const identifier = recordIdentifierFor(this);
|
|
203
|
-
return this.store.
|
|
284
|
+
return fragmentCacheFor(this.store).hasChangedAttrs(identifier);
|
|
204
285
|
},
|
|
205
286
|
configurable: true,
|
|
206
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;
|