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.
- data/app/assets/javascripts/backbone-nested-attributes/model.js +30 -9
- data/app/assets/javascripts/backbone-nested-attributes/undoable.js +20 -3
- data/lib/backbone-nested-attributes/version.rb +1 -1
- data/spec/javascripts/backbone-nested-attributes/ModelSpec.js +74 -0
- data/spec/javascripts/backbone-nested-attributes/UndoableSpec.js +75 -0
- metadata +4 -4
@@ -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
|
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
|
105
|
-
|
106
|
-
|
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
|
-
|
109
|
-
|
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.
|
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
|
-
|
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.
|
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
|
-
|
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
|
|
@@ -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.
|
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-
|
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: -
|
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: -
|
111
|
+
hash: -1599110684641567877
|
112
112
|
requirements: []
|
113
113
|
rubyforge_project:
|
114
114
|
rubygems_version: 1.8.24
|