ember-data-source 1.0.0.beta.19.2 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c527ad1dee24165b1d12194be3a58ab5f5da8f38
4
- data.tar.gz: 684f894be28fdb14b8d143b9d79ce093718bd9dd
3
+ metadata.gz: 3d08b43fc451e761f174b661069d98e37e72f3a4
4
+ data.tar.gz: 2018bc208fceb8c6aa502c2a267c3bcdb892e08e
5
5
  SHA512:
6
- metadata.gz: 8f8a66449f11d93a943ff1778b2f4cfb1dd334056399e4528c2157106a738e1299c8b81987064c4c62d1853a6b2a3be5790443c07d3a4f9f677d8e500ffafe06
7
- data.tar.gz: d89516410dd17fae26f6a2a741678b795dd8c1caaa05b40be3d87a9d7009cdf28f009aa87f6f20f5e5644e3b8e1e6275aeb82325fb491b4fc5e304af043429a5
6
+ metadata.gz: 8acf7be1bf0228e50fb657b8b393847129c262b722a8102e954efb2ce3042a707745bd7fd2df4353d455b1f5794cdb20d22b325dd05618be5b34d444651472c7
7
+ data.tar.gz: a285b67c58fc0827c4d0e2426bff97cb5dd84e515facff6ecbc1dd919e26faee278128accdd0b7e355e471576ae098f86d429a36be9128aee5cfc296231b9062
@@ -49,8 +49,22 @@ test('activemodel-adapter/tests/integration/active-model-adapter-test.js should
49
49
  }
50
50
  if (!QUnit.urlParams.nojshint) {
51
51
  module('JSHint - activemodel-adapter/tests/integration');
52
- test('activemodel-adapter/tests/integration/active-model-serializer-namespaced-modelname-test.js should pass jshint', function() {
53
- ok(true, 'activemodel-adapter/tests/integration/active-model-serializer-namespaced-modelname-test.js should pass jshint.');
52
+ test('activemodel-adapter/tests/integration/active-model-serializer-namespaced-model-name-new-test.js should pass jshint', function() {
53
+ ok(true, 'activemodel-adapter/tests/integration/active-model-serializer-namespaced-model-name-new-test.js should pass jshint.');
54
+ });
55
+
56
+ }
57
+ if (!QUnit.urlParams.nojshint) {
58
+ module('JSHint - activemodel-adapter/tests/integration');
59
+ test('activemodel-adapter/tests/integration/active-model-serializer-namespaced-model-name-test.js should pass jshint', function() {
60
+ ok(true, 'activemodel-adapter/tests/integration/active-model-serializer-namespaced-model-name-test.js should pass jshint.');
61
+ });
62
+
63
+ }
64
+ if (!QUnit.urlParams.nojshint) {
65
+ module('JSHint - activemodel-adapter/tests/integration');
66
+ test('activemodel-adapter/tests/integration/active-model-serializer-new-test.js should pass jshint', function() {
67
+ ok(true, 'activemodel-adapter/tests/integration/active-model-serializer-new-test.js should pass jshint.');
54
68
  });
55
69
 
56
70
  }
@@ -108,6 +122,9 @@ define(
108
122
  test('errors are camelCased and are expected under the `errors` property of the payload', function () {
109
123
  var jqXHR = {
110
124
  status: 422,
125
+ getAllResponseHeaders: function () {
126
+ return '';
127
+ },
111
128
  responseText: JSON.stringify({
112
129
  errors: {
113
130
  first_name: ['firstName error']
@@ -168,30 +185,171 @@ define(
168
185
  equal(adapter.buildURL('superUser', 1), '/super_users/1');
169
186
  });
170
187
 
171
- test('ajaxError - returns invalid error if 422 response', function () {
188
+ test('handleResponse - returns invalid error if 422 response', function () {
172
189
 
173
190
  var jqXHR = {
174
191
  status: 422,
175
- responseText: JSON.stringify({ name: 'can\'t be blank' })
192
+ responseText: JSON.stringify({ errors: { name: 'can\'t be blank' } })
176
193
  };
177
194
 
178
- equal(adapter.ajaxError(jqXHR).errors.name, 'can\'t be blank');
195
+ var json = adapter.parseErrorResponse(jqXHR.responseText);
196
+
197
+ var error = adapter.handleResponse(jqXHR.status, {}, json).errors[0];
198
+
199
+ equal(error.details, 'can\'t be blank');
200
+ equal(error.source.pointer, 'data/attributes/name');
179
201
  });
180
202
 
181
- test('ajaxError - returns ajax response if not 422 response', function () {
203
+ test('handleResponse - returns ajax response if not 422 response', function () {
182
204
  var jqXHR = {
183
205
  status: 500,
184
206
  responseText: 'Something went wrong'
185
207
  };
186
208
 
187
- equal(adapter.ajaxError(jqXHR), jqXHR);
209
+ var json = adapter.parseErrorResponse(jqXHR.responseText);
210
+
211
+ ok(adapter.handleResponse(jqXHR.status, {}, json) instanceof DS.AdapterError, 'must be a DS.AdapterError');
212
+ });
213
+ }
214
+ );
215
+
216
+
217
+ define(
218
+ "activemodel-adapter/tests/integration/active-model-serializer-namespaced-model-name-new-test",
219
+ ["exports"],
220
+ function(__exports__) {
221
+ "use strict";
222
+
223
+ function __es6_export__(name, value) {
224
+ __exports__[name] = value;
225
+ }
226
+
227
+ var SuperVillain, EvilMinion, YellowMinion, DoomsdayDevice, MediocreVillain, TestSerializer, env;
228
+ var run = Ember.run;
229
+
230
+ module('integration/active_model - AMS-namespaced-model-names (new API)', {
231
+ setup: function () {
232
+ SuperVillain = DS.Model.extend({
233
+ firstName: DS.attr('string'),
234
+ lastName: DS.attr('string'),
235
+ evilMinions: DS.hasMany('evilMinion')
236
+ });
237
+
238
+ EvilMinion = DS.Model.extend({
239
+ superVillain: DS.belongsTo('superVillain'),
240
+ name: DS.attr('string')
241
+ });
242
+ YellowMinion = EvilMinion.extend();
243
+ DoomsdayDevice = DS.Model.extend({
244
+ name: DS.attr('string'),
245
+ evilMinion: DS.belongsTo('evilMinion', { polymorphic: true })
246
+ });
247
+ MediocreVillain = DS.Model.extend({
248
+ name: DS.attr('string'),
249
+ evilMinions: DS.hasMany('evilMinion', { polymorphic: true })
250
+ });
251
+ TestSerializer = DS.ActiveModelSerializer.extend({
252
+ isNewSerializerAPI: true
253
+ });
254
+ env = setupStore({
255
+ superVillain: SuperVillain,
256
+ evilMinion: EvilMinion,
257
+ 'evilMinions/yellowMinion': YellowMinion,
258
+ doomsdayDevice: DoomsdayDevice,
259
+ mediocreVillain: MediocreVillain
260
+ });
261
+ env.store.modelFor('superVillain');
262
+ env.store.modelFor('evilMinion');
263
+ env.store.modelFor('evilMinions/yellowMinion');
264
+ env.store.modelFor('doomsdayDevice');
265
+ env.store.modelFor('mediocreVillain');
266
+ env.registry.register('serializer:application', TestSerializer);
267
+ env.registry.register('serializer:-active-model', TestSerializer);
268
+ env.registry.register('adapter:-active-model', TestSerializer);
269
+ env.amsSerializer = env.container.lookup('serializer:-active-model');
270
+ env.amsAdapter = env.container.lookup('adapter:-active-model');
271
+ },
272
+
273
+ teardown: function () {
274
+ run(env.store, 'destroy');
275
+ }
276
+ });
277
+
278
+ test('extractPolymorphic hasMany', function () {
279
+ var json_hash = {
280
+ mediocre_villain: { id: 1, name: 'Dr Horrible', evil_minion_ids: [{ type: 'EvilMinions::YellowMinion', id: 12 }] },
281
+ 'evil-minions/yellow-minion': [{ id: 12, name: 'Alex', doomsday_device_ids: [1] }]
282
+ };
283
+ var json;
284
+
285
+ run(function () {
286
+ json = env.amsSerializer.normalizeResponse(env.store, MediocreVillain, json_hash, '1', 'findRecord');
287
+ });
288
+
289
+ deepEqual(json, {
290
+ 'data': {
291
+ 'id': '1',
292
+ 'type': 'mediocre-villain',
293
+ 'attributes': {
294
+ 'name': 'Dr Horrible'
295
+ },
296
+ 'relationships': {
297
+ 'evilMinions': {
298
+ 'data': [{ 'id': '12', 'type': 'evil-minions/yellow-minion' }]
299
+ }
300
+ }
301
+ },
302
+ 'included': [{
303
+ 'id': '12',
304
+ 'type': 'evil-minions/yellow-minion',
305
+ 'attributes': {
306
+ 'name': 'Alex'
307
+ },
308
+ 'relationships': {}
309
+ }]
310
+ });
311
+ });
312
+
313
+ test('extractPolymorphic belongsTo', function () {
314
+ var json_hash = {
315
+ doomsday_device: { id: 1, name: 'DeathRay', evil_minion_id: { type: 'EvilMinions::YellowMinion', id: 12 } },
316
+ 'evil-minions/yellow-minion': [{ id: 12, name: 'Alex', doomsday_device_ids: [1] }]
317
+ };
318
+ var json;
319
+
320
+ run(function () {
321
+ json = env.amsSerializer.normalizeResponse(env.store, DoomsdayDevice, json_hash, '1', 'findRecord');
322
+ });
323
+
324
+ deepEqual(json, {
325
+ 'data': {
326
+ 'id': '1',
327
+ 'type': 'doomsday-device',
328
+ 'attributes': {
329
+ 'name': 'DeathRay'
330
+ },
331
+ 'relationships': {
332
+ 'evilMinion': {
333
+ 'data': { 'id': '12', 'type': 'evil-minions/yellow-minion' }
334
+ }
335
+ }
336
+ },
337
+ 'included': [{
338
+ 'id': '12',
339
+ 'type': 'evil-minions/yellow-minion',
340
+ 'attributes': {
341
+ 'name': 'Alex'
342
+ },
343
+ 'relationships': {}
344
+ }]
345
+ });
188
346
  });
189
347
  }
190
348
  );
191
349
 
192
350
 
193
351
  define(
194
- "activemodel-adapter/tests/integration/active-model-serializer-namespaced-modelname-test",
352
+ "activemodel-adapter/tests/integration/active-model-serializer-namespaced-model-name-test",
195
353
  ["exports"],
196
354
  function(__exports__) {
197
355
  "use strict";
@@ -324,7 +482,7 @@ define(
324
482
 
325
483
 
326
484
  define(
327
- "activemodel-adapter/tests/integration/active-model-serializer-test",
485
+ "activemodel-adapter/tests/integration/active-model-serializer-new-test",
328
486
  ["exports"],
329
487
  function(__exports__) {
330
488
  "use strict";
@@ -333,34 +491,36 @@ define(
333
491
  __exports__[name] = value;
334
492
  }
335
493
 
336
- var get = Ember.get;
337
- var HomePlanet, league, SuperVillain, EvilMinion, YellowMinion, DoomsdayDevice, MediocreVillain, env;
494
+ var HomePlanet, SuperVillain, EvilMinion, YellowMinion, DoomsdayDevice, MediocreVillain, TestSerializer, env;
338
495
  var run = Ember.run;
339
496
 
340
- module('integration/active_model - ActiveModelSerializer', {
497
+ module('integration/active_model - ActiveModelSerializer (new API)', {
341
498
  setup: function () {
342
499
  SuperVillain = DS.Model.extend({
343
500
  firstName: DS.attr('string'),
344
501
  lastName: DS.attr('string'),
345
- homePlanet: DS.belongsTo('home-planet'),
346
- evilMinions: DS.hasMany('evil-minion')
502
+ homePlanet: DS.belongsTo('homePlanet'),
503
+ evilMinions: DS.hasMany('evilMinion')
347
504
  });
348
505
  HomePlanet = DS.Model.extend({
349
506
  name: DS.attr('string'),
350
- superVillains: DS.hasMany('super-villain', { async: true })
507
+ superVillains: DS.hasMany('superVillain', { async: true })
351
508
  });
352
509
  EvilMinion = DS.Model.extend({
353
- superVillain: DS.belongsTo('super-villain'),
510
+ superVillain: DS.belongsTo('superVillain'),
354
511
  name: DS.attr('string')
355
512
  });
356
513
  YellowMinion = EvilMinion.extend();
357
514
  DoomsdayDevice = DS.Model.extend({
358
515
  name: DS.attr('string'),
359
- evilMinion: DS.belongsTo('evil-minion', { polymorphic: true })
516
+ evilMinion: DS.belongsTo('evilMinion', { polymorphic: true })
360
517
  });
361
518
  MediocreVillain = DS.Model.extend({
362
519
  name: DS.attr('string'),
363
- evilMinions: DS.hasMany('evil-minion', { polymorphic: true })
520
+ evilMinions: DS.hasMany('evilMinion', { polymorphic: true })
521
+ });
522
+ TestSerializer = DS.ActiveModelSerializer.extend({
523
+ isNewSerializerAPI: true
364
524
  });
365
525
  env = setupStore({
366
526
  superVillain: SuperVillain,
@@ -370,14 +530,14 @@ define(
370
530
  doomsdayDevice: DoomsdayDevice,
371
531
  mediocreVillain: MediocreVillain
372
532
  });
373
- env.store.modelFor('super-villain');
374
- env.store.modelFor('home-planet');
375
- env.store.modelFor('evil-minion');
376
- env.store.modelFor('yellow-minion');
377
- env.store.modelFor('doomsday-device');
378
- env.store.modelFor('mediocre-villain');
379
- env.registry.register('serializer:application', DS.ActiveModelSerializer);
380
- env.registry.register('serializer:-active-model', DS.ActiveModelSerializer);
533
+ env.store.modelFor('superVillain');
534
+ env.store.modelFor('homePlanet');
535
+ env.store.modelFor('evilMinion');
536
+ env.store.modelFor('yellowMinion');
537
+ env.store.modelFor('doomsdayDevice');
538
+ env.store.modelFor('mediocreVillain');
539
+ env.registry.register('serializer:application', TestSerializer);
540
+ env.registry.register('serializer:-active-model', TestSerializer);
381
541
  env.registry.register('adapter:-active-model', DS.ActiveModelAdapter);
382
542
  env.amsSerializer = env.container.lookup('serializer:-active-model');
383
543
  env.amsAdapter = env.container.lookup('adapter:-active-model');
@@ -388,67 +548,38 @@ define(
388
548
  }
389
549
  });
390
550
 
391
- test('serialize', function () {
392
- var tom;
393
- run(function () {
394
- league = env.store.createRecord('home-planet', { name: 'Villain League', id: '123' });
395
- tom = env.store.createRecord('super-villain', { firstName: 'Tom', lastName: 'Dale', homePlanet: league });
396
- });
397
-
398
- var json = env.amsSerializer.serialize(tom._createSnapshot());
399
-
400
- deepEqual(json, {
401
- first_name: 'Tom',
402
- last_name: 'Dale',
403
- home_planet_id: get(league, 'id')
404
- });
405
- });
406
-
407
- test('serializeIntoHash', function () {
408
- run(function () {
409
- league = env.store.createRecord('home-planet', { name: 'Umber', id: '123' });
410
- });
411
- var json = {};
412
-
413
- env.amsSerializer.serializeIntoHash(json, HomePlanet, league._createSnapshot());
414
-
415
- deepEqual(json, {
416
- home_planet: {
417
- name: 'Umber'
418
- }
419
- });
420
- });
421
-
422
- test('serializeIntoHash with decamelized types', function () {
423
- HomePlanet.modelName = 'home-planet';
424
- run(function () {
425
- league = env.store.createRecord('home-planet', { name: 'Umber', id: '123' });
426
- });
427
- var json = {};
428
-
429
- env.amsSerializer.serializeIntoHash(json, HomePlanet, league._createSnapshot());
430
-
431
- deepEqual(json, {
432
- home_planet: {
433
- name: 'Umber'
434
- }
435
- });
436
- });
437
-
438
551
  test('normalize', function () {
439
552
  SuperVillain.reopen({
440
- yellowMinion: DS.belongsTo('yellow-minion')
553
+ yellowMinion: DS.belongsTo('yellowMinion')
441
554
  });
442
555
 
443
- var superVillain_hash = { first_name: 'Tom', last_name: 'Dale', home_planet_id: '123', evil_minion_ids: [1, 2] };
556
+ var superVillain_hash = {
557
+ id: '1',
558
+ first_name: 'Tom',
559
+ last_name: 'Dale',
560
+ home_planet_id: '123',
561
+ evil_minion_ids: [1, 2]
562
+ };
444
563
 
445
564
  var json = env.amsSerializer.normalize(SuperVillain, superVillain_hash, 'superVillain');
446
565
 
447
566
  deepEqual(json, {
448
- firstName: 'Tom',
449
- lastName: 'Dale',
450
- homePlanet: '123',
451
- evilMinions: [1, 2]
567
+ 'data': {
568
+ 'id': '1',
569
+ 'type': 'super-villain',
570
+ 'attributes': {
571
+ 'firstName': 'Tom',
572
+ 'lastName': 'Dale'
573
+ },
574
+ 'relationships': {
575
+ 'evilMinions': {
576
+ 'data': [{ 'id': '1', 'type': 'evil-minion' }, { 'id': '2', 'type': 'evil-minion' }]
577
+ },
578
+ 'homePlanet': {
579
+ 'data': { 'id': '123', 'type': 'home-planet' }
580
+ }
581
+ }
582
+ }
452
583
  });
453
584
  });
454
585
 
@@ -461,10 +592,10 @@ define(
461
592
 
462
593
  var json = env.amsSerializer.normalize(HomePlanet, home_planet, 'homePlanet');
463
594
 
464
- equal(json.links.superVillains, '/api/super_villians/1', 'normalize links');
595
+ equal(json.data.relationships.superVillains.links.related, '/api/super_villians/1', 'normalize links');
465
596
  });
466
597
 
467
- test('extractSingle', function () {
598
+ test('normalizeSingleResponse', function () {
468
599
  env.registry.register('adapter:superVillain', DS.ActiveModelAdapter);
469
600
 
470
601
  var json_hash = {
@@ -479,23 +610,39 @@ define(
479
610
 
480
611
  var json;
481
612
  run(function () {
482
- json = env.amsSerializer.extractSingle(env.store, HomePlanet, json_hash);
613
+ json = env.amsSerializer.normalizeSingleResponse(env.store, HomePlanet, json_hash, '1', 'findRecord');
483
614
  });
484
615
 
485
616
  deepEqual(json, {
486
- 'id': '1',
487
- 'name': 'Umber',
488
- 'superVillains': [1]
489
- });
490
-
491
- run(function () {
492
- env.store.find('super-villain', 1).then(function (minion) {
493
- equal(minion.get('firstName'), 'Tom');
494
- });
617
+ 'data': {
618
+ 'id': '1',
619
+ 'type': 'home-planet',
620
+ 'attributes': {
621
+ 'name': 'Umber'
622
+ },
623
+ 'relationships': {
624
+ 'superVillains': {
625
+ 'data': [{ 'id': '1', 'type': 'super-villain' }]
626
+ }
627
+ }
628
+ },
629
+ 'included': [{
630
+ 'id': '1',
631
+ 'type': 'super-villain',
632
+ 'attributes': {
633
+ 'firstName': 'Tom',
634
+ 'lastName': 'Dale'
635
+ },
636
+ 'relationships': {
637
+ 'homePlanet': {
638
+ 'data': { 'id': '1', 'type': 'home-planet' }
639
+ }
640
+ }
641
+ }]
495
642
  });
496
643
  });
497
644
 
498
- test('extractArray', function () {
645
+ test('normalizeArrayResponse', function () {
499
646
  env.registry.register('adapter:superVillain', DS.ActiveModelAdapter);
500
647
  var array;
501
648
 
@@ -505,59 +652,36 @@ define(
505
652
  };
506
653
 
507
654
  run(function () {
508
- array = env.amsSerializer.extractArray(env.store, HomePlanet, json_hash);
509
- });
510
-
511
- deepEqual(array, [{
512
- 'id': '1',
513
- 'name': 'Umber',
514
- 'superVillains': [1]
515
- }]);
516
-
517
- run(function () {
518
- env.store.find('super-villain', 1).then(function (minion) {
519
- equal(minion.get('firstName'), 'Tom');
520
- });
521
- });
522
- });
523
-
524
- test('serialize polymorphic', function () {
525
- var tom, ray;
526
- run(function () {
527
- tom = env.store.createRecord('yellow-minion', { name: 'Alex', id: '124' });
528
- ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: 'DeathRay' });
529
- });
530
-
531
- var json = env.amsSerializer.serialize(ray._createSnapshot());
532
-
533
- deepEqual(json, {
534
- name: 'DeathRay',
535
- evil_minion_type: 'YellowMinion',
536
- evil_minion_id: '124'
537
- });
538
- });
539
-
540
- test('serialize polymorphic when type key is not camelized', function () {
541
- YellowMinion.modelName = 'yellow-minion';
542
- var tom, ray;
543
- run(function () {
544
- tom = env.store.createRecord('yellow-minion', { name: 'Alex', id: '124' });
545
- ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: 'DeathRay' });
655
+ array = env.amsSerializer.normalizeArrayResponse(env.store, HomePlanet, json_hash, null, 'findAll');
546
656
  });
547
657
 
548
- var json = env.amsSerializer.serialize(ray._createSnapshot());
549
-
550
- deepEqual(json['evil_minion_type'], 'YellowMinion');
551
- });
552
-
553
- test('serialize polymorphic when associated object is null', function () {
554
- var ray, json;
555
- run(function () {
556
- ray = env.store.createRecord('doomsday-device', { name: 'DeathRay' });
557
- json = env.amsSerializer.serialize(ray._createSnapshot());
658
+ deepEqual(array, {
659
+ 'data': [{
660
+ 'id': '1',
661
+ 'type': 'home-planet',
662
+ 'attributes': {
663
+ 'name': 'Umber'
664
+ },
665
+ 'relationships': {
666
+ 'superVillains': {
667
+ 'data': [{ 'id': '1', 'type': 'super-villain' }]
668
+ }
669
+ }
670
+ }],
671
+ 'included': [{
672
+ 'id': '1',
673
+ 'type': 'super-villain',
674
+ 'attributes': {
675
+ 'firstName': 'Tom',
676
+ 'lastName': 'Dale'
677
+ },
678
+ 'relationships': {
679
+ 'homePlanet': {
680
+ 'data': { 'id': '1', 'type': 'home-planet' }
681
+ }
682
+ }
683
+ }]
558
684
  });
559
-
560
- deepEqual(json['evil_minion_type'], null);
561
685
  });
562
686
 
563
687
  test('extractPolymorphic hasMany', function () {
@@ -570,26 +694,40 @@ define(
570
694
  };
571
695
 
572
696
  var json_hash = {
573
- mediocre_villain: { id: 1, name: 'Dr Horrible', evil_minions: [{ type: 'yellow_minion', id: 12 }] },
574
- evil_minions: [{ id: 12, name: 'Alex', doomsday_device_ids: [1] }]
697
+ mediocre_villain: { id: 1, name: 'Dr Horrible', evil_minion_ids: [{ type: 'yellow_minion', id: 12 }] },
698
+ yellow_minions: [{ id: 12, name: 'Alex' }]
575
699
  };
576
700
  var json;
577
701
 
578
702
  run(function () {
579
- json = env.amsSerializer.extractSingle(env.store, MediocreVillain, json_hash);
703
+ json = env.amsSerializer.normalizeResponse(env.store, MediocreVillain, json_hash, '1', 'findRecord');
580
704
  });
581
705
 
582
706
  deepEqual(json, {
583
- 'id': 1,
584
- 'name': 'Dr Horrible',
585
- 'evilMinions': [{
586
- type: 'yellow-minion',
587
- id: 12
707
+ 'data': {
708
+ 'id': '1',
709
+ 'type': 'mediocre-villain',
710
+ 'attributes': {
711
+ 'name': 'Dr Horrible'
712
+ },
713
+ 'relationships': {
714
+ 'evilMinions': {
715
+ 'data': [{ 'id': '12', 'type': 'yellow-minion' }]
716
+ }
717
+ }
718
+ },
719
+ 'included': [{
720
+ 'id': '12',
721
+ 'type': 'yellow-minion',
722
+ 'attributes': {
723
+ 'name': 'Alex'
724
+ },
725
+ 'relationships': {}
588
726
  }]
589
727
  });
590
728
  });
591
729
 
592
- test('extractPolymorphic', function () {
730
+ test('extractPolymorphic belongsTo', function () {
593
731
  env.registry.register('adapter:yellowMinion', DS.ActiveModelAdapter);
594
732
  EvilMinion.toString = function () {
595
733
  return 'EvilMinion';
@@ -599,22 +737,36 @@ define(
599
737
  };
600
738
 
601
739
  var json_hash = {
602
- doomsday_device: { id: 1, name: 'DeathRay', evil_minion: { type: 'yellow_minion', id: 12 } },
603
- evil_minions: [{ id: 12, name: 'Alex', doomsday_device_ids: [1] }]
740
+ doomsday_device: { id: 1, name: 'DeathRay', evil_minion_id: { type: 'yellow_minion', id: 12 } },
741
+ yellow_minions: [{ id: 12, name: 'Alex', doomsday_device_ids: [1] }]
604
742
  };
605
743
  var json;
606
744
 
607
745
  run(function () {
608
- json = env.amsSerializer.extractSingle(env.store, DoomsdayDevice, json_hash);
746
+ json = env.amsSerializer.normalizeResponse(env.store, DoomsdayDevice, json_hash, '1', 'findRecord');
609
747
  });
610
748
 
611
749
  deepEqual(json, {
612
- 'id': 1,
613
- 'name': 'DeathRay',
614
- 'evilMinion': {
615
- type: 'yellow-minion',
616
- id: 12
617
- }
750
+ 'data': {
751
+ 'id': '1',
752
+ 'type': 'doomsday-device',
753
+ 'attributes': {
754
+ 'name': 'DeathRay'
755
+ },
756
+ 'relationships': {
757
+ 'evilMinion': {
758
+ 'data': { 'id': '12', 'type': 'yellow-minion' }
759
+ }
760
+ }
761
+ },
762
+ 'included': [{
763
+ 'id': '12',
764
+ 'type': 'yellow-minion',
765
+ 'attributes': {
766
+ 'name': 'Alex'
767
+ },
768
+ 'relationships': {}
769
+ }]
618
770
  });
619
771
  });
620
772
 
@@ -625,13 +777,26 @@ define(
625
777
  };
626
778
 
627
779
  run(function () {
628
- json = env.amsSerializer.extractSingle(env.store, DoomsdayDevice, json);
780
+ json = env.amsSerializer.normalizeResponse(env.store, DoomsdayDevice, json, '1', 'findRecord');
629
781
  });
630
782
 
631
783
  deepEqual(json, {
632
- 'id': 1,
633
- 'name': 'DeathRay',
634
- 'evilMinion': undefined
784
+ 'data': {
785
+ 'id': '1',
786
+ 'type': 'doomsday-device',
787
+ 'attributes': {
788
+ 'name': 'DeathRay'
789
+ },
790
+ 'relationships': {}
791
+ },
792
+ 'included': [{
793
+ 'id': '12',
794
+ 'type': 'evil-minion',
795
+ 'attributes': {
796
+ 'name': 'Alex'
797
+ },
798
+ 'relationships': {}
799
+ }]
635
800
  });
636
801
  });
637
802
 
@@ -641,45 +806,45 @@ define(
641
806
  };
642
807
 
643
808
  run(function () {
644
- json = env.amsSerializer.extractSingle(env.store, MediocreVillain, json);
809
+ json = env.amsSerializer.normalizeResponse(env.store, MediocreVillain, json, '1', 'findRecord');
645
810
  });
646
811
 
647
812
  deepEqual(json, {
648
- 'id': 1,
649
- 'name': 'Dr Horrible',
650
- 'evilMinions': undefined
813
+ 'data': {
814
+ 'id': '1',
815
+ 'type': 'mediocre-villain',
816
+ 'attributes': {
817
+ 'name': 'Dr Horrible'
818
+ },
819
+ 'relationships': {}
820
+ },
821
+ 'included': []
651
822
  });
652
823
  });
653
824
 
654
825
  test('extractPolymorphic does not break hasMany relationships', function () {
655
826
  var json = {
656
- mediocre_villain: { id: 1, name: 'Dr. Evil', evil_minions: [] }
827
+ mediocre_villain: { id: 1, name: 'Dr. Evil', evil_minion_ids: [] }
657
828
  };
658
829
 
659
830
  run(function () {
660
- json = env.amsSerializer.extractSingle(env.store, MediocreVillain, json);
831
+ json = env.amsSerializer.normalizeResponse(env.store, MediocreVillain, json, '1', 'findRecord');
661
832
  });
662
833
 
663
834
  deepEqual(json, {
664
- 'id': 1,
665
- 'name': 'Dr. Evil',
666
- 'evilMinions': []
667
- });
668
- });
669
-
670
- test('extractErrors camelizes keys', function () {
671
- var payload = {
672
- errors: {
673
- first_name: ['firstName not evil enough']
674
- }
675
- };
676
-
677
- run(function () {
678
- payload = env.amsSerializer.extractErrors(env.store, SuperVillain, payload);
679
- });
680
-
681
- deepEqual(payload, {
682
- firstName: ['firstName not evil enough']
835
+ 'data': {
836
+ 'id': '1',
837
+ 'type': 'mediocre-villain',
838
+ 'attributes': {
839
+ 'name': 'Dr. Evil'
840
+ },
841
+ 'relationships': {
842
+ 'evilMinions': {
843
+ 'data': []
844
+ }
845
+ }
846
+ },
847
+ 'included': []
683
848
  });
684
849
  });
685
850
  }
@@ -687,7 +852,7 @@ define(
687
852
 
688
853
 
689
854
  define(
690
- "activemodel-adapter/tests/unit/adapter/path-for-type-test",
855
+ "activemodel-adapter/tests/integration/active-model-serializer-test",
691
856
  ["exports"],
692
857
  function(__exports__) {
693
858
  "use strict";
@@ -696,378 +861,366 @@ define(
696
861
  __exports__[name] = value;
697
862
  }
698
863
 
699
- var env, adapter;
700
- module('unit/adapter/path_for_type - DS.ActiveModelAdapter#pathForType', {
864
+ var get = Ember.get;
865
+ var HomePlanet, league, SuperVillain, EvilMinion, YellowMinion, DoomsdayDevice, MediocreVillain, env;
866
+ var run = Ember.run;
867
+
868
+ module('integration/active_model - ActiveModelSerializer', {
701
869
  setup: function () {
870
+ SuperVillain = DS.Model.extend({
871
+ firstName: DS.attr('string'),
872
+ lastName: DS.attr('string'),
873
+ homePlanet: DS.belongsTo('home-planet'),
874
+ evilMinions: DS.hasMany('evil-minion')
875
+ });
876
+ HomePlanet = DS.Model.extend({
877
+ name: DS.attr('string'),
878
+ superVillains: DS.hasMany('super-villain', { async: true })
879
+ });
880
+ EvilMinion = DS.Model.extend({
881
+ superVillain: DS.belongsTo('super-villain'),
882
+ name: DS.attr('string')
883
+ });
884
+ YellowMinion = EvilMinion.extend();
885
+ DoomsdayDevice = DS.Model.extend({
886
+ name: DS.attr('string'),
887
+ evilMinion: DS.belongsTo('evil-minion', { polymorphic: true })
888
+ });
889
+ MediocreVillain = DS.Model.extend({
890
+ name: DS.attr('string'),
891
+ evilMinions: DS.hasMany('evil-minion', { polymorphic: true })
892
+ });
702
893
  env = setupStore({
703
- adapter: DS.ActiveModelAdapter
894
+ superVillain: SuperVillain,
895
+ homePlanet: HomePlanet,
896
+ evilMinion: EvilMinion,
897
+ yellowMinion: YellowMinion,
898
+ doomsdayDevice: DoomsdayDevice,
899
+ mediocreVillain: MediocreVillain
704
900
  });
901
+ env.store.modelFor('super-villain');
902
+ env.store.modelFor('home-planet');
903
+ env.store.modelFor('evil-minion');
904
+ env.store.modelFor('yellow-minion');
905
+ env.store.modelFor('doomsday-device');
906
+ env.store.modelFor('mediocre-villain');
907
+ env.registry.register('serializer:application', DS.ActiveModelSerializer);
908
+ env.registry.register('serializer:-active-model', DS.ActiveModelSerializer);
909
+ env.registry.register('adapter:-active-model', DS.ActiveModelAdapter);
910
+ env.amsSerializer = env.container.lookup('serializer:-active-model');
911
+ env.amsAdapter = env.container.lookup('adapter:-active-model');
912
+ },
705
913
 
706
- adapter = env.adapter;
914
+ teardown: function () {
915
+ run(env.store, 'destroy');
707
916
  }
708
917
  });
709
918
 
710
- test('pathForType - works with camelized types', function () {
711
- equal(adapter.pathForType('superUser'), 'super_users');
712
- });
919
+ test('serialize', function () {
920
+ var tom;
921
+ run(function () {
922
+ league = env.store.createRecord('home-planet', { name: 'Villain League', id: '123' });
923
+ tom = env.store.createRecord('super-villain', { firstName: 'Tom', lastName: 'Dale', homePlanet: league });
924
+ });
713
925
 
714
- test('pathForType - works with dasherized types', function () {
715
- equal(adapter.pathForType('super-user'), 'super_users');
716
- });
926
+ var json = env.amsSerializer.serialize(tom._createSnapshot());
717
927
 
718
- test('pathForType - works with underscored types', function () {
719
- equal(adapter.pathForType('super_user'), 'super_users');
928
+ deepEqual(json, {
929
+ first_name: 'Tom',
930
+ last_name: 'Dale',
931
+ home_planet_id: get(league, 'id')
932
+ });
720
933
  });
721
- }
722
- );
723
934
 
935
+ test('serializeIntoHash', function () {
936
+ run(function () {
937
+ league = env.store.createRecord('home-planet', { name: 'Umber', id: '123' });
938
+ });
939
+ var json = {};
724
940
 
725
- define(
726
- "ember-data/tests/helpers/custom-adapter",
727
- ["exports"],
728
- function(__exports__) {
729
- "use strict";
730
-
731
- function __es6_export__(name, value) {
732
- __exports__[name] = value;
733
- }
941
+ env.amsSerializer.serializeIntoHash(json, HomePlanet, league._createSnapshot());
734
942
 
735
- __es6_export__("default", function (env, adapterDefinition) {
736
- var adapter = adapterDefinition;
737
- if (!DS.Adapter.detect(adapterDefinition)) {
738
- adapter = DS.Adapter.extend(adapterDefinition);
739
- }
740
- var store = env.store;
741
- env.registry.register('adapter:-custom', adapter);
742
- Ember.run(function () {
743
- return store.set('adapter', '-custom');
943
+ deepEqual(json, {
944
+ home_planet: {
945
+ name: 'Umber'
946
+ }
744
947
  });
745
948
  });
746
- }
747
- );
748
-
749
-
750
- define(
751
- "ember-data/tests/integration/adapter/build-url-mixin-test",
752
- ["exports"],
753
- function(__exports__) {
754
- "use strict";
755
949
 
756
- function __es6_export__(name, value) {
757
- __exports__[name] = value;
758
- }
950
+ test('serializeIntoHash with decamelized types', function () {
951
+ HomePlanet.modelName = 'home-planet';
952
+ run(function () {
953
+ league = env.store.createRecord('home-planet', { name: 'Umber', id: '123' });
954
+ });
955
+ var json = {};
759
956
 
760
- var env, store, adapter, Post, Comment, SuperUser;
761
- var passedUrl;
762
- var run = Ember.run;
957
+ env.amsSerializer.serializeIntoHash(json, HomePlanet, league._createSnapshot());
763
958
 
764
- module("integration/adapter/build-url-mixin - BuildURLMixin with RESTAdapter", {
765
- setup: function () {
766
- Post = DS.Model.extend({
767
- name: DS.attr("string")
768
- });
959
+ deepEqual(json, {
960
+ home_planet: {
961
+ name: 'Umber'
962
+ }
963
+ });
964
+ });
769
965
 
770
- Post.toString = function () {
771
- return "Post";
772
- };
966
+ test('normalize', function () {
967
+ SuperVillain.reopen({
968
+ yellowMinion: DS.belongsTo('yellow-minion')
969
+ });
773
970
 
774
- Comment = DS.Model.extend({
775
- name: DS.attr("string")
776
- });
971
+ var superVillain_hash = { first_name: 'Tom', last_name: 'Dale', home_planet_id: '123', evil_minion_ids: [1, 2] };
777
972
 
778
- SuperUser = DS.Model.extend();
973
+ var json = env.amsSerializer.normalize(SuperVillain, superVillain_hash, 'superVillain');
779
974
 
780
- env = setupStore({
781
- post: Post,
782
- comment: Comment,
783
- superUser: SuperUser,
784
- adapter: DS.RESTAdapter
785
- });
975
+ deepEqual(json, {
976
+ firstName: 'Tom',
977
+ lastName: 'Dale',
978
+ homePlanet: '123',
979
+ evilMinions: [1, 2]
980
+ });
981
+ });
786
982
 
787
- store = env.store;
788
- adapter = env.adapter;
983
+ test('normalize links', function () {
984
+ var home_planet = {
985
+ id: '1',
986
+ name: 'Umber',
987
+ links: { super_villains: '/api/super_villians/1' }
988
+ };
789
989
 
790
- Post = store.modelFor("post");
791
- Comment = store.modelFor("comment");
792
- SuperUser = store.modelFor("super-user");
990
+ var json = env.amsSerializer.normalize(HomePlanet, home_planet, 'homePlanet');
793
991
 
794
- passedUrl = null;
795
- }
992
+ equal(json.links.superVillains, '/api/super_villians/1', 'normalize links');
796
993
  });
797
994
 
798
- function ajaxResponse(value) {
799
- adapter.ajax = function (url, verb, hash) {
800
- passedUrl = url;
995
+ test('extractSingle', function () {
996
+ env.registry.register('adapter:superVillain', DS.ActiveModelAdapter);
801
997
 
802
- return run(Ember.RSVP, "resolve", Ember.copy(value, true));
998
+ var json_hash = {
999
+ home_planet: { id: '1', name: 'Umber', super_villain_ids: [1] },
1000
+ super_villains: [{
1001
+ id: '1',
1002
+ first_name: 'Tom',
1003
+ last_name: 'Dale',
1004
+ home_planet_id: '1'
1005
+ }]
803
1006
  };
804
- }
805
1007
 
806
- test("buildURL - with host and namespace", function () {
1008
+ var json;
807
1009
  run(function () {
808
- adapter.setProperties({
809
- host: "http://example.com",
810
- namespace: "api/v1"
811
- });
1010
+ json = env.amsSerializer.extractSingle(env.store, HomePlanet, json_hash);
812
1011
  });
813
1012
 
814
- ajaxResponse({ posts: [{ id: 1 }] });
815
-
816
- run(store, "find", "post", 1).then(async(function (post) {
817
- equal(passedUrl, "http://example.com/api/v1/posts/1");
818
- }));
819
- });
1013
+ deepEqual(json, {
1014
+ 'id': '1',
1015
+ 'name': 'Umber',
1016
+ 'superVillains': [1]
1017
+ });
820
1018
 
821
- test("buildURL - with relative paths in links", function () {
822
1019
  run(function () {
823
- adapter.setProperties({
824
- host: "http://example.com",
825
- namespace: "api/v1"
1020
+ env.store.findRecord('super-villain', 1).then(function (minion) {
1021
+ equal(minion.get('firstName'), 'Tom');
826
1022
  });
827
1023
  });
828
- Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
829
- Comment.reopen({ post: DS.belongsTo("post") });
1024
+ });
830
1025
 
831
- ajaxResponse({ posts: [{ id: 1, links: { comments: "comments" } }] });
1026
+ test('extractArray', function () {
1027
+ env.registry.register('adapter:superVillain', DS.ActiveModelAdapter);
1028
+ var array;
832
1029
 
833
- run(store, "find", "post", "1").then(async(function (post) {
834
- ajaxResponse({ comments: [{ id: 1 }] });
835
- return post.get("comments");
836
- })).then(async(function (comments) {
837
- equal(passedUrl, "http://example.com/api/v1/posts/1/comments");
838
- }));
839
- });
1030
+ var json_hash = {
1031
+ home_planets: [{ id: '1', name: 'Umber', super_villain_ids: [1] }],
1032
+ super_villains: [{ id: '1', first_name: 'Tom', last_name: 'Dale', home_planet_id: '1' }]
1033
+ };
840
1034
 
841
- test("buildURL - with absolute paths in links", function () {
842
1035
  run(function () {
843
- adapter.setProperties({
844
- host: "http://example.com",
845
- namespace: "api/v1"
846
- });
1036
+ array = env.amsSerializer.extractArray(env.store, HomePlanet, json_hash);
847
1037
  });
848
- Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
849
- Comment.reopen({ post: DS.belongsTo("post") });
850
1038
 
851
- ajaxResponse({ posts: [{ id: 1, links: { comments: "/api/v1/posts/1/comments" } }] });
852
-
853
- run(store, "find", "post", 1).then(async(function (post) {
854
- ajaxResponse({ comments: [{ id: 1 }] });
855
- return post.get("comments");
856
- })).then(async(function (comments) {
857
- equal(passedUrl, "http://example.com/api/v1/posts/1/comments");
858
- }));
859
- });
1039
+ deepEqual(array, [{
1040
+ 'id': '1',
1041
+ 'name': 'Umber',
1042
+ 'superVillains': [1]
1043
+ }]);
860
1044
 
861
- test("buildURL - with absolute paths in links and protocol relative host", function () {
862
1045
  run(function () {
863
- adapter.setProperties({
864
- host: "//example.com",
865
- namespace: "api/v1"
1046
+ env.store.findRecord('super-villain', 1).then(function (minion) {
1047
+ equal(minion.get('firstName'), 'Tom');
866
1048
  });
867
1049
  });
868
- Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
869
- Comment.reopen({ post: DS.belongsTo("post") });
870
-
871
- ajaxResponse({ posts: [{ id: 1, links: { comments: "/api/v1/posts/1/comments" } }] });
872
-
873
- run(store, "find", "post", 1).then(async(function (post) {
874
- ajaxResponse({ comments: [{ id: 1 }] });
875
- return post.get("comments");
876
- })).then(async(function (comments) {
877
- equal(passedUrl, "//example.com/api/v1/posts/1/comments");
878
- }));
879
1050
  });
880
1051
 
881
- test("buildURL - with full URLs in links", function () {
882
- adapter.setProperties({
883
- host: "http://example.com",
884
- namespace: "api/v1"
1052
+ test('serialize polymorphic', function () {
1053
+ var tom, ray;
1054
+ run(function () {
1055
+ tom = env.store.createRecord('yellow-minion', { name: 'Alex', id: '124' });
1056
+ ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: 'DeathRay' });
885
1057
  });
886
- Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
887
- Comment.reopen({ post: DS.belongsTo("post") });
888
1058
 
889
- ajaxResponse({
890
- posts: [{ id: 1,
891
- links: { comments: "http://example.com/api/v1/posts/1/comments" }
892
- }]
893
- });
1059
+ var json = env.amsSerializer.serialize(ray._createSnapshot());
894
1060
 
895
- run(function () {
896
- store.find("post", 1).then(async(function (post) {
897
- ajaxResponse({ comments: [{ id: 1 }] });
898
- return post.get("comments");
899
- })).then(async(function (comments) {
900
- equal(passedUrl, "http://example.com/api/v1/posts/1/comments");
901
- }));
1061
+ deepEqual(json, {
1062
+ name: 'DeathRay',
1063
+ evil_minion_type: 'YellowMinion',
1064
+ evil_minion_id: '124'
902
1065
  });
903
1066
  });
904
1067
 
905
- test("buildURL - with camelized names", function () {
906
- adapter.setProperties({
907
- pathForType: function (type) {
908
- var decamelized = Ember.String.decamelize(type);
909
- return Ember.String.underscore(Ember.String.pluralize(decamelized));
910
- }
1068
+ test('serialize polymorphic when type key is not camelized', function () {
1069
+ YellowMinion.modelName = 'yellow-minion';
1070
+ var tom, ray;
1071
+ run(function () {
1072
+ tom = env.store.createRecord('yellow-minion', { name: 'Alex', id: '124' });
1073
+ ray = env.store.createRecord('doomsday-device', { evilMinion: tom, name: 'DeathRay' });
911
1074
  });
912
1075
 
913
- ajaxResponse({ superUsers: [{ id: 1 }] });
1076
+ var json = env.amsSerializer.serialize(ray._createSnapshot());
1077
+
1078
+ deepEqual(json['evil_minion_type'], 'YellowMinion');
1079
+ });
914
1080
 
1081
+ test('serialize polymorphic when associated object is null', function () {
1082
+ var ray, json;
915
1083
  run(function () {
916
- store.find("super-user", 1).then(async(function (post) {
917
- equal(passedUrl, "/super_users/1");
918
- }));
1084
+ ray = env.store.createRecord('doomsday-device', { name: 'DeathRay' });
1085
+ json = env.amsSerializer.serialize(ray._createSnapshot());
919
1086
  });
1087
+
1088
+ deepEqual(json['evil_minion_type'], null);
920
1089
  });
921
1090
 
922
- test("buildURL - buildURL takes a record from find", function () {
923
- Comment.reopen({ post: DS.belongsTo("post") });
924
- adapter.buildURL = function (type, id, snapshot) {
925
- return "/posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/" + snapshot.id;
1091
+ test('extractPolymorphic hasMany', function () {
1092
+ env.registry.register('adapter:yellowMinion', DS.ActiveModelAdapter);
1093
+ MediocreVillain.toString = function () {
1094
+ return 'MediocreVillain';
1095
+ };
1096
+ YellowMinion.toString = function () {
1097
+ return 'YellowMinion';
926
1098
  };
927
1099
 
928
- ajaxResponse({ comments: [{ id: 1 }] });
1100
+ var json_hash = {
1101
+ mediocre_villain: { id: 1, name: 'Dr Horrible', evil_minions: [{ type: 'yellow_minion', id: 12 }] },
1102
+ evil_minions: [{ id: 12, name: 'Alex', doomsday_device_ids: [1] }]
1103
+ };
1104
+ var json;
929
1105
 
930
- var post;
931
1106
  run(function () {
932
- post = store.push("post", { id: 2 });
1107
+ json = env.amsSerializer.extractSingle(env.store, MediocreVillain, json_hash);
933
1108
  });
934
1109
 
935
- run(function () {
936
- store.find("comment", 1, { post: post }).then(async(function (post) {
937
- equal(passedUrl, "/posts/2/comments/1");
938
- }));
1110
+ deepEqual(json, {
1111
+ 'id': 1,
1112
+ 'name': 'Dr Horrible',
1113
+ 'evilMinions': [{
1114
+ type: 'yellow-minion',
1115
+ id: 12
1116
+ }]
939
1117
  });
940
1118
  });
941
1119
 
942
- test("buildURL - buildURL takes the records from findMany", function () {
943
- Comment.reopen({ post: DS.belongsTo("post") });
944
- Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
945
-
946
- adapter.buildURL = function (type, ids, snapshots) {
947
- if (Ember.isArray(snapshots)) {
948
- return "/posts/" + snapshots.get("firstObject").belongsTo("post", { id: true }) + "/comments/";
949
- }
950
- return "";
1120
+ test('extractPolymorphic', function () {
1121
+ env.registry.register('adapter:yellowMinion', DS.ActiveModelAdapter);
1122
+ EvilMinion.toString = function () {
1123
+ return 'EvilMinion';
1124
+ };
1125
+ YellowMinion.toString = function () {
1126
+ return 'YellowMinion';
951
1127
  };
952
- adapter.coalesceFindRequests = true;
953
1128
 
954
- ajaxResponse({ comments: [{ id: 1 }, { id: 2 }, { id: 3 }] });
955
- var post;
1129
+ var json_hash = {
1130
+ doomsday_device: { id: 1, name: 'DeathRay', evil_minion: { type: 'yellow_minion', id: 12 } },
1131
+ evil_minions: [{ id: 12, name: 'Alex', doomsday_device_ids: [1] }]
1132
+ };
1133
+ var json;
956
1134
 
957
1135
  run(function () {
958
- post = store.push("post", { id: 2, comments: [1, 2, 3] });
959
- post.get("comments").then(async(function (post) {
960
- equal(passedUrl, "/posts/2/comments/");
961
- }));
1136
+ json = env.amsSerializer.extractSingle(env.store, DoomsdayDevice, json_hash);
1137
+ });
1138
+
1139
+ deepEqual(json, {
1140
+ 'id': 1,
1141
+ 'name': 'DeathRay',
1142
+ 'evilMinion': {
1143
+ type: 'yellow-minion',
1144
+ id: 12
1145
+ }
962
1146
  });
963
1147
  });
964
1148
 
965
- test("buildURL - buildURL takes a record from create", function () {
966
- Comment.reopen({ post: DS.belongsTo("post") });
967
- adapter.buildURL = function (type, id, snapshot) {
968
- return "/posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/";
1149
+ test('extractPolymorphic when the related data is not specified', function () {
1150
+ var json = {
1151
+ doomsday_device: { id: 1, name: 'DeathRay' },
1152
+ evil_minions: [{ id: 12, name: 'Alex', doomsday_device_ids: [1] }]
969
1153
  };
970
1154
 
971
- ajaxResponse({ comments: [{ id: 1 }] });
972
-
973
1155
  run(function () {
974
- var post = store.push("post", { id: 2 });
975
- var comment = store.createRecord("comment");
976
- comment.set("post", post);
977
- comment.save().then(async(function (post) {
978
- equal(passedUrl, "/posts/2/comments/");
979
- }));
1156
+ json = env.amsSerializer.extractSingle(env.store, DoomsdayDevice, json);
980
1157
  });
981
- });
982
1158
 
983
- test("buildURL - buildURL takes a record from create to query a resolved async belongsTo relationship", function () {
984
- Comment.reopen({ post: DS.belongsTo("post", { async: true }) });
1159
+ deepEqual(json, {
1160
+ 'id': 1,
1161
+ 'name': 'DeathRay',
1162
+ 'evilMinion': undefined
1163
+ });
1164
+ });
985
1165
 
986
- ajaxResponse({ posts: [{ id: 2 }] });
1166
+ test('extractPolymorphic hasMany when the related data is not specified', function () {
1167
+ var json = {
1168
+ mediocre_villain: { id: 1, name: 'Dr Horrible' }
1169
+ };
987
1170
 
988
1171
  run(function () {
989
- store.find("post", 2).then(async(function (post) {
990
- equal(post.get("id"), 2);
991
-
992
- adapter.buildURL = function (type, id, snapshot) {
993
- return "/posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/";
994
- };
995
-
996
- ajaxResponse({ comments: [{ id: 1 }] });
1172
+ json = env.amsSerializer.extractSingle(env.store, MediocreVillain, json);
1173
+ });
997
1174
 
998
- var comment = store.createRecord("comment");
999
- comment.set("post", post);
1000
- comment.save().then(async(function (post) {
1001
- equal(passedUrl, "/posts/2/comments/");
1002
- }));
1003
- }));
1175
+ deepEqual(json, {
1176
+ 'id': 1,
1177
+ 'name': 'Dr Horrible',
1178
+ 'evilMinions': undefined
1004
1179
  });
1005
1180
  });
1006
1181
 
1007
- test("buildURL - buildURL takes a record from update", function () {
1008
- Comment.reopen({ post: DS.belongsTo("post") });
1009
- adapter.buildURL = function (type, id, snapshot) {
1010
- return "/posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/" + snapshot.id;
1182
+ test('extractPolymorphic does not break hasMany relationships', function () {
1183
+ var json = {
1184
+ mediocre_villain: { id: 1, name: 'Dr. Evil', evil_minions: [] }
1011
1185
  };
1012
1186
 
1013
- ajaxResponse({ comments: [{ id: 1 }] });
1014
-
1015
- var post, comment;
1016
1187
  run(function () {
1017
- post = store.push("post", { id: 2 });
1018
- comment = store.push("comment", { id: 1 });
1019
- comment.set("post", post);
1188
+ json = env.amsSerializer.extractSingle(env.store, MediocreVillain, json);
1020
1189
  });
1021
- run(function () {
1022
- comment.save().then(async(function (post) {
1023
- equal(passedUrl, "/posts/2/comments/1");
1024
- }));
1190
+
1191
+ deepEqual(json, {
1192
+ 'id': 1,
1193
+ 'name': 'Dr. Evil',
1194
+ 'evilMinions': []
1025
1195
  });
1026
1196
  });
1027
1197
 
1028
- test("buildURL - buildURL takes a record from delete", function () {
1029
- Comment.reopen({ post: DS.belongsTo("post") });
1030
- Post.reopen({ comments: DS.hasMany("comment") });
1031
- adapter.buildURL = function (type, id, snapshot) {
1032
- return "posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/" + snapshot.id;
1198
+ test('extractErrors camelizes keys', function () {
1199
+ var error = {
1200
+ errors: [{
1201
+ source: {
1202
+ pointer: 'data/attributes/first_name'
1203
+ },
1204
+ details: 'firstName not evil enough'
1205
+ }]
1033
1206
  };
1034
1207
 
1035
- ajaxResponse({ comments: [{ id: 1 }] });
1036
-
1037
- var post, comment;
1038
- run(function () {
1039
- post = store.push("post", { id: 2 });
1040
- comment = store.push("comment", { id: 1 });
1208
+ var payload;
1041
1209
 
1042
- comment.set("post", post);
1043
- comment.deleteRecord();
1044
- });
1045
1210
  run(function () {
1046
- comment.save().then(async(function (post) {
1047
- equal(passedUrl, "posts/2/comments/1");
1048
- }));
1211
+ payload = env.amsSerializer.extractErrors(env.store, SuperVillain, error);
1049
1212
  });
1050
- });
1051
1213
 
1052
- test("buildURL - with absolute namespace", function () {
1053
- run(function () {
1054
- adapter.setProperties({
1055
- namespace: "/api/v1"
1056
- });
1214
+ deepEqual(payload, {
1215
+ firstName: ['firstName not evil enough']
1057
1216
  });
1058
-
1059
- ajaxResponse({ posts: [{ id: 1 }] });
1060
-
1061
- run(store, "find", "post", 1).then(async(function (post) {
1062
- equal(passedUrl, "/api/v1/posts/1");
1063
- }));
1064
1217
  });
1065
1218
  }
1066
1219
  );
1067
1220
 
1068
1221
 
1069
1222
  define(
1070
- "ember-data/tests/integration/adapter/find-all-test",
1223
+ "activemodel-adapter/tests/unit/adapter/path-for-type-test",
1071
1224
  ["exports"],
1072
1225
  function(__exports__) {
1073
1226
  "use strict";
@@ -1076,145 +1229,59 @@ define(
1076
1229
  __exports__[name] = value;
1077
1230
  }
1078
1231
 
1079
- var get = Ember.get;
1080
- var Person, store, allRecords;
1081
- var run = Ember.run;
1082
- var env;
1083
-
1084
- module('integration/adapter/find_all - Finding All Records of a Type', {
1232
+ var env, adapter;
1233
+ module('unit/adapter/path_for_type - DS.ActiveModelAdapter#pathForType', {
1085
1234
  setup: function () {
1086
- Person = DS.Model.extend({
1087
- updatedAt: DS.attr('string'),
1088
- name: DS.attr('string'),
1089
- firstName: DS.attr('string'),
1090
- lastName: DS.attr('string')
1091
- });
1092
-
1093
- allRecords = null;
1094
-
1095
1235
  env = setupStore({
1096
- person: Person
1236
+ adapter: DS.ActiveModelAdapter
1097
1237
  });
1098
- store = env.store;
1099
- },
1100
1238
 
1101
- teardown: function () {
1102
- run(function () {
1103
- if (allRecords) {
1104
- allRecords.destroy();
1105
- }
1106
- store.destroy();
1107
- });
1239
+ adapter = env.adapter;
1108
1240
  }
1109
1241
  });
1110
1242
 
1111
- test('When all records for a type are requested, the store should call the adapter\'s `findAll` method.', function () {
1112
- expect(5);
1113
-
1114
- env.registry.register('adapter:person', DS.Adapter.extend({
1115
- findAll: function (store, type, since) {
1116
- // this will get called twice
1117
- ok(true, 'the adapter\'s findAll method should be invoked');
1118
-
1119
- return Ember.RSVP.resolve([{ id: 1, name: 'Braaaahm Dale' }]);
1120
- }
1121
- }));
1122
-
1123
- var allRecords;
1124
-
1125
- run(function () {
1126
- store.find('person').then(function (all) {
1127
- allRecords = all;
1128
- equal(get(all, 'length'), 1, 'the record array\'s length is 1 after a record is loaded into it');
1129
- equal(all.objectAt(0).get('name'), 'Braaaahm Dale', 'the first item in the record array is Braaaahm Dale');
1130
- });
1131
- });
1132
-
1133
- run(function () {
1134
- store.find('person').then(function (all) {
1135
- // Only one record array per type should ever be created (identity map)
1136
- strictEqual(allRecords, all, 'the same record array is returned every time all records of a type are requested');
1137
- });
1138
- });
1243
+ test('pathForType - works with camelized types', function () {
1244
+ equal(adapter.pathForType('superUser'), 'super_users');
1139
1245
  });
1140
1246
 
1141
- test('When all records for a type are requested, a rejection should reject the promise', function () {
1142
- expect(5);
1143
-
1144
- var count = 0;
1145
- env.registry.register('adapter:person', DS.Adapter.extend({
1146
- findAll: function (store, type, since) {
1147
- // this will get called twice
1148
- ok(true, 'the adapter\'s findAll method should be invoked');
1149
-
1150
- if (count++ === 0) {
1151
- return Ember.RSVP.reject();
1152
- } else {
1153
- return Ember.RSVP.resolve([{ id: 1, name: 'Braaaahm Dale' }]);
1154
- }
1155
- }
1156
- }));
1157
-
1158
- var allRecords;
1159
-
1160
- run(function () {
1161
- store.find('person').then(null, function () {
1162
- ok(true, 'The rejection should get here');
1163
- return store.find('person');
1164
- }).then(function (all) {
1165
- allRecords = all;
1166
- equal(get(all, 'length'), 1, 'the record array\'s length is 1 after a record is loaded into it');
1167
- equal(all.objectAt(0).get('name'), 'Braaaahm Dale', 'the first item in the record array is Braaaahm Dale');
1168
- });
1169
- });
1247
+ test('pathForType - works with dasherized types', function () {
1248
+ equal(adapter.pathForType('super-user'), 'super_users');
1170
1249
  });
1171
1250
 
1172
- test('When all records for a type are requested, records that are already loaded should be returned immediately.', function () {
1173
- expect(3);
1174
- store = createStore({
1175
- adapter: DS.Adapter.extend(),
1176
- person: Person
1177
- });
1178
-
1179
- run(function () {
1180
- // Load a record from the server
1181
- store.push('person', { id: 1, name: 'Jeremy Ashkenas' });
1182
- // Create a new, unsaved record in the store
1183
- store.createRecord('person', { name: 'Alex MacCaw' });
1184
- });
1185
-
1186
- allRecords = store.all('person');
1187
-
1188
- equal(get(allRecords, 'length'), 2, 'the record array\'s length is 2');
1189
- equal(allRecords.objectAt(0).get('name'), 'Jeremy Ashkenas', 'the first item in the record array is Jeremy Ashkenas');
1190
- equal(allRecords.objectAt(1).get('name'), 'Alex MacCaw', 'the second item in the record array is Alex MacCaw');
1251
+ test('pathForType - works with underscored types', function () {
1252
+ equal(adapter.pathForType('super_user'), 'super_users');
1191
1253
  });
1254
+ }
1255
+ );
1192
1256
 
1193
- test('When all records for a type are requested, records that are created on the client should be added to the record array.', function () {
1194
- expect(3);
1195
-
1196
- store = createStore({
1197
- adapter: DS.Adapter.extend(),
1198
- person: Person
1199
- });
1200
1257
 
1201
- allRecords = store.all('person');
1258
+ define(
1259
+ "ember-data/tests/helpers/custom-adapter",
1260
+ ["exports"],
1261
+ function(__exports__) {
1262
+ "use strict";
1202
1263
 
1203
- equal(get(allRecords, 'length'), 0, 'precond - the record array\'s length is zero before any records are loaded');
1264
+ function __es6_export__(name, value) {
1265
+ __exports__[name] = value;
1266
+ }
1204
1267
 
1205
- run(function () {
1206
- store.createRecord('person', { name: 'Carsten Nielsen' });
1268
+ __es6_export__("default", function (env, adapterDefinition) {
1269
+ var adapter = adapterDefinition;
1270
+ if (!DS.Adapter.detect(adapterDefinition)) {
1271
+ adapter = DS.Adapter.extend(adapterDefinition);
1272
+ }
1273
+ var store = env.store;
1274
+ env.registry.register('adapter:-custom', adapter);
1275
+ Ember.run(function () {
1276
+ return store.set('adapter', '-custom');
1207
1277
  });
1208
-
1209
- equal(get(allRecords, 'length'), 1, 'the record array\'s length is 1');
1210
- equal(allRecords.objectAt(0).get('name'), 'Carsten Nielsen', 'the first item in the record array is Carsten Nielsen');
1211
1278
  });
1212
1279
  }
1213
1280
  );
1214
1281
 
1215
1282
 
1216
1283
  define(
1217
- "ember-data/tests/integration/adapter/find-test",
1284
+ "ember-data/tests/integration/adapter/build-url-mixin-test",
1218
1285
  ["exports"],
1219
1286
  function(__exports__) {
1220
1287
  "use strict";
@@ -1223,151 +1290,345 @@ define(
1223
1290
  __exports__[name] = value;
1224
1291
  }
1225
1292
 
1226
- var Person, store, env;
1293
+ var env, store, adapter, Post, Comment, SuperUser;
1294
+ var passedUrl;
1227
1295
  var run = Ember.run;
1228
1296
 
1229
- module('integration/adapter/find - Finding Records', {
1297
+ module("integration/adapter/build-url-mixin - BuildURLMixin with RESTAdapter", {
1230
1298
  setup: function () {
1231
- Person = DS.Model.extend({
1232
- updatedAt: DS.attr('string'),
1233
- name: DS.attr('string'),
1234
- firstName: DS.attr('string'),
1235
- lastName: DS.attr('string')
1299
+ Post = DS.Model.extend({
1300
+ name: DS.attr("string")
1301
+ });
1302
+
1303
+ Post.toString = function () {
1304
+ return "Post";
1305
+ };
1306
+
1307
+ Comment = DS.Model.extend({
1308
+ name: DS.attr("string")
1236
1309
  });
1237
1310
 
1311
+ SuperUser = DS.Model.extend();
1312
+
1238
1313
  env = setupStore({
1239
- person: Person
1314
+ post: Post,
1315
+ comment: Comment,
1316
+ superUser: SuperUser,
1317
+ adapter: DS.RESTAdapter
1240
1318
  });
1319
+
1241
1320
  store = env.store;
1242
- },
1321
+ adapter = env.adapter;
1243
1322
 
1244
- teardown: function () {
1245
- run(store, 'destroy');
1323
+ Post = store.modelFor("post");
1324
+ Comment = store.modelFor("comment");
1325
+ SuperUser = store.modelFor("super-user");
1326
+
1327
+ passedUrl = null;
1246
1328
  }
1247
1329
  });
1248
1330
 
1249
- test('It raises an assertion when no type is passed', function () {
1331
+ function ajaxResponse(value) {
1332
+ adapter.ajax = function (url, verb, hash) {
1333
+ passedUrl = url;
1250
1334
 
1251
- expectAssertion(function () {
1252
- store.find();
1253
- }, 'You need to pass a type to the store\'s find method');
1254
- });
1335
+ return run(Ember.RSVP, "resolve", Ember.copy(value, true));
1336
+ };
1337
+ }
1255
1338
 
1256
- test('It raises an assertion when `undefined` is passed as id (#1705)', function () {
1339
+ test("buildURL - with host and namespace", function () {
1340
+ run(function () {
1341
+ adapter.setProperties({
1342
+ host: "http://example.com",
1343
+ namespace: "api/v1"
1344
+ });
1345
+ });
1257
1346
 
1258
- expectAssertion(function () {
1259
- store.find('person', undefined);
1260
- }, 'You may not pass `undefined` as id to the store\'s find method');
1347
+ ajaxResponse({ posts: [{ id: 1 }] });
1261
1348
 
1262
- expectAssertion(function () {
1263
- store.find('person', null);
1264
- }, 'You may not pass `null` as id to the store\'s find method');
1349
+ run(store, "findRecord", "post", 1).then(async(function (post) {
1350
+ equal(passedUrl, "http://example.com/api/v1/posts/1");
1351
+ }));
1265
1352
  });
1266
1353
 
1267
- test('When a single record is requested, the adapter\'s find method should be called unless it\'s loaded.', function () {
1268
- expect(2);
1269
-
1270
- var count = 0;
1271
-
1272
- env.registry.register('adapter:person', DS.Adapter.extend({
1273
- find: function (store, type, id, snapshot) {
1274
- equal(type, Person, 'the find method is called with the correct type');
1275
- equal(count, 0, 'the find method is only called once');
1354
+ test("buildURL - with relative paths in links", function () {
1355
+ run(function () {
1356
+ adapter.setProperties({
1357
+ host: "http://example.com",
1358
+ namespace: "api/v1"
1359
+ });
1360
+ });
1361
+ Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
1362
+ Comment.reopen({ post: DS.belongsTo("post", { async: false }) });
1276
1363
 
1277
- count++;
1278
- return { id: 1, name: 'Braaaahm Dale' };
1279
- }
1364
+ ajaxResponse({ posts: [{ id: 1, links: { comments: "comments" } }] });
1365
+
1366
+ run(store, "findRecord", "post", "1").then(async(function (post) {
1367
+ ajaxResponse({ comments: [{ id: 1 }] });
1368
+ return post.get("comments");
1369
+ })).then(async(function (comments) {
1370
+ equal(passedUrl, "http://example.com/api/v1/posts/1/comments");
1280
1371
  }));
1372
+ });
1281
1373
 
1374
+ test("buildURL - with absolute paths in links", function () {
1282
1375
  run(function () {
1283
- store.find('person', 1);
1284
- store.find('person', 1);
1376
+ adapter.setProperties({
1377
+ host: "http://example.com",
1378
+ namespace: "api/v1"
1379
+ });
1285
1380
  });
1381
+ Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
1382
+ Comment.reopen({ post: DS.belongsTo("post", { async: false }) });
1383
+
1384
+ ajaxResponse({ posts: [{ id: 1, links: { comments: "/api/v1/posts/1/comments" } }] });
1385
+
1386
+ run(store, "findRecord", "post", 1).then(async(function (post) {
1387
+ ajaxResponse({ comments: [{ id: 1 }] });
1388
+ return post.get("comments");
1389
+ })).then(async(function (comments) {
1390
+ equal(passedUrl, "http://example.com/api/v1/posts/1/comments");
1391
+ }));
1286
1392
  });
1287
1393
 
1288
- test('When a single record is requested multiple times, all .find() calls are resolved after the promise is resolved', function () {
1289
- var deferred = Ember.RSVP.defer();
1394
+ test("buildURL - with absolute paths in links and protocol relative host", function () {
1395
+ run(function () {
1396
+ adapter.setProperties({
1397
+ host: "//example.com",
1398
+ namespace: "api/v1"
1399
+ });
1400
+ });
1401
+ Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
1402
+ Comment.reopen({ post: DS.belongsTo("post", { async: false }) });
1290
1403
 
1291
- env.registry.register('adapter:person', DS.Adapter.extend({
1292
- find: function (store, type, id, snapshot) {
1293
- return deferred.promise;
1294
- }
1404
+ ajaxResponse({ posts: [{ id: 1, links: { comments: "/api/v1/posts/1/comments" } }] });
1405
+
1406
+ run(store, "findRecord", "post", 1).then(async(function (post) {
1407
+ ajaxResponse({ comments: [{ id: 1 }] });
1408
+ return post.get("comments");
1409
+ })).then(async(function (comments) {
1410
+ equal(passedUrl, "//example.com/api/v1/posts/1/comments");
1295
1411
  }));
1412
+ });
1413
+
1414
+ test("buildURL - with full URLs in links", function () {
1415
+ adapter.setProperties({
1416
+ host: "http://example.com",
1417
+ namespace: "api/v1"
1418
+ });
1419
+ Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
1420
+ Comment.reopen({ post: DS.belongsTo("post", { async: false }) });
1421
+
1422
+ ajaxResponse({
1423
+ posts: [{ id: 1,
1424
+ links: { comments: "http://example.com/api/v1/posts/1/comments" }
1425
+ }]
1426
+ });
1296
1427
 
1297
1428
  run(function () {
1298
- store.find('person', 1).then(async(function (person) {
1299
- equal(person.get('id'), '1');
1300
- equal(person.get('name'), 'Braaaahm Dale');
1429
+ store.findRecord("post", 1).then(async(function (post) {
1430
+ ajaxResponse({ comments: [{ id: 1 }] });
1431
+ return post.get("comments");
1432
+ })).then(async(function (comments) {
1433
+ equal(passedUrl, "http://example.com/api/v1/posts/1/comments");
1434
+ }));
1435
+ });
1436
+ });
1301
1437
 
1302
- stop();
1303
- deferred.promise.then(function (value) {
1304
- start();
1305
- ok(true, 'expected deferred.promise to fulfill');
1306
- }, function (reason) {
1307
- start();
1308
- ok(false, 'expected deferred.promise to fulfill, but rejected');
1309
- });
1438
+ test("buildURL - with camelized names", function () {
1439
+ adapter.setProperties({
1440
+ pathForType: function (type) {
1441
+ var decamelized = Ember.String.decamelize(type);
1442
+ return Ember.String.underscore(Ember.String.pluralize(decamelized));
1443
+ }
1444
+ });
1445
+
1446
+ ajaxResponse({ superUsers: [{ id: 1 }] });
1447
+
1448
+ run(function () {
1449
+ store.findRecord("super-user", 1).then(async(function (post) {
1450
+ equal(passedUrl, "/super_users/1");
1310
1451
  }));
1311
1452
  });
1453
+ });
1312
1454
 
1455
+ test("buildURL - buildURL takes a record from find", function () {
1456
+ Comment.reopen({ post: DS.belongsTo("post", { async: false }) });
1457
+ adapter.buildURL = function (type, id, snapshot) {
1458
+ return "/posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/" + snapshot.id;
1459
+ };
1460
+
1461
+ ajaxResponse({ comments: [{ id: 1 }] });
1462
+
1463
+ var post;
1313
1464
  run(function () {
1314
- store.find('person', 1).then(async(function (post) {
1315
- equal(post.get('id'), '1');
1316
- equal(post.get('name'), 'Braaaahm Dale');
1465
+ post = store.push("post", { id: 2 });
1466
+ });
1317
1467
 
1318
- stop();
1319
- deferred.promise.then(function (value) {
1320
- start();
1321
- ok(true, 'expected deferred.promise to fulfill');
1322
- }, function (reason) {
1323
- start();
1324
- ok(false, 'expected deferred.promise to fulfill, but rejected');
1325
- });
1468
+ run(function () {
1469
+ store.findRecord("comment", 1, { preload: { post: post } }).then(async(function (post) {
1470
+ equal(passedUrl, "/posts/2/comments/1");
1326
1471
  }));
1327
1472
  });
1473
+ });
1328
1474
 
1329
- Ember.run(function () {
1330
- deferred.resolve({ id: 1, name: 'Braaaahm Dale' });
1475
+ test("buildURL - buildURL takes the records from findMany", function () {
1476
+ Comment.reopen({ post: DS.belongsTo("post", { async: false }) });
1477
+ Post.reopen({ comments: DS.hasMany("comment", { async: true }) });
1478
+
1479
+ adapter.buildURL = function (type, ids, snapshots) {
1480
+ if (Ember.isArray(snapshots)) {
1481
+ return "/posts/" + snapshots.get("firstObject").belongsTo("post", { id: true }) + "/comments/";
1482
+ }
1483
+ return "";
1484
+ };
1485
+ adapter.coalesceFindRequests = true;
1486
+
1487
+ ajaxResponse({ comments: [{ id: 1 }, { id: 2 }, { id: 3 }] });
1488
+ var post;
1489
+
1490
+ run(function () {
1491
+ post = store.push("post", { id: 2, comments: [1, 2, 3] });
1492
+ post.get("comments").then(async(function (post) {
1493
+ equal(passedUrl, "/posts/2/comments/");
1494
+ }));
1331
1495
  });
1332
1496
  });
1333
1497
 
1334
- test('When a single record is requested, and the promise is rejected, .find() is rejected.', function () {
1335
- env.registry.register('adapter:person', DS.Adapter.extend({
1336
- find: function (store, type, id, snapshot) {
1337
- return Ember.RSVP.reject();
1338
- }
1339
- }));
1498
+ test("buildURL - buildURL takes a record from create", function () {
1499
+ Comment.reopen({ post: DS.belongsTo("post", { async: false }) });
1500
+ adapter.buildURL = function (type, id, snapshot) {
1501
+ return "/posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/";
1502
+ };
1503
+
1504
+ ajaxResponse({ comments: [{ id: 1 }] });
1340
1505
 
1341
1506
  run(function () {
1342
- store.find('person', 1).then(null, async(function (reason) {
1343
- ok(true, 'The rejection handler was called');
1507
+ var post = store.push("post", { id: 2 });
1508
+ var comment = store.createRecord("comment");
1509
+ comment.set("post", post);
1510
+ comment.save().then(async(function (post) {
1511
+ equal(passedUrl, "/posts/2/comments/");
1344
1512
  }));
1345
1513
  });
1346
1514
  });
1347
1515
 
1348
- test('When a single record is requested, and the promise is rejected, the record should be unloaded.', function () {
1349
- expect(2);
1516
+ test("buildURL - buildURL takes a record from create to query a resolved async belongsTo relationship", function () {
1517
+ Comment.reopen({ post: DS.belongsTo("post", { async: true }) });
1350
1518
 
1351
- env.registry.register('adapter:person', DS.Adapter.extend({
1352
- find: function (store, type, id, snapshot) {
1353
- return Ember.RSVP.reject();
1354
- }
1355
- }));
1519
+ ajaxResponse({ posts: [{ id: 2 }] });
1356
1520
 
1357
1521
  run(function () {
1358
- store.find('person', 1).then(null, async(function (reason) {
1359
- ok(true, 'The rejection handler was called');
1522
+ store.findRecord("post", 2).then(async(function (post) {
1523
+ equal(post.get("id"), 2);
1524
+
1525
+ adapter.buildURL = function (type, id, snapshot) {
1526
+ return "/posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/";
1527
+ };
1528
+
1529
+ ajaxResponse({ comments: [{ id: 1 }] });
1530
+
1531
+ var comment = store.createRecord("comment");
1532
+ comment.set("post", post);
1533
+ comment.save().then(async(function (post) {
1534
+ equal(passedUrl, "/posts/2/comments/");
1535
+ }));
1360
1536
  }));
1361
1537
  });
1538
+ });
1362
1539
 
1363
- ok(!store.hasRecordForId('person', 1), 'The record has been unloaded');
1540
+ test("buildURL - buildURL takes a record from update", function () {
1541
+ Comment.reopen({ post: DS.belongsTo("post", { async: false }) });
1542
+ adapter.buildURL = function (type, id, snapshot) {
1543
+ return "/posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/" + snapshot.id;
1544
+ };
1545
+
1546
+ ajaxResponse({ comments: [{ id: 1 }] });
1547
+
1548
+ var post, comment;
1549
+ run(function () {
1550
+ post = store.push("post", { id: 2 });
1551
+ comment = store.push("comment", { id: 1 });
1552
+ comment.set("post", post);
1553
+ });
1554
+ run(function () {
1555
+ comment.save().then(async(function (post) {
1556
+ equal(passedUrl, "/posts/2/comments/1");
1557
+ }));
1558
+ });
1559
+ });
1560
+
1561
+ test("buildURL - buildURL takes a record from delete", function () {
1562
+ Comment.reopen({ post: DS.belongsTo("post", { async: false }) });
1563
+ Post.reopen({ comments: DS.hasMany("comment", { async: false }) });
1564
+ adapter.buildURL = function (type, id, snapshot) {
1565
+ return "posts/" + snapshot.belongsTo("post", { id: true }) + "/comments/" + snapshot.id;
1566
+ };
1567
+
1568
+ ajaxResponse({ comments: [{ id: 1 }] });
1569
+
1570
+ var post, comment;
1571
+ run(function () {
1572
+ post = store.push("post", { id: 2 });
1573
+ comment = store.push("comment", { id: 1 });
1574
+
1575
+ comment.set("post", post);
1576
+ comment.deleteRecord();
1577
+ });
1578
+ run(function () {
1579
+ comment.save().then(async(function (post) {
1580
+ equal(passedUrl, "posts/2/comments/1");
1581
+ }));
1582
+ });
1583
+ });
1584
+
1585
+ test("buildURL - with absolute namespace", function () {
1586
+ run(function () {
1587
+ adapter.setProperties({
1588
+ namespace: "/api/v1"
1589
+ });
1590
+ });
1591
+
1592
+ ajaxResponse({ posts: [{ id: 1 }] });
1593
+
1594
+ run(store, "findRecord", "post", 1).then(async(function (post) {
1595
+ equal(passedUrl, "/api/v1/posts/1");
1596
+ }));
1597
+ });
1598
+
1599
+ test("buildURL - urlForFindRecord calls deprecated urlForFind", function () {
1600
+ expect(2);
1601
+
1602
+ var adapter = DS.RESTAdapter.extend({
1603
+ urlForFind: function () {
1604
+ ok(true, "urlForFind should be called");
1605
+ }
1606
+ }).create();
1607
+
1608
+ expectDeprecation(function () {
1609
+ adapter.buildURL("post", 1, {}, "findRecord");
1610
+ }, /urlForFindRecord/);
1611
+ });
1612
+
1613
+ test("buildURL - urlForQuery calls deprecated urlForFindQuery", function () {
1614
+ expect(2);
1615
+
1616
+ var adapter = DS.RESTAdapter.extend({
1617
+ urlForFindQuery: function () {
1618
+ ok(true, "urlForFindQuery should be called");
1619
+ }
1620
+ }).create();
1621
+
1622
+ expectDeprecation(function () {
1623
+ adapter.buildURL("post", 1, {}, "query");
1624
+ }, /urlForQuery/);
1364
1625
  });
1365
1626
  }
1366
1627
  );
1367
1628
 
1368
1629
 
1369
1630
  define(
1370
- "ember-data/tests/integration/adapter/queries-test",
1631
+ "ember-data/tests/integration/adapter/find-all-test",
1371
1632
  ["exports"],
1372
1633
  function(__exports__) {
1373
1634
  "use strict";
@@ -1377,10 +1638,11 @@ define(
1377
1638
  }
1378
1639
 
1379
1640
  var get = Ember.get;
1380
- var Person, env, store, adapter;
1641
+ var Person, store, allRecords;
1381
1642
  var run = Ember.run;
1643
+ var env;
1382
1644
 
1383
- module('integration/adapter/queries - Queries', {
1645
+ module('integration/adapter/find_all - Finding All Records of a Type', {
1384
1646
  setup: function () {
1385
1647
  Person = DS.Model.extend({
1386
1648
  updatedAt: DS.attr('string'),
@@ -1389,37 +1651,131 @@ define(
1389
1651
  lastName: DS.attr('string')
1390
1652
  });
1391
1653
 
1392
- env = setupStore({ person: Person });
1654
+ allRecords = null;
1655
+
1656
+ env = setupStore({
1657
+ person: Person
1658
+ });
1393
1659
  store = env.store;
1394
- adapter = env.adapter;
1395
1660
  },
1396
1661
 
1397
1662
  teardown: function () {
1398
- run(env.container, 'destroy');
1663
+ run(function () {
1664
+ if (allRecords) {
1665
+ allRecords.destroy();
1666
+ }
1667
+ store.destroy();
1668
+ });
1399
1669
  }
1400
1670
  });
1401
1671
 
1402
- test('When a query is made, the adapter should receive a record array it can populate with the results of the query.', function () {
1403
- adapter.findQuery = function (store, type, query, recordArray) {
1404
- equal(type, Person, 'the find method is called with the correct type');
1672
+ test('When all records for a type are requested, the store should call the adapter\'s `findAll` method.', function () {
1673
+ expect(5);
1405
1674
 
1406
- return Ember.RSVP.resolve([{ id: 1, name: 'Peter Wagenet' }, { id: 2, name: 'Brohuda Katz' }]);
1407
- };
1675
+ env.registry.register('adapter:person', DS.Adapter.extend({
1676
+ findAll: function (store, type, since) {
1677
+ // this will get called twice
1678
+ ok(true, 'the adapter\'s findAll method should be invoked');
1408
1679
 
1409
- store.find('person', { page: 1 }).then(async(function (queryResults) {
1410
- equal(get(queryResults, 'length'), 2, 'the record array has a length of 2 after the results are loaded');
1411
- equal(get(queryResults, 'isLoaded'), true, 'the record array\'s `isLoaded` property should be true');
1680
+ return Ember.RSVP.resolve([{ id: 1, name: 'Braaaahm Dale' }]);
1681
+ }
1682
+ }));
1412
1683
 
1413
- equal(queryResults.objectAt(0).get('name'), 'Peter Wagenet', 'the first record is \'Peter Wagenet\'');
1414
- equal(queryResults.objectAt(1).get('name'), 'Brohuda Katz', 'the second record is \'Brohuda Katz\'');
1684
+ var allRecords;
1685
+
1686
+ run(function () {
1687
+ store.findAll('person').then(function (all) {
1688
+ allRecords = all;
1689
+ equal(get(all, 'length'), 1, 'the record array\'s length is 1 after a record is loaded into it');
1690
+ equal(all.objectAt(0).get('name'), 'Braaaahm Dale', 'the first item in the record array is Braaaahm Dale');
1691
+ });
1692
+ });
1693
+
1694
+ run(function () {
1695
+ store.findAll('person').then(function (all) {
1696
+ // Only one record array per type should ever be created (identity map)
1697
+ strictEqual(allRecords, all, 'the same record array is returned every time all records of a type are requested');
1698
+ });
1699
+ });
1700
+ });
1701
+
1702
+ test('When all records for a type are requested, a rejection should reject the promise', function () {
1703
+ expect(5);
1704
+
1705
+ var count = 0;
1706
+ env.registry.register('adapter:person', DS.Adapter.extend({
1707
+ findAll: function (store, type, since) {
1708
+ // this will get called twice
1709
+ ok(true, 'the adapter\'s findAll method should be invoked');
1710
+
1711
+ if (count++ === 0) {
1712
+ return Ember.RSVP.reject();
1713
+ } else {
1714
+ return Ember.RSVP.resolve([{ id: 1, name: 'Braaaahm Dale' }]);
1715
+ }
1716
+ }
1415
1717
  }));
1718
+
1719
+ var allRecords;
1720
+
1721
+ run(function () {
1722
+ store.findAll('person').then(null, function () {
1723
+ ok(true, 'The rejection should get here');
1724
+ return store.findAll('person');
1725
+ }).then(function (all) {
1726
+ allRecords = all;
1727
+ equal(get(all, 'length'), 1, 'the record array\'s length is 1 after a record is loaded into it');
1728
+ equal(all.objectAt(0).get('name'), 'Braaaahm Dale', 'the first item in the record array is Braaaahm Dale');
1729
+ });
1730
+ });
1731
+ });
1732
+
1733
+ test('When all records for a type are requested, records that are already loaded should be returned immediately.', function () {
1734
+ expect(3);
1735
+ store = createStore({
1736
+ adapter: DS.Adapter.extend(),
1737
+ person: Person
1738
+ });
1739
+
1740
+ run(function () {
1741
+ // Load a record from the server
1742
+ store.push('person', { id: 1, name: 'Jeremy Ashkenas' });
1743
+ // Create a new, unsaved record in the store
1744
+ store.createRecord('person', { name: 'Alex MacCaw' });
1745
+ });
1746
+
1747
+ allRecords = store.peekAll('person');
1748
+
1749
+ equal(get(allRecords, 'length'), 2, 'the record array\'s length is 2');
1750
+ equal(allRecords.objectAt(0).get('name'), 'Jeremy Ashkenas', 'the first item in the record array is Jeremy Ashkenas');
1751
+ equal(allRecords.objectAt(1).get('name'), 'Alex MacCaw', 'the second item in the record array is Alex MacCaw');
1752
+ });
1753
+
1754
+ test('When all records for a type are requested, records that are created on the client should be added to the record array.', function () {
1755
+ expect(3);
1756
+
1757
+ store = createStore({
1758
+ adapter: DS.Adapter.extend(),
1759
+ person: Person
1760
+ });
1761
+
1762
+ allRecords = store.peekAll('person');
1763
+
1764
+ equal(get(allRecords, 'length'), 0, 'precond - the record array\'s length is zero before any records are loaded');
1765
+
1766
+ run(function () {
1767
+ store.createRecord('person', { name: 'Carsten Nielsen' });
1768
+ });
1769
+
1770
+ equal(get(allRecords, 'length'), 1, 'the record array\'s length is 1');
1771
+ equal(allRecords.objectAt(0).get('name'), 'Carsten Nielsen', 'the first item in the record array is Carsten Nielsen');
1416
1772
  });
1417
1773
  }
1418
1774
  );
1419
1775
 
1420
1776
 
1421
1777
  define(
1422
- "ember-data/tests/integration/adapter/record-persistence-test",
1778
+ "ember-data/tests/integration/adapter/find-test",
1423
1779
  ["exports"],
1424
1780
  function(__exports__) {
1425
1781
  "use strict";
@@ -1428,262 +1784,163 @@ define(
1428
1784
  __exports__[name] = value;
1429
1785
  }
1430
1786
 
1431
- var get = Ember.get;
1432
- var set = Ember.set;
1433
- var attr = DS.attr;
1434
- var Person, env, store;
1787
+ var Person, store, env;
1435
1788
  var run = Ember.run;
1436
1789
 
1437
- var all = Ember.RSVP.all;
1438
- var hash = Ember.RSVP.hash;
1439
-
1440
- function assertClean(promise) {
1441
- return promise.then(async(function (record) {
1442
- equal(record.get("isDirty"), false, "The record is now clean");
1443
- return record;
1444
- }));
1445
- }
1446
-
1447
- module("integration/adapter/record_persistence - Persisting Records", {
1790
+ module('integration/adapter/find - Finding Records', {
1448
1791
  setup: function () {
1449
1792
  Person = DS.Model.extend({
1450
- updatedAt: attr("string"),
1451
- name: attr("string"),
1452
- firstName: attr("string"),
1453
- lastName: attr("string")
1793
+ updatedAt: DS.attr('string'),
1794
+ name: DS.attr('string'),
1795
+ firstName: DS.attr('string'),
1796
+ lastName: DS.attr('string')
1454
1797
  });
1455
- Person.toString = function () {
1456
- return "Person";
1457
- };
1458
1798
 
1459
- env = setupStore({ person: Person });
1799
+ env = setupStore({
1800
+ person: Person
1801
+ });
1460
1802
  store = env.store;
1461
1803
  },
1462
1804
 
1463
1805
  teardown: function () {
1464
- run(env.container, "destroy");
1806
+ run(store, 'destroy');
1465
1807
  }
1466
1808
  });
1467
1809
 
1468
- test("When a store is committed, the adapter's `commit` method should be called with records that have been changed.", function () {
1469
- expect(2);
1470
-
1471
- env.adapter.updateRecord = function (store, type, snapshot) {
1472
- equal(type, Person, "the type is correct");
1473
- equal(snapshot.record, tom, "the record is correct");
1474
-
1475
- return run(Ember.RSVP, "resolve");
1476
- };
1810
+ test('It raises an assertion when no type is passed', function () {
1811
+ expectAssertion(function () {
1812
+ store.find();
1813
+ }, 'You need to pass a type to the store\'s find method');
1814
+ });
1477
1815
 
1478
- run(function () {
1479
- env.store.push("person", { id: 1, name: "Braaaahm Dale" });
1480
- });
1816
+ test('It raises an assertion when `undefined` is passed as id (#1705)', function () {
1817
+ expectAssertion(function () {
1818
+ store.find('person', undefined);
1819
+ }, 'You may not pass `undefined` as id to the store\'s find method');
1481
1820
 
1482
- var tom;
1821
+ expectAssertion(function () {
1822
+ store.find('person', null);
1823
+ }, 'You may not pass `null` as id to the store\'s find method');
1824
+ });
1483
1825
 
1484
- env.store.find("person", 1).then(async(function (person) {
1485
- tom = person;
1486
- set(tom, "name", "Tom Dale");
1487
- tom.save();
1826
+ test('store.find(type) is deprecated', function () {
1827
+ env.registry.register('adapter:person', DS.Adapter.extend({
1828
+ findAll: function (store, typeClass) {
1829
+ return [];
1830
+ }
1488
1831
  }));
1832
+
1833
+ expectDeprecation(function () {
1834
+ run(function () {
1835
+ store.find('person');
1836
+ });
1837
+ }, 'Using store.find(type) has been deprecated. Use store.findAll(type) to retrieve all records for a given type.');
1489
1838
  });
1490
1839
 
1491
- test("When a store is committed, the adapter's `commit` method should be called with records that have been created.", function () {
1840
+ test('When a single record is requested, the adapter\'s find method should be called unless it\'s loaded.', function () {
1492
1841
  expect(2);
1493
- var tom;
1494
1842
 
1495
- env.adapter.createRecord = function (store, type, snapshot) {
1496
- equal(type, Person, "the type is correct");
1497
- equal(snapshot.record, tom, "the record is correct");
1843
+ var count = 0;
1498
1844
 
1499
- return Ember.RSVP.resolve({ id: 1, name: "Tom Dale" });
1500
- };
1845
+ env.registry.register('adapter:person', DS.Adapter.extend({
1846
+ findRecord: function (store, type, id, snapshot) {
1847
+ equal(type, Person, 'the find method is called with the correct type');
1848
+ equal(count, 0, 'the find method is only called once');
1849
+
1850
+ count++;
1851
+ return { id: 1, name: 'Braaaahm Dale' };
1852
+ }
1853
+ }));
1501
1854
 
1502
1855
  run(function () {
1503
- tom = env.store.createRecord("person", { name: "Tom Dale" });
1504
- tom.save();
1856
+ store.findRecord('person', 1);
1857
+ store.findRecord('person', 1);
1505
1858
  });
1506
1859
  });
1507
1860
 
1508
- test("After a created record has been assigned an ID, finding a record by that ID returns the original record.", function () {
1509
- expect(1);
1510
- var tom;
1861
+ test('When a single record is requested multiple times, all .find() calls are resolved after the promise is resolved', function () {
1862
+ var deferred = Ember.RSVP.defer();
1511
1863
 
1512
- env.adapter.createRecord = function (store, type, snapshot) {
1513
- return Ember.RSVP.resolve({ id: 1, name: "Tom Dale" });
1514
- };
1864
+ env.registry.register('adapter:person', DS.Adapter.extend({
1865
+ findRecord: function (store, type, id, snapshot) {
1866
+ return deferred.promise;
1867
+ }
1868
+ }));
1515
1869
 
1516
1870
  run(function () {
1517
- tom = env.store.createRecord("person", { name: "Tom Dale" });
1518
- tom.save();
1871
+ store.findRecord('person', 1).then(async(function (person) {
1872
+ equal(person.get('id'), '1');
1873
+ equal(person.get('name'), 'Braaaahm Dale');
1874
+
1875
+ stop();
1876
+ deferred.promise.then(function (value) {
1877
+ start();
1878
+ ok(true, 'expected deferred.promise to fulfill');
1879
+ }, function (reason) {
1880
+ start();
1881
+ ok(false, 'expected deferred.promise to fulfill, but rejected');
1882
+ });
1883
+ }));
1519
1884
  });
1520
1885
 
1521
- asyncEqual(tom, env.store.find("person", 1), "the retrieved record is the same as the created record");
1522
- });
1886
+ run(function () {
1887
+ store.findRecord('person', 1).then(async(function (post) {
1888
+ equal(post.get('id'), '1');
1889
+ equal(post.get('name'), 'Braaaahm Dale');
1523
1890
 
1524
- test("when a store is committed, the adapter's `commit` method should be called with records that have been deleted.", function () {
1525
- env.adapter.deleteRecord = function (store, type, snapshot) {
1526
- equal(type, Person, "the type is correct");
1527
- equal(snapshot.record, tom, "the record is correct");
1891
+ stop();
1892
+ deferred.promise.then(function (value) {
1893
+ start();
1894
+ ok(true, 'expected deferred.promise to fulfill');
1895
+ }, function (reason) {
1896
+ start();
1897
+ ok(false, 'expected deferred.promise to fulfill, but rejected');
1898
+ });
1899
+ }));
1900
+ });
1528
1901
 
1529
- return run(Ember.RSVP, "resolve");
1530
- };
1902
+ Ember.run(function () {
1903
+ deferred.resolve({ id: 1, name: 'Braaaahm Dale' });
1904
+ });
1905
+ });
1531
1906
 
1532
- var tom;
1533
-
1534
- run(function () {
1535
- env.store.push("person", { id: 1, name: "Tom Dale" });
1536
- });
1537
- env.store.find("person", 1).then(async(function (person) {
1538
- tom = person;
1539
- tom.deleteRecord();
1540
- return tom.save();
1541
- })).then(async(function (tom) {
1542
- equal(get(tom, "isDeleted"), true, "record is marked as deleted");
1907
+ test('When a single record is requested, and the promise is rejected, .find() is rejected.', function () {
1908
+ env.registry.register('adapter:person', DS.Adapter.extend({
1909
+ findRecord: function (store, type, id, snapshot) {
1910
+ return Ember.RSVP.reject();
1911
+ }
1543
1912
  }));
1544
- });
1545
-
1546
- test("An adapter can notify the store that records were updated by calling `didSaveRecords`.", function () {
1547
- expect(6);
1548
-
1549
- var tom, yehuda;
1550
-
1551
- env.adapter.updateRecord = function (store, type, snapshot) {
1552
- return Ember.RSVP.resolve();
1553
- };
1554
1913
 
1555
1914
  run(function () {
1556
- env.store.push("person", { id: 1 });
1557
- env.store.push("person", { id: 2 });
1558
- });
1559
-
1560
- all([env.store.find("person", 1), env.store.find("person", 2)]).then(async(function (array) {
1561
- tom = array[0];
1562
- yehuda = array[1];
1563
-
1564
- tom.set("name", "Michael Phelps");
1565
- yehuda.set("name", "Usain Bolt");
1566
-
1567
- ok(tom.get("isDirty"), "tom is dirty");
1568
- ok(yehuda.get("isDirty"), "yehuda is dirty");
1569
-
1570
- assertClean(tom.save()).then(async(function (record) {
1571
- equal(record, tom, "The record is correct");
1572
- }));
1573
-
1574
- assertClean(yehuda.save()).then(async(function (record) {
1575
- equal(record, yehuda, "The record is correct");
1915
+ store.findRecord('person', 1).then(null, async(function (reason) {
1916
+ ok(true, 'The rejection handler was called');