@ember-data/model 4.10.0-alpha.2 → 4.10.0-alpha.21

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.
Files changed (40) hide show
  1. package/addon/{-private/diff-array.ts → -private.js} +41 -13
  2. package/addon/-private.js.map +1 -0
  3. package/addon/has-many-357b6265.js +6277 -0
  4. package/addon/has-many-357b6265.js.map +1 -0
  5. package/addon/index.js +1 -0
  6. package/addon/index.js.map +1 -0
  7. package/addon-main.js +90 -0
  8. package/package.json +43 -15
  9. package/addon/-private/attr.js +0 -162
  10. package/addon/-private/belongs-to.js +0 -268
  11. package/addon/-private/debug/assert-polymorphic-type.js +0 -71
  12. package/addon/-private/deprecated-promise-proxy.ts +0 -76
  13. package/addon/-private/errors.ts +0 -425
  14. package/addon/-private/has-many.js +0 -280
  15. package/addon/-private/index.ts +0 -14
  16. package/addon/-private/legacy-data-fetch.js +0 -395
  17. package/addon/-private/legacy-data-utils.ts +0 -92
  18. package/addon/-private/legacy-relationships-support.ts +0 -774
  19. package/addon/-private/many-array.ts +0 -401
  20. package/addon/-private/model-for-mixin.ts +0 -38
  21. package/addon/-private/model.js +0 -2516
  22. package/addon/-private/notify-changes.ts +0 -72
  23. package/addon/-private/promise-belongs-to.ts +0 -73
  24. package/addon/-private/promise-many-array.ts +0 -425
  25. package/addon/-private/promise-proxy-base.js +0 -4
  26. package/addon/-private/record-state.ts +0 -468
  27. package/addon/-private/references/belongs-to.ts +0 -624
  28. package/addon/-private/references/has-many.ts +0 -669
  29. package/addon/-private/relationship-meta.ts +0 -98
  30. package/addon/-private/util.ts +0 -31
  31. package/addon/index.ts +0 -39
  32. package/blueprints/model/HELP.md +0 -26
  33. package/blueprints/model/files/__root__/__path__/__name__.js +0 -5
  34. package/blueprints/model/index.js +0 -158
  35. package/blueprints/model/native-files/__root__/__path__/__name__.js +0 -5
  36. package/blueprints/model-test/index.js +0 -33
  37. package/blueprints/model-test/mocha-files/__root__/__path__/__test__.js +0 -18
  38. package/blueprints/model-test/mocha-rfc-232-files/__root__/__path__/__test__.js +0 -15
  39. package/blueprints/model-test/qunit-files/__root__/__path__/__test__.js +0 -14
  40. package/index.js +0 -47
@@ -1,2516 +0,0 @@
1
- /**
2
- @module @ember-data/model
3
- */
4
-
5
- import { assert, deprecate, warn } from '@ember/debug';
6
- import EmberObject from '@ember/object';
7
- import { dependentKeyCompat } from '@ember/object/compat';
8
- import { run } from '@ember/runloop';
9
- import { DEBUG } from '@glimmer/env';
10
- import { tracked } from '@glimmer/tracking';
11
- import Ember from 'ember';
12
-
13
- import { resolve } from 'rsvp';
14
-
15
- import { HAS_DEBUG_PACKAGE } from '@ember-data/private-build-infra';
16
- import {
17
- DEPRECATE_EARLY_STATIC,
18
- DEPRECATE_MODEL_REOPEN,
19
- DEPRECATE_NON_EXPLICIT_POLYMORPHISM,
20
- DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
21
- DEPRECATE_SAVE_PROMISE_ACCESS,
22
- } from '@ember-data/private-build-infra/deprecations';
23
- import { recordIdentifierFor, storeFor } from '@ember-data/store';
24
- import { coerceId, recordDataFor } from '@ember-data/store/-private';
25
-
26
- import { deprecatedPromiseObject } from './deprecated-promise-proxy';
27
- import Errors from './errors';
28
- import { LegacySupport } from './legacy-relationships-support';
29
- import notifyChanges from './notify-changes';
30
- import RecordState, { peekTag, tagged } from './record-state';
31
- import { relationshipFromMeta } from './relationship-meta';
32
-
33
- const { changeProperties } = Ember;
34
- export const LEGACY_SUPPORT = new Map();
35
-
36
- export function lookupLegacySupport(record) {
37
- const identifier = recordIdentifierFor(record);
38
- let support = LEGACY_SUPPORT.get(identifier);
39
-
40
- if (!support) {
41
- support = new LegacySupport(record);
42
- LEGACY_SUPPORT.set(identifier, support);
43
- LEGACY_SUPPORT.set(record, support);
44
- }
45
-
46
- return support;
47
- }
48
-
49
- function findPossibleInverses(type, inverseType, name, relationshipsSoFar) {
50
- let possibleRelationships = relationshipsSoFar || [];
51
-
52
- let relationshipMap = inverseType.relationships;
53
- if (!relationshipMap) {
54
- return possibleRelationships;
55
- }
56
-
57
- let relationshipsForType = relationshipMap.get(type.modelName);
58
- let relationships = Array.isArray(relationshipsForType)
59
- ? relationshipsForType.filter((relationship) => {
60
- let optionsForRelationship = relationship.options;
61
-
62
- if (!optionsForRelationship.inverse && optionsForRelationship.inverse !== null) {
63
- return true;
64
- }
65
-
66
- return name === optionsForRelationship.inverse;
67
- })
68
- : null;
69
-
70
- if (relationships) {
71
- possibleRelationships.push.apply(possibleRelationships, relationships);
72
- }
73
-
74
- //Recurse to support polymorphism
75
- if (type.superclass) {
76
- findPossibleInverses(type.superclass, inverseType, name, possibleRelationships);
77
- }
78
-
79
- return possibleRelationships;
80
- }
81
-
82
- /*
83
- * This decorator allows us to lazily compute
84
- * an expensive getter on first-access and thereafter
85
- * never recompute it.
86
- */
87
- function computeOnce(target, key, desc) {
88
- const cache = new WeakMap();
89
- let getter = desc.get;
90
- desc.get = function () {
91
- let meta = cache.get(this);
92
-
93
- if (!meta) {
94
- meta = { hasComputed: false, value: undefined };
95
- cache.set(this, meta);
96
- }
97
-
98
- if (!meta.hasComputed) {
99
- meta.value = getter.call(this);
100
- meta.hasComputed = true;
101
- }
102
-
103
- return meta.value;
104
- };
105
- return desc;
106
- }
107
-
108
- /**
109
- Base class from which Models can be define.
110
-
111
- ```js
112
- import Model, { attr } from '@ember-data/model';
113
-
114
- export default class User extends Model {
115
- @attr name;
116
- }
117
- ```
118
-
119
- @class Model
120
- @public
121
- @extends Ember.EmberObject
122
- */
123
- class Model extends EmberObject {
124
- ___private_notifications;
125
-
126
- init(options = {}) {
127
- if (DEBUG) {
128
- if (!options._secretInit && !options._createProps) {
129
- throw new Error(
130
- 'You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.'
131
- );
132
- }
133
- }
134
- const createProps = options._createProps;
135
- const _secretInit = options._secretInit;
136
- options._createProps = null;
137
- options._secretInit = null;
138
-
139
- let store = (this.store = _secretInit.store);
140
- super.init(options);
141
-
142
- let identity = _secretInit.identifier;
143
- _secretInit.cb(this, _secretInit.recordData, identity, _secretInit.store);
144
-
145
- this.___recordState = DEBUG ? new RecordState(this) : null;
146
-
147
- this.setProperties(createProps);
148
-
149
- let notifications = store._notificationManager;
150
- this.___private_notifications = notifications.subscribe(identity, (identifier, type, key) => {
151
- notifyChanges(identifier, type, key, this, store);
152
- });
153
- }
154
-
155
- destroy() {
156
- const identifier = recordIdentifierFor(this);
157
- this.___recordState?.destroy();
158
- const store = storeFor(this);
159
- store._notificationManager.unsubscribe(this.___private_notifications);
160
- // Legacy behavior is to notify the relationships on destroy
161
- // such that they "clear". It's uncertain this behavior would
162
- // be good for a new model paradigm, likely cheaper and safer
163
- // to simply not notify, for this reason the store does not itself
164
- // notify individual changes once the delete has been signaled,
165
- // this decision is left to model instances.
166
-
167
- this.eachRelationship((key, meta) => {
168
- if (meta.kind === 'belongsTo') {
169
- this.notifyPropertyChange(key);
170
- }
171
- });
172
- LEGACY_SUPPORT.get(this)?.destroy();
173
- LEGACY_SUPPORT.delete(this);
174
- LEGACY_SUPPORT.delete(identifier);
175
-
176
- super.destroy();
177
- }
178
-
179
- /**
180
- If this property is `true` the record is in the `empty`
181
- state. Empty is the first state all records enter after they have
182
- been created. Most records created by the store will quickly
183
- transition to the `loading` state if data needs to be fetched from
184
- the server or the `created` state if the record is created on the
185
- client. A record can also enter the empty state if the adapter is
186
- unable to locate the record.
187
-
188
- @property isEmpty
189
- @public
190
- @type {Boolean}
191
- @readOnly
192
- */
193
- @dependentKeyCompat
194
- get isEmpty() {
195
- return this.currentState.isEmpty;
196
- }
197
-
198
- /**
199
- If this property is `true` the record is in the `loading` state. A
200
- record enters this state when the store asks the adapter for its
201
- data. It remains in this state until the adapter provides the
202
- requested data.
203
-
204
- @property isLoading
205
- @public
206
- @type {Boolean}
207
- @readOnly
208
- */
209
- @dependentKeyCompat
210
- get isLoading() {
211
- return this.currentState.isLoading;
212
- }
213
-
214
- /**
215
- If this property is `true` the record is in the `loaded` state. A
216
- record enters this state when its data is populated. Most of a
217
- record's lifecycle is spent inside substates of the `loaded`
218
- state.
219
-
220
- Example
221
-
222
- ```javascript
223
- let record = store.createRecord('model');
224
- record.isLoaded; // true
225
-
226
- store.findRecord('model', 1).then(function(model) {
227
- model.isLoaded; // true
228
- });
229
- ```
230
-
231
- @property isLoaded
232
- @public
233
- @type {Boolean}
234
- @readOnly
235
- */
236
- @dependentKeyCompat
237
- get isLoaded() {
238
- return this.currentState.isLoaded;
239
- }
240
-
241
- /**
242
- If this property is `true` the record is in the `dirty` state. The
243
- record has local changes that have not yet been saved by the
244
- adapter. This includes records that have been created (but not yet
245
- saved) or deleted.
246
-
247
- Example
248
-
249
- ```javascript
250
- let record = store.createRecord('model');
251
- record.hasDirtyAttributes; // true
252
-
253
- store.findRecord('model', 1).then(function(model) {
254
- model.hasDirtyAttributes; // false
255
- model.set('foo', 'some value');
256
- model.hasDirtyAttributes; // true
257
- });
258
- ```
259
-
260
- @since 1.13.0
261
- @property hasDirtyAttributes
262
- @public
263
- @type {Boolean}
264
- @readOnly
265
- */
266
- @dependentKeyCompat
267
- get hasDirtyAttributes() {
268
- return this.currentState.isDirty;
269
- }
270
-
271
- /**
272
- If this property is `true` the record is in the `saving` state. A
273
- record enters the saving state when `save` is called, but the
274
- adapter has not yet acknowledged that the changes have been
275
- persisted to the backend.
276
-
277
- Example
278
-
279
- ```javascript
280
- let record = store.createRecord('model');
281
- record.isSaving; // false
282
- let promise = record.save();
283
- record.isSaving; // true
284
- promise.then(function() {
285
- record.isSaving; // false
286
- });
287
- ```
288
-
289
- @property isSaving
290
- @public
291
- @type {Boolean}
292
- @readOnly
293
- */
294
- @dependentKeyCompat
295
- get isSaving() {
296
- return this.currentState.isSaving;
297
- }
298
-
299
- /**
300
- If this property is `true` the record is in the `deleted` state
301
- and has been marked for deletion. When `isDeleted` is true and
302
- `hasDirtyAttributes` is true, the record is deleted locally but the deletion
303
- was not yet persisted. When `isSaving` is true, the change is
304
- in-flight. When both `hasDirtyAttributes` and `isSaving` are false, the
305
- change has persisted.
306
-
307
- Example
308
-
309
- ```javascript
310
- let record = store.createRecord('model');
311
- record.isDeleted; // false
312
- record.deleteRecord();
313
-
314
- // Locally deleted
315
- record.isDeleted; // true
316
- record.hasDirtyAttributes; // true
317
- record.isSaving; // false
318
-
319
- // Persisting the deletion
320
- let promise = record.save();
321
- record.isDeleted; // true
322
- record.isSaving; // true
323
-
324
- // Deletion Persisted
325
- promise.then(function() {
326
- record.isDeleted; // true
327
- record.isSaving; // false
328
- record.hasDirtyAttributes; // false
329
- });
330
- ```
331
-
332
- @property isDeleted
333
- @public
334
- @type {Boolean}
335
- @readOnly
336
- */
337
- @dependentKeyCompat
338
- get isDeleted() {
339
- return this.currentState.isDeleted;
340
- }
341
-
342
- /**
343
- If this property is `true` the record is in the `new` state. A
344
- record will be in the `new` state when it has been created on the
345
- client and the adapter has not yet report that it was successfully
346
- saved.
347
-
348
- Example
349
-
350
- ```javascript
351
- let record = store.createRecord('model');
352
- record.isNew; // true
353
-
354
- record.save().then(function(model) {
355
- model.isNew; // false
356
- });
357
- ```
358
-
359
- @property isNew
360
- @public
361
- @type {Boolean}
362
- @readOnly
363
- */
364
- @dependentKeyCompat
365
- get isNew() {
366
- return this.currentState.isNew;
367
- }
368
-
369
- /**
370
- If this property is `true` the record is in the `valid` state.
371
-
372
- A record will be in the `valid` state when the adapter did not report any
373
- server-side validation failures.
374
-
375
- @property isValid
376
- @public
377
- @type {Boolean}
378
- @readOnly
379
- */
380
- @dependentKeyCompat
381
- get isValid() {
382
- return this.currentState.isValid;
383
- }
384
-
385
- /**
386
- If the record is in the dirty state this property will report what
387
- kind of change has caused it to move into the dirty
388
- state. Possible values are:
389
-
390
- - `created` The record has been created by the client and not yet saved to the adapter.
391
- - `updated` The record has been updated by the client and not yet saved to the adapter.
392
- - `deleted` The record has been deleted by the client and not yet saved to the adapter.
393
-
394
- Example
395
-
396
- ```javascript
397
- let record = store.createRecord('model');
398
- record.dirtyType; // 'created'
399
- ```
400
-
401
- @property dirtyType
402
- @public
403
- @type {String}
404
- @readOnly
405
- */
406
- @dependentKeyCompat
407
- get dirtyType() {
408
- return this.currentState.dirtyType;
409
- }
410
-
411
- /**
412
- If `true` the adapter reported that it was unable to save local
413
- changes to the backend for any reason other than a server-side
414
- validation error.
415
-
416
- Example
417
-
418
- ```javascript
419
- record.isError; // false
420
- record.set('foo', 'valid value');
421
- record.save().then(null, function() {
422
- record.isError; // true
423
- });
424
- ```
425
-
426
- @property isError
427
- @public
428
- @type {Boolean}
429
- @readOnly
430
- */
431
- @dependentKeyCompat
432
- get isError() {
433
- return this.currentState.isError;
434
- }
435
- set isError(v) {
436
- if (DEBUG) {
437
- throw new Error(`isError is not directly settable`);
438
- }
439
- }
440
-
441
- /**
442
- If `true` the store is attempting to reload the record from the adapter.
443
-
444
- Example
445
-
446
- ```javascript
447
- record.isReloading; // false
448
- record.reload();
449
- record.isReloading; // true
450
- ```
451
-
452
- @property isReloading
453
- @public
454
- @type {Boolean}
455
- @readOnly
456
- */
457
- @tracked isReloading = false;
458
-
459
- /**
460
- All ember models have an id property. This is an identifier
461
- managed by an external source. These are always coerced to be
462
- strings before being used internally. Note when declaring the
463
- attributes for a model it is an error to declare an id
464
- attribute.
465
-
466
- ```javascript
467
- let record = store.createRecord('model');
468
- record.id; // null
469
-
470
- store.findRecord('model', 1).then(function(model) {
471
- model.id; // '1'
472
- });
473
- ```
474
-
475
- @property id
476
- @public
477
- @type {String}
478
- */
479
- @tagged
480
- get id() {
481
- // this guard exists, because some dev-only deprecation code
482
- // (addListener via validatePropertyInjections) invokes toString before the
483
- // object is real.
484
- if (DEBUG) {
485
- try {
486
- return recordIdentifierFor(this).id;
487
- } catch {
488
- return void 0;
489
- }
490
- }
491
- return recordIdentifierFor(this).id;
492
- }
493
- set id(id) {
494
- const normalizedId = coerceId(id);
495
- const identifier = recordIdentifierFor(this);
496
- let didChange = normalizedId !== identifier.id;
497
- assert(
498
- `Cannot set ${identifier.type} record's id to ${id}, because id is already ${identifier.id}`,
499
- !didChange || identifier.id === null
500
- );
501
-
502
- if (normalizedId !== null && didChange) {
503
- this.store._instanceCache.setRecordId(identifier, normalizedId);
504
- this.store._notificationManager.notify(identifier, 'identity');
505
- }
506
- }
507
-
508
- toString() {
509
- return `<model::${this.constructor.modelName}:${this.id}>`;
510
- }
511
-
512
- /**
513
- @property currentState
514
- @private
515
- @type {Object}
516
- */
517
- // TODO we can probably make this a computeOnce
518
- // we likely do not need to notify the currentState root anymore
519
- @tagged
520
- get currentState() {
521
- // descriptors are called with the wrong `this` context during mergeMixins
522
- // when using legacy/classic ember classes. Basically: lazy in prod and eager in dev.
523
- // so we do this to try to steer folks to the nicer "dont user currentState"
524
- // error.
525
- if (!DEBUG) {
526
- if (!this.___recordState) {
527
- this.___recordState = new RecordState(this);
528
- }
529
- }
530
- return this.___recordState;
531
- }
532
- set currentState(_v) {
533
- throw new Error('cannot set currentState');
534
- }
535
-
536
- /**
537
- The store service instance which created this record instance
538
-
539
- @property store
540
- @public
541
- */
542
-
543
- /**
544
- When the record is in the `invalid` state this object will contain
545
- any errors returned by the adapter. When present the errors hash
546
- contains keys corresponding to the invalid property names
547
- and values which are arrays of Javascript objects with two keys:
548
-
549
- - `message` A string containing the error message from the backend
550
- - `attribute` The name of the property associated with this error message
551
-
552
- ```javascript
553
- record.errors.length; // 0
554
- record.set('foo', 'invalid value');
555
- record.save().catch(function() {
556
- record.errors.foo;
557
- // [{message: 'foo should be a number.', attribute: 'foo'}]
558
- });
559
- ```
560
-
561
- The `errors` property is useful for displaying error messages to
562
- the user.
563
-
564
- ```handlebars
565
- <label>Username: <Input @value={{@model.username}} /> </label>
566
- {{#each @model.errors.username as |error|}}
567
- <div class="error">
568
- {{error.message}}
569
- </div>
570
- {{/each}}
571
- <label>Email: <Input @value={{@model.email}} /> </label>
572
- {{#each @model.errors.email as |error|}}
573
- <div class="error">
574
- {{error.message}}
575
- </div>
576
- {{/each}}
577
- ```
578
-
579
-
580
- You can also access the special `messages` property on the error
581
- object to get an array of all the error strings.
582
-
583
- ```handlebars
584
- {{#each @model.errors.messages as |message|}}
585
- <div class="error">
586
- {{message}}
587
- </div>
588
- {{/each}}
589
- ```
590
-
591
- @property errors
592
- @public
593
- @type {Errors}
594
- */
595
- @computeOnce
596
- get errors() {
597
- let errors = Errors.create({ __record: this });
598
- this.currentState.updateInvalidErrors(errors);
599
- return errors;
600
- }
601
-
602
- /**
603
- This property holds the `AdapterError` object with which
604
- last adapter operation was rejected.
605
-
606
- @property adapterError
607
- @public
608
- @type {AdapterError}
609
- */
610
- @dependentKeyCompat
611
- get adapterError() {
612
- return this.currentState.adapterError;
613
- }
614
- set adapterError(v) {
615
- throw new Error(`adapterError is not directly settable`);
616
- }
617
-
618
- /**
619
- Create a JSON representation of the record, using the serialization
620
- strategy of the store's adapter.
621
-
622
- `serialize` takes an optional hash as a parameter, currently
623
- supported options are:
624
-
625
- - `includeId`: `true` if the record's ID should be included in the
626
- JSON representation.
627
-
628
- @method serialize
629
- @public
630
- @param {Object} options
631
- @return {Object} an object whose values are primitive JSON values only
632
- */
633
- serialize(options) {
634
- return storeFor(this)._instanceCache.createSnapshot(recordIdentifierFor(this)).serialize(options);
635
- }
636
-
637
- /*
638
- We hook the default implementation to ensure
639
- our tagged properties are properly notified
640
- as well. We still super for everything because
641
- sync observers require a direct call occuring
642
- to trigger their flush. We wouldn't need to
643
- super in 4.0+ where sync observers are removed.
644
- */
645
- notifyPropertyChange(key) {
646
- let tag = peekTag(this, key);
647
- if (tag) {
648
- tag.notify();
649
- }
650
- super.notifyPropertyChange(key);
651
- }
652
-
653
- /**
654
- Marks the record as deleted but does not save it. You must call
655
- `save` afterwards if you want to persist it. You might use this
656
- method if you want to allow the user to still `rollbackAttributes()`
657
- after a delete was made.
658
-
659
- Example
660
-
661
- ```app/controllers/model/delete.js
662
- import Controller from '@ember/controller';
663
- import { action } from '@ember/object';
664
-
665
- export default class ModelDeleteController extends Controller {
666
- @action
667
- softDelete() {
668
- this.model.deleteRecord();
669
- }
670
-
671
- @action
672
- confirm() {
673
- this.model.save();
674
- }
675
-
676
- @action
677
- undo() {
678
- this.model.rollbackAttributes();
679
- }
680
- }
681
- ```
682
-
683
- @method deleteRecord
684
- @public
685
- */
686
- deleteRecord() {
687
- // ensure we've populated currentState prior to deleting a new record
688
- if (this.currentState) {
689
- storeFor(this).deleteRecord(this);
690
- }
691
- }
692
-
693
- /**
694
- Same as `deleteRecord`, but saves the record immediately.
695
-
696
- Example
697
-
698
- ```app/controllers/model/delete.js
699
- import Controller from '@ember/controller';
700
- import { action } from '@ember/object';
701
-
702
- export default class ModelDeleteController extends Controller {
703
- @action
704
- delete() {
705
- this.model.destroyRecord().then(function() {
706
- this.transitionToRoute('model.index');
707
- });
708
- }
709
- }
710
- ```
711
-
712
- If you pass an object on the `adapterOptions` property of the options
713
- argument it will be passed to your adapter via the snapshot
714
-
715
- ```js
716
- record.destroyRecord({ adapterOptions: { subscribe: false } });
717
- ```
718
-
719
- ```app/adapters/post.js
720
- import MyCustomAdapter from './custom-adapter';
721
-
722
- export default class PostAdapter extends MyCustomAdapter {
723
- deleteRecord(store, type, snapshot) {
724
- if (snapshot.adapterOptions.subscribe) {
725
- // ...
726
- }
727
- // ...
728
- }
729
- }
730
- ```
731
-
732
- @method destroyRecord
733
- @public
734
- @param {Object} options
735
- @return {Promise} a promise that will be resolved when the adapter returns
736
- successfully or rejected if the adapter returns with an error.
737
- */
738
- destroyRecord(options) {
739
- const { isNew } = this.currentState;
740
- this.deleteRecord();
741
- if (isNew) {
742
- return resolve(this);
743
- }
744
- return this.save(options).then((_) => {
745
- run(() => {
746
- this.unloadRecord();
747
- });
748
- return this;
749
- });
750
- }
751
-
752
- /**
753
- Unloads the record from the store. This will not send a delete request
754
- to your server, it just unloads the record from memory.
755
-
756
- @method unloadRecord
757
- @public
758
- */
759
- unloadRecord() {
760
- if (this.currentState.isNew && (this.isDestroyed || this.isDestroying)) {
761
- return;
762
- }
763
- storeFor(this).unloadRecord(this);
764
- }
765
-
766
- /**
767
- @method _notifyProperties
768
- @private
769
- */
770
- _notifyProperties(keys) {
771
- // changeProperties defers notifications until after the delegate
772
- // and protects with a try...finally block
773
- // previously used begin...endPropertyChanges but this is private API
774
- changeProperties(() => {
775
- let key;
776
- for (let i = 0, length = keys.length; i < length; i++) {
777
- key = keys[i];
778
- this.notifyPropertyChange(key);
779
- }
780
- });
781
- }
782
-
783
- /**
784
- Returns an object, whose keys are changed properties, and value is
785
- an [oldProp, newProp] array.
786
-
787
- The array represents the diff of the canonical state with the local state
788
- of the model. Note: if the model is created locally, the canonical state is
789
- empty since the adapter hasn't acknowledged the attributes yet:
790
-
791
- Example
792
-
793
- ```app/models/mascot.js
794
- import Model, { attr } from '@ember-data/model';
795
-
796
- export default class MascotModel extends Model {
797
- @attr('string') name;
798
- @attr('boolean', {
799
- defaultValue: false
800
- })
801
- isAdmin;
802
- }
803
- ```
804
-
805
- ```javascript
806
- let mascot = store.createRecord('mascot');
807
-
808
- mascot.changedAttributes(); // {}
809
-
810
- mascot.set('name', 'Tomster');
811
- mascot.changedAttributes(); // { name: [undefined, 'Tomster'] }
812
-
813
- mascot.set('isAdmin', true);
814
- mascot.changedAttributes(); // { isAdmin: [undefined, true], name: [undefined, 'Tomster'] }
815
-
816
- mascot.save().then(function() {
817
- mascot.changedAttributes(); // {}
818
-
819
- mascot.set('isAdmin', false);
820
- mascot.changedAttributes(); // { isAdmin: [true, false] }
821
- });
822
- ```
823
-
824
- @method changedAttributes
825
- @public
826
- @return {Object} an object, whose keys are changed properties,
827
- and value is an [oldProp, newProp] array.
828
- */
829
- changedAttributes() {
830
- return recordDataFor(this).changedAttrs(recordIdentifierFor(this));
831
- }
832
-
833
- /**
834
- If the model `hasDirtyAttributes` this function will discard any unsaved
835
- changes. If the model `isNew` it will be removed from the store.
836
-
837
- Example
838
-
839
- ```javascript
840
- record.name; // 'Untitled Document'
841
- record.set('name', 'Doc 1');
842
- record.name; // 'Doc 1'
843
- record.rollbackAttributes();
844
- record.name; // 'Untitled Document'
845
- ```
846
-
847
- @since 1.13.0
848
- @method rollbackAttributes
849
- @public
850
- */
851
- rollbackAttributes() {
852
- const { currentState } = this;
853
- const { isNew } = currentState;
854
-
855
- storeFor(this)._join(() => {
856
- recordDataFor(this).rollbackAttrs(recordIdentifierFor(this));
857
- this.errors.clear();
858
- currentState.cleanErrorRequests();
859
- if (isNew) {
860
- this.unloadRecord();
861
- }
862
- });
863
- }
864
-
865
- /**
866
- @method _createSnapshot
867
- @private
868
- */
869
- // TODO @deprecate in favor of a public API or examples of how to test successfully
870
- _createSnapshot() {
871
- return storeFor(this)._instanceCache.createSnapshot(recordIdentifierFor(this));
872
- }
873
-
874
- /**
875
- Save the record and persist any changes to the record to an
876
- external source via the adapter.
877
-
878
- Example
879
-
880
- ```javascript
881
- record.set('name', 'Tomster');
882
- record.save().then(function() {
883
- // Success callback
884
- }, function() {
885
- // Error callback
886
- });
887
- ```
888
-
889
- If you pass an object using the `adapterOptions` property of the options
890
- argument it will be passed to your adapter via the snapshot.
891
-
892
- ```js
893
- record.save({ adapterOptions: { subscribe: false } });
894
- ```
895
-
896
- ```app/adapters/post.js
897
- import MyCustomAdapter from './custom-adapter';
898
-
899
- export default class PostAdapter extends MyCustomAdapter {
900
- updateRecord(store, type, snapshot) {
901
- if (snapshot.adapterOptions.subscribe) {
902
- // ...
903
- }
904
- // ...
905
- }
906
- }
907
- ```
908
-
909
- @method save
910
- @public
911
- @param {Object} options
912
- @return {Promise} a promise that will be resolved when the adapter returns
913
- successfully or rejected if the adapter returns with an error.
914
- */
915
- save(options) {
916
- let promise;
917
-
918
- if (this.currentState.isNew && this.currentState.isDeleted) {
919
- promise = resolve(this);
920
- } else {
921
- promise = storeFor(this).saveRecord(this, options);
922
- }
923
-
924
- if (DEPRECATE_SAVE_PROMISE_ACCESS) {
925
- return deprecatedPromiseObject(promise);
926
- }
927
-
928
- return promise;
929
- }
930
-
931
- /**
932
- Reload the record from the adapter.
933
-
934
- This will only work if the record has already finished loading.
935
-
936
- Example
937
-
938
- ```app/controllers/model/view.js
939
- import Controller from '@ember/controller';
940
- import { action } from '@ember/object';
941
-
942
- export default class ViewController extends Controller {
943
- @action
944
- reload() {
945
- this.model.reload().then(function(model) {
946
- // do something with the reloaded model
947
- });
948
- }
949
- }
950
- ```
951
-
952
- @method reload
953
- @public
954
- @param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter request
955
-
956
- @return {Promise} a promise that will be resolved with the record when the
957
- adapter returns successfully or rejected if the adapter returns
958
- with an error.
959
- */
960
- reload(_options) {
961
- let options = {};
962
-
963
- if (typeof _options === 'object' && _options !== null && _options.adapterOptions) {
964
- options.adapterOptions = _options.adapterOptions;
965
- }
966
-
967
- options.isReloading = true;
968
- let identifier = recordIdentifierFor(this);
969
- assert(`You cannot reload a record without an ID`, identifier.id);
970
- this.isReloading = true;
971
- const promise = storeFor(this)
972
- ._fetchManager.scheduleFetch(identifier, options)
973
- .then(() => this)
974
- .finally(() => {
975
- this.isReloading = false;
976
- });
977
-
978
- if (DEPRECATE_SAVE_PROMISE_ACCESS) {
979
- return deprecatedPromiseObject(promise);
980
- }
981
- return promise;
982
- }
983
-
984
- attr() {
985
- assert(
986
- 'The `attr` method is not available on Model, a Snapshot was probably expected. Are you passing a Model instead of a Snapshot to your serializer?',
987
- false
988
- );
989
- }
990
-
991
- /**
992
- Get the reference for the specified belongsTo relationship.
993
-
994
- Example
995
-
996
- ```app/models/blog.js
997
- import Model, { belongsTo } from '@ember-data/model';
998
-
999
- export default class BlogModel extends Model {
1000
- @belongsTo('user', { async: true, inverse: null }) user;
1001
- }
1002
- ```
1003
-
1004
- ```javascript
1005
- let blog = store.push({
1006
- data: {
1007
- type: 'blog',
1008
- id: 1,
1009
- relationships: {
1010
- user: {
1011
- data: { type: 'user', id: 1 }
1012
- }
1013
- }
1014
- }
1015
- });
1016
- let userRef = blog.belongsTo('user');
1017
-
1018
- // check if the user relationship is loaded
1019
- let isLoaded = userRef.value() !== null;
1020
-
1021
- // get the record of the reference (null if not yet available)
1022
- let user = userRef.value();
1023
-
1024
- // get the identifier of the reference
1025
- if (userRef.remoteType() === "id") {
1026
- let id = userRef.id();
1027
- } else if (userRef.remoteType() === "link") {
1028
- let link = userRef.link();
1029
- }
1030
-
1031
- // load user (via store.findRecord or store.findBelongsTo)
1032
- userRef.load().then(...)
1033
-
1034
- // or trigger a reload
1035
- userRef.reload().then(...)
1036
-
1037
- // provide data for reference
1038
- userRef.push({
1039
- type: 'user',
1040
- id: 1,
1041
- attributes: {
1042
- username: "@user"
1043
- }
1044
- }).then(function(user) {
1045
- userRef.value() === user;
1046
- });
1047
- ```
1048
-
1049
- @method belongsTo
1050
- @public
1051
- @param {String} name of the relationship
1052
- @since 2.5.0
1053
- @return {BelongsToReference} reference for this relationship
1054
- */
1055
- belongsTo(name) {
1056
- return lookupLegacySupport(this).referenceFor('belongsTo', name);
1057
- }
1058
-
1059
- /**
1060
- Get the reference for the specified hasMany relationship.
1061
-
1062
- Example
1063
-
1064
- ```app/models/blog.js
1065
- import Model, { hasMany } from '@ember-data/model';
1066
-
1067
- export default class BlogModel extends Model {
1068
- @hasMany('comment', { async: true, inverse: null }) comments;
1069
- }
1070
-
1071
- let blog = store.push({
1072
- data: {
1073
- type: 'blog',
1074
- id: 1,
1075
- relationships: {
1076
- comments: {
1077
- data: [
1078
- { type: 'comment', id: 1 },
1079
- { type: 'comment', id: 2 }
1080
- ]
1081
- }
1082
- }
1083
- }
1084
- });
1085
- let commentsRef = blog.hasMany('comments');
1086
-
1087
- // check if the comments are loaded already
1088
- let isLoaded = commentsRef.value() !== null;
1089
-
1090
- // get the records of the reference (null if not yet available)
1091
- let comments = commentsRef.value();
1092
-
1093
- // get the identifier of the reference
1094
- if (commentsRef.remoteType() === "ids") {
1095
- let ids = commentsRef.ids();
1096
- } else if (commentsRef.remoteType() === "link") {
1097
- let link = commentsRef.link();
1098
- }
1099
-
1100
- // load comments (via store.findMany or store.findHasMany)
1101
- commentsRef.load().then(...)
1102
-
1103
- // or trigger a reload
1104
- commentsRef.reload().then(...)
1105
-
1106
- // provide data for reference
1107
- commentsRef.push([{ type: 'comment', id: 1 }, { type: 'comment', id: 2 }]).then(function(comments) {
1108
- commentsRef.value() === comments;
1109
- });
1110
- ```
1111
-
1112
- @method hasMany
1113
- @public
1114
- @param {String} name of the relationship
1115
- @since 2.5.0
1116
- @return {HasManyReference} reference for this relationship
1117
- */
1118
- hasMany(name) {
1119
- return lookupLegacySupport(this).referenceFor('hasMany', name);
1120
- }
1121
-
1122
- /**
1123
- Given a callback, iterates over each of the relationships in the model,
1124
- invoking the callback with the name of each relationship and its relationship
1125
- descriptor.
1126
-
1127
-
1128
- The callback method you provide should have the following signature (all
1129
- parameters are optional):
1130
-
1131
- ```javascript
1132
- function(name, descriptor);
1133
- ```
1134
-
1135
- - `name` the name of the current property in the iteration
1136
- - `descriptor` the meta object that describes this relationship
1137
-
1138
- The relationship descriptor argument is an object with the following properties.
1139
-
1140
- - **key** <span class="type">String</span> the name of this relationship on the Model
1141
- - **kind** <span class="type">String</span> "hasMany" or "belongsTo"
1142
- - **options** <span class="type">Object</span> the original options hash passed when the relationship was declared
1143
- - **parentType** <span class="type">Model</span> the type of the Model that owns this relationship
1144
- - **type** <span class="type">String</span> the type name of the related Model
1145
-
1146
- Note that in addition to a callback, you can also pass an optional target
1147
- object that will be set as `this` on the context.
1148
-
1149
- Example
1150
-
1151
- ```app/serializers/application.js
1152
- import JSONSerializer from '@ember-data/serializer/json';
1153
-
1154
- export default class ApplicationSerializer extends JSONSerializer {
1155
- serialize(record, options) {
1156
- let json = {};
1157
-
1158
- record.eachRelationship(function(name, descriptor) {
1159
- if (descriptor.kind === 'hasMany') {
1160
- let serializedHasManyName = name.toUpperCase() + '_IDS';
1161
- json[serializedHasManyName] = record.get(name).map(r => r.id);
1162
- }
1163
- });
1164
-
1165
- return json;
1166
- }
1167
- }
1168
- ```
1169
-
1170
- @method eachRelationship
1171
- @public
1172
- @param {Function} callback the callback to invoke
1173
- @param {any} binding the value to which the callback's `this` should be bound
1174
- */
1175
- eachRelationship(callback, binding) {
1176
- this.constructor.eachRelationship(callback, binding);
1177
- }
1178
-
1179
- relationshipFor(name) {
1180
- return this.constructor.relationshipsByName.get(name);
1181
- }
1182
-
1183
- inverseFor(key) {
1184
- return this.constructor.inverseFor(key, storeFor(this));
1185
- }
1186
-
1187
- eachAttribute(callback, binding) {
1188
- this.constructor.eachAttribute(callback, binding);
1189
- }
1190
-
1191
- static isModel = true;
1192
-
1193
- /**
1194
- Create should only ever be called by the store. To create an instance of a
1195
- `Model` in a dirty state use `store.createRecord`.
1196
-
1197
- To create instances of `Model` in a clean state, use `store.push`
1198
-
1199
- @method create
1200
- @private
1201
- @static
1202
- */
1203
-
1204
- /**
1205
- Represents the model's class name as a string. This can be used to look up the model's class name through
1206
- `Store`'s modelFor method.
1207
-
1208
- `modelName` is generated for you by Ember Data. It will be a lowercased, dasherized string.
1209
- For example:
1210
-
1211
- ```javascript
1212
- store.modelFor('post').modelName; // 'post'
1213
- store.modelFor('blog-post').modelName; // 'blog-post'
1214
- ```
1215
-
1216
- The most common place you'll want to access `modelName` is in your serializer's `payloadKeyFromModelName` method. For example, to change payload
1217
- keys to underscore (instead of dasherized), you might use the following code:
1218
-
1219
- ```javascript
1220
- import RESTSerializer from '@ember-data/serializer/rest';
1221
- import { underscore } from '<app-name>/utils/string-utils';
1222
-
1223
- export default const PostSerializer = RESTSerializer.extend({
1224
- payloadKeyFromModelName(modelName) {
1225
- return underscore(modelName);
1226
- }
1227
- });
1228
- ```
1229
- @property modelName
1230
- @public
1231
- @type String
1232
- @readonly
1233
- @static
1234
- */
1235
- static modelName = null;
1236
-
1237
- /*
1238
- These class methods below provide relationship
1239
- introspection abilities about relationships.
1240
-
1241
- A note about the computed properties contained here:
1242
-
1243
- **These properties are effectively sealed once called for the first time.**
1244
- To avoid repeatedly doing expensive iteration over a model's fields, these
1245
- values are computed once and then cached for the remainder of the runtime of
1246
- your application.
1247
-
1248
- If your application needs to modify a class after its initial definition
1249
- (for example, using `reopen()` to add additional attributes), make sure you
1250
- do it before using your model with the store, which uses these properties
1251
- extensively.
1252
- */
1253
-
1254
- /**
1255
- For a given relationship name, returns the model type of the relationship.
1256
-
1257
- For example, if you define a model like this:
1258
-
1259
- ```app/models/post.js
1260
- import Model, { hasMany } from '@ember-data/model';
1261
-
1262
- export default class PostModel extends Model {
1263
- @hasMany('comment') comments;
1264
- }
1265
- ```
1266
-
1267
- Calling `store.modelFor('post').typeForRelationship('comments', store)` will return `Comment`.
1268
-
1269
- @method typeForRelationship
1270
- @public
1271
- @static
1272
- @param {String} name the name of the relationship
1273
- @param {store} store an instance of Store
1274
- @return {Model} the type of the relationship, or undefined
1275
- */
1276
- static typeForRelationship(name, store) {
1277
- if (DEPRECATE_EARLY_STATIC) {
1278
- deprecate(
1279
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1280
- this.modelName,
1281
- {
1282
- id: 'ember-data:deprecate-early-static',
1283
- for: 'ember-data',
1284
- until: '5.0',
1285
- since: { available: '4.7', enabled: '4.7' },
1286
- }
1287
- );
1288
- } else {
1289
- assert(
1290
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1291
- this.modelName
1292
- );
1293
- }
1294
- let relationship = this.relationshipsByName.get(name);
1295
- return relationship && store.modelFor(relationship.type);
1296
- }
1297
-
1298
- @computeOnce
1299
- static get inverseMap() {
1300
- if (DEPRECATE_EARLY_STATIC) {
1301
- deprecate(
1302
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1303
- this.modelName,
1304
- {
1305
- id: 'ember-data:deprecate-early-static',
1306
- for: 'ember-data',
1307
- until: '5.0',
1308
- since: { available: '4.7', enabled: '4.7' },
1309
- }
1310
- );
1311
- } else {
1312
- assert(
1313
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1314
- this.modelName
1315
- );
1316
- }
1317
- return Object.create(null);
1318
- }
1319
-
1320
- /**
1321
- Find the relationship which is the inverse of the one asked for.
1322
-
1323
- For example, if you define models like this:
1324
-
1325
- ```app/models/post.js
1326
- import Model, { hasMany } from '@ember-data/model';
1327
-
1328
- export default class PostModel extends Model {
1329
- @hasMany('message') comments;
1330
- }
1331
- ```
1332
-
1333
- ```app/models/message.js
1334
- import Model, { belongsTo } from '@ember-data/model';
1335
-
1336
- export default class MessageModel extends Model {
1337
- @belongsTo('post') owner;
1338
- }
1339
- ```
1340
-
1341
- ``` js
1342
- store.modelFor('post').inverseFor('comments', store) // { type: App.Message, name: 'owner', kind: 'belongsTo' }
1343
- store.modelFor('message').inverseFor('owner', store) // { type: App.Post, name: 'comments', kind: 'hasMany' }
1344
- ```
1345
-
1346
- @method inverseFor
1347
- @public
1348
- @static
1349
- @param {String} name the name of the relationship
1350
- @param {Store} store
1351
- @return {Object} the inverse relationship, or null
1352
- */
1353
- static inverseFor(name, store) {
1354
- if (DEPRECATE_EARLY_STATIC) {
1355
- deprecate(
1356
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1357
- this.modelName,
1358
- {
1359
- id: 'ember-data:deprecate-early-static',
1360
- for: 'ember-data',
1361
- until: '5.0',
1362
- since: { available: '4.7', enabled: '4.7' },
1363
- }
1364
- );
1365
- } else {
1366
- assert(
1367
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1368
- this.modelName
1369
- );
1370
- }
1371
- let inverseMap = this.inverseMap;
1372
- if (inverseMap[name]) {
1373
- return inverseMap[name];
1374
- } else {
1375
- let inverse = this._findInverseFor(name, store);
1376
- inverseMap[name] = inverse;
1377
- return inverse;
1378
- }
1379
- }
1380
-
1381
- //Calculate the inverse, ignoring the cache
1382
- static _findInverseFor(name, store) {
1383
- if (DEPRECATE_EARLY_STATIC) {
1384
- deprecate(
1385
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1386
- this.modelName,
1387
- {
1388
- id: 'ember-data:deprecate-early-static',
1389
- for: 'ember-data',
1390
- until: '5.0',
1391
- since: { available: '4.7', enabled: '4.7' },
1392
- }
1393
- );
1394
- } else {
1395
- assert(
1396
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1397
- this.modelName
1398
- );
1399
- }
1400
-
1401
- const relationship = this.relationshipsByName.get(name);
1402
- const { options } = relationship;
1403
- const isPolymorphic = options.polymorphic;
1404
-
1405
- //If inverse is manually specified to be null, like `comments: hasMany('message', { inverse: null })`
1406
- const isExplicitInverseNull = options.inverse === null;
1407
- const isAbstractType =
1408
- !isExplicitInverseNull && isPolymorphic && !store.getSchemaDefinitionService().doesTypeExist(relationship.type);
1409
-
1410
- if (isExplicitInverseNull || isAbstractType) {
1411
- assert(
1412
- `No schema for the abstract type '${relationship.type}' for the polymorphic relationship '${name}' on '${this.modelName}' was provided by the SchemaDefinitionService.`,
1413
- !isPolymorphic || isExplicitInverseNull
1414
- );
1415
- return null;
1416
- }
1417
-
1418
- let fieldOnInverse, inverseKind, inverseRelationship, inverseOptions;
1419
- let inverseSchema = this.typeForRelationship(name, store);
1420
-
1421
- // if the type does not exist and we are not polymorphic
1422
- //If inverse is specified manually, return the inverse
1423
- if (options.inverse !== undefined) {
1424
- fieldOnInverse = options.inverse;
1425
- inverseRelationship = inverseSchema && inverseSchema.relationshipsByName.get(fieldOnInverse);
1426
-
1427
- assert(
1428
- `We found no field named '${fieldOnInverse}' on the schema for '${inverseSchema.modelName}' to be the inverse of the '${name}' relationship on '${this.modelName}'. This is most likely due to a missing field on your model definition.`,
1429
- inverseRelationship
1430
- );
1431
-
1432
- // TODO probably just return the whole inverse here
1433
- inverseKind = inverseRelationship.kind;
1434
- inverseOptions = inverseRelationship.options;
1435
- } else {
1436
- //No inverse was specified manually, we need to use a heuristic to guess one
1437
- if (relationship.type === relationship.parentModelName) {
1438
- warn(
1439
- `Detected a reflexive relationship named '${name}' on the schema for '${relationship.type}' without an inverse option. Look at https://guides.emberjs.com/current/models/relationships/#toc_reflexive-relations for how to explicitly specify inverses.`,
1440
- false,
1441
- {
1442
- id: 'ds.model.reflexive-relationship-without-inverse',
1443
- }
1444
- );
1445
- }
1446
-
1447
- let possibleRelationships = findPossibleInverses(this, inverseSchema, name);
1448
-
1449
- if (possibleRelationships.length === 0) {
1450
- return null;
1451
- }
1452
-
1453
- if (DEBUG) {
1454
- let filteredRelationships = possibleRelationships.filter((possibleRelationship) => {
1455
- let optionsForRelationship = possibleRelationship.options;
1456
- return name === optionsForRelationship.inverse;
1457
- });
1458
-
1459
- assert(
1460
- "You defined the '" +
1461
- name +
1462
- "' relationship on " +
1463
- this +
1464
- ', but you defined the inverse relationships of type ' +
1465
- inverseSchema.toString() +
1466
- ' multiple times. Look at https://guides.emberjs.com/current/models/relationships/#toc_explicit-inverses for how to explicitly specify inverses',
1467
- filteredRelationships.length < 2
1468
- );
1469
- }
1470
-
1471
- let explicitRelationship = possibleRelationships.find((relationship) => relationship.options.inverse === name);
1472
- if (explicitRelationship) {
1473
- possibleRelationships = [explicitRelationship];
1474
- }
1475
-
1476
- assert(
1477
- "You defined the '" +
1478
- name +
1479
- "' relationship on " +
1480
- this +
1481
- ', but multiple possible inverse relationships of type ' +
1482
- this +
1483
- ' were found on ' +
1484
- inverseSchema +
1485
- '. Look at https://guides.emberjs.com/current/models/relationships/#toc_explicit-inverses for how to explicitly specify inverses',
1486
- possibleRelationships.length === 1
1487
- );
1488
-
1489
- fieldOnInverse = possibleRelationships[0].name;
1490
- inverseKind = possibleRelationships[0].kind;
1491
- inverseOptions = possibleRelationships[0].options;
1492
- }
1493
-
1494
- // ensure inverse is properly configured
1495
- if (DEBUG) {
1496
- if (isPolymorphic) {
1497
- if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
1498
- if (!inverseOptions.as) {
1499
- deprecate(
1500
- `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${fieldOnInverse}' on type '${inverseSchema.modelName}' is misconfigured.`,
1501
- false,
1502
- {
1503
- id: 'ember-data:non-explicit-relationships',
1504
- since: { enabled: '4.7', available: '4.7' },
1505
- until: '5.0',
1506
- for: 'ember-data',
1507
- }
1508
- );
1509
- }
1510
- } else {
1511
- assert(
1512
- `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${fieldOnInverse}' on type '${inverseSchema.modelName}' is misconfigured.`,
1513
- inverseOptions.as
1514
- );
1515
- assert(
1516
- `options.as should match the expected type of the polymorphic relationship. Expected field '${fieldOnInverse}' on type '${inverseSchema.modelName}' to specify '${relationship.type}' but found '${inverseOptions.as}'`,
1517
- !!inverseOptions.as && relationship.type === inverseOptions.as
1518
- );
1519
- }
1520
- }
1521
- }
1522
-
1523
- // ensure we are properly configured
1524
- if (DEBUG) {
1525
- if (inverseOptions.polymorphic) {
1526
- if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
1527
- if (!options.as) {
1528
- deprecate(
1529
- `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${name}' on type '${this.modelName}' is misconfigured.`,
1530
- false,
1531
- {
1532
- id: 'ember-data:non-explicit-relationships',
1533
- since: { enabled: '4.7', available: '4.7' },
1534
- until: '5.0',
1535
- for: 'ember-data',
1536
- }
1537
- );
1538
- }
1539
- } else {
1540
- assert(
1541
- `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${name}' on type '${this.modelName}' is misconfigured.`,
1542
- options.as
1543
- );
1544
- assert(
1545
- `options.as should match the expected type of the polymorphic relationship. Expected field '${name}' on type '${this.modelName}' to specify '${inverseRelationship.type}' but found '${options.as}'`,
1546
- !!options.as && inverseRelationship.type === options.as
1547
- );
1548
- }
1549
- }
1550
- }
1551
-
1552
- assert(
1553
- `The ${inverseSchema.modelName}:${fieldOnInverse} relationship declares 'inverse: null', but it was resolved as the inverse for ${this.modelName}:${name}.`,
1554
- inverseOptions.inverse !== null
1555
- );
1556
-
1557
- return {
1558
- type: inverseSchema,
1559
- name: fieldOnInverse,
1560
- kind: inverseKind,
1561
- options: inverseOptions,
1562
- };
1563
- }
1564
-
1565
- /**
1566
- The model's relationships as a map, keyed on the type of the
1567
- relationship. The value of each entry is an array containing a descriptor
1568
- for each relationship with that type, describing the name of the relationship
1569
- as well as the type.
1570
-
1571
- For example, given the following model definition:
1572
-
1573
- ```app/models/blog.js
1574
- import Model, { belongsTo, hasMany } from '@ember-data/model';
1575
-
1576
- export default class BlogModel extends Model {
1577
- @hasMany('user') users;
1578
- @belongsTo('user') owner;
1579
- @hasMany('post') posts;
1580
- }
1581
- ```
1582
-
1583
- This computed property would return a map describing these
1584
- relationships, like this:
1585
-
1586
- ```javascript
1587
- import { get } from '@ember/object';
1588
- import Blog from 'app/models/blog';
1589
- import User from 'app/models/user';
1590
- import Post from 'app/models/post';
1591
-
1592
- let relationships = Blog.relationships;
1593
- relationships.user;
1594
- //=> [ { name: 'users', kind: 'hasMany' },
1595
- // { name: 'owner', kind: 'belongsTo' } ]
1596
- relationships.post;
1597
- //=> [ { name: 'posts', kind: 'hasMany' } ]
1598
- ```
1599
-
1600
- @property relationships
1601
- @public
1602
- @static
1603
- @type Map
1604
- @readOnly
1605
- */
1606
-
1607
- @computeOnce
1608
- static get relationships() {
1609
- if (DEPRECATE_EARLY_STATIC) {
1610
- deprecate(
1611
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1612
- this.modelName,
1613
- {
1614
- id: 'ember-data:deprecate-early-static',
1615
- for: 'ember-data',
1616
- until: '5.0',
1617
- since: { available: '4.7', enabled: '4.7' },
1618
- }
1619
- );
1620
- } else {
1621
- assert(
1622
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1623
- this.modelName
1624
- );
1625
- }
1626
- let map = new Map();
1627
- let relationshipsByName = this.relationshipsByName;
1628
-
1629
- // Loop through each computed property on the class
1630
- relationshipsByName.forEach((desc) => {
1631
- let { type } = desc;
1632
-
1633
- if (!map.has(type)) {
1634
- map.set(type, []);
1635
- }
1636
-
1637
- map.get(type).push(desc);
1638
- });
1639
-
1640
- return map;
1641
- }
1642
-
1643
- /**
1644
- A hash containing lists of the model's relationships, grouped
1645
- by the relationship kind. For example, given a model with this
1646
- definition:
1647
-
1648
- ```app/models/blog.js
1649
- import Model, { belongsTo, hasMany } from '@ember-data/model';
1650
-
1651
- export default class BlogModel extends Model {
1652
- @hasMany('user') users;
1653
- @belongsTo('user') owner;
1654
-
1655
- @hasMany('post') posts;
1656
- }
1657
- ```
1658
-
1659
- This property would contain the following:
1660
-
1661
- ```javascript
1662
- import { get } from '@ember/object';
1663
- import Blog from 'app/models/blog';
1664
-
1665
- let relationshipNames = Blog.relationshipNames;
1666
- relationshipNames.hasMany;
1667
- //=> ['users', 'posts']
1668
- relationshipNames.belongsTo;
1669
- //=> ['owner']
1670
- ```
1671
-
1672
- @property relationshipNames
1673
- @public
1674
- @static
1675
- @type Object
1676
- @readOnly
1677
- */
1678
- @computeOnce
1679
- static get relationshipNames() {
1680
- if (DEPRECATE_EARLY_STATIC) {
1681
- deprecate(
1682
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1683
- this.modelName,
1684
- {
1685
- id: 'ember-data:deprecate-early-static',
1686
- for: 'ember-data',
1687
- until: '5.0',
1688
- since: { available: '4.7', enabled: '4.7' },
1689
- }
1690
- );
1691
- } else {
1692
- assert(
1693
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1694
- this.modelName
1695
- );
1696
- }
1697
- let names = {
1698
- hasMany: [],
1699
- belongsTo: [],
1700
- };
1701
-
1702
- this.eachComputedProperty((name, meta) => {
1703
- if (meta.isRelationship) {
1704
- names[meta.kind].push(name);
1705
- }
1706
- });
1707
-
1708
- return names;
1709
- }
1710
-
1711
- /**
1712
- An array of types directly related to a model. Each type will be
1713
- included once, regardless of the number of relationships it has with
1714
- the model.
1715
-
1716
- For example, given a model with this definition:
1717
-
1718
- ```app/models/blog.js
1719
- import Model, { belongsTo, hasMany } from '@ember-data/model';
1720
-
1721
- export default class BlogModel extends Model {
1722
- @hasMany('user') users;
1723
- @belongsTo('user') owner;
1724
-
1725
- @hasMany('post') posts;
1726
- }
1727
- ```
1728
-
1729
- This property would contain the following:
1730
-
1731
- ```javascript
1732
- import { get } from '@ember/object';
1733
- import Blog from 'app/models/blog';
1734
-
1735
- let relatedTypes = Blog.relatedTypes');
1736
- //=> [ User, Post ]
1737
- ```
1738
-
1739
- @property relatedTypes
1740
- @public
1741
- @static
1742
- @type Ember.Array
1743
- @readOnly
1744
- */
1745
- @computeOnce
1746
- static get relatedTypes() {
1747
- if (DEPRECATE_EARLY_STATIC) {
1748
- deprecate(
1749
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1750
- this.modelName,
1751
- {
1752
- id: 'ember-data:deprecate-early-static',
1753
- for: 'ember-data',
1754
- until: '5.0',
1755
- since: { available: '4.7', enabled: '4.7' },
1756
- }
1757
- );
1758
- } else {
1759
- assert(
1760
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1761
- this.modelName
1762
- );
1763
- }
1764
- let types = [];
1765
-
1766
- let rels = this.relationshipsObject;
1767
- let relationships = Object.keys(rels);
1768
-
1769
- // create an array of the unique types involved
1770
- // in relationships
1771
- for (let i = 0; i < relationships.length; i++) {
1772
- let name = relationships[i];
1773
- let meta = rels[name];
1774
- let modelName = meta.type;
1775
-
1776
- if (types.indexOf(modelName) === -1) {
1777
- types.push(modelName);
1778
- }
1779
- }
1780
-
1781
- return types;
1782
- }
1783
-
1784
- /**
1785
- A map whose keys are the relationships of a model and whose values are
1786
- relationship descriptors.
1787
-
1788
- For example, given a model with this
1789
- definition:
1790
-
1791
- ```app/models/blog.js
1792
- import Model, { belongsTo, hasMany } from '@ember-data/model';
1793
-
1794
- export default class BlogModel extends Model {
1795
- @hasMany('user') users;
1796
- @belongsTo('user') owner;
1797
-
1798
- @hasMany('post') posts;
1799
- }
1800
- ```
1801
-
1802
- This property would contain the following:
1803
-
1804
- ```javascript
1805
- import { get } from '@ember/object';
1806
- import Blog from 'app/models/blog';
1807
-
1808
- let relationshipsByName = Blog.relationshipsByName;
1809
- relationshipsByName.users;
1810
- //=> { key: 'users', kind: 'hasMany', type: 'user', options: Object, isRelationship: true }
1811
- relationshipsByName.owner;
1812
- //=> { key: 'owner', kind: 'belongsTo', type: 'user', options: Object, isRelationship: true }
1813
- ```
1814
-
1815
- @property relationshipsByName
1816
- @public
1817
- @static
1818
- @type Map
1819
- @readOnly
1820
- */
1821
- @computeOnce
1822
- static get relationshipsByName() {
1823
- if (DEPRECATE_EARLY_STATIC) {
1824
- deprecate(
1825
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1826
- this.modelName,
1827
- {
1828
- id: 'ember-data:deprecate-early-static',
1829
- for: 'ember-data',
1830
- until: '5.0',
1831
- since: { available: '4.7', enabled: '4.7' },
1832
- }
1833
- );
1834
- } else {
1835
- assert(
1836
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1837
- this.modelName
1838
- );
1839
- }
1840
- let map = new Map();
1841
- let rels = this.relationshipsObject;
1842
- let relationships = Object.keys(rels);
1843
-
1844
- for (let i = 0; i < relationships.length; i++) {
1845
- let key = relationships[i];
1846
- let value = rels[key];
1847
-
1848
- map.set(value.key, value);
1849
- }
1850
-
1851
- return map;
1852
- }
1853
-
1854
- @computeOnce
1855
- static get relationshipsObject() {
1856
- if (DEPRECATE_EARLY_STATIC) {
1857
- deprecate(
1858
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1859
- this.modelName,
1860
- {
1861
- id: 'ember-data:deprecate-early-static',
1862
- for: 'ember-data',
1863
- until: '5.0',
1864
- since: { available: '4.7', enabled: '4.7' },
1865
- }
1866
- );
1867
- } else {
1868
- assert(
1869
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1870
- this.modelName
1871
- );
1872
- }
1873
- let relationships = Object.create(null);
1874
- let modelName = this.modelName;
1875
- this.eachComputedProperty((name, meta) => {
1876
- if (meta.isRelationship) {
1877
- meta.key = name;
1878
- meta.name = name;
1879
- meta.parentModelName = modelName;
1880
- relationships[name] = DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE ? relationshipFromMeta(meta) : meta;
1881
- }
1882
- });
1883
- return relationships;
1884
- }
1885
-
1886
- /**
1887
- A map whose keys are the fields of the model and whose values are strings
1888
- describing the kind of the field. A model's fields are the union of all of its
1889
- attributes and relationships.
1890
-
1891
- For example:
1892
-
1893
- ```app/models/blog.js
1894
- import Model, { attr, belongsTo, hasMany } from '@ember-data/model';
1895
-
1896
- export default class BlogModel extends Model {
1897
- @hasMany('user') users;
1898
- @belongsTo('user') owner;
1899
-
1900
- @hasMany('post') posts;
1901
-
1902
- @attr('string') title;
1903
- }
1904
- ```
1905
-
1906
- ```js
1907
- import { get } from '@ember/object';
1908
- import Blog from 'app/models/blog'
1909
-
1910
- let fields = Blog.fields;
1911
- fields.forEach(function(kind, field) {
1912
- // do thing
1913
- });
1914
-
1915
- // prints:
1916
- // users, hasMany
1917
- // owner, belongsTo
1918
- // posts, hasMany
1919
- // title, attribute
1920
- ```
1921
-
1922
- @property fields
1923
- @public
1924
- @static
1925
- @type Map
1926
- @readOnly
1927
- */
1928
- @computeOnce
1929
- static get fields() {
1930
- if (DEPRECATE_EARLY_STATIC) {
1931
- deprecate(
1932
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1933
- this.modelName,
1934
- {
1935
- id: 'ember-data:deprecate-early-static',
1936
- for: 'ember-data',
1937
- until: '5.0',
1938
- since: { available: '4.7', enabled: '4.7' },
1939
- }
1940
- );
1941
- } else {
1942
- assert(
1943
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1944
- this.modelName
1945
- );
1946
- }
1947
- let map = new Map();
1948
-
1949
- this.eachComputedProperty((name, meta) => {
1950
- // TODO end reliance on these booleans and stop leaking them in the spec
1951
- if (meta.isRelationship) {
1952
- map.set(name, meta.kind);
1953
- } else if (meta.isAttribute) {
1954
- map.set(name, 'attribute');
1955
- }
1956
- });
1957
-
1958
- return map;
1959
- }
1960
-
1961
- /**
1962
- Given a callback, iterates over each of the relationships in the model,
1963
- invoking the callback with the name of each relationship and its relationship
1964
- descriptor.
1965
-
1966
- @method eachRelationship
1967
- @public
1968
- @static
1969
- @param {Function} callback the callback to invoke
1970
- @param {any} binding the value to which the callback's `this` should be bound
1971
- */
1972
- static eachRelationship(callback, binding) {
1973
- if (DEPRECATE_EARLY_STATIC) {
1974
- deprecate(
1975
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
1976
- this.modelName,
1977
- {
1978
- id: 'ember-data:deprecate-early-static',
1979
- for: 'ember-data',
1980
- until: '5.0',
1981
- since: { available: '4.7', enabled: '4.7' },
1982
- }
1983
- );
1984
- } else {
1985
- assert(
1986
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
1987
- this.modelName
1988
- );
1989
- }
1990
- this.relationshipsByName.forEach((relationship, name) => {
1991
- callback.call(binding, name, relationship);
1992
- });
1993
- }
1994
-
1995
- /**
1996
- Given a callback, iterates over each of the types related to a model,
1997
- invoking the callback with the related type's class. Each type will be
1998
- returned just once, regardless of how many different relationships it has
1999
- with a model.
2000
-
2001
- @method eachRelatedType
2002
- @public
2003
- @static
2004
- @param {Function} callback the callback to invoke
2005
- @param {any} binding the value to which the callback's `this` should be bound
2006
- */
2007
- static eachRelatedType(callback, binding) {
2008
- if (DEPRECATE_EARLY_STATIC) {
2009
- deprecate(
2010
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
2011
- this.modelName,
2012
- {
2013
- id: 'ember-data:deprecate-early-static',
2014
- for: 'ember-data',
2015
- until: '5.0',
2016
- since: { available: '4.7', enabled: '4.7' },
2017
- }
2018
- );
2019
- } else {
2020
- assert(
2021
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
2022
- this.modelName
2023
- );
2024
- }
2025
- let relationshipTypes = this.relatedTypes;
2026
-
2027
- for (let i = 0; i < relationshipTypes.length; i++) {
2028
- let type = relationshipTypes[i];
2029
- callback.call(binding, type);
2030
- }
2031
- }
2032
-
2033
- static determineRelationshipType(knownSide, store) {
2034
- if (DEPRECATE_EARLY_STATIC) {
2035
- deprecate(
2036
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
2037
- this.modelName,
2038
- {
2039
- id: 'ember-data:deprecate-early-static',
2040
- for: 'ember-data',
2041
- until: '5.0',
2042
- since: { available: '4.7', enabled: '4.7' },
2043
- }
2044
- );
2045
- } else {
2046
- assert(
2047
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
2048
- this.modelName
2049
- );
2050
- }
2051
- let knownKey = knownSide.key;
2052
- let knownKind = knownSide.kind;
2053
- let inverse = this.inverseFor(knownKey, store);
2054
- // let key;
2055
- let otherKind;
2056
-
2057
- if (!inverse) {
2058
- return knownKind === 'belongsTo' ? 'oneToNone' : 'manyToNone';
2059
- }
2060
-
2061
- // key = inverse.name;
2062
- otherKind = inverse.kind;
2063
-
2064
- if (otherKind === 'belongsTo') {
2065
- return knownKind === 'belongsTo' ? 'oneToOne' : 'manyToOne';
2066
- } else {
2067
- return knownKind === 'belongsTo' ? 'oneToMany' : 'manyToMany';
2068
- }
2069
- }
2070
-
2071
- /**
2072
- A map whose keys are the attributes of the model (properties
2073
- described by attr) and whose values are the meta object for the
2074
- property.
2075
-
2076
- Example
2077
-
2078
- ```app/models/person.js
2079
- import Model, { attr } from '@ember-data/model';
2080
-
2081
- export default class PersonModel extends Model {
2082
- @attr('string') firstName;
2083
- @attr('string') lastName;
2084
- @attr('date') birthday;
2085
- }
2086
- ```
2087
-
2088
- ```javascript
2089
- import { get } from '@ember/object';
2090
- import Person from 'app/models/person'
2091
-
2092
- let attributes = Person.attributes
2093
-
2094
- attributes.forEach(function(meta, name) {
2095
- // do thing
2096
- });
2097
-
2098
- // prints:
2099
- // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
2100
- // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
2101
- // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
2102
- ```
2103
-
2104
- @property attributes
2105
- @public
2106
- @static
2107
- @type {Map}
2108
- @readOnly
2109
- */
2110
- @computeOnce
2111
- static get attributes() {
2112
- if (DEPRECATE_EARLY_STATIC) {
2113
- deprecate(
2114
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
2115
- this.modelName,
2116
- {
2117
- id: 'ember-data:deprecate-early-static',
2118
- for: 'ember-data',
2119
- until: '5.0',
2120
- since: { available: '4.7', enabled: '4.7' },
2121
- }
2122
- );
2123
- } else {
2124
- assert(
2125
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
2126
- this.modelName
2127
- );
2128
- }
2129
- let map = new Map();
2130
-
2131
- this.eachComputedProperty((name, meta) => {
2132
- if (meta.isAttribute) {
2133
- assert(
2134
- "You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: attr('<type>')` from " +
2135
- this.toString(),
2136
- name !== 'id'
2137
- );
2138
-
2139
- meta.name = name;
2140
- map.set(name, meta);
2141
- }
2142
- });
2143
-
2144
- return map;
2145
- }
2146
-
2147
- /**
2148
- A map whose keys are the attributes of the model (properties
2149
- described by attr) and whose values are type of transformation
2150
- applied to each attribute. This map does not include any
2151
- attributes that do not have an transformation type.
2152
-
2153
- Example
2154
-
2155
- ```app/models/person.js
2156
- import Model, { attr } from '@ember-data/model';
2157
-
2158
- export default class PersonModel extends Model {
2159
- @attr firstName;
2160
- @attr('string') lastName;
2161
- @attr('date') birthday;
2162
- }
2163
- ```
2164
-
2165
- ```javascript
2166
- import { get } from '@ember/object';
2167
- import Person from 'app/models/person';
2168
-
2169
- let transformedAttributes = Person.transformedAttributes
2170
-
2171
- transformedAttributes.forEach(function(field, type) {
2172
- // do thing
2173
- });
2174
-
2175
- // prints:
2176
- // lastName string
2177
- // birthday date
2178
- ```
2179
-
2180
- @property transformedAttributes
2181
- @public
2182
- @static
2183
- @type {Map}
2184
- @readOnly
2185
- */
2186
- @computeOnce
2187
- static get transformedAttributes() {
2188
- if (DEPRECATE_EARLY_STATIC) {
2189
- deprecate(
2190
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
2191
- this.modelName,
2192
- {
2193
- id: 'ember-data:deprecate-early-static',
2194
- for: 'ember-data',
2195
- until: '5.0',
2196
- since: { available: '4.7', enabled: '4.7' },
2197
- }
2198
- );
2199
- } else {
2200
- assert(
2201
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
2202
- this.modelName
2203
- );
2204
- }
2205
- let map = new Map();
2206
-
2207
- this.eachAttribute((key, meta) => {
2208
- if (meta.type) {
2209
- map.set(key, meta.type);
2210
- }
2211
- });
2212
-
2213
- return map;
2214
- }
2215
-
2216
- /**
2217
- Iterates through the attributes of the model, calling the passed function on each
2218
- attribute.
2219
-
2220
- The callback method you provide should have the following signature (all
2221
- parameters are optional):
2222
-
2223
- ```javascript
2224
- function(name, meta);
2225
- ```
2226
-
2227
- - `name` the name of the current property in the iteration
2228
- - `meta` the meta object for the attribute property in the iteration
2229
-
2230
- Note that in addition to a callback, you can also pass an optional target
2231
- object that will be set as `this` on the context.
2232
-
2233
- Example
2234
-
2235
- ```javascript
2236
- import Model, { attr } from '@ember-data/model';
2237
-
2238
- class PersonModel extends Model {
2239
- @attr('string') firstName;
2240
- @attr('string') lastName;
2241
- @attr('date') birthday;
2242
- }
2243
-
2244
- PersonModel.eachAttribute(function(name, meta) {
2245
- // do thing
2246
- });
2247
-
2248
- // prints:
2249
- // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
2250
- // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
2251
- // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
2252
- ```
2253
-
2254
- @method eachAttribute
2255
- @public
2256
- @param {Function} callback The callback to execute
2257
- @param {Object} [binding] the value to which the callback's `this` should be bound
2258
- @static
2259
- */
2260
- static eachAttribute(callback, binding) {
2261
- if (DEPRECATE_EARLY_STATIC) {
2262
- deprecate(
2263
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
2264
- this.modelName,
2265
- {
2266
- id: 'ember-data:deprecate-early-static',
2267
- for: 'ember-data',
2268
- until: '5.0',
2269
- since: { available: '4.7', enabled: '4.7' },
2270
- }
2271
- );
2272
- } else {
2273
- assert(
2274
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
2275
- this.modelName
2276
- );
2277
- }
2278
- this.attributes.forEach((meta, name) => {
2279
- callback.call(binding, name, meta);
2280
- });
2281
- }
2282
-
2283
- /**
2284
- Iterates through the transformedAttributes of the model, calling
2285
- the passed function on each attribute. Note the callback will not be
2286
- called for any attributes that do not have an transformation type.
2287
-
2288
- The callback method you provide should have the following signature (all
2289
- parameters are optional):
2290
-
2291
- ```javascript
2292
- function(name, type);
2293
- ```
2294
-
2295
- - `name` the name of the current property in the iteration
2296
- - `type` a string containing the name of the type of transformed
2297
- applied to the attribute
2298
-
2299
- Note that in addition to a callback, you can also pass an optional target
2300
- object that will be set as `this` on the context.
2301
-
2302
- Example
2303
-
2304
- ```javascript
2305
- import Model, { attr } from '@ember-data/model';
2306
-
2307
- let Person = Model.extend({
2308
- firstName: attr(),
2309
- lastName: attr('string'),
2310
- birthday: attr('date')
2311
- });
2312
-
2313
- Person.eachTransformedAttribute(function(name, type) {
2314
- // do thing
2315
- });
2316
-
2317
- // prints:
2318
- // lastName string
2319
- // birthday date
2320
- ```
2321
-
2322
- @method eachTransformedAttribute
2323
- @public
2324
- @param {Function} callback The callback to execute
2325
- @param {Object} [binding] the value to which the callback's `this` should be bound
2326
- @static
2327
- */
2328
- static eachTransformedAttribute(callback, binding) {
2329
- if (DEPRECATE_EARLY_STATIC) {
2330
- deprecate(
2331
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
2332
- this.modelName,
2333
- {
2334
- id: 'ember-data:deprecate-early-static',
2335
- for: 'ember-data',
2336
- until: '5.0',
2337
- since: { available: '4.7', enabled: '4.7' },
2338
- }
2339
- );
2340
- } else {
2341
- assert(
2342
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
2343
- this.modelName
2344
- );
2345
- }
2346
- this.transformedAttributes.forEach((type, name) => {
2347
- callback.call(binding, name, type);
2348
- });
2349
- }
2350
-
2351
- /**
2352
- Returns the name of the model class.
2353
-
2354
- @method toString
2355
- @public
2356
- @static
2357
- */
2358
- static toString() {
2359
- if (DEPRECATE_EARLY_STATIC) {
2360
- deprecate(
2361
- `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
2362
- this.modelName,
2363
- {
2364
- id: 'ember-data:deprecate-early-static',
2365
- for: 'ember-data',
2366
- until: '5.0',
2367
- since: { available: '4.7', enabled: '4.7' },
2368
- }
2369
- );
2370
- } else {
2371
- assert(
2372
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
2373
- this.modelName
2374
- );
2375
- }
2376
- return `model:${this.modelName}`;
2377
- }
2378
- }
2379
-
2380
- // this is required to prevent `init` from passing
2381
- // the values initialized during create to `setUnknownProperty`
2382
- Model.prototype._createProps = null;
2383
- Model.prototype._secretInit = null;
2384
-
2385
- if (HAS_DEBUG_PACKAGE) {
2386
- /**
2387
- Provides info about the model for debugging purposes
2388
- by grouping the properties into more semantic groups.
2389
-
2390
- Meant to be used by debugging tools such as the Chrome Ember Extension.
2391
-
2392
- - Groups all attributes in "Attributes" group.
2393
- - Groups all belongsTo relationships in "Belongs To" group.
2394
- - Groups all hasMany relationships in "Has Many" group.
2395
- - Groups all flags in "Flags" group.
2396
- - Flags relationship CPs as expensive properties.
2397
-
2398
- @method _debugInfo
2399
- @for Model
2400
- @private
2401
- */
2402
- Model.prototype._debugInfo = function () {
2403
- let attributes = ['id'];
2404
- let relationships = {};
2405
- let expensiveProperties = [];
2406
-
2407
- this.eachAttribute((name, meta) => attributes.push(name));
2408
-
2409
- let groups = [
2410
- {
2411
- name: 'Attributes',
2412
- properties: attributes,
2413
- expand: true,
2414
- },
2415
- ];
2416
-
2417
- this.eachRelationship((name, relationship) => {
2418
- let properties = relationships[relationship.kind];
2419
-
2420
- if (properties === undefined) {
2421
- properties = relationships[relationship.kind] = [];
2422
- groups.push({
2423
- name: relationship.kind,
2424
- properties,
2425
- expand: true,
2426
- });
2427
- }
2428
- properties.push(name);
2429
- expensiveProperties.push(name);
2430
- });
2431
-
2432
- groups.push({
2433
- name: 'Flags',
2434
- properties: ['isLoaded', 'hasDirtyAttributes', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid'],
2435
- });
2436
-
2437
- return {
2438
- propertyInfo: {
2439
- // include all other mixins / properties (not just the grouped ones)
2440
- includeOtherProperties: true,
2441
- groups: groups,
2442
- // don't pre-calculate unless cached
2443
- expensiveProperties: expensiveProperties,
2444
- },
2445
- };
2446
- };
2447
- }
2448
-
2449
- if (DEBUG) {
2450
- let lookupDescriptor = function lookupDescriptor(obj, keyName) {
2451
- let current = obj;
2452
- do {
2453
- let descriptor = Object.getOwnPropertyDescriptor(current, keyName);
2454
- if (descriptor !== undefined) {
2455
- return descriptor;
2456
- }
2457
- current = Object.getPrototypeOf(current);
2458
- } while (current !== null);
2459
- return null;
2460
- };
2461
-
2462
- Model.reopen({
2463
- init() {
2464
- this._super(...arguments);
2465
-
2466
- let ourDescriptor = lookupDescriptor(Model.prototype, 'currentState');
2467
- let theirDescriptor = lookupDescriptor(this, 'currentState');
2468
- let realState = this.___recordState;
2469
- if (ourDescriptor.get !== theirDescriptor.get || realState !== this.currentState) {
2470
- throw new Error(
2471
- `'currentState' is a reserved property name on instances of classes extending Model. Please choose a different property name for ${this.constructor.toString()}`
2472
- );
2473
- }
2474
-
2475
- const ID_DESCRIPTOR = lookupDescriptor(Model.prototype, 'id');
2476
- let idDesc = lookupDescriptor(this, 'id');
2477
-
2478
- if (idDesc.get !== ID_DESCRIPTOR.get) {
2479
- throw new Error(
2480
- `You may not set 'id' as an attribute on your model. Please remove any lines that look like: \`id: attr('<type>')\` from ${this.constructor.toString()}`
2481
- );
2482
- }
2483
- },
2484
- });
2485
-
2486
- if (DEPRECATE_MODEL_REOPEN) {
2487
- const originalReopen = Model.reopen;
2488
- const originalReopenClass = Model.reopenClass;
2489
-
2490
- Model.reopen = function deprecatedReopen() {
2491
- deprecate(`Model.reopen is deprecated. Use Foo extends Model to extend your class instead.`, false, {
2492
- id: 'ember-data:deprecate-model-reopen',
2493
- for: 'ember-data',
2494
- until: '5.0',
2495
- since: { available: '4.7', enabled: '4.7' },
2496
- });
2497
- return originalReopen.call(this, ...arguments);
2498
- };
2499
-
2500
- Model.reopenClass = function deprecatedReopenClass() {
2501
- deprecate(
2502
- `Model.reopenClass is deprecated. Use Foo extends Model to add static methods and properties to your class instead.`,
2503
- false,
2504
- {
2505
- id: 'ember-data:deprecate-model-reopenclass',
2506
- for: 'ember-data',
2507
- until: '5.0',
2508
- since: { available: '4.7', enabled: '4.7' },
2509
- }
2510
- );
2511
- return originalReopenClass.call(this, ...arguments);
2512
- };
2513
- }
2514
- }
2515
-
2516
- export default Model;