ember-data-model-fragments 4.0.0 → 5.0.0-beta.3
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/.vscode/settings.json +7 -0
- package/CHANGELOG.md +10 -1
- package/LICENSE.md +9 -0
- package/README.md +11 -6
- package/addon/array/fragment.js +7 -7
- package/addon/array/stateful.js +3 -3
- package/addon/attributes.js +26 -190
- package/addon/ext.js +43 -72
- package/addon/fragment.js +7 -7
- package/addon/record-data.js +531 -0
- package/addon/states.js +15 -12
- package/addon/transforms/array.js +7 -6
- package/addon/transforms/fragment-array.js +3 -2
- package/addon/transforms/fragment.js +9 -7
- package/app/initializers/model-fragments.js +3 -4
- package/index.js +1 -1
- package/package.json +43 -31
- package/.eslintignore +0 -16
- package/.eslintrc.js +0 -66
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
###
|
|
3
|
+
### v5.0.0-beta.1 (November 12, 2020)
|
|
4
|
+
|
|
5
|
+
* [#381](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/381) Fix `hasDirtyAttributes` when resetting a property (@VincentMolinie)
|
|
6
|
+
* [#385](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/385) Upgrade to Ember 3.20 LTS (@patocallaghan)
|
|
7
|
+
|
|
8
|
+
### v5.0.0-beta.0 (May 28, 2020)
|
|
9
|
+
|
|
10
|
+
* [#360](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/360) Upgrade to work with Ember Data 3.16 (@richgt, @igorT)
|
|
11
|
+
|
|
12
|
+
### v4.0.0 (January 25, 2019)
|
|
4
13
|
|
|
5
14
|
* Ember 3.5.0 compatibility with breaking changes related to `RecordData`. (@cohitre)
|
|
6
15
|
* Fixed `changedAtributes` with fragments (@Gorzas)
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Ember Data Model Fragments
|
|
2
2
|
|
|
3
|
-
[](https://github.com/adopted-ember-addons/ember-data-model-fragments/actions/workflows/ci.yml)
|
|
4
4
|
[](http://badge.fury.io/js/ember-data-model-fragments)
|
|
5
5
|
[](http://emberobserver.com/addons/ember-data-model-fragments)
|
|
6
6
|
|
|
@@ -27,7 +27,9 @@ Use the following table to decide which version of this project to use with your
|
|
|
27
27
|
| >= v2.14.x < v3.0.x | v2.14.x |
|
|
28
28
|
| >= v3.0.x < v3.2.x | v3.0.x-beta.1 |
|
|
29
29
|
| >= v3.2.x < v3.4.x | v3.3.x |
|
|
30
|
-
| >= v3.5.x | v4.0.x |
|
|
30
|
+
| >= v3.5.x < v3.12.x | v4.0.x |
|
|
31
|
+
| >= v3.13.x | v5.0.x |
|
|
32
|
+
| >= v3.28.x | Not fully compatible (See [issue](https://github.com/adopted-ember-addons/ember-data-model-fragments/issues/406)) |
|
|
31
33
|
|
|
32
34
|
#### Notes
|
|
33
35
|
|
|
@@ -40,6 +42,7 @@ Use the following table to decide which version of this project to use with your
|
|
|
40
42
|
- Ember Data 3.0 changed `ContainerInstanceCache` import paths. See [e4749c10](https://github.com/lytics/ember-data-model-fragments/pull/287/commits/e4749c107610a6d0dd6032a58c66356e6064562a).
|
|
41
43
|
- Ember Data 3.2 changed `InternalModel#fields`. See: [#310](https://github.com/lytics/ember-data-model-fragments/pull/310).
|
|
42
44
|
- Ember Data 3.5 added `RecordData` interfaces. See: [#324](https://github.com/lytics/ember-data-model-fragments/pull/324), [emberjs/rfcs#293](https://github.com/emberjs/rfcs/pull/293), and [emberjs/data#5616](https://github.com/emberjs/data/pull/5616).
|
|
45
|
+
- Ember Data 3.13 changed `InternalModel` Private APIs. See: [#360] (https://github.com/lytics/ember-data-model-fragments/pull/360)
|
|
43
46
|
|
|
44
47
|
## Installation
|
|
45
48
|
|
|
@@ -455,7 +458,7 @@ with `polymorphic` set to true. In addition the `typeKey` can be set, which defa
|
|
|
455
458
|
|
|
456
459
|
The `typeKey`'s value must be the lowercase name of a class that is assignment-compatible to the declared type of the fragment attribute. That is, it must be the declared type itself or a subclass. Additionally, the `typeKey`'s value must be a field on the parent class.
|
|
457
460
|
|
|
458
|
-
In the following example the declared type of `animals` is `animal`, which corresponds to the class `
|
|
461
|
+
In the following example the declared type of `animals` is `animal`, which corresponds to the class `Animal`. `Animal` has two subclasses: `Elephant` and `Lion`,
|
|
459
462
|
so to `typeKey`'s value can be `'animal'`, `'elephant'` or `'lion'`.
|
|
460
463
|
|
|
461
464
|
```javascript
|
|
@@ -476,7 +479,7 @@ export default Model.extend({
|
|
|
476
479
|
import Fragment from 'ember-data-model-fragments/fragment';
|
|
477
480
|
import attr from 'ember-data/attr';
|
|
478
481
|
|
|
479
|
-
|
|
482
|
+
export default Fragment.extend({
|
|
480
483
|
$type: attr('string'),
|
|
481
484
|
name: attr('string'),
|
|
482
485
|
});
|
|
@@ -540,14 +543,16 @@ Serializing the fragment type back to JSON is not currently supported out of the
|
|
|
540
543
|
```javascript
|
|
541
544
|
// app/serializers/animal.js
|
|
542
545
|
import JSONSerializer from 'ember-data/serializers/json';
|
|
546
|
+
import Elephant from 'app/models/elephant';
|
|
547
|
+
import Lion from 'app/models/elephant';
|
|
543
548
|
|
|
544
549
|
export default JSONSerializer.extend({
|
|
545
550
|
serialize(record, options) {
|
|
546
551
|
let json = this._super(...arguments);
|
|
547
552
|
|
|
548
|
-
if (record instanceof
|
|
553
|
+
if (record instanceof Elephant) {
|
|
549
554
|
json.$type = 'elephant';
|
|
550
|
-
} else if (record instanceof
|
|
555
|
+
} else if (record instanceof Lion) {
|
|
551
556
|
json.$type = 'lion';
|
|
552
557
|
} else {
|
|
553
558
|
json.$type = 'animal';
|
package/addon/array/fragment.js
CHANGED
|
@@ -87,7 +87,7 @@ const FragmentArray = StatefulArray.extend({
|
|
|
87
87
|
@param {Object} data
|
|
88
88
|
*/
|
|
89
89
|
_normalizeData(data) {
|
|
90
|
-
let content =
|
|
90
|
+
let content = this.content;
|
|
91
91
|
|
|
92
92
|
return normalizeFragmentArray(this, content, data, true);
|
|
93
93
|
},
|
|
@@ -113,16 +113,16 @@ const FragmentArray = StatefulArray.extend({
|
|
|
113
113
|
},
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
|
-
@method
|
|
116
|
+
@method _didCommit
|
|
117
117
|
@private
|
|
118
118
|
*/
|
|
119
|
-
|
|
119
|
+
_didCommit(data) {
|
|
120
120
|
this._super(...arguments);
|
|
121
121
|
|
|
122
122
|
// Notify all records of commit; if the adapter update did not contain new
|
|
123
123
|
// data, just notify each fragment so it can transition to a clean state
|
|
124
124
|
this.forEach((fragment, index) => {
|
|
125
|
-
fragment.
|
|
125
|
+
fragment._didCommit(data && data[index]);
|
|
126
126
|
});
|
|
127
127
|
},
|
|
128
128
|
|
|
@@ -200,7 +200,7 @@ const FragmentArray = StatefulArray.extend({
|
|
|
200
200
|
@private
|
|
201
201
|
*/
|
|
202
202
|
replaceContent(index, amount, objs) {
|
|
203
|
-
let content =
|
|
203
|
+
let content = this.content;
|
|
204
204
|
let replacedContent = content.slice(index, index + amount);
|
|
205
205
|
let fragments = normalizeFragmentArray(this, replacedContent, objs);
|
|
206
206
|
|
|
@@ -239,9 +239,9 @@ const FragmentArray = StatefulArray.extend({
|
|
|
239
239
|
@return {MF.Fragment} the newly added fragment
|
|
240
240
|
*/
|
|
241
241
|
createFragment(props) {
|
|
242
|
-
let record =
|
|
242
|
+
let record = this.owner;
|
|
243
243
|
let store = get(record, 'store');
|
|
244
|
-
let type =
|
|
244
|
+
let type = this.type;
|
|
245
245
|
let fragment = store.createFragment(type, props);
|
|
246
246
|
|
|
247
247
|
return this.pushObject(fragment);
|
package/addon/array/stateful.js
CHANGED
|
@@ -77,7 +77,6 @@ const StatefulArray = ArrayProxy.extend(Copyable, {
|
|
|
77
77
|
|
|
78
78
|
// Completely replace the contents with the new data
|
|
79
79
|
content.replace(0, get(content, 'length'), processedData);
|
|
80
|
-
|
|
81
80
|
this._pendingData = undefined;
|
|
82
81
|
},
|
|
83
82
|
|
|
@@ -105,10 +104,10 @@ const StatefulArray = ArrayProxy.extend(Copyable, {
|
|
|
105
104
|
_flushChangedAttributes() {},
|
|
106
105
|
|
|
107
106
|
/**
|
|
108
|
-
@method
|
|
107
|
+
@method _didCommit
|
|
109
108
|
@private
|
|
110
109
|
*/
|
|
111
|
-
|
|
110
|
+
_didCommit(data) {
|
|
112
111
|
if (data) {
|
|
113
112
|
this.setupData(data);
|
|
114
113
|
} else {
|
|
@@ -140,6 +139,7 @@ const StatefulArray = ArrayProxy.extend(Copyable, {
|
|
|
140
139
|
@readOnly
|
|
141
140
|
*/
|
|
142
141
|
hasDirtyAttributes: computed('[]', '_originalState', function() {
|
|
142
|
+
|
|
143
143
|
return compare(this.toArray(), get(this, '_originalState')) !== 0;
|
|
144
144
|
}),
|
|
145
145
|
|
package/addon/attributes.js
CHANGED
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
import { assert } from '@ember/debug';
|
|
2
|
-
import {
|
|
3
|
-
import { typeOf } from '@ember/utils';
|
|
4
|
-
import { isArray } from '@ember/array';
|
|
5
|
-
import { get, setProperties, computed } from '@ember/object';
|
|
6
|
-
import StatefulArray from './array/stateful';
|
|
7
|
-
import FragmentArray from './array/fragment';
|
|
8
|
-
import {
|
|
9
|
-
fragmentDidDirty,
|
|
10
|
-
fragmentDidReset
|
|
11
|
-
} from './states';
|
|
2
|
+
import { computed } from '@ember/object';
|
|
12
3
|
import {
|
|
13
4
|
internalModelFor,
|
|
14
|
-
setFragmentOwner,
|
|
15
|
-
setFragmentData,
|
|
16
|
-
createFragment,
|
|
17
5
|
isFragment
|
|
18
6
|
} from './fragment';
|
|
19
|
-
import isInstanceOfType from './util/instance-of-type';
|
|
20
7
|
|
|
21
8
|
/**
|
|
22
9
|
@module ember-data-model-fragments
|
|
@@ -80,76 +67,7 @@ function fragment(declaredModelName, options) {
|
|
|
80
67
|
|
|
81
68
|
let metaType = metaTypeFor('fragment', declaredModelName, options);
|
|
82
69
|
|
|
83
|
-
|
|
84
|
-
let internalModel = internalModelFor(record);
|
|
85
|
-
let data = getWithDefault(internalModel, key, options, 'object');
|
|
86
|
-
let fragment = internalModel._recordData.getFragment(key);
|
|
87
|
-
|
|
88
|
-
// Regardless of whether being called as a setter or getter, the fragment
|
|
89
|
-
// may not be initialized yet, in which case the data will contain a
|
|
90
|
-
// raw response or a stashed away fragment
|
|
91
|
-
|
|
92
|
-
// If we already have a processed fragment in _data and our current fragment is
|
|
93
|
-
// null simply reuse the one from data. We can be in this state after a rollback
|
|
94
|
-
// for example
|
|
95
|
-
if (!fragment && isFragment(data)) {
|
|
96
|
-
fragment = data;
|
|
97
|
-
// Else initialize the fragment
|
|
98
|
-
} else if (data && data !== fragment) {
|
|
99
|
-
if (fragment) {
|
|
100
|
-
// It's important to update internal model data to fragment before calling
|
|
101
|
-
// setFragmentData since updating the fragment can trigger calls to
|
|
102
|
-
// notifyPropertyChange which can in turn call setupFragment again, creating
|
|
103
|
-
// an infinite recursion loop. Since it's a reference anyway doing the
|
|
104
|
-
// assignation sooner has no side effect
|
|
105
|
-
internalModel._recordData._data[key] = fragment;
|
|
106
|
-
setFragmentData(fragment, data);
|
|
107
|
-
} else {
|
|
108
|
-
fragment = createFragment(store, declaredModelName, record, key, options, data);
|
|
109
|
-
internalModel._recordData._data[key] = fragment;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
} else {
|
|
113
|
-
// Handle the adapter setting the fragment to null
|
|
114
|
-
fragment = data;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return fragment;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function setFragmentValue(record, key, fragment, value) {
|
|
121
|
-
let store = record.store;
|
|
122
|
-
let internalModel = internalModelFor(record);
|
|
123
|
-
|
|
124
|
-
assert(`You can only assign \`null\`, an object literal or a '${declaredModelName}' fragment instance to this property`, value === null || typeOf(value) === 'object' || isInstanceOfType(store.modelFor(declaredModelName), value));
|
|
125
|
-
|
|
126
|
-
if (!value) {
|
|
127
|
-
fragment = null;
|
|
128
|
-
} else if (isFragment(value)) {
|
|
129
|
-
// A fragment instance was given, so just replace the existing value
|
|
130
|
-
fragment = setFragmentOwner(value, record, key);
|
|
131
|
-
} else if (!fragment) {
|
|
132
|
-
// A property hash was given but the property was null, so create a new
|
|
133
|
-
// fragment with the data
|
|
134
|
-
fragment = createFragment(store, declaredModelName, record, key, options, value);
|
|
135
|
-
} else {
|
|
136
|
-
// The fragment already exists and a property hash is given, so just set
|
|
137
|
-
// its values and let the state machine take care of the dirtiness
|
|
138
|
-
setProperties(fragment, value);
|
|
139
|
-
|
|
140
|
-
return fragment;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (internalModel._recordData._data[key] !== fragment) {
|
|
144
|
-
fragmentDidDirty(record, key, fragment);
|
|
145
|
-
} else {
|
|
146
|
-
fragmentDidReset(record, key);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return fragment;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return fragmentProperty(metaType, options, setupFragment, setFragmentValue);
|
|
70
|
+
return fragmentProperty(metaType, options, declaredModelName);
|
|
153
71
|
}
|
|
154
72
|
|
|
155
73
|
/**
|
|
@@ -197,14 +115,8 @@ function fragmentArray(modelName, options) {
|
|
|
197
115
|
|
|
198
116
|
let metaType = metaTypeFor('fragment-array', modelName, options);
|
|
199
117
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
type: modelName,
|
|
203
|
-
options: options,
|
|
204
|
-
name: key,
|
|
205
|
-
owner: record
|
|
206
|
-
});
|
|
207
|
-
});
|
|
118
|
+
// fragmentArrayProperty takes type, options, modelName, isFragmentArray
|
|
119
|
+
return fragmentArrayProperty(metaType, options, modelName, true);
|
|
208
120
|
}
|
|
209
121
|
|
|
210
122
|
/**
|
|
@@ -245,16 +157,11 @@ function array(type, options) {
|
|
|
245
157
|
|
|
246
158
|
let metaType = metaTypeFor('array', type);
|
|
247
159
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
options: options,
|
|
251
|
-
name: key,
|
|
252
|
-
owner: record
|
|
253
|
-
});
|
|
254
|
-
});
|
|
160
|
+
// fragmentArrayProperty takes type, options, modelName, isArray, isFragmentArray
|
|
161
|
+
return fragmentArrayProperty(metaType, options, null, false);
|
|
255
162
|
}
|
|
256
163
|
|
|
257
|
-
function fragmentProperty(type, options,
|
|
164
|
+
function fragmentProperty(type, options, declaredModelName, isArray = false, isFragmentArray = false) {
|
|
258
165
|
options = options || {};
|
|
259
166
|
|
|
260
167
|
let meta = {
|
|
@@ -266,68 +173,32 @@ function fragmentProperty(type, options, setupFragment, setFragmentValue) {
|
|
|
266
173
|
|
|
267
174
|
return computed({
|
|
268
175
|
get(key) {
|
|
176
|
+
let fragment;
|
|
269
177
|
let internalModel = internalModelFor(this);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
178
|
+
if (isArray) {
|
|
179
|
+
fragment = internalModel._recordData.getFragmentArray(key, options, declaredModelName, this, isFragmentArray);
|
|
180
|
+
} else {
|
|
181
|
+
fragment = internalModel._recordData.getFragment(key, options, declaredModelName, this);
|
|
182
|
+
}
|
|
183
|
+
return fragment;
|
|
273
184
|
},
|
|
274
185
|
set(key, value) {
|
|
186
|
+
let fragment;
|
|
275
187
|
let internalModel = internalModelFor(this);
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
188
|
+
if (isArray) {
|
|
189
|
+
fragment = internalModel._recordData.getFragmentArray(key, options, declaredModelName, this, isFragmentArray);
|
|
190
|
+
fragment = internalModel._recordData.setFragmentArrayValue(key, fragment, value, this, declaredModelName, options, isFragmentArray);
|
|
191
|
+
} else {
|
|
192
|
+
fragment = internalModel._recordData.getFragment(key, options, declaredModelName, this);
|
|
193
|
+
fragment = internalModel._recordData.setFragmentValue(key, fragment, value, this, declaredModelName, options);
|
|
194
|
+
}
|
|
195
|
+
return internalModel._recordData.fragments[key] = fragment;
|
|
281
196
|
}
|
|
282
197
|
}).meta(meta);
|
|
283
198
|
}
|
|
284
199
|
|
|
285
|
-
function fragmentArrayProperty(metaType, options,
|
|
286
|
-
|
|
287
|
-
let internalModel = internalModelFor(record);
|
|
288
|
-
let data = getWithDefault(internalModel, key, options, 'array');
|
|
289
|
-
let fragments = internalModel._recordData.getFragment(key) || null;
|
|
290
|
-
|
|
291
|
-
// If we already have a processed fragment in _data and our current fragment is
|
|
292
|
-
// null simply reuse the one from data. We can be in this state after a rollback
|
|
293
|
-
// for example
|
|
294
|
-
if (data instanceof StatefulArray && !fragments) {
|
|
295
|
-
fragments = data;
|
|
296
|
-
// Create a fragment array and initialize with data
|
|
297
|
-
} else if (data && data !== fragments) {
|
|
298
|
-
fragments || (fragments = createArray(record, key));
|
|
299
|
-
internalModel._recordData._data[key] = fragments;
|
|
300
|
-
fragments.setupData(data);
|
|
301
|
-
} else {
|
|
302
|
-
// Handle the adapter setting the fragment array to null
|
|
303
|
-
fragments = data;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return fragments;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function setFragmentValue(record, key, fragments, value) {
|
|
310
|
-
let internalModel = internalModelFor(record);
|
|
311
|
-
|
|
312
|
-
if (isArray(value)) {
|
|
313
|
-
fragments || (fragments = createArray(record, key));
|
|
314
|
-
fragments.setObjects(value);
|
|
315
|
-
} else if (value === null) {
|
|
316
|
-
fragments = null;
|
|
317
|
-
} else {
|
|
318
|
-
assert('A fragment array property can only be assigned an array or null');
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (internalModel._recordData._data[key] !== fragments || (fragments && get(fragments, 'hasDirtyAttributes'))) {
|
|
322
|
-
fragmentDidDirty(record, key, fragments);
|
|
323
|
-
} else {
|
|
324
|
-
fragmentDidReset(record, key);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return fragments;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return fragmentProperty(metaType, options, setupFragmentArray, setFragmentValue);
|
|
200
|
+
function fragmentArrayProperty(metaType, options, declaredModelName, isFragmentArray) {
|
|
201
|
+
return fragmentProperty(metaType, options, declaredModelName, true, isFragmentArray);
|
|
331
202
|
}
|
|
332
203
|
|
|
333
204
|
/**
|
|
@@ -357,47 +228,12 @@ function fragmentOwner() {
|
|
|
357
228
|
return computed(function() {
|
|
358
229
|
assert('Fragment owner properties can only be used on fragments.', isFragment(this));
|
|
359
230
|
|
|
360
|
-
return internalModelFor(this)._recordData.
|
|
231
|
+
return internalModelFor(this)._recordData._owner;
|
|
361
232
|
}).meta({
|
|
362
233
|
isFragmentOwner: true
|
|
363
234
|
}).readOnly();
|
|
364
235
|
}
|
|
365
236
|
|
|
366
|
-
// The default value of a fragment is either an array or an object,
|
|
367
|
-
// which should automatically get deep copied
|
|
368
|
-
function getDefaultValue(record, options, type) {
|
|
369
|
-
let value;
|
|
370
|
-
|
|
371
|
-
if (typeof options.defaultValue === 'function') {
|
|
372
|
-
value = options.defaultValue();
|
|
373
|
-
} else if ('defaultValue' in options) {
|
|
374
|
-
value = options.defaultValue;
|
|
375
|
-
} else if (type === 'array') {
|
|
376
|
-
value = [];
|
|
377
|
-
} else {
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
assert(`The fragment's default value must be an ${type}`, (typeOf(value) == type) || (value === null));
|
|
382
|
-
|
|
383
|
-
// No need to copy value if it was a function
|
|
384
|
-
if (typeof options.defaultValue === 'function') {
|
|
385
|
-
return value;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// Create a deep copy of the resulting value to avoid shared reference errors
|
|
389
|
-
return copy(value, true);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Returns the value of the property or the default propery
|
|
393
|
-
function getWithDefault(internalModel, key, options, type) {
|
|
394
|
-
if (key in internalModel._recordData._data) {
|
|
395
|
-
return internalModel._recordData._data[key];
|
|
396
|
-
} else {
|
|
397
|
-
return getDefaultValue(internalModel, options, type);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
237
|
export {
|
|
402
238
|
fragment,
|
|
403
239
|
fragmentArray,
|
package/addon/ext.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { assert } from '@ember/debug';
|
|
2
|
-
import Store from 'ember-data/store';
|
|
3
|
-
import Model from 'ember-data/model';
|
|
2
|
+
import Store from '@ember-data/store';
|
|
3
|
+
import Model from '@ember-data/model';
|
|
4
|
+
// eslint-disable-next-line ember/use-ember-data-rfc-395-imports
|
|
4
5
|
import { coerceId, RecordData, InternalModel, normalizeModelName } from 'ember-data/-private';
|
|
5
|
-
import JSONSerializer from 'ember-data/
|
|
6
|
+
import JSONSerializer from '@ember-data/serializer/json';
|
|
6
7
|
import FragmentRootState from './states';
|
|
8
|
+
import FragmentRecordData from './record-data';
|
|
7
9
|
import {
|
|
8
10
|
internalModelFor,
|
|
9
11
|
default as Fragment
|
|
10
12
|
} from './fragment';
|
|
11
|
-
import FragmentArray from './array/fragment';
|
|
12
13
|
import { isPresent } from '@ember/utils';
|
|
13
14
|
import { computed } from '@ember/object';
|
|
14
15
|
import { getOwner } from '@ember/application';
|
|
16
|
+
import { assign } from '@ember/polyfills';
|
|
17
|
+
import { lte, gte } from 'ember-compatibility-helpers';
|
|
15
18
|
|
|
16
19
|
function serializerForFragment(owner, normalizedModelName) {
|
|
17
20
|
let serializer = owner.lookup(`serializer:${normalizedModelName}`);
|
|
@@ -39,7 +42,7 @@ function serializerForFragment(owner, normalizedModelName) {
|
|
|
39
42
|
const InternalModelPrototype = InternalModel.prototype;
|
|
40
43
|
const RecordDataPrototype = RecordData.prototype;
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
assign(RecordDataPrototype, {
|
|
43
46
|
eachFragmentKey(fn) {
|
|
44
47
|
this._fragments = this._fragments || Object.create({});
|
|
45
48
|
Object.keys(this._fragments).forEach(fn);
|
|
@@ -82,6 +85,11 @@ Object.assign(RecordDataPrototype, {
|
|
|
82
85
|
},
|
|
83
86
|
|
|
84
87
|
didCommit(data) {
|
|
88
|
+
if (this._attributes) {
|
|
89
|
+
// willCommit was never called
|
|
90
|
+
this._inFlightAttributes = this._attributes;
|
|
91
|
+
this._attributes = null;
|
|
92
|
+
}
|
|
85
93
|
this._isNew = false;
|
|
86
94
|
if (data) {
|
|
87
95
|
if (data.relationships) {
|
|
@@ -95,15 +103,14 @@ Object.assign(RecordDataPrototype, {
|
|
|
95
103
|
data = data.attributes;
|
|
96
104
|
|
|
97
105
|
// Notify fragments that the record was committed
|
|
98
|
-
this.eachFragmentKeyValue((key, fragment) => fragment.
|
|
106
|
+
this.eachFragmentKeyValue((key, fragment) => fragment._didCommit(data[key]));
|
|
99
107
|
} else {
|
|
100
|
-
this.eachFragmentKeyValue((key, fragment) => fragment.
|
|
108
|
+
this.eachFragmentKeyValue((key, fragment) => fragment._didCommit());
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
const changedKeys = this._changedKeys(data);
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
this._attributes = null;
|
|
113
|
+
assign(this._data, this._inFlightAttributes, data);
|
|
107
114
|
this._inFlightAttributes = null;
|
|
108
115
|
this._updateChangedAttributes();
|
|
109
116
|
|
|
@@ -116,6 +123,19 @@ Object.assign(RecordDataPrototype, {
|
|
|
116
123
|
@namespace DS
|
|
117
124
|
*/
|
|
118
125
|
Store.reopen({
|
|
126
|
+
createRecordDataFor(type, id, lid, storeWrapper) {
|
|
127
|
+
let identifier;
|
|
128
|
+
if (lte('ember-data', '3.13.0')) {
|
|
129
|
+
throw new Error('This version of Ember Data Model Fragments is incompatible with Ember Data Versions below 3.13. See matrix at https://github.com/lytics/ember-data-model-fragments#compatibility for details.');
|
|
130
|
+
}
|
|
131
|
+
if (gte('ember-data', '3.15.0')) {
|
|
132
|
+
identifier = this.identifierCache.getOrCreateRecordIdentifier({ type, id, lid });
|
|
133
|
+
} else {
|
|
134
|
+
identifier = { type, id, clientId: lid };
|
|
135
|
+
}
|
|
136
|
+
return new FragmentRecordData(identifier, storeWrapper);
|
|
137
|
+
},
|
|
138
|
+
|
|
119
139
|
/**
|
|
120
140
|
Create a new fragment that does not yet have an owner record.
|
|
121
141
|
The properties passed to this method are set on the newly created
|
|
@@ -138,16 +158,22 @@ Store.reopen({
|
|
|
138
158
|
*/
|
|
139
159
|
createFragment(modelName, props) {
|
|
140
160
|
assert(`The '${modelName}' model must be a subclass of MF.Fragment`, this.isFragment(modelName));
|
|
141
|
-
|
|
142
|
-
|
|
161
|
+
let internalModel;
|
|
162
|
+
if (gte('ember-data', '3.15.0')) {
|
|
163
|
+
const identifier = this.identifierCache.createIdentifierForNewRecord({ type: modelName });
|
|
164
|
+
internalModel = this._internalModelForResource(identifier);
|
|
165
|
+
} else {
|
|
166
|
+
let identifier = { type: modelName, id: `${Math.random()}`, lid: `${Math.random()}` };
|
|
167
|
+
internalModel = this._internalModelForResource(identifier);
|
|
168
|
+
}
|
|
143
169
|
|
|
144
170
|
// Re-wire the internal model to use the fragment state machine
|
|
145
171
|
internalModel.currentState = FragmentRootState.empty;
|
|
146
172
|
|
|
147
173
|
internalModel._recordData._name = null;
|
|
148
|
-
internalModel._recordData.
|
|
174
|
+
internalModel._recordData._owner = null;
|
|
149
175
|
|
|
150
|
-
internalModel.loadedData
|
|
176
|
+
internalModel.send('loadedData');
|
|
151
177
|
|
|
152
178
|
let fragment = internalModel.getRecord();
|
|
153
179
|
|
|
@@ -191,7 +217,7 @@ Store.reopen({
|
|
|
191
217
|
|
|
192
218
|
if (this.isFragment(normalizedModelName)) {
|
|
193
219
|
return serializerForFragment(owner, normalizedModelName);
|
|
194
|
-
} else
|
|
220
|
+
} else {
|
|
195
221
|
return this._super(...arguments);
|
|
196
222
|
}
|
|
197
223
|
}
|
|
@@ -202,29 +228,14 @@ Store.reopen({
|
|
|
202
228
|
@namespace DS
|
|
203
229
|
*/
|
|
204
230
|
Model.reopen({
|
|
231
|
+
|
|
205
232
|
willDestroy() {
|
|
206
233
|
this._super(...arguments);
|
|
207
234
|
|
|
208
235
|
let internalModel = internalModelFor(this);
|
|
209
|
-
let key, fragment;
|
|
210
236
|
|
|
211
237
|
// destroy the current state
|
|
212
|
-
|
|
213
|
-
fragment = internalModel._recordData._fragments[key];
|
|
214
|
-
if (fragment) {
|
|
215
|
-
fragment.destroy();
|
|
216
|
-
delete internalModel._recordData._fragments[key];
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// destroy the original state
|
|
221
|
-
for (key in internalModel._recordData._data) {
|
|
222
|
-
fragment = internalModel._recordData._data[key];
|
|
223
|
-
if (fragment instanceof Fragment || fragment instanceof FragmentArray) {
|
|
224
|
-
fragment.destroy();
|
|
225
|
-
delete internalModel._recordData._data[key];
|
|
226
|
-
}
|
|
227
|
-
}
|
|
238
|
+
internalModel._recordData.resetFragments();
|
|
228
239
|
}
|
|
229
240
|
});
|
|
230
241
|
|
|
@@ -259,14 +270,6 @@ function decorateMethod(obj, name, fn) {
|
|
|
259
270
|
};
|
|
260
271
|
}
|
|
261
272
|
|
|
262
|
-
function decorateMethodBefore(obj, name, fn) {
|
|
263
|
-
const originalFn = obj[name];
|
|
264
|
-
obj[name] = function() {
|
|
265
|
-
fn.apply(this, arguments);
|
|
266
|
-
return originalFn.apply(this, arguments);
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
273
|
/**
|
|
271
274
|
Override parent method to snapshot fragment attributes before they are
|
|
272
275
|
passed to the `DS.Model#serialize`.
|
|
@@ -281,6 +284,7 @@ decorateMethod(InternalModelPrototype, 'createSnapshot', function createFragment
|
|
|
281
284
|
// If the attribute has a `_createSnapshot` method, invoke it before the
|
|
282
285
|
// snapshot gets passed to the serializer
|
|
283
286
|
if (attr && typeof attr._createSnapshot === 'function') {
|
|
287
|
+
|
|
284
288
|
attrs[key] = attr._createSnapshot();
|
|
285
289
|
}
|
|
286
290
|
});
|
|
@@ -288,39 +292,6 @@ decorateMethod(InternalModelPrototype, 'createSnapshot', function createFragment
|
|
|
288
292
|
return snapshot;
|
|
289
293
|
});
|
|
290
294
|
|
|
291
|
-
decorateMethod(InternalModelPrototype, 'adapterDidError', function adapterDidErrorFragments(returnValue, args) {
|
|
292
|
-
const error = args[0] || Object.create(null);
|
|
293
|
-
this._recordData.eachFragmentKeyValue((key, value) => {
|
|
294
|
-
value._adapterDidError(error);
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
decorateMethod(InternalModelPrototype, 'rollbackAttributes', function rollbackFragments() {
|
|
299
|
-
this._recordData.eachFragmentKeyValue((key, value) => {
|
|
300
|
-
value.rollbackAttributes();
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
decorateMethod(RecordDataPrototype, 'changedAttributes', function changedAttributes(diffData) {
|
|
305
|
-
this.eachFragmentKey((name) => {
|
|
306
|
-
if (name in this._attributes) {
|
|
307
|
-
diffData[name] = [
|
|
308
|
-
diffData[name][0],
|
|
309
|
-
diffData[name][1] ? diffData[name][1]._record : diffData[name][1]
|
|
310
|
-
];
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
return diffData;
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
decorateMethodBefore(RecordDataPrototype, 'willCommit', function willCommit() {
|
|
317
|
-
this.eachFragmentKeyValue((key, fragment) => fragment._flushChangedAttributes());
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
decorateMethodBefore(RecordDataPrototype, 'commitWasRejected', function commitWasRejected() {
|
|
321
|
-
this.eachFragmentKeyValue((key, fragment) => fragment._adapterDidError());
|
|
322
|
-
});
|
|
323
|
-
|
|
324
295
|
/**
|
|
325
296
|
@class JSONSerializer
|
|
326
297
|
@namespace DS
|