@ember-data/model 4.8.0-alpha.3 → 4.8.0-alpha.4
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/addon/-private/attr.js +5 -31
- package/addon/-private/belongs-to.js +74 -18
- package/addon/-private/has-many.js +70 -14
- package/addon/-private/legacy-data-fetch.js +28 -8
- package/addon/-private/legacy-relationships-support.ts +102 -96
- package/addon/-private/many-array.ts +24 -18
- package/addon/-private/model.js +14 -12
- package/addon/-private/notify-changes.ts +1 -1
- package/addon/-private/record-state.ts +6 -12
- package/addon/-private/references/belongs-to.ts +29 -24
- package/addon/-private/references/has-many.ts +45 -35
- package/addon/-private/relationship-meta.ts +3 -23
- package/package.json +11 -7
package/addon/-private/attr.js
CHANGED
|
@@ -11,19 +11,6 @@ import { computedMacroWithOptionalParams } from './util';
|
|
|
11
11
|
@module @ember-data/model
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
function getDefaultValue(record, options, key) {
|
|
15
|
-
if (typeof options.defaultValue === 'function') {
|
|
16
|
-
return options.defaultValue.apply(null, arguments);
|
|
17
|
-
} else {
|
|
18
|
-
let defaultValue = options.defaultValue;
|
|
19
|
-
assert(
|
|
20
|
-
`Non primitive defaultValues are not supported because they are shared between all instances. If you would like to use a complex object as a default value please provide a function that returns the complex object.`,
|
|
21
|
-
typeof defaultValue !== 'object' || defaultValue === null
|
|
22
|
-
);
|
|
23
|
-
return defaultValue;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
14
|
/**
|
|
28
15
|
`attr` defines an attribute on a [Model](/ember-data/release/classes/Model).
|
|
29
16
|
By default, attributes are passed through as-is, however you can specify an
|
|
@@ -123,7 +110,6 @@ function attr(type, options) {
|
|
|
123
110
|
let meta = {
|
|
124
111
|
type: type,
|
|
125
112
|
isAttribute: true,
|
|
126
|
-
kind: 'attribute',
|
|
127
113
|
options: options,
|
|
128
114
|
};
|
|
129
115
|
|
|
@@ -139,20 +125,7 @@ function attr(type, options) {
|
|
|
139
125
|
if (this.isDestroyed || this.isDestroying) {
|
|
140
126
|
return;
|
|
141
127
|
}
|
|
142
|
-
|
|
143
|
-
// TODO hasAttr is not spec'd
|
|
144
|
-
// essentially this is needed because
|
|
145
|
-
// there is a difference between "undefined" meaning never set
|
|
146
|
-
// and "undefined" meaning set to "undefined". In the "key present"
|
|
147
|
-
// case we want to return undefined. In the "key absent" case
|
|
148
|
-
// we want to return getDefaultValue. RecordDataV2 can fix this
|
|
149
|
-
// by providing the attributes blob such that we can make our
|
|
150
|
-
// own determination.
|
|
151
|
-
if (recordData.hasAttr(key)) {
|
|
152
|
-
return recordData.getAttr(key);
|
|
153
|
-
} else {
|
|
154
|
-
return getDefaultValue(this, options, key);
|
|
155
|
-
}
|
|
128
|
+
return recordDataFor(this).getAttr(recordIdentifierFor(this), key);
|
|
156
129
|
},
|
|
157
130
|
set(key, value) {
|
|
158
131
|
if (DEBUG) {
|
|
@@ -166,10 +139,11 @@ function attr(type, options) {
|
|
|
166
139
|
`Attempted to set '${key}' on the deleted record ${recordIdentifierFor(this)}`,
|
|
167
140
|
!this.currentState.isDeleted
|
|
168
141
|
);
|
|
169
|
-
const
|
|
170
|
-
|
|
142
|
+
const identifier = recordIdentifierFor(this);
|
|
143
|
+
const recordData = storeFor(this)._instanceCache.getRecordData(identifier);
|
|
144
|
+
let currentValue = recordData.getAttr(identifier, key);
|
|
171
145
|
if (currentValue !== value) {
|
|
172
|
-
recordData.
|
|
146
|
+
recordData.setAttr(identifier, key, value);
|
|
173
147
|
|
|
174
148
|
if (!this.isValid) {
|
|
175
149
|
const { errors } = this;
|
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
import { assert,
|
|
1
|
+
import { assert, deprecate, warn } from '@ember/debug';
|
|
2
2
|
import { computed } from '@ember/object';
|
|
3
|
+
import { dasherize } from '@ember/string';
|
|
3
4
|
import { DEBUG } from '@glimmer/env';
|
|
4
5
|
|
|
6
|
+
import {
|
|
7
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC,
|
|
8
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
|
|
9
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE,
|
|
10
|
+
} from '@ember-data/private-build-infra/deprecations';
|
|
11
|
+
|
|
5
12
|
import { LEGACY_SUPPORT } from './model';
|
|
6
13
|
import { computedMacroWithOptionalParams } from './util';
|
|
7
14
|
|
|
15
|
+
function normalizeType(type) {
|
|
16
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE && !type) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return dasherize(type);
|
|
21
|
+
}
|
|
8
22
|
/**
|
|
9
23
|
@module @ember-data/model
|
|
10
24
|
*/
|
|
@@ -111,29 +125,71 @@ import { computedMacroWithOptionalParams } from './util';
|
|
|
111
125
|
@return {Ember.computed} relationship
|
|
112
126
|
*/
|
|
113
127
|
function belongsTo(modelName, options) {
|
|
114
|
-
let opts
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
128
|
+
let opts = options;
|
|
129
|
+
let userEnteredModelName = modelName;
|
|
130
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE && (typeof modelName !== 'string' || !modelName.length)) {
|
|
131
|
+
deprecate('belongsTo() must specify the string type of the related resource as the first parameter', false, {
|
|
132
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
133
|
+
for: 'ember-data',
|
|
134
|
+
until: '5.0',
|
|
135
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (typeof modelName === 'object') {
|
|
139
|
+
opts = modelName;
|
|
140
|
+
userEnteredModelName = undefined;
|
|
141
|
+
} else {
|
|
142
|
+
opts = options;
|
|
143
|
+
userEnteredModelName = modelName;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
assert(
|
|
147
|
+
'The first argument to belongsTo must be a string representing a model type key, not an instance of ' +
|
|
148
|
+
typeof userEnteredModelName +
|
|
149
|
+
". E.g., to define a relation to the Person model, use belongsTo('person')",
|
|
150
|
+
typeof userEnteredModelName === 'string' || typeof userEnteredModelName === 'undefined'
|
|
151
|
+
);
|
|
121
152
|
}
|
|
122
153
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
154
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC && (!opts || typeof opts.async !== 'boolean')) {
|
|
155
|
+
opts = opts || {};
|
|
156
|
+
if (!('async' in opts)) {
|
|
157
|
+
opts.async = true;
|
|
158
|
+
}
|
|
159
|
+
deprecate('belongsTo(<type>, <options>) must specify options.async as either `true` or `false`.', false, {
|
|
160
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
161
|
+
for: 'ember-data',
|
|
162
|
+
until: '5.0',
|
|
163
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
assert(`Expected belongsTo options.async to be a boolean`, opts && typeof opts.async === 'boolean');
|
|
167
|
+
}
|
|
129
168
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
opts.
|
|
169
|
+
if (
|
|
170
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE &&
|
|
171
|
+
opts.inverse !== null &&
|
|
172
|
+
(typeof opts.inverse !== 'string' || opts.inverse.length === 0)
|
|
173
|
+
) {
|
|
174
|
+
deprecate(
|
|
175
|
+
'belongsTo(<type>, <options>) must specify options.inverse as either `null` or string type of the related resource.',
|
|
176
|
+
false,
|
|
177
|
+
{
|
|
178
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
179
|
+
for: 'ember-data',
|
|
180
|
+
until: '5.0',
|
|
181
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
} else {
|
|
185
|
+
assert(
|
|
186
|
+
`Expected belongsTo options.inverse to be either null or the string type of the related resource.`,
|
|
187
|
+
opts.inverse === null || (typeof opts.inverse === 'string' && opts.inverse.length > 0)
|
|
188
|
+
);
|
|
133
189
|
}
|
|
134
190
|
|
|
135
191
|
let meta = {
|
|
136
|
-
type: userEnteredModelName,
|
|
192
|
+
type: normalizeType(userEnteredModelName),
|
|
137
193
|
isRelationship: true,
|
|
138
194
|
options: opts,
|
|
139
195
|
kind: 'belongsTo',
|
|
@@ -2,13 +2,30 @@
|
|
|
2
2
|
@module @ember-data/model
|
|
3
3
|
*/
|
|
4
4
|
import { A } from '@ember/array';
|
|
5
|
-
import { assert, inspect } from '@ember/debug';
|
|
5
|
+
import { assert, deprecate, inspect } from '@ember/debug';
|
|
6
6
|
import { computed } from '@ember/object';
|
|
7
|
+
import { dasherize } from '@ember/string';
|
|
7
8
|
import { DEBUG } from '@glimmer/env';
|
|
8
9
|
|
|
10
|
+
import { singularize } from 'ember-inflector';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC,
|
|
14
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
|
|
15
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE,
|
|
16
|
+
} from '@ember-data/private-build-infra/deprecations';
|
|
17
|
+
|
|
9
18
|
import { LEGACY_SUPPORT } from './model';
|
|
10
19
|
import { computedMacroWithOptionalParams } from './util';
|
|
11
20
|
|
|
21
|
+
function normalizeType(type) {
|
|
22
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE && !type) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return singularize(dasherize(type));
|
|
27
|
+
}
|
|
28
|
+
|
|
12
29
|
/**
|
|
13
30
|
`hasMany` is used to define One-To-Many and Many-To-Many
|
|
14
31
|
relationships on a [Model](/ember-data/release/classes/Model).
|
|
@@ -151,21 +168,60 @@ import { computedMacroWithOptionalParams } from './util';
|
|
|
151
168
|
@return {Ember.computed} relationship
|
|
152
169
|
*/
|
|
153
170
|
function hasMany(type, options) {
|
|
154
|
-
if (typeof type
|
|
155
|
-
|
|
156
|
-
|
|
171
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE && (typeof type !== 'string' || !type.length)) {
|
|
172
|
+
deprecate(
|
|
173
|
+
'hasMany(<type>, <options>) must specify the string type of the related resource as the first parameter',
|
|
174
|
+
false,
|
|
175
|
+
{
|
|
176
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
177
|
+
for: 'ember-data',
|
|
178
|
+
until: '5.0',
|
|
179
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
if (typeof type === 'object') {
|
|
183
|
+
options = type;
|
|
184
|
+
type = undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
assert(
|
|
188
|
+
`The first argument to hasMany must be a string representing a model type key, not an instance of ${inspect(
|
|
189
|
+
type
|
|
190
|
+
)}. E.g., to define a relation to the Comment model, use hasMany('comment')`,
|
|
191
|
+
typeof type === 'string' || typeof type === 'undefined'
|
|
192
|
+
);
|
|
157
193
|
}
|
|
158
194
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
195
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC && (!options || typeof options.async !== 'boolean')) {
|
|
196
|
+
options = options || {};
|
|
197
|
+
if (!('async' in options)) {
|
|
198
|
+
options.async = true;
|
|
199
|
+
}
|
|
200
|
+
deprecate('hasMany(<type>, <options>) must specify options.async as either `true` or `false`.', false, {
|
|
201
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
202
|
+
for: 'ember-data',
|
|
203
|
+
until: '5.0',
|
|
204
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
205
|
+
});
|
|
206
|
+
} else {
|
|
207
|
+
assert(`Expected hasMany options.async to be a boolean`, options && typeof options.async === 'boolean');
|
|
208
|
+
}
|
|
165
209
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
options.
|
|
210
|
+
if (
|
|
211
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE &&
|
|
212
|
+
options.inverse !== null &&
|
|
213
|
+
(typeof options.inverse !== 'string' || options.inverse.length === 0)
|
|
214
|
+
) {
|
|
215
|
+
deprecate(
|
|
216
|
+
'hasMany(<type>, <options>) must specify options.inverse as either `null` or string type of the related resource.',
|
|
217
|
+
false,
|
|
218
|
+
{
|
|
219
|
+
id: 'ember-data:deprecate-non-strict-relationships',
|
|
220
|
+
for: 'ember-data',
|
|
221
|
+
until: '5.0',
|
|
222
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
223
|
+
}
|
|
224
|
+
);
|
|
169
225
|
}
|
|
170
226
|
|
|
171
227
|
// Metadata about relationships is stored on the meta of
|
|
@@ -173,7 +229,7 @@ function hasMany(type, options) {
|
|
|
173
229
|
// serialization. Note that `key` is populated lazily
|
|
174
230
|
// the first time the CP is called.
|
|
175
231
|
let meta = {
|
|
176
|
-
type,
|
|
232
|
+
type: normalizeType(type),
|
|
177
233
|
options,
|
|
178
234
|
isRelationship: true,
|
|
179
235
|
kind: 'hasMany',
|
|
@@ -3,7 +3,10 @@ import { DEBUG } from '@glimmer/env';
|
|
|
3
3
|
|
|
4
4
|
import { resolve } from 'rsvp';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
|
|
8
|
+
DEPRECATE_RSVP_PROMISE,
|
|
9
|
+
} from '@ember-data/private-build-infra/deprecations';
|
|
7
10
|
|
|
8
11
|
import { iterateData, normalizeResponseHelper } from './legacy-data-utils';
|
|
9
12
|
|
|
@@ -231,19 +234,36 @@ function ensureRelationshipIsSetToParent(payload, parentIdentifier, store, paren
|
|
|
231
234
|
}
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
function
|
|
235
|
-
return
|
|
237
|
+
function metaIsRelationshipDefinition(meta) {
|
|
238
|
+
return typeof meta._inverseKey === 'function';
|
|
236
239
|
}
|
|
237
240
|
|
|
238
|
-
function
|
|
241
|
+
function inverseForRelationship(store, identifier, key) {
|
|
242
|
+
const definition = store.getSchemaDefinitionService().relationshipsDefinitionFor(identifier)[key];
|
|
243
|
+
if (!definition) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE && metaIsRelationshipDefinition(definition)) {
|
|
248
|
+
const modelClass = store.modelFor(identifier.type);
|
|
249
|
+
return definition._inverseKey(store, modelClass);
|
|
250
|
+
}
|
|
251
|
+
assert(
|
|
252
|
+
`Expected the relationship defintion to specify the inverse type or null.`,
|
|
253
|
+
definition.options?.inverse === null ||
|
|
254
|
+
(typeof definition.options?.inverse === 'string' && definition.options.inverse.length > 0)
|
|
255
|
+
);
|
|
256
|
+
return definition.options.inverse;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function getInverse(store, parentIdentifier, parentRelationship, type) {
|
|
239
260
|
let { name: lhs_relationshipName } = parentRelationship;
|
|
240
261
|
let { type: parentType } = parentIdentifier;
|
|
241
|
-
let inverseKey =
|
|
262
|
+
let inverseKey = inverseForRelationship(store, { type: parentType }, lhs_relationshipName);
|
|
242
263
|
|
|
243
264
|
if (inverseKey) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
} = store._instanceCache._storeWrapper.relationshipsDefinitionFor(type)[inverseKey];
|
|
265
|
+
const definition = store.getSchemaDefinitionService().relationshipsDefinitionFor({ type });
|
|
266
|
+
let { kind } = definition[inverseKey];
|
|
247
267
|
return {
|
|
248
268
|
inverseKey,
|
|
249
269
|
kind,
|