brainstem-js 0.2.1 → 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.
- 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
|
+
[](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()
|