backbone-nested-attributes 0.2.3 → 0.3.0

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.
@@ -35,10 +35,11 @@
35
35
  function setNestedAttributeFor(model, relation, attributes) {
36
36
  var key = relation.key,
37
37
  value = attributes[key],
38
+ deletedValue = attributes['deleted_' + key],
38
39
  currentValue = model.get(key),
39
40
  nested = currentValue || createNestedAttributeCollection(relation)
40
41
 
41
- value = value instanceof Backbone.Collection ? value.slice() : value
42
+ value = valueOrSliceCollection(value)
42
43
 
43
44
  configureEventBubbling(model, nested, relation)
44
45
 
@@ -46,11 +47,22 @@
46
47
  nested.set(value)
47
48
  }
48
49
 
50
+ if (deletedValue) {
51
+ delete attributes['deleted_' + key]
52
+
53
+ deletedValue = valueOrSliceCollection(deletedValue)
54
+ nested.deletedModels.set(deletedValue)
55
+ }
56
+
49
57
  attributes[key] = nested
50
58
 
51
59
  return attributes
52
60
  }
53
61
 
62
+ function valueOrSliceCollection(value) {
63
+ return value instanceof Backbone.Collection ? value.slice() : value
64
+ }
65
+
54
66
  function clearDeletedModelsFor(model) {
55
67
  _(model.relations).each(function (relation) {
56
68
  var collection = model.get(relation.key)
@@ -101,12 +113,20 @@
101
113
  jsonValue
102
114
 
103
115
  if (value) {
104
- if (options && options.nested) {
105
- delete json[key]
106
- key = key + '_attributes'
116
+ if (options) {
117
+ if (options.withDeleted) {
118
+ if (value.deletedModels) {
119
+ json['deleted_' + key] = value.deletedModels.toJSON(options)
120
+ }
121
+ }
122
+
123
+ if (options.nested) {
124
+ if (value.deletedModels) {
125
+ deleted = value.deletedModels.toJSON(options)
126
+ }
107
127
 
108
- if (value.deletedModels) {
109
- deleted = value.deletedModels.toJSON(options)
128
+ delete json[key]
129
+ key = key + '_attributes'
110
130
  }
111
131
  }
112
132
 
@@ -130,15 +150,16 @@
130
150
  collection.model = _(relation).result('relatedModel') || collection.model
131
151
 
132
152
  collection.deletedModels = new Backbone.Collection
133
- collection.on('remove', nestedModelRemoved, collection)
153
+ collection.deletedModels.model = collection.model
154
+ collection.on('remove', nestedModelRemoved)
134
155
 
135
156
  return collection
136
157
  }
137
158
 
138
- function nestedModelRemoved(model) {
159
+ function nestedModelRemoved(model, collection) {
139
160
  if (!model.isNew()) {
140
161
  model.set({ _destroy: true })
141
- this.deletedModels.add(model) // this refers to the collection
162
+ collection.deletedModels.add(model)
142
163
  }
143
164
  }
144
165
 
@@ -16,17 +16,34 @@
16
16
  },
17
17
 
18
18
  save: function () {
19
- this.attributes = this.model.toJSON()
20
- this.changed = false
19
+ this.updateAttributes()
21
20
 
22
21
  this.model.trigger('state:store')
23
22
  },
24
23
 
25
24
  undo: function () {
25
+ this.attributesToUnset().each(function (attribute) {
26
+ this.model.unset(attribute)
27
+ }, this)
28
+
26
29
  this.model.set(this.attributes)
27
- this.changed = false
30
+
31
+ this.updateAttributes()
28
32
 
29
33
  this.model.trigger('state:restore')
34
+ },
35
+
36
+ attributesToUnset: function () {
37
+ var previousAttributes = _(this.attributes || {}).keys()
38
+
39
+ return _(this.model.attributes).chain().keys().select(function (attribute) {
40
+ return !_(previousAttributes).include(attribute)
41
+ })
42
+ },
43
+
44
+ updateAttributes: function () {
45
+ this.attributes = this.model.toJSON({ withDeleted: true })
46
+ this.changed = false
30
47
  }
31
48
  })
32
49
 
@@ -1,5 +1,5 @@
1
1
  module Backbone
2
2
  module NestedAttributes
3
- VERSION = "0.2.3"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -285,6 +285,26 @@ describe("Backbone.NestedAttributesModel", function() {
285
285
  expect(model.get('comments').at(1)).toBe(comments.at(1))
286
286
  })
287
287
  })
288
+
289
+ describe("passing a deleted_<collection> attribute", function() {
290
+ var comments
291
+
292
+ beforeEach(function() {
293
+ model = new Post({ title: 'Some Title', deleted_comments: [{ id: 123, body: "some deleted comment", _destroy: true }] })
294
+ })
295
+
296
+ it("does not save this key as an attribute", function() {
297
+ expect(model.get('deleted_comments')).toBeUndefined()
298
+ })
299
+
300
+ it("adds the models in the deleted_comments attribute to the deletedModels collection inside the relation collection", function() {
301
+ comments = model.get('comments')
302
+ expect(comments.deletedModels.at(0)).toBeAnInstanceOf(Comment)
303
+ expect(comments.deletedModels.at(0).get('id')).toEqual(123)
304
+ expect(comments.deletedModels.at(0).get('body')).toEqual('some deleted comment')
305
+ expect(comments.deletedModels.at(0).get('_destroy')).toBeTruthy()
306
+ })
307
+ })
288
308
  })
289
309
  })
290
310
 
@@ -355,6 +375,29 @@ describe("Backbone.NestedAttributesModel", function() {
355
375
  expect(model.get('comments').at(1)).toBe(comments.at(1))
356
376
  })
357
377
  })
378
+
379
+ describe("passing a deleted_<collection> attribute", function() {
380
+ var comments
381
+
382
+ beforeEach(function() {
383
+ model = new Post({ title: 'Some Title' })
384
+ })
385
+
386
+ it("does not save this key as an attribute", function() {
387
+ model.set({ deleted_comments: [{ id: 123, body: "some deleted comment", _destroy: true }] })
388
+ expect(model.get('deleted_comments')).toBeUndefined()
389
+ })
390
+
391
+ it("adds the models in the deleted_comments attribute to the deletedModels collection inside the relation collection", function() {
392
+ model.set({ deleted_comments: [{ id: 123, body: "some deleted comment", _destroy: true }] })
393
+ comments = model.get('comments')
394
+
395
+ expect(comments.deletedModels.at(0)).toBeAnInstanceOf(Comment)
396
+ expect(comments.deletedModels.at(0).get('id')).toEqual(123)
397
+ expect(comments.deletedModels.at(0).get('body')).toEqual('some deleted comment')
398
+ expect(comments.deletedModels.at(0).get('_destroy')).toBeTruthy()
399
+ })
400
+ })
358
401
  })
359
402
 
360
403
  describe("cloning", function() {
@@ -621,6 +664,37 @@ describe("Backbone.NestedAttributesModel", function() {
621
664
  })
622
665
  })
623
666
  })
667
+
668
+ describe("with deleted models", function() {
669
+ beforeEach(function() {
670
+ model = new Post({ id: 321, title: 'Some Title', comments: [{ id: 123, body: 'some comment' }] })
671
+ comment = model.get('comments').at(0)
672
+ })
673
+
674
+ it("serializes the deleted models in a relation in a delete_<relation> key", function() {
675
+ model.get('comments').remove(comment)
676
+
677
+ expect(model.toJSON({ withDeleted: true })).toEqual({
678
+ id: 321,
679
+ title: 'Some Title',
680
+ comments: [],
681
+ deleted_comments: [ {id: 123, body: 'some comment', _destroy: true} ]
682
+ })
683
+ })
684
+
685
+ describe("and nested attributes support", function() {
686
+ it("serializes the deleted models in a relation in a delete_<relation> key", function() {
687
+ model.get('comments').remove(comment)
688
+
689
+ expect(model.toJSON({ withDeleted: true, nested: true })).toEqual({
690
+ id: 321,
691
+ title: 'Some Title',
692
+ comments_attributes: [{ id: 123, body: 'some comment', _destroy: true }],
693
+ deleted_comments: [ { id: 123, body: 'some comment', _destroy: true } ]
694
+ })
695
+ })
696
+ })
697
+ })
624
698
  })
625
699
  })
626
700
  })
@@ -293,6 +293,14 @@ describe("Backbone.UndoableModel", function() {
293
293
  })
294
294
  })
295
295
 
296
+ describe("when an attribute is added", function() {
297
+ it("unsets this attribute on undo", function() {
298
+ model.set({ newAttr: 'foo' })
299
+ model.undo()
300
+ expect(model.has('newAttr')).toBeFalsy()
301
+ })
302
+ })
303
+
296
304
  describe("when it has a has one relationship", function() {
297
305
  beforeEach(function() {
298
306
  Post = Backbone.UndoableModel.extend({
@@ -485,5 +493,72 @@ describe("Backbone.UndoableModel", function() {
485
493
  expect(called).toBeTruthy()
486
494
  })
487
495
  })
496
+
497
+
498
+ describe("when undoing changes after a nested model had been removed", function() {
499
+ beforeEach(function() {
500
+ originalAttributes = { id: 321, title: 'some title', comments: [ { id: 123, body: 'some body' } ] }
501
+ post = new Post(_(originalAttributes).clone())
502
+ comments = post.get('comments')
503
+ comment = comments.at(0)
504
+
505
+ comments.remove(comment)
506
+ })
507
+
508
+ it("undoes the deletedModels in the relation collection as well", function() {
509
+ post.undo()
510
+ expect(comments.deletedModels.length).toEqual(0)
511
+ })
512
+
513
+ describe("and the state saved", function() {
514
+ beforeEach(function() {
515
+ post.saveState()
516
+ })
517
+
518
+ it("keeps the deletedModels in the relation collection", function() {
519
+ post.undo()
520
+ expect(comments.deletedModels.at(0)).toBe(comment)
521
+ })
522
+ })
523
+ })
524
+
525
+ describe("when undoing changes after a undo", function() {
526
+ it("reverts the changes properly", function() {
527
+ post.set({ comments: [{ id: 123, body: 'first body' }] })
528
+ post.undo()
529
+ post.set({ comments: [{ id: 123, body: 'last body' }] })
530
+ post.undo()
531
+ expect(post.get('comments').at(0).get('body')).toEqual('some body')
532
+ })
533
+
534
+ describe("on a deep nested model", function() {
535
+ var Tag, tag
536
+
537
+ beforeEach(function() {
538
+ Comment = Backbone.UndoableModel.extend({
539
+ relations: [
540
+ {
541
+ key: 'tags',
542
+ relatedModel: function () { return Tag }
543
+ }
544
+ ]
545
+ })
546
+
547
+ Tag = Backbone.UndoableModel.extend({})
548
+
549
+ originalAttributes = { id: 321, title: 'some title', comments: [ { id: 123, body: 'some body', tags: [ { id: 456, name: 'javascript' } ] } ] }
550
+ post = new Post(originalAttributes)
551
+ })
552
+
553
+ it("reverts the changes properly", function() {
554
+ post.set({ comments: [ { id: 123, body: 'some body', tags: [ { id: 456, name: 'ruby' } ] } ] })
555
+ post.undo()
556
+ post.set({ comments: [ { id: 123, body: 'some body', tags: [ { id: 456, name: 'python' } ] } ] })
557
+ post.undo()
558
+
559
+ expect(post.get('comments').at(0).get('tags').at(0).get('name')).toEqual('javascript')
560
+ })
561
+ })
562
+ })
488
563
  })
489
564
  })
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backbone-nested-attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-22 00:00:00.000000000 Z
12
+ date: 2013-05-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -99,7 +99,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
99
  version: '0'
100
100
  segments:
101
101
  - 0
102
- hash: -2817376123414823357
102
+ hash: -1599110684641567877
103
103
  required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  none: false
105
105
  requirements:
@@ -108,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
108
  version: '0'
109
109
  segments:
110
110
  - 0
111
- hash: -2817376123414823357
111
+ hash: -1599110684641567877
112
112
  requirements: []
113
113
  rubyforge_project:
114
114
  rubygems_version: 1.8.24