brainstem-js 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -6
- data/Gemfile.lock +1 -1
- data/README.md +3 -1
- data/Rakefile +2 -0
- data/lib/brainstem/js/version.rb +1 -1
- data/spec/brainstem-collection-spec.coffee +25 -0
- data/spec/{brianstem-expectation-spec.coffee → brainstem-expectation-spec.coffee} +132 -17
- data/spec/brainstem-model-spec.coffee +67 -27
- data/spec/brainstem-sync-spec.coffee +29 -6
- data/spec/brainstem-utils-spec.coffee +11 -1
- data/spec/helpers/builders.coffee +2 -2
- data/spec/helpers/models/post.coffee +1 -1
- data/spec/helpers/models/project.coffee +1 -1
- data/spec/helpers/models/task.coffee +2 -2
- data/spec/helpers/models/time-entry.coffee +1 -1
- data/spec/helpers/models/user.coffee +1 -1
- data/spec/helpers/spec-helper.coffee +21 -0
- data/spec/loaders/abstract-loader-shared-behavior.coffee +604 -0
- data/spec/loaders/abstract-loader-spec.coffee +3 -0
- data/spec/loaders/collection-loader-spec.coffee +146 -0
- data/spec/loaders/model-loader-spec.coffee +99 -0
- data/spec/storage-manager-spec.coffee +242 -56
- data/vendor/assets/javascripts/brainstem/brainstem-collection.coffee +16 -6
- data/vendor/assets/javascripts/brainstem/brainstem-expectation.coffee +70 -20
- data/vendor/assets/javascripts/brainstem/brainstem-model.coffee +13 -13
- data/vendor/assets/javascripts/brainstem/brainstem-sync.coffee +8 -3
- data/vendor/assets/javascripts/brainstem/loaders/abstract-loader.coffee +289 -0
- data/vendor/assets/javascripts/brainstem/loaders/collection-loader.coffee +68 -0
- data/vendor/assets/javascripts/brainstem/loaders/model-loader.coffee +35 -0
- data/vendor/assets/javascripts/brainstem/loading-mixin.coffee +1 -7
- data/vendor/assets/javascripts/brainstem/storage-manager.coffee +79 -196
- data/vendor/assets/javascripts/brainstem/utils.coffee +18 -4
- metadata +17 -6
data/.travis.yml
CHANGED
@@ -1,12 +1,9 @@
|
|
1
1
|
language: ruby
|
2
|
-
rvm:
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
3
5
|
|
4
6
|
before_install: gem install bundler
|
5
7
|
|
6
|
-
notifications:
|
7
|
-
hipchat: 09a2719b3284ea0d0d247355fa5542@Mavenlink Dev
|
8
|
-
|
9
8
|
git:
|
10
9
|
depth: 1
|
11
|
-
|
12
|
-
source_key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBOHBpRjdldDZKM2FmWCt0WDlWRG5HZEt4dDdZZVJVVUh3K29JQVJDekorejBvS3MxCnNwaEUxZlBSQksyUDFMUVV5cngyUThzalgxdnA1NUt6U3lnVWExMFRlUG90VnVWN3hZbVFqTG5FM2FueTJBNXIKMFhXZG4wS2k2cUt5TkVwd2ZJa3JYam8xSmY4SHJ0ajZ5Yi9VTFcySnZxbTBGTTZRNmU1TCtmUGdQN0JqeS9WSApUWHF4UWVRaTVaSVppa2JoeGxoK3RQNVZaM1JMRTF3bEhRdXpESUFZemRFdnFXeEFxYkhvNktyMjJSZzRhaGNPCnNEZ1FGK0drR0h2bjBKTkpmc05Cd3d2YUQ2Y3M5N2pnY045b1VrK1phOFNlbFZhazRjWVhRUk5WaTdPMVdkQnkKSE1xaVNPVjFZdEhSWWdwSFpPaHdYSUdxK3ZhUXVFR21SRzdBY1FJREFRQUJBb0lCQVFDOXBiQ2xjdjFXbG13MwpEd0wrK3RUL0llL2VmeGVnN1RzSjFBMlh6NWRPc2ZYM0dJRHM4ZzUxOTVuQi8zQytSbDB1dEMvOEJYVE1ta3o3CnhIbzNXY2pFdWNsOFBJOXZMQTBiT3RSdXZ0Y0F0bGZxd1ROV1ZvejNNSit0cjZ5Q0ptTlRaK1FvVUhhMkVtM08KS1QrOHNpTEx0S01IRXlGOVZwS0EzZEkxUDRwaUhodlFBdkgzQkdRR3piNW9UczV6RXJnRmZNbEZKWFc0S2NxcApYc1NnWDBjd3d3Nit3WDFLblRGY3lNak9QbTB2MWZSSTdHMGdtOG02VVJrakdVeXhpRXhkTVNEdm5yVEt4ajlvCitIK1NucU14RW5uczJRR1lnSEtnV1ZaSFFBUU9UenBNSVdvNndHODNMMDlyOUo3S1dzZHRwNStKeDNZWWE2eTMKU0tod3NQdGxBb0dCQVAvZTJ2SitCVmpkRXVOSjJibWo4YnlGK2lJd292Vm9FYW4xUExkZ3JJbTNQWkFrLytyMgpiSFV0d0xDVjBSR1l5Z1ZqaW42YmEzUitNM2ptanErM3Z1N0FsVlJNazdvcFVXcjUwanRzbDhhT1k4WkIxTlhuClRlRW5keEswTnBWaWs1VlFhNzBqcmh3NGtmekxhUk9xdUV1SGt6Qk95MXhJeStyUkNMNDMzT1ZmQW9HQkFQSzMKOHNXYm5YRzR0WjdHMU5QVjZ0YXJ5bEt4bFluRnhseG4rUmZueTFHbG1NejU4YW1Jb1F2SnJuSHlSR29xQ2dNOQo4eWJFMmVoVjJFM20wKzUwampDeFNrMjhxbkNybDdnbTdNcjhsVzdGQnZLSUw2NDFsM1pDT3pLSnBKWFdGN1BaCmdYVGplTmduRzliQkljMldBNHpzaGVDK05CRWxidmFkekRrT0hkd3ZBb0dBWkRlZ1lCdzE4ZkZkQllNV2NSeWkKZ1Nta3FDR09vam9wdVB6aDFCMWNWdkJiZjRyT1pmUXcxTkNmeVVwVXdlU3JNK01pQ3FiTE5xeDdjcDR6UXVYZwpOZGxlWTg4K2lVckhwZlBGZ1JydWM0bXYwS1pXTzVYR0xpcnIrM3AwYXB4YW04QU5BdDdud2d2eU9pWmR1S05FClhlanpJSmVzRlRBNkZuWGJTODNMaWxjQ2dZRUExai9kd3VUOGM3ZnlTZmVGUW9DZnpXTFRNMitpYW56ei9mbWgKZmFLVWJMdmFSNFdSOW02dWlmTTdVMFhoY2owdG5YTC93WWNlT3VJY0Q1ZmtGNmMzSkhBN0FLZTdZNzEwTFkvZQprY2VvT0tFZTR0T29Fd1VuYjdKREF2ZFJHeHBpemRUL1d5aTRNVVZFWTZzVHBaLzMvbHVDU2NKYnY0N2xoamdBClg1VEFjdTBDZ1lCRFhnNUF2VHVXTE5pRk91RW5RekhrRmp0N0lwRVVUN2lpZDByMHBJempMVmc1c08zSXRqTkQKNjlvWitIYUlCSVpMWkNQc2lpS2dmQkFIS0xXY3NEQTRnelY2ckpUWDdmdXYvcHZUTk9tdmJlcG5OK0JGd05xdApVU2c5UmxFZ0lFWEE3Q041aVFvb2l2SmFiT3VEREl0NUhiTS9Gd1M0V3QvQVdnQUZkYmpab3c9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Brainstem.js
|
2
2
|
|
3
|
+
[![Build Status](https://travis-ci.org/mavenlink/brainstem-js.png)](https://travis-ci.org/mavenlink/brainstem-js)
|
4
|
+
|
3
5
|
[Brainstem](https://github.com/mavenlink/brainstem) is designed to power rich APIs in Rails. The Brainstem gem provides a presenter library that handles converting ActiveRecord objects into structured JSON and a set of API abstractions that allow users to request sorts, filters, and association loads, allowing for simpler implementations, fewer requests, and smaller responses.
|
4
6
|
|
5
7
|
The Brainstem.js library is a companion library for Backbone.js that makes integration with Brainstem APIs a breeze. Brainstem.js adds an identity map and relational models to Backbone.
|
@@ -23,7 +25,7 @@ What follows is an overview.
|
|
23
25
|
|
24
26
|
### StorageManager
|
25
27
|
|
26
|
-
The `
|
28
|
+
The `Brainstem.StorageManager` is in charge of loading data over the API, as well as returning already cached data. We recommend setting one up in a singleton App class.
|
27
29
|
|
28
30
|
class Application
|
29
31
|
constructor: ->
|
data/Rakefile
CHANGED
@@ -9,6 +9,7 @@ task :build do
|
|
9
9
|
sh "rakep build"
|
10
10
|
end
|
11
11
|
|
12
|
+
desc 'Run jasmine specs with phantomjs'
|
12
13
|
task :spec => :build do
|
13
14
|
begin
|
14
15
|
exec *%w(phantomjs build/headless.js build/headless.html)
|
@@ -18,6 +19,7 @@ task :spec => :build do
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
22
|
+
desc 'Start WEBrick server to run jasmine specs in browser'
|
21
23
|
task :server => :clean do
|
22
24
|
exec "rakep server"
|
23
25
|
end
|
data/lib/brainstem/js/version.rb
CHANGED
@@ -53,6 +53,31 @@ describe 'Brainstem.Collection', ->
|
|
53
53
|
expect(collection.lastFetchOptions.page).toEqual 3
|
54
54
|
expect(collection.length).toEqual 5
|
55
55
|
|
56
|
+
it "fetches based on the last limit and offset if they were the pagination options used", ->
|
57
|
+
respondWith server, "/api/time_entries?limit=2&offset=0", resultsFrom: "time_entries", data: { time_entries: [buildTimeEntry(), buildTimeEntry()] }
|
58
|
+
respondWith server, "/api/time_entries?limit=2&offset=2", resultsFrom: "time_entries", data: { time_entries: [buildTimeEntry(), buildTimeEntry()] }
|
59
|
+
respondWith server, "/api/time_entries?limit=2&offset=4", resultsFrom: "time_entries", data: { time_entries: [buildTimeEntry()] }
|
60
|
+
collection = base.data.loadCollection "time_entries", limit: 2, offset: 0
|
61
|
+
expect(collection.length).toEqual 0
|
62
|
+
server.respond()
|
63
|
+
expect(collection.length).toEqual 2
|
64
|
+
expect(collection.lastFetchOptions.offset).toEqual 0
|
65
|
+
|
66
|
+
spy = jasmine.createSpy()
|
67
|
+
collection.loadNextPage success: spy
|
68
|
+
server.respond()
|
69
|
+
expect(spy).toHaveBeenCalledWith(collection, true)
|
70
|
+
expect(collection.lastFetchOptions.offset).toEqual 2
|
71
|
+
expect(collection.length).toEqual 4
|
72
|
+
|
73
|
+
spy = jasmine.createSpy()
|
74
|
+
collection.loadNextPage success: spy
|
75
|
+
expect(collection.length).toEqual 4
|
76
|
+
server.respond()
|
77
|
+
expect(spy).toHaveBeenCalledWith(collection, false)
|
78
|
+
expect(collection.lastFetchOptions.offset).toEqual 4
|
79
|
+
expect(collection.length).toEqual 5
|
80
|
+
|
56
81
|
describe "reload", ->
|
57
82
|
it "reloads the collection with the original params", ->
|
58
83
|
respondWith server, "/api/posts?include=replies&parents_only=true&per_page=5&page=1", resultsFrom: "posts", data: { posts: [buildPost(message: "old post", reply_ids: [])] }
|
@@ -63,6 +63,32 @@ describe 'Brainstem Expectations', ->
|
|
63
63
|
expect(collection.get(1).get("tasks").models).toEqual [task1]
|
64
64
|
expect(collection.get(2).get("tasks").models).toEqual []
|
65
65
|
|
66
|
+
describe 'recursive loading', ->
|
67
|
+
context 'recursive option is false', ->
|
68
|
+
it "should not try to recursively load includes in an expectation", ->
|
69
|
+
expectation = manager.stub "projects", include: '*', response: (stub) ->
|
70
|
+
stub.results = [project1, project2]
|
71
|
+
stub.associated.projects = [project1, project2]
|
72
|
+
stub.associated.tasks = [task1]
|
73
|
+
|
74
|
+
spy = spyOn(Brainstem.AbstractLoader.prototype, '_loadAdditionalIncludes')
|
75
|
+
collection = manager.loadCollection "projects", include: ["tasks" : ["time_entries"]]
|
76
|
+
expectation.respond()
|
77
|
+
expect(spy).not.toHaveBeenCalled()
|
78
|
+
|
79
|
+
context 'recursive option is true', ->
|
80
|
+
it "should recursively load includes in an expectation", ->
|
81
|
+
expectation = manager.stub "projects", include: '*', response: (stub) ->
|
82
|
+
stub.results = [project1, project2]
|
83
|
+
stub.associated.projects = [project1, project2]
|
84
|
+
stub.associated.tasks = [task1]
|
85
|
+
stub.recursive = true
|
86
|
+
|
87
|
+
spy = spyOn(Brainstem.AbstractLoader.prototype, '_loadAdditionalIncludes')
|
88
|
+
collection = manager.loadCollection "projects", include: ["tasks" : ["time_entries"]]
|
89
|
+
expectation.respond()
|
90
|
+
expect(spy).toHaveBeenCalled()
|
91
|
+
|
66
92
|
describe "triggering errors", ->
|
67
93
|
it "triggers errors when asked to do so", ->
|
68
94
|
errorSpy = jasmine.createSpy()
|
@@ -80,8 +106,7 @@ describe 'Brainstem Expectations', ->
|
|
80
106
|
|
81
107
|
expectation.respond()
|
82
108
|
expect(errorSpy).toHaveBeenCalled()
|
83
|
-
expect(errorSpy.mostRecentCall.args[0]
|
84
|
-
expect(errorSpy.mostRecentCall.args[1]).toEqual resp
|
109
|
+
expect(errorSpy.mostRecentCall.args[0]).toEqual resp
|
85
110
|
|
86
111
|
it "does not trigger errors when asked not to", ->
|
87
112
|
errorSpy = jasmine.createSpy()
|
@@ -92,6 +117,10 @@ describe 'Brainstem Expectations', ->
|
|
92
117
|
expectation.respond()
|
93
118
|
expect(errorSpy).not.toHaveBeenCalled()
|
94
119
|
|
120
|
+
it "should work without specifying results", ->
|
121
|
+
manager.stubImmediate "projects"
|
122
|
+
expect(-> manager.loadCollection("projects")).not.toThrow()
|
123
|
+
|
95
124
|
describe "responding immediately", ->
|
96
125
|
it "uses stubImmediate", ->
|
97
126
|
expectation = manager.stubImmediate "projects", include: ["tasks"], response: (stub) ->
|
@@ -133,6 +162,7 @@ describe 'Brainstem Expectations', ->
|
|
133
162
|
it "should allow wildcard params", ->
|
134
163
|
manager.stubImmediate "projects", include: '*', response: (stub) ->
|
135
164
|
stub.results = [project1, project2]
|
165
|
+
stub.associated.tasks = [task1]
|
136
166
|
expect(manager.loadCollection("projects", include: ["tasks"]).models).toEqual [project1, project2]
|
137
167
|
expect(manager.loadCollection("projects", include: ["users"]).models).toEqual [project1, project2]
|
138
168
|
expect(manager.loadCollection("projects").models).toEqual [project1, project2]
|
@@ -181,29 +211,114 @@ describe 'Brainstem Expectations', ->
|
|
181
211
|
describe "optionsMatch", ->
|
182
212
|
it "should ignore wrapping arrays", ->
|
183
213
|
expectation = new Brainstem.Expectation("projects", { include: "workspaces" }, manager)
|
184
|
-
|
185
|
-
|
214
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
215
|
+
|
216
|
+
loader.setup(name: "projects", include: "workspaces")
|
217
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
218
|
+
|
219
|
+
loader.setup(name: "projects", include: ["workspaces"])
|
220
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
186
221
|
|
187
222
|
it "should treat * as an any match", ->
|
188
223
|
expectation = new Brainstem.Expectation("projects", { include: "*" }, manager)
|
189
|
-
|
190
|
-
|
191
|
-
|
224
|
+
|
225
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
226
|
+
loader.setup(name: "projects", include: "workspaces")
|
227
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
228
|
+
|
229
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
230
|
+
loader.setup(name: "projects", include: ["anything"])
|
231
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
232
|
+
|
233
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
234
|
+
loader.setup(name: "projects", {})
|
235
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
192
236
|
|
193
237
|
it "should treat strings and numbers the same when appropriate", ->
|
194
238
|
expectation = new Brainstem.Expectation("projects", { only: "1" }, manager)
|
195
|
-
|
196
|
-
|
239
|
+
|
240
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
241
|
+
loader.setup(name: "projects", only: 1)
|
242
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
243
|
+
|
244
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
245
|
+
loader.setup(name: "projects", only: "1")
|
246
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
197
247
|
|
198
248
|
it "should treat null, empty array, and empty object the same", ->
|
199
249
|
expectation = new Brainstem.Expectation("projects", { filters: {} }, manager)
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
expect(expectation.
|
250
|
+
|
251
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
252
|
+
loader.setup(name: "projects", filters: null)
|
253
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
254
|
+
|
255
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
256
|
+
loader.setup(name: "projects", filters: {})
|
257
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
258
|
+
|
259
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
260
|
+
loader.setup(name: "projects", {})
|
261
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
262
|
+
|
263
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
264
|
+
loader.setup(name: "projects", filters: { foo: "bar" })
|
265
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe false
|
204
266
|
|
205
267
|
expectation = new Brainstem.Expectation("projects", {}, manager)
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
expect(expectation.
|
268
|
+
|
269
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
270
|
+
loader.setup(name: "projects", filters: null)
|
271
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
272
|
+
|
273
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
274
|
+
loader.setup(name: "projects", filters: {})
|
275
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
276
|
+
|
277
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
278
|
+
loader.setup(name: "projects", {})
|
279
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe true
|
280
|
+
|
281
|
+
loader = new Brainstem.CollectionLoader(storageManager: manager)
|
282
|
+
loader.setup(name: "projects", filters: { foo: "bar" })
|
283
|
+
expect(expectation.loaderOptionsMatch(loader)).toBe false
|
284
|
+
|
285
|
+
describe 'stubbing models', ->
|
286
|
+
context 'a model that matches the load is already in the storage manager', ->
|
287
|
+
it 'updates that model', ->
|
288
|
+
project = buildAndCacheProject()
|
289
|
+
|
290
|
+
expectation = manager.stubModel 'project', project.id, response: (stub) ->
|
291
|
+
stub.result = buildProject(id: project.id, title: 'foobar')
|
292
|
+
|
293
|
+
loaderSpy = jasmine.createSpy('loader').andCallFake (model) ->
|
294
|
+
expect(model).toEqual project
|
295
|
+
expect(model.get('title')).toEqual 'foobar'
|
296
|
+
|
297
|
+
loader = manager.loadModel 'project', project.id
|
298
|
+
loader.done(loaderSpy)
|
299
|
+
|
300
|
+
expectation.respond()
|
301
|
+
expect(loaderSpy).toHaveBeenCalled()
|
302
|
+
expect(manager.storage('projects').length).toEqual 1
|
303
|
+
|
304
|
+
context 'a model is not already in the storage manager', ->
|
305
|
+
it 'adds the model from the loader to the storageManager', ->
|
306
|
+
project = buildProject()
|
307
|
+
stubbedProject = buildProject(id: project.id, title: 'foobar')
|
308
|
+
|
309
|
+
expectation = manager.stubModel 'project', project.id, response: (stub) ->
|
310
|
+
stub.result = stubbedProject
|
311
|
+
|
312
|
+
loader = manager.loadModel 'project', project.id
|
313
|
+
|
314
|
+
loaderSpy = jasmine.createSpy('loader').andCallFake (model) ->
|
315
|
+
expect(model).toEqual loader.getModel()
|
316
|
+
expect(model.attributes).toEqual stubbedProject.attributes
|
317
|
+
expect(manager.storage('projects').get(project.id)).toEqual loader.getModel()
|
318
|
+
expect(model.get('title')).toEqual 'foobar'
|
319
|
+
|
320
|
+
loader.done(loaderSpy)
|
321
|
+
|
322
|
+
expectation.respond()
|
323
|
+
expect(loaderSpy).toHaveBeenCalled()
|
324
|
+
expect(manager.storage('projects').length).toEqual 1
|
@@ -28,6 +28,51 @@ describe 'Brainstem.Model', ->
|
|
28
28
|
expect(base.data.storage('users').get(5).attributes).toEqual(response.users[5])
|
29
29
|
expect(base.data.storage('users').get(6).attributes).toEqual(response.users[6])
|
30
30
|
|
31
|
+
describe 'adding new models to the storage manager', ->
|
32
|
+
context 'there is an ID on the model already', ->
|
33
|
+
# usually happens when fetching an existing model and not using StorageManager#loadModel
|
34
|
+
# new App.Models.Task(id: 5).fetch()
|
35
|
+
|
36
|
+
beforeEach ->
|
37
|
+
model.set('id', 1)
|
38
|
+
|
39
|
+
context 'model ID matches response ID', ->
|
40
|
+
it 'should add the parsing model to the storage manager', ->
|
41
|
+
response.tasks[1].id = 1
|
42
|
+
expect(base.data.storage('tasks').get(1)).toBeUndefined()
|
43
|
+
|
44
|
+
model.parse(response)
|
45
|
+
expect(base.data.storage('tasks').get(1)).not.toBeUndefined()
|
46
|
+
expect(base.data.storage('tasks').get(1)).toEqual model
|
47
|
+
expect(base.data.storage('tasks').get(1).attributes).toEqual response.tasks[1]
|
48
|
+
|
49
|
+
context 'model ID does not match response ID', ->
|
50
|
+
# this only happens when an association has the same brainstemKey as the parent record
|
51
|
+
# we want to add a new model to the storage manager and not worry about ourself
|
52
|
+
|
53
|
+
it 'should not add the parsing model to the storage manager', ->
|
54
|
+
response.tasks[1].id = 2345
|
55
|
+
expect(base.data.storage('tasks').get(1)).toBeUndefined()
|
56
|
+
|
57
|
+
model.parse(response)
|
58
|
+
expect(base.data.storage('tasks').get(1)).toBeUndefined()
|
59
|
+
expect(base.data.storage('tasks').get(2345)).not.toEqual model
|
60
|
+
|
61
|
+
context 'there is not an ID on the model instance already', ->
|
62
|
+
# usually happens when creating a new model:
|
63
|
+
# new App.Models.Task(title: 'test').save()
|
64
|
+
|
65
|
+
beforeEach ->
|
66
|
+
expect(model.id).toBeUndefined()
|
67
|
+
|
68
|
+
it 'should add the parsing model to the storage manager', ->
|
69
|
+
response.tasks[1].title = 'Hello'
|
70
|
+
expect(base.data.storage('tasks').get(1)).toBeUndefined()
|
71
|
+
|
72
|
+
model.parse(response)
|
73
|
+
expect(base.data.storage('tasks').get(1)).toEqual(model)
|
74
|
+
expect(base.data.storage('tasks').get(1).get('title')).toEqual('Hello')
|
75
|
+
|
31
76
|
it 'should work with an empty response', ->
|
32
77
|
expect( -> model.parse(tasks: {}, results: [], count: 0)).not.toThrow()
|
33
78
|
|
@@ -74,33 +119,6 @@ describe 'Brainstem.Model', ->
|
|
74
119
|
expect(base.data.storage('users').get(5).get('created_at')).toEqual(1361820357000)
|
75
120
|
expect(base.data.storage('users').get(6).get('created_at')).toEqual(1359573957000)
|
76
121
|
|
77
|
-
describe 'setLoaded', ->
|
78
|
-
it "should set the values of @loaded", ->
|
79
|
-
model.setLoaded true
|
80
|
-
expect(model.loaded).toEqual(true)
|
81
|
-
model.setLoaded false
|
82
|
-
expect(model.loaded).toEqual(false)
|
83
|
-
|
84
|
-
it "triggers 'loaded' when becoming true", ->
|
85
|
-
spy = jasmine.createSpy()
|
86
|
-
model.bind "loaded", spy
|
87
|
-
model.setLoaded false
|
88
|
-
expect(spy).not.toHaveBeenCalled()
|
89
|
-
model.setLoaded true
|
90
|
-
expect(spy).toHaveBeenCalled()
|
91
|
-
|
92
|
-
it "doesn't trigger loaded if trigger: false is provided", ->
|
93
|
-
spy = jasmine.createSpy()
|
94
|
-
model.bind "loaded", spy
|
95
|
-
model.setLoaded true, trigger: false
|
96
|
-
expect(spy).not.toHaveBeenCalled()
|
97
|
-
|
98
|
-
it "returns self", ->
|
99
|
-
spy = jasmine.createSpy()
|
100
|
-
model.bind "loaded", spy
|
101
|
-
model.setLoaded true
|
102
|
-
expect(spy).toHaveBeenCalledWith(model)
|
103
|
-
|
104
122
|
describe 'associations', ->
|
105
123
|
describe 'associationDetails', ->
|
106
124
|
|
@@ -181,6 +199,28 @@ describe 'Brainstem.Model', ->
|
|
181
199
|
expect(project.associationsAreLoaded(["time_entries"])).toBeTruthy()
|
182
200
|
expect(project.associationsAreLoaded(["tasks"])).toBeFalsy()
|
183
201
|
|
202
|
+
describe "when supplying associations that do not exist", ->
|
203
|
+
class TestClass extends Brainstem.Model
|
204
|
+
@associations:
|
205
|
+
user: "users"
|
206
|
+
|
207
|
+
testClass = null
|
208
|
+
|
209
|
+
beforeEach ->
|
210
|
+
testClass = new TestClass()
|
211
|
+
|
212
|
+
it "returns true when supplying only an association that does not exist", ->
|
213
|
+
expect(testClass.associationsAreLoaded(['foobar'])).toBe true
|
214
|
+
expect(testClass.associationsAreLoaded(['user'])).toBe false
|
215
|
+
|
216
|
+
it "returns false when supplying both a real association that is not loaded and an association that does not exist", ->
|
217
|
+
expect(testClass.associationsAreLoaded(['user', 'foobar'])).toBe false
|
218
|
+
|
219
|
+
it "returns true when supplying a real association that is loaded and an association that does not exist", ->
|
220
|
+
testClass.set('user_id', buildAndCacheUser().id)
|
221
|
+
expect(testClass.associationsAreLoaded(['user'])).toBe true
|
222
|
+
expect(testClass.associationsAreLoaded(['user', 'foobar'])).toBe true
|
223
|
+
|
184
224
|
describe "get", ->
|
185
225
|
it "should delegate to Backbone.Model#get for anything that is not an association", ->
|
186
226
|
timeEntry = new App.Models.TimeEntry(id: 5, project_id: 10, task_id: 2, title: "foo")
|
@@ -1,22 +1,45 @@
|
|
1
1
|
describe "Brainstem.Sync", ->
|
2
|
-
|
3
|
-
ajaxSpy = null
|
2
|
+
ajaxSpy = null
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
beforeEach ->
|
5
|
+
ajaxSpy = spyOn($, 'ajax')
|
7
6
|
|
7
|
+
describe "updating models", ->
|
8
8
|
it "should use toServerJSON instead of toJSON", ->
|
9
9
|
modelSpy = spyOn(Brainstem.Model.prototype, 'toServerJSON')
|
10
10
|
model = buildTimeEntry()
|
11
11
|
model.save()
|
12
12
|
expect(modelSpy).toHaveBeenCalled()
|
13
13
|
|
14
|
-
it "should pass options.
|
14
|
+
it "should pass options.include through the JSON", ->
|
15
15
|
model = buildTimeEntry()
|
16
16
|
model.save({}, include: 'creator')
|
17
17
|
expect(ajaxSpy.mostRecentCall.args[0].data).toMatch(/"include":"creator"/)
|
18
18
|
|
19
|
+
it "should accept an array for options.include", ->
|
20
|
+
model = buildTimeEntry()
|
21
|
+
model.save({}, include: ['creator', 'story'])
|
22
|
+
expect(ajaxSpy.mostRecentCall.args[0].data).toMatch(/"include":"creator,story"/)
|
23
|
+
|
19
24
|
it "should setup param roots when models have a paramRoot set", ->
|
20
25
|
model = buildTimeEntry()
|
21
26
|
model.save({})
|
22
|
-
expect(ajaxSpy.mostRecentCall.args[0].data).toMatch(/"time_entry":{/)
|
27
|
+
expect(ajaxSpy.mostRecentCall.args[0].data).toMatch(/"time_entry":{/)
|
28
|
+
|
29
|
+
describe 'error handler', ->
|
30
|
+
it 'wraps the error handler in an errorInterceptor', ->
|
31
|
+
model = buildTimeEntry()
|
32
|
+
base.data.errorInterceptor = jasmine.createSpy('errorInterceptor')
|
33
|
+
|
34
|
+
model.save({})
|
35
|
+
ajaxSpy.mostRecentCall.args[0].error()
|
36
|
+
expect(base.data.errorInterceptor).toHaveBeenCalled()
|
37
|
+
|
38
|
+
it 'only wraps the error handler if base.data.errorInterceptor is defined', ->
|
39
|
+
delete base.data.errorInterceptor
|
40
|
+
model = buildTimeEntry()
|
41
|
+
errorSpy = jasmine.createSpy('error spy')
|
42
|
+
|
43
|
+
model.save({}, error: errorSpy)
|
44
|
+
ajaxSpy.mostRecentCall.args[0].error()
|
45
|
+
expect(errorSpy).toHaveBeenCalled()
|