backbone-nested-attributes 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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