backbone-nested-attributes 0.2.2

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 (52) hide show
  1. data/.gitignore +21 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +12 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +187 -0
  6. data/Rakefile +15 -0
  7. data/app/assets/javascripts/backbone-nested-attributes/all.js +2 -0
  8. data/app/assets/javascripts/backbone-nested-attributes/model.js +175 -0
  9. data/app/assets/javascripts/backbone-nested-attributes/undoable.js +60 -0
  10. data/backbone-nested-attributes.gemspec +20 -0
  11. data/lib/backbone-nested-attributes.rb +6 -0
  12. data/lib/backbone-nested-attributes/engine.rb +7 -0
  13. data/lib/backbone-nested-attributes/version.rb +5 -0
  14. data/spec/dummy/README.rdoc +261 -0
  15. data/spec/dummy/Rakefile +7 -0
  16. data/spec/dummy/app/assets/javascripts/application.js +18 -0
  17. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  18. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  19. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  20. data/spec/dummy/app/mailers/.gitkeep +0 -0
  21. data/spec/dummy/app/models/.gitkeep +0 -0
  22. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  23. data/spec/dummy/config.ru +4 -0
  24. data/spec/dummy/config/application.rb +79 -0
  25. data/spec/dummy/config/boot.rb +10 -0
  26. data/spec/dummy/config/database.yml +25 -0
  27. data/spec/dummy/config/environment.rb +5 -0
  28. data/spec/dummy/config/environments/development.rb +37 -0
  29. data/spec/dummy/config/environments/production.rb +67 -0
  30. data/spec/dummy/config/environments/test.rb +37 -0
  31. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  32. data/spec/dummy/config/initializers/inflections.rb +15 -0
  33. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  34. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  35. data/spec/dummy/config/initializers/session_store.rb +8 -0
  36. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  37. data/spec/dummy/config/locales/en.yml +5 -0
  38. data/spec/dummy/config/routes.rb +58 -0
  39. data/spec/dummy/db/.gitkeep +0 -0
  40. data/spec/dummy/lib/assets/.gitkeep +0 -0
  41. data/spec/dummy/log/.gitkeep +0 -0
  42. data/spec/dummy/public/404.html +26 -0
  43. data/spec/dummy/public/422.html +26 -0
  44. data/spec/dummy/public/500.html +25 -0
  45. data/spec/dummy/public/favicon.ico +0 -0
  46. data/spec/dummy/script/rails +6 -0
  47. data/spec/javascripts/backbone-nested-attributes/ModelSpec.js +626 -0
  48. data/spec/javascripts/backbone-nested-attributes/UndoableSpec.js +489 -0
  49. data/spec/javascripts/helpers/SpecHelper.js +7 -0
  50. data/spec/javascripts/helpers/mock-ajax.js +207 -0
  51. data/spec/javascripts/support/jasmine.yml +76 -0
  52. metadata +156 -0
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ </div>
24
+ </body>
25
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,626 @@
1
+ describe("Backbone.NestedAttributesModel", function() {
2
+ var model,
3
+ Post,
4
+ post,
5
+ Comment,
6
+ comment,
7
+ Person,
8
+ author
9
+
10
+ beforeEach(function() {
11
+ model = new Backbone.NestedAttributesModel({})
12
+ })
13
+
14
+ it("should be a Backbone.Model", function() {
15
+ expect(model).toBeAnInstanceOf(Backbone.Model)
16
+ })
17
+
18
+ describe("with a has one relationship", function() {
19
+ beforeEach(function() {
20
+ Post = Backbone.NestedAttributesModel.extend({
21
+ relations: [
22
+ {
23
+ type: 'one',
24
+ key: 'author',
25
+ relatedModel: function () { return Person }
26
+ }
27
+ ]
28
+ })
29
+
30
+ Person = Backbone.NestedAttributesModel.extend({})
31
+ })
32
+
33
+ describe("when creating", function() {
34
+ it("does not initializes the author attribute", function() {
35
+ model = new Post
36
+ expect(model.get('author')).not.toBeDefined()
37
+ });
38
+
39
+ describe("while setting attributes", function() {
40
+ beforeEach(function() {
41
+ model = new Post({ title: 'Some Title', author: { name: 'Jon Snow'} })
42
+ })
43
+
44
+ it("sets the normal attributes", function() {
45
+ expect(model.get('title')).toEqual('Some Title')
46
+ })
47
+
48
+ it("creates the author model", function() {
49
+ author = model.get('author')
50
+
51
+ expect(author).toBeAnInstanceOf(Person)
52
+ expect(author.get('name')).toEqual('Jon Snow')
53
+ })
54
+
55
+ describe("passing a model to the relation attribute", function() {
56
+ beforeEach(function() {
57
+ author = new Person({ name: 'Jon Snow' })
58
+ model = new Post({ title: 'Some Title', author: author })
59
+ })
60
+
61
+ it("store the given model in the relation attribute", function() {
62
+ expect(model.get('author')).toBe(author)
63
+ })
64
+ })
65
+ })
66
+ })
67
+
68
+ describe("when updating", function() {
69
+ beforeEach(function() {
70
+ model = new Post
71
+ })
72
+
73
+ it("store the given model in the relation attribute", function() {
74
+ model.set({ author: { name: 'Jon Snow' } })
75
+
76
+ author = model.get('author')
77
+
78
+ expect(author).toBeAnInstanceOf(Person)
79
+ expect(author.get('name')).toEqual('Jon Snow')
80
+ })
81
+
82
+ it("allows passing key, value when setting a relation attribute", function() {
83
+ model.set('author', { name: 'Jon Snow' })
84
+
85
+ author = model.get('author')
86
+
87
+ expect(author).toBeAnInstanceOf(Person)
88
+ expect(author.get('name')).toEqual('Jon Snow')
89
+ })
90
+
91
+ describe("passing a model to the relation attribute", function() {
92
+ beforeEach(function() {
93
+ author = new Person({ name: 'Jon Snow' })
94
+ model = new Post
95
+ })
96
+
97
+ it("store the given model in the relation attribute", function() {
98
+ model.set({ title: 'Some title', author: author })
99
+ expect(model.get('author')).toBe(author)
100
+ })
101
+ })
102
+ })
103
+
104
+ describe("cloning", function() {
105
+ beforeEach(function() {
106
+ post = new Post({ title: 'Some Title', author: { name: 'Jon Snow' } })
107
+ })
108
+
109
+ it("creates a new post object, with the same attributes, including nested has one relations, that are not shared", function() {
110
+ var newPost = post.clone()
111
+
112
+ expect(newPost).not.toBe(post)
113
+ expect(newPost.get('author')).not.toBe(post.get('author'))
114
+
115
+ expect(newPost.get('title')).toEqual(post.get('title'))
116
+ expect(newPost.get('author').get('name')).toEqual(post.get('author').get('name'))
117
+ })
118
+ })
119
+
120
+ describe("event bubbling", function() {
121
+ var changedModel, changedCount
122
+
123
+ beforeEach(function() {
124
+ changedCount = 0
125
+ changedModel = undefined
126
+
127
+ model = new Post({ title: 'Some Title', author: { name: 'Jon Snow' } })
128
+ author = model.get('author')
129
+ })
130
+
131
+ describe("when a nested model is changed", function() {
132
+ it("triggers a nested:change event on the parent, with the changed model as an argument", function() {
133
+ model.on('nested:change', function (model) {
134
+ changedModel = model
135
+ })
136
+
137
+ author.set({ name: 'Robb Stark' })
138
+ expect(changedModel).toBe(author)
139
+ })
140
+
141
+ it("triggers a nested:change only once, after setting nested attributes again", function() {
142
+ model.set({ title: 'Some Title', author: { name: 'Jon Snow' } })
143
+ author = model.get('author')
144
+
145
+ model.on('nested:change', function (model) {
146
+ changedModel = model
147
+ changedCount += 1
148
+ })
149
+
150
+ author.set({ name: 'Robb Stark' })
151
+ expect(changedModel).toBe(author)
152
+ expect(changedCount).toEqual(1)
153
+ })
154
+
155
+ it("triggers a change:<relationKey> event on the parent, with the changed model as an argument", function() {
156
+ model.on('change:author', function (model) {
157
+ changedModel = model
158
+ })
159
+
160
+ author.set({ name: 'Robb Stark' })
161
+ expect(changedModel).toBe(author)
162
+ })
163
+
164
+ it("triggers a change:<relationKey> only once, after setting nested attributes again", function() {
165
+ model.set({ title: 'Some Title', author: { body: 'Jon Snow' } })
166
+ author = model.get('author')
167
+
168
+ model.on('change:author', function (model) {
169
+ changedModel = model
170
+ changedCount += 1
171
+ })
172
+
173
+ author.set({ name: 'Robb Stark' })
174
+ expect(changedModel).toBe(author)
175
+ expect(changedCount).toEqual(1)
176
+ })
177
+
178
+ describe("and the nested model triggers a nested:change", function() {
179
+ it("triggers a nested:change on the parent", function() {
180
+ model.on('nested:change', function (model) {
181
+ changedModel = model
182
+ })
183
+
184
+ author.trigger('nested:change', author)
185
+ expect(changedModel).toBe(author)
186
+ })
187
+ })
188
+ })
189
+
190
+ describe("when clearing", function() {
191
+ it("stops listening to relation nested:change events", function() {
192
+ model.on('nested:change', function (model) {
193
+ changedModel = model
194
+ })
195
+
196
+ model.clear()
197
+ author.set({ name: 'Robb Stark' })
198
+ expect(changedModel).toBeUndefined()
199
+ })
200
+
201
+ it("stops listening to relation change:<relationKey> events", function() {
202
+ model.clear()
203
+
204
+ model.on('change:author', function (model) {
205
+ changedModel = model
206
+ })
207
+
208
+ author.set({ name: 'Robb Stark' })
209
+ expect(changedModel).toBeUndefined()
210
+ })
211
+ })
212
+ })
213
+
214
+ describe("toJSON", function() {
215
+ beforeEach(function() {
216
+ model = new Post({ title: 'Some Title', author: { name: 'Jon Snow' } })
217
+ })
218
+
219
+ it("serializes its own attributes, as well as the relation ones", function() {
220
+ expect(model.toJSON()).toEqual({ title: 'Some Title', author: { name: 'Jon Snow' } })
221
+ })
222
+
223
+ describe("with nested attributes support", function() {
224
+ it("serializes the relation attributes with a _attributes suffix", function() {
225
+ expect(model.toJSON({ nested: true })).toEqual({
226
+ title: 'Some Title',
227
+ author_attributes: { name: 'Jon Snow' }
228
+ })
229
+ })
230
+ })
231
+ })
232
+ })
233
+
234
+ describe("with a has many relationship", function() {
235
+ beforeEach(function() {
236
+ Post = Backbone.NestedAttributesModel.extend({
237
+ relations: [
238
+ {
239
+ key: 'comments',
240
+ relatedModel: function () { return Comment }
241
+ }
242
+ ]
243
+ })
244
+
245
+ Comment = Backbone.NestedAttributesModel.extend({})
246
+ })
247
+
248
+ describe("when creating", function() {
249
+ it("initializes the comments attribute with an empty collection", function() {
250
+ model = new Post
251
+ expect(model.get('comments')).toBeDefined()
252
+ expect(model.get('comments')).toBeAnInstanceOf(Backbone.Collection)
253
+ });
254
+
255
+ describe("while setting attributes", function() {
256
+ beforeEach(function() {
257
+ model = new Post({ title: 'Some Title', comments: [{ body: 'some comment'} ] })
258
+ })
259
+
260
+ it("sets the normal attributes", function() {
261
+ expect(model.get('title')).toEqual('Some Title')
262
+ })
263
+
264
+ it("creates the comment inside comments collection", function() {
265
+ comment = model.get('comments').at(0)
266
+
267
+ expect(comment).toBeAnInstanceOf(Comment)
268
+ expect(comment.get('body')).toEqual('some comment')
269
+ })
270
+
271
+ describe("passing a collection to a relation attribute", function() {
272
+ var comments, Comments
273
+
274
+ beforeEach(function() {
275
+ Comments = Backbone.Collection.extend({ model: Comment })
276
+ comments = new Comments({ body: 'some comment' }, { body: 'some other comment' })
277
+ model = new Post({ title: 'Some Title', comments: comments })
278
+ })
279
+
280
+ it("does not store the given collection, but instead creates a new one with models from the given collection", function() {
281
+ expect(model.get('comments')).not.toBe(comments)
282
+ expect(model.get('comments')).not.toBeAnInstanceOf(Comments)
283
+ expect(model.get('comments')).toBeAnInstanceOf(Backbone.Collection)
284
+ expect(model.get('comments').at(0)).toBe(comments.at(0))
285
+ expect(model.get('comments').at(1)).toBe(comments.at(1))
286
+ })
287
+ })
288
+ })
289
+ })
290
+
291
+ describe("when updating", function() {
292
+ beforeEach(function() {
293
+ model = new Post
294
+ })
295
+
296
+ it("creates the comment inside comments collection", function() {
297
+ model.set({ comments: [{ body: 'some comment' }] })
298
+
299
+ comment = model.get('comments').at(0)
300
+
301
+ expect(comment).toBeAnInstanceOf(Comment)
302
+ expect(comment.get('body')).toEqual('some comment')
303
+ })
304
+
305
+ it("does not creates a new collection, but updates it instead", function() {
306
+ var originalCollection = model.get('comments')
307
+
308
+ model.set({ comments: [{ body: 'some comment' }] })
309
+ expect(model.get('comments')).toBe(originalCollection)
310
+ })
311
+
312
+ it("updates existing models in the collection", function() {
313
+ var existingComment
314
+
315
+ model.set({ comments: [{ id: '123', body: 'some comment' }] })
316
+ existingComment = model.get('comments').at(0)
317
+
318
+ model.set({ comments: [{ id: existingComment.id, body: 'some other comment' }] })
319
+
320
+ expect(model.get('comments').at(0).get('body')).toEqual('some other comment')
321
+ })
322
+
323
+ it("allows passing key, value when setting a relation attribute", function() {
324
+ model.set('comments', [{ body: 'some comment' }])
325
+
326
+ comment = model.get('comments').at(0)
327
+
328
+ expect(comment).toBeAnInstanceOf(Comment)
329
+ expect(comment.get('body')).toEqual('some comment')
330
+ })
331
+
332
+ it("allows one to update non-nested attributes without modifying nested ones", function() {
333
+ model = new Post({ title: 'new title', comments: [{ body: 'some comment' }] })
334
+ model.set('title', 'other title')
335
+ expect(model.get('title')).toEqual('other title')
336
+ expect(model.get('comments').length).toEqual(1)
337
+ })
338
+
339
+ describe("passing a collection to a relation attribute", function() {
340
+ var comments, Comments
341
+
342
+ beforeEach(function() {
343
+ Comments = Backbone.Collection.extend({ model: Comment })
344
+ comments = new Comments({ body: 'some comment' }, { body: 'some other comment' })
345
+ model = new Post
346
+ })
347
+
348
+ it("does not store the given collection, but instead updates the existing one with models from the given collection", function() {
349
+ var originalCollection = model.get('comments')
350
+
351
+ model.set({ title: 'Some Title', comments: comments })
352
+
353
+ expect(model.get('comments')).toBe(originalCollection)
354
+ expect(model.get('comments').at(0)).toBe(comments.at(0))
355
+ expect(model.get('comments').at(1)).toBe(comments.at(1))
356
+ })
357
+ })
358
+ })
359
+
360
+ describe("cloning", function() {
361
+ beforeEach(function() {
362
+ post = new Post({ title: 'Some Title', comments: [{ body: 'some comment' }] })
363
+ })
364
+
365
+ it("creates a new post object, with the same attributes, including nested, but do not share the nested collections", function() {
366
+ var newPost = post.clone()
367
+
368
+ expect(newPost).not.toBe(post)
369
+ expect(newPost.get('comments')).not.toBe(post.get('comments'))
370
+ expect(newPost.get('comments').at(0)).not.toBe(post.get('comments').at(0))
371
+
372
+ expect(newPost.get('title')).toEqual(post.get('title'))
373
+ expect(newPost.get('comments').at(0).get('body')).toEqual(post.get('comments').at(0).get('body'))
374
+ })
375
+ })
376
+
377
+ describe("specifying the collection type", function() {
378
+ var Comments
379
+
380
+ beforeEach(function() {
381
+ Comments = Backbone.Collection.extend({ model: Comment })
382
+
383
+ Post = Backbone.NestedAttributesModel.extend({
384
+ relations: [
385
+ {
386
+ key: 'comments',
387
+ collectionType: function () { return Comments }
388
+ }
389
+ ]
390
+ })
391
+
392
+ model = new Post({ title: 'Some Title', comments: [{ body: 'some comment' }] })
393
+ })
394
+
395
+ it("uses the given collection as the relation attribute", function() {
396
+ expect(model.get('comments')).toBeAnInstanceOf(Comments)
397
+ expect(model.get('comments').at(0)).toBeAnInstanceOf(Comment)
398
+ })
399
+ })
400
+
401
+ describe("event bubbling", function() {
402
+ var changedModel, changedCount
403
+
404
+ beforeEach(function() {
405
+ changedCount = 0
406
+ changedModel = undefined
407
+
408
+ model = new Post({ title: 'Some Title', comments: [{ body: 'some comment' }] })
409
+ comment = model.get('comments').at(0)
410
+ })
411
+
412
+ describe("when a nested model is changed", function() {
413
+ it("triggers a nested:change event on the parent, with the changed model as an argument", function() {
414
+ model.on('nested:change', function (model) {
415
+ changedModel = model
416
+ })
417
+
418
+ comment.set({ body: 'some new body' })
419
+ expect(changedModel).toBe(comment)
420
+ })
421
+
422
+ it("triggers a nested:change only once, after setting nested attributes again", function() {
423
+ model.set({ title: 'Some Title', comments: [{ body: 'some other comment' }] })
424
+ comment = model.get('comments').at(0)
425
+
426
+ model.on('nested:change', function (model) {
427
+ changedModel = model
428
+ changedCount += 1
429
+ })
430
+
431
+ comment.set({ body: 'some new body' })
432
+ expect(changedModel).toBe(comment)
433
+ expect(changedCount).toEqual(1)
434
+ })
435
+
436
+ it("triggers a change:<relationKey> event on the parent, with the changed model as an argument", function() {
437
+ model.on('change:comments', function (model) {
438
+ changedModel = model
439
+ })
440
+
441
+ comment.set({ body: 'some new body' })
442
+ expect(changedModel).toBe(comment)
443
+ })
444
+
445
+ it("triggers a change:<relationKey> only once, after setting nested attributes again", function() {
446
+ model.set({ title: 'Some Title', comments: [{ body: 'some other comment' }] })
447
+ comment = model.get('comments').at(0)
448
+
449
+ model.on('change:comments', function (model) {
450
+ changedModel = model
451
+ changedCount += 1
452
+ })
453
+
454
+ comment.set({ body: 'some new body' })
455
+ expect(changedModel).toBe(comment)
456
+ expect(changedCount).toEqual(1)
457
+ })
458
+
459
+ describe("and the nested model triggers a nested:change", function() {
460
+ it("triggers a nested:change on the parent", function() {
461
+ model.on('nested:change', function (model) {
462
+ changedModel = model
463
+ })
464
+
465
+ comment.trigger('nested:change', comment)
466
+ expect(changedModel).toBe(comment)
467
+ })
468
+ })
469
+ })
470
+
471
+ describe("when adding a new model to a nested relation", function() {
472
+ it("triggers a nested:change event on the parent, with the added model as an argument", function() {
473
+ model.on('nested:change', function (model) {
474
+ changedModel = model
475
+ })
476
+
477
+ model.get('comments').add({ body: 'other comment' })
478
+ comment = model.get('comments').at(1)
479
+ expect(changedModel).toBe(comment)
480
+ })
481
+
482
+ it("triggers a change:<relationKey> event on the parent, with the added model as an argument", function() {
483
+ model.on('change:comments', function (model) {
484
+ changedModel = model
485
+ })
486
+
487
+ model.get('comments').add({ body: 'other comment' })
488
+ comment = model.get('comments').at(1)
489
+ expect(changedModel).toBe(comment)
490
+ })
491
+ })
492
+
493
+ describe("when removing a new model to a nested relation", function() {
494
+ it("triggers a nested:change event on the parent, with the removed model as an argument", function() {
495
+ model.on('nested:change', function (model) {
496
+ changedModel = model
497
+ })
498
+
499
+ comment = model.get('comments').at(0)
500
+ model.get('comments').remove(comment)
501
+ expect(changedModel).toBe(comment)
502
+ })
503
+
504
+ it("triggers a change:<relationKey> event on the parent, with the removed model as an argument", function() {
505
+ model.on('change:comments', function (model) {
506
+ changedModel = model
507
+ })
508
+
509
+ comment = model.get('comments').at(0)
510
+ model.get('comments').remove(comment)
511
+ expect(changedModel).toBe(comment)
512
+ })
513
+ })
514
+
515
+ describe("when clearing", function() {
516
+ it("stops listening to collection nested:change events", function() {
517
+ model.on('nested:change', function (model) {
518
+ changedModel = model
519
+ })
520
+
521
+ model.clear()
522
+ comment.set({ body: 'some new body' })
523
+ expect(changedModel).toBeUndefined()
524
+ })
525
+
526
+ it("stops listening to collection change:<relationKey> events", function() {
527
+ model.on('change:comments', function (model) {
528
+ changedModel = model
529
+ })
530
+
531
+ model.clear()
532
+ comment.set({ body: 'some new body' })
533
+ expect(changedModel).toBeUndefined()
534
+ })
535
+ })
536
+ })
537
+
538
+ describe("toJSON", function() {
539
+ beforeEach(function() {
540
+ model = new Post({ title: 'Some Title', comments: [{ body: 'some comment' }] })
541
+ })
542
+
543
+ it("serializes its own attributes, as well as the relation one", function() {
544
+ expect(model.toJSON()).toEqual({ title: 'Some Title', comments: [{ body: 'some comment' }] })
545
+ })
546
+
547
+ describe("with nested attributes support", function() {
548
+ it("serializes the relation attributes with a _attributes suffix", function() {
549
+ expect(model.toJSON({ nested: true })).toEqual({
550
+ title: 'Some Title',
551
+ comments_attributes: [{ body: 'some comment' }]
552
+ })
553
+ })
554
+
555
+ describe("when a nested model is removed", function() {
556
+ describe("and it is a new model", function() {
557
+ beforeEach(function() {
558
+ comment = model.get('comments').at(0)
559
+ })
560
+
561
+ it("is not serialized on the relation", function() {
562
+ model.get('comments').remove(comment)
563
+
564
+ expect(model.toJSON({ nested: true })).toEqual({
565
+ title: 'Some Title',
566
+ comments_attributes: []
567
+ })
568
+ })
569
+ })
570
+
571
+ describe("and it is not a new model", function() {
572
+ beforeEach(function() {
573
+ comment = model.get('comments').at(0)
574
+ comment.set({ id: '123' })
575
+ })
576
+
577
+ it("is serialized on the relation, with { _destroy: true } attribute, besides its own attributes", function() {
578
+ model.get('comments').remove(comment)
579
+
580
+ expect(model.toJSON({ nested: true })).toEqual({
581
+ title: 'Some Title',
582
+ comments_attributes: [{
583
+ id: comment.get('id'),
584
+ body: comment.get('body'),
585
+ _destroy: true
586
+ }]
587
+ })
588
+ })
589
+
590
+ describe("after synchronizing the parent model", function() {
591
+ beforeEach(function() {
592
+ jasmine.Ajax.useMock()
593
+ model.url = 'http://someapi.com'
594
+ })
595
+
596
+ it("is not serialized on the relation", function() {
597
+ model.get('comments').remove(comment)
598
+ model.save()
599
+
600
+ var request = mostRecentAjaxRequest();
601
+ request.response({status: 200, responseText: { title: 'Some Title', comments: [] }})
602
+
603
+ expect(model.toJSON({ nested: true })).toEqual({
604
+ title: 'Some Title',
605
+ comments_attributes: []
606
+ })
607
+ })
608
+ })
609
+
610
+ describe("after clearing the parent model", function() {
611
+ it("is not serialized on the relation", function() {
612
+ model.get('comments').remove(comment)
613
+ model.clear()
614
+
615
+ expect(model.toJSON({ nested: true })).toEqual({
616
+ title: undefined,
617
+ comments_attributes: []
618
+ })
619
+ })
620
+ })
621
+ })
622
+ })
623
+ })
624
+ })
625
+ })
626
+ })