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.
- data/.gitignore +21 -0
- data/.rvmrc +1 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +187 -0
- data/Rakefile +15 -0
- data/app/assets/javascripts/backbone-nested-attributes/all.js +2 -0
- data/app/assets/javascripts/backbone-nested-attributes/model.js +175 -0
- data/app/assets/javascripts/backbone-nested-attributes/undoable.js +60 -0
- data/backbone-nested-attributes.gemspec +20 -0
- data/lib/backbone-nested-attributes.rb +6 -0
- data/lib/backbone-nested-attributes/engine.rb +7 -0
- data/lib/backbone-nested-attributes/version.rb +5 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +18 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +79 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/.gitkeep +0 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/javascripts/backbone-nested-attributes/ModelSpec.js +626 -0
- data/spec/javascripts/backbone-nested-attributes/UndoableSpec.js +489 -0
- data/spec/javascripts/helpers/SpecHelper.js +7 -0
- data/spec/javascripts/helpers/mock-ajax.js +207 -0
- data/spec/javascripts/support/jasmine.yml +76 -0
- 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
|
+
})
|