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
@@ -0,0 +1,68 @@
|
|
1
|
+
window.Brainstem ?= {}
|
2
|
+
|
3
|
+
class Brainstem.CollectionLoader extends Brainstem.AbstractLoader
|
4
|
+
getCollection: ->
|
5
|
+
@externalObject
|
6
|
+
|
7
|
+
_getCollectionName: ->
|
8
|
+
@loadOptions.name
|
9
|
+
|
10
|
+
_getExpectationName: ->
|
11
|
+
@_getCollectionName()
|
12
|
+
|
13
|
+
_createObjects: ->
|
14
|
+
@internalObject = @storageManager.createNewCollection @loadOptions.name, []
|
15
|
+
|
16
|
+
@externalObject = @loadOptions.collection || @storageManager.createNewCollection @loadOptions.name, []
|
17
|
+
@externalObject.setLoaded false
|
18
|
+
@externalObject.reset([], silent: false) if @loadOptions.reset
|
19
|
+
@externalObject.lastFetchOptions = _.pick($.extend(true, {}, @loadOptions), 'name', 'filters', 'page', 'perPage', 'limit', 'offset', 'order', 'search')
|
20
|
+
@externalObject.lastFetchOptions.include = @originalOptions.include
|
21
|
+
|
22
|
+
_updateStorageManagerFromResponse: (resp) ->
|
23
|
+
# The server response should look something like this:
|
24
|
+
# {
|
25
|
+
# count: 200,
|
26
|
+
# results: [{ key: "tasks", id: 10 }, { key: "tasks", id: 11 }],
|
27
|
+
# time_entries: [{ id: 2, title: "te1", project_id: 6, task_id: [10, 11] }]
|
28
|
+
# projects: [{id: 6, title: "some project", time_entry_ids: [2] }]
|
29
|
+
# tasks: [{id: 10, title: "some task" }, {id: 11, title: "some other task" }]
|
30
|
+
# }
|
31
|
+
# Loop over all returned data types and update our local storage to represent any new data.
|
32
|
+
|
33
|
+
results = resp['results']
|
34
|
+
keys = _.reject(_.keys(resp), (key) -> key == 'count' || key == 'results')
|
35
|
+
unless _.isEmpty(results)
|
36
|
+
keys.splice(keys.indexOf(@loadOptions.name), 1) if keys.indexOf(@loadOptions.name) != -1
|
37
|
+
keys.push(@loadOptions.name)
|
38
|
+
|
39
|
+
for underscoredModelName in keys
|
40
|
+
@storageManager.storage(underscoredModelName).update _(resp[underscoredModelName]).values()
|
41
|
+
|
42
|
+
if @loadOptions.cache && !@loadOptions.only?
|
43
|
+
@storageManager.getCollectionDetails(@loadOptions.name).cache[@loadOptions.cacheKey] = results
|
44
|
+
|
45
|
+
if @loadOptions.only?
|
46
|
+
data = _.map(@loadOptions.only, (id) => @cachedCollection.get(id))
|
47
|
+
else
|
48
|
+
data = _.map(results, (result) => @storageManager.storage(result.key).get(result.id))
|
49
|
+
|
50
|
+
data
|
51
|
+
|
52
|
+
_updateObjects: (object, data, silent = false) ->
|
53
|
+
object.setLoaded true, trigger: false
|
54
|
+
|
55
|
+
if data
|
56
|
+
data = data.models if data.models?
|
57
|
+
if object.length
|
58
|
+
object.add data
|
59
|
+
else
|
60
|
+
object.reset data
|
61
|
+
|
62
|
+
object.setLoaded true unless silent
|
63
|
+
|
64
|
+
_getModel: ->
|
65
|
+
@internalObject.model
|
66
|
+
|
67
|
+
_getModelsForAssociation: (association) ->
|
68
|
+
@internalObject.map (m) => @_modelsOrObj(m.get(association))
|
@@ -0,0 +1,35 @@
|
|
1
|
+
window.Brainstem ?= {}
|
2
|
+
|
3
|
+
class Brainstem.ModelLoader extends Brainstem.AbstractLoader
|
4
|
+
getModel: ->
|
5
|
+
@externalObject
|
6
|
+
|
7
|
+
_getCollectionName: ->
|
8
|
+
@loadOptions.name.pluralize()
|
9
|
+
|
10
|
+
_getExpectationName: ->
|
11
|
+
@loadOptions.name
|
12
|
+
|
13
|
+
_createObjects: ->
|
14
|
+
id = @loadOptions.only[0]
|
15
|
+
|
16
|
+
@internalObject = @storageManager.storage(@_getCollectionName()).get(id) || @storageManager.createNewModel(@loadOptions.name, id: id)
|
17
|
+
@externalObject = @internalObject
|
18
|
+
|
19
|
+
_updateStorageManagerFromResponse: (resp) ->
|
20
|
+
@internalObject.parse(resp)
|
21
|
+
|
22
|
+
_updateObjects: (object, data) ->
|
23
|
+
if _.isArray(data) && data.length == 1
|
24
|
+
data = data[0]
|
25
|
+
|
26
|
+
if data instanceof Backbone.Model
|
27
|
+
data = data.attributes
|
28
|
+
|
29
|
+
object.set(data)
|
30
|
+
|
31
|
+
_getModel: ->
|
32
|
+
@internalObject.constructor
|
33
|
+
|
34
|
+
_getModelsForAssociation: (association) ->
|
35
|
+
@_modelsOrObj(@internalObject.get(association))
|
@@ -4,10 +4,4 @@ Brainstem.LoadingMixin =
|
|
4
4
|
setLoaded: (state, options) ->
|
5
5
|
options = { trigger: true } unless options? && options.trigger? && !options.trigger
|
6
6
|
@loaded = state
|
7
|
-
@trigger 'loaded',
|
8
|
-
|
9
|
-
whenLoaded: (func) ->
|
10
|
-
if @loaded
|
11
|
-
func()
|
12
|
-
else
|
13
|
-
@bind "loaded", => func()
|
7
|
+
@trigger 'loaded', this if state && options.trigger
|
@@ -22,51 +22,44 @@ class window.Brainstem.StorageManager
|
|
22
22
|
|
23
23
|
# Access the cache for a particular collection.
|
24
24
|
# manager.storage("time_entries").get(12).get("title")
|
25
|
-
storage: (name)
|
25
|
+
storage: (name) ->
|
26
26
|
@getCollectionDetails(name).storage
|
27
27
|
|
28
|
-
dataUsage:
|
28
|
+
dataUsage: ->
|
29
29
|
sum = 0
|
30
30
|
for dataType in @collectionNames()
|
31
31
|
sum += @storage(dataType).length
|
32
32
|
sum
|
33
33
|
|
34
|
-
reset:
|
34
|
+
reset: ->
|
35
35
|
for name, attributes of @collections
|
36
36
|
attributes.storage.reset []
|
37
37
|
attributes.cache = {}
|
38
38
|
|
39
39
|
# Access details of a collection. An error will be thrown if the collection cannot be found.
|
40
|
-
getCollectionDetails: (name)
|
40
|
+
getCollectionDetails: (name) ->
|
41
41
|
@collections[name] || @collectionError(name)
|
42
42
|
|
43
|
-
collectionNames:
|
43
|
+
collectionNames: ->
|
44
44
|
_.keys(@collections)
|
45
45
|
|
46
|
-
collectionExists: (name)
|
46
|
+
collectionExists: (name) ->
|
47
47
|
!!@collections[name]
|
48
48
|
|
49
|
-
setErrorInterceptor: (interceptor)
|
50
|
-
@errorInterceptor = interceptor || (handler, modelOrCollection, options, jqXHR, requestParams) -> handler?(
|
49
|
+
setErrorInterceptor: (interceptor) ->
|
50
|
+
@errorInterceptor = interceptor || (handler, modelOrCollection, options, jqXHR, requestParams) -> handler?(jqXHR)
|
51
51
|
|
52
|
-
# Request a model to be loaded, optionally ensuring that associations be included as well. A
|
53
|
-
# when the load, and any dependent loads, are complete.
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
loadModel
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
only: id
|
64
|
-
success: (collection) ->
|
65
|
-
model.setLoaded true, trigger: false
|
66
|
-
model.set collection.get(id).attributes
|
67
|
-
model.setLoaded true
|
68
|
-
oldSuccess(model) if oldSuccess
|
69
|
-
model
|
52
|
+
# Request a model to be loaded, optionally ensuring that associations be included as well. A loader (which is a jQuery promise) is returned immediately and is resolved
|
53
|
+
# with the model from the StorageManager when the load, and any dependent loads, are complete.
|
54
|
+
# loader = manager.loadModel "time_entry", 2
|
55
|
+
# loader = manager.loadModel "time_entry", 2, fields: ["title", "notes"]
|
56
|
+
# loader = manager.loadModel "time_entry", 2, include: ["project", "task"]
|
57
|
+
# manager.loadModel("time_entry", 2, include: ["project", "task"]).done (model) -> console.log model
|
58
|
+
loadModel: (name, id, options = {}) ->
|
59
|
+
return if not id
|
60
|
+
|
61
|
+
loader = @loadObject(name, $.extend({}, options, only: id), isCollection: false)
|
62
|
+
loader
|
70
63
|
|
71
64
|
# Request a set of data to be loaded, optionally ensuring that associations be included as well. A collection is returned immediately and is reset
|
72
65
|
# when the load, and any dependent loads, are complete.
|
@@ -77,183 +70,26 @@ class window.Brainstem.StorageManager
|
|
77
70
|
# collection = manager.loadCollection "time_entries", include: ["project:title,description", "task:due_date"]
|
78
71
|
# collection = manager.loadCollection "tasks", include: ["assets", { "assignees": "account" }, { "sub_tasks": ["assignees", "assets"] }]
|
79
72
|
# collection = manager.loadCollection "time_entries", filters: ["project_id:6", "editable:true"], order: "updated_at:desc", page: 1, perPage: 20
|
80
|
-
loadCollection: (name, options)
|
81
|
-
|
82
|
-
|
83
|
-
include = @_wrapObjects(Brainstem.Utils.extractArray "include", options)
|
84
|
-
if options.search
|
85
|
-
options.cache = false
|
86
|
-
|
87
|
-
collection = options.collection || @createNewCollection name, []
|
88
|
-
collection.setLoaded false
|
89
|
-
collection.reset([], silent: false) if options.reset
|
90
|
-
collection.lastFetchOptions = _.pick($.extend(true, {}, options), 'name', 'filters', 'include', 'page', 'perPage', 'order', 'search')
|
91
|
-
|
92
|
-
if @expectations?
|
93
|
-
@handleExpectations name, collection, options
|
94
|
-
else
|
95
|
-
@_loadCollectionWithFirstLayer($.extend({}, options, include: include, success: ((firstLayerCollection) =>
|
96
|
-
expectedAdditionalLoads = @_countRequiredServerRequests(include) - 1
|
97
|
-
if expectedAdditionalLoads > 0
|
98
|
-
timesCalled = 0
|
99
|
-
@_handleNextLayer firstLayerCollection, include, =>
|
100
|
-
timesCalled += 1
|
101
|
-
if timesCalled == expectedAdditionalLoads
|
102
|
-
@_success(options, collection, firstLayerCollection)
|
103
|
-
else
|
104
|
-
@_success(options, collection, firstLayerCollection)
|
105
|
-
)))
|
106
|
-
|
107
|
-
collection
|
108
|
-
|
109
|
-
_handleNextLayer: (collection, include, callback) =>
|
110
|
-
# Collection is a fully populated collection of tasks whose first layer of associations are loaded.
|
111
|
-
# include is a hierarchical list of associations on those tasks:
|
112
|
-
# [{ 'time_entries': ['project': [], 'task': [{ 'assignees': []}]] }, { 'project': [] }]
|
113
|
-
|
114
|
-
_(include).each (hash) => # { 'time_entries': ['project': [], 'task': [{ 'assignees': []}]] }
|
115
|
-
association = _.keys(hash)[0] # time_entries
|
116
|
-
nextLevelInclude = hash[association] # ['project': [], 'task': [{ 'assignees': []}]]
|
117
|
-
if nextLevelInclude.length
|
118
|
-
association_ids = _(collection.models).chain().
|
119
|
-
map((m) -> if (a = m.get(association)) instanceof Backbone.Collection then a.models else a).
|
120
|
-
flatten().uniq().compact().pluck("id").sort().value()
|
121
|
-
newCollectionName = collection.model.associationDetails(association).collectionName
|
122
|
-
@_loadCollectionWithFirstLayer name: newCollectionName, only: association_ids, include: nextLevelInclude, success: (loadedAssociationCollection) =>
|
123
|
-
@_handleNextLayer(loadedAssociationCollection, nextLevelInclude, callback)
|
124
|
-
callback()
|
125
|
-
|
126
|
-
_loadCollectionWithFirstLayer: (options) =>
|
127
|
-
options = $.extend({}, options)
|
128
|
-
name = options.name
|
129
|
-
only = if options.only then _.map((Brainstem.Utils.extractArray "only", options), (id) -> String(id)) else null
|
130
|
-
search = options.search
|
131
|
-
include = _(options.include).map((i) -> _.keys(i)[0]) # pull off the top layer of includes
|
132
|
-
filters = options.filters || {}
|
133
|
-
order = options.order || "updated_at:desc"
|
134
|
-
cacheKey = "#{order}|#{_.chain(filters).pairs().map(([k, v]) -> "#{k}:#{v}" ).value().join(",")}|#{options.page}|#{options.perPage}"
|
73
|
+
loadCollection: (name, options = {}) ->
|
74
|
+
loader = @loadObject(name, options)
|
75
|
+
loader.externalObject
|
135
76
|
|
136
|
-
|
137
|
-
collection = @createNewCollection name, []
|
138
|
-
|
139
|
-
unless options.cache == false
|
140
|
-
if only?
|
141
|
-
alreadyLoadedIds = _.select only, (id) => cachedCollection.get(id)?.associationsAreLoaded(include)
|
142
|
-
if alreadyLoadedIds.length == only.length
|
143
|
-
# We've already seen every id that is being asked for and have all the associated data.
|
144
|
-
@_success options, collection, _.map only, (id) => cachedCollection.get(id)
|
145
|
-
return collection
|
146
|
-
else
|
147
|
-
# Check if we have, at some point, requested enough records with this this order and filter(s).
|
148
|
-
if @getCollectionDetails(name).cache[cacheKey]
|
149
|
-
subset = _(@getCollectionDetails(name).cache[cacheKey]).map (result) -> base.data.storage(result.key).get(result.id)
|
150
|
-
if (_.all(subset, (model) => model.associationsAreLoaded(include)))
|
151
|
-
@_success options, collection, subset
|
152
|
-
return collection
|
153
|
-
|
154
|
-
# If we haven't returned yet, we need to go to the server to load some missing data.
|
155
|
-
syncOptions =
|
156
|
-
data: {}
|
157
|
-
parse: true
|
158
|
-
error: options.error
|
159
|
-
success: (resp, status, xhr) =>
|
160
|
-
# The server response should look something like this:
|
161
|
-
# {
|
162
|
-
# count: 200,
|
163
|
-
# results: [{ key: "tasks", id: 10 }, { key: "tasks", id: 11 }],
|
164
|
-
# time_entries: [{ id: 2, title: "te1", project_id: 6, task_id: [10, 11] }]
|
165
|
-
# projects: [{id: 6, title: "some project", time_entry_ids: [2] }]
|
166
|
-
# tasks: [{id: 10, title: "some task" }, {id: 11, title: "some other task" }]
|
167
|
-
# }
|
168
|
-
# Loop over all returned data types and update our local storage to represent any new data.
|
169
|
-
|
170
|
-
results = resp['results']
|
171
|
-
keys = _.reject(_.keys(resp), (key) -> key == 'count' || key == 'results')
|
172
|
-
unless _.isEmpty(results)
|
173
|
-
keys.splice(keys.indexOf(name), 1) if keys.indexOf(name) != -1
|
174
|
-
keys.push(name)
|
175
|
-
|
176
|
-
for underscoredModelName in keys
|
177
|
-
@storage(underscoredModelName).update _(resp[underscoredModelName]).values()
|
178
|
-
|
179
|
-
unless options.cache == false || only?
|
180
|
-
@getCollectionDetails(name).cache[cacheKey] = results
|
181
|
-
|
182
|
-
if only?
|
183
|
-
@_success options, collection, _.map(only, (id) -> cachedCollection.get(id))
|
184
|
-
else
|
185
|
-
@_success options, collection, _(results).map (result) -> base.data.storage(result.key).get(result.id)
|
186
|
-
|
187
|
-
|
188
|
-
syncOptions.data.include = include.join(",") if include.length
|
189
|
-
syncOptions.data.only = _.difference(only, alreadyLoadedIds).join(",") if only?
|
190
|
-
syncOptions.data.order = options.order if options.order?
|
191
|
-
_.extend(syncOptions.data, _(filters).omit('include', 'only', 'order', 'per_page', 'page', 'search')) if _(filters).keys().length
|
192
|
-
syncOptions.data.per_page = options.perPage unless only?
|
193
|
-
syncOptions.data.page = options.page unless only?
|
194
|
-
syncOptions.data.search = search if search
|
195
|
-
|
196
|
-
Backbone.sync.call collection, 'read', collection, syncOptions
|
197
|
-
|
198
|
-
collection
|
199
|
-
|
200
|
-
_success: (options, collection, data) =>
|
201
|
-
if data
|
202
|
-
data = data.models if data.models?
|
203
|
-
collection.setLoaded true, trigger: false
|
204
|
-
if collection.length
|
205
|
-
collection.add data
|
206
|
-
else
|
207
|
-
collection.reset data
|
208
|
-
collection.setLoaded true
|
209
|
-
options.success(collection) if options.success?
|
210
|
-
|
211
|
-
_checkPageSettings: (options) =>
|
212
|
-
options.perPage = options.perPage || 20
|
213
|
-
options.perPage = 1 if options.perPage < 1
|
214
|
-
options.page = options.page || 1
|
215
|
-
options.page = 1 if options.page < 1
|
216
|
-
|
217
|
-
collectionError: (name) =>
|
77
|
+
collectionError: (name) ->
|
218
78
|
Brainstem.Utils.throwError("Unknown collection #{name} in StorageManager. Known collections: #{_(@collections).keys().join(", ")}")
|
219
79
|
|
220
|
-
createNewCollection: (collectionName, models = [], options = {})
|
80
|
+
createNewCollection: (collectionName, models = [], options = {}) ->
|
221
81
|
loaded = options.loaded
|
222
82
|
delete options.loaded
|
223
83
|
collection = new (@getCollectionDetails(collectionName).klass)(models, options)
|
224
84
|
collection.setLoaded(true, trigger: false) if loaded
|
225
85
|
collection
|
226
86
|
|
227
|
-
createNewModel: (modelName, options)
|
87
|
+
createNewModel: (modelName, options) ->
|
228
88
|
new (@getCollectionDetails(modelName.pluralize()).modelKlass)(options || {})
|
229
89
|
|
230
|
-
_wrapObjects: (array) =>
|
231
|
-
output = []
|
232
|
-
_(array).each (elem) =>
|
233
|
-
if elem.constructor == Object
|
234
|
-
for key, value of elem
|
235
|
-
o = {}
|
236
|
-
o[key] = @_wrapObjects(if value instanceof Array then value else [value])
|
237
|
-
output.push o
|
238
|
-
else
|
239
|
-
o = {}
|
240
|
-
o[elem] = []
|
241
|
-
output.push o
|
242
|
-
output
|
243
|
-
|
244
|
-
_countRequiredServerRequests: (array, wrapped = false) =>
|
245
|
-
if array?.length
|
246
|
-
array = @_wrapObjects(array) unless wrapped
|
247
|
-
sum = 1
|
248
|
-
_(array).each (elem) =>
|
249
|
-
sum += @_countRequiredServerRequests(_(elem).values()[0], true)
|
250
|
-
sum
|
251
|
-
else
|
252
|
-
0
|
253
|
-
|
254
90
|
# Expectations and stubbing
|
255
91
|
|
256
|
-
stub: (collectionName, options)
|
92
|
+
stub: (collectionName, options = {}) ->
|
257
93
|
if @expectations?
|
258
94
|
expectation = new Brainstem.Expectation(collectionName, options, @)
|
259
95
|
@expectations.push expectation
|
@@ -261,15 +97,62 @@ class window.Brainstem.StorageManager
|
|
261
97
|
else
|
262
98
|
throw "You must call #enableExpectations on your instance of Brainstem.StorageManager before you can set expectations."
|
263
99
|
|
264
|
-
|
100
|
+
stubModel: (modelName, modelId, options = {}) ->
|
101
|
+
@stub(modelName, $.extend({}, options, only: modelId))
|
102
|
+
|
103
|
+
stubImmediate: (collectionName, options) ->
|
265
104
|
@stub collectionName, $.extend({}, options, immediate: true)
|
266
105
|
|
267
|
-
enableExpectations:
|
106
|
+
enableExpectations: ->
|
268
107
|
@expectations = []
|
269
108
|
|
270
|
-
handleExpectations: (
|
109
|
+
handleExpectations: (loader) ->
|
271
110
|
for expectation in @expectations
|
272
|
-
if expectation.
|
273
|
-
expectation.recordRequest(
|
111
|
+
if expectation.loaderOptionsMatch(loader)
|
112
|
+
expectation.recordRequest(loader)
|
274
113
|
return
|
275
|
-
throw "No expectation matched #{name} with #{JSON.stringify
|
114
|
+
throw "No expectation matched #{name} with #{JSON.stringify loader.originalOptions}"
|
115
|
+
|
116
|
+
# Helpers
|
117
|
+
loadObject: (name, loadOptions, options = {}) ->
|
118
|
+
options = $.extend({}, { isCollection: true }, options)
|
119
|
+
|
120
|
+
successCallback = loadOptions.success
|
121
|
+
loadOptions = _.omit(loadOptions, 'success')
|
122
|
+
loadOptions = $.extend({}, loadOptions, name: name)
|
123
|
+
|
124
|
+
if options.isCollection
|
125
|
+
loaderClass = Brainstem.CollectionLoader
|
126
|
+
else
|
127
|
+
loaderClass = Brainstem.ModelLoader
|
128
|
+
|
129
|
+
@_checkPageSettings loadOptions
|
130
|
+
|
131
|
+
loader = new loaderClass(storageManager: this)
|
132
|
+
loader.setup(loadOptions)
|
133
|
+
loader.done(successCallback) if successCallback? && _.isFunction(successCallback)
|
134
|
+
|
135
|
+
if @expectations?
|
136
|
+
@handleExpectations(loader)
|
137
|
+
else
|
138
|
+
loader.load()
|
139
|
+
|
140
|
+
loader
|
141
|
+
|
142
|
+
_checkPageSettings: (options) ->
|
143
|
+
if options.limit? && options.limit != '' && options.offset? && options.offset != ''
|
144
|
+
options.perPage = options.page = undefined
|
145
|
+
else
|
146
|
+
options.limit = options.offset = undefined
|
147
|
+
|
148
|
+
@_setDefaultPageSettings(options)
|
149
|
+
|
150
|
+
_setDefaultPageSettings: (options) ->
|
151
|
+
if options.limit? && options.offset?
|
152
|
+
options.limit = 1 if options.limit < 1
|
153
|
+
options.offset = 0 if options.offset < 0
|
154
|
+
else
|
155
|
+
options.perPage = options.perPage || 20
|
156
|
+
options.perPage = 1 if options.perPage < 1
|
157
|
+
options.page = options.page || 1
|
158
|
+
options.page = 1 if options.page < 1
|
@@ -4,10 +4,10 @@ class window.Brainstem.Utils
|
|
4
4
|
@warn: (args...) ->
|
5
5
|
console?.log "Error:", args...
|
6
6
|
|
7
|
-
@throwError: (message)
|
7
|
+
@throwError: (message) ->
|
8
8
|
throw new Error("#{Backbone.history.getFragment()}: #{message}")
|
9
9
|
|
10
|
-
@matches: (obj1, obj2)
|
10
|
+
@matches: (obj1, obj2) ->
|
11
11
|
if @empty(obj1) && @empty(obj2)
|
12
12
|
true
|
13
13
|
else if obj1 instanceof Array && obj2 instanceof Array
|
@@ -19,7 +19,7 @@ class window.Brainstem.Utils
|
|
19
19
|
else
|
20
20
|
String(obj1) == String(obj2)
|
21
21
|
|
22
|
-
@empty: (thing)
|
22
|
+
@empty: (thing) ->
|
23
23
|
if thing == null || thing == undefined || thing == ""
|
24
24
|
true
|
25
25
|
if thing instanceof Array
|
@@ -29,7 +29,21 @@ class window.Brainstem.Utils
|
|
29
29
|
else
|
30
30
|
false
|
31
31
|
|
32
|
-
@extractArray: (option, options)
|
32
|
+
@extractArray: (option, options) ->
|
33
33
|
result = options[option]
|
34
34
|
result = [result] unless result instanceof Array
|
35
35
|
_.compact(result)
|
36
|
+
|
37
|
+
@wrapObjects: (array) ->
|
38
|
+
output = []
|
39
|
+
_(array).each (elem) =>
|
40
|
+
if elem.constructor == Object
|
41
|
+
for key, value of elem
|
42
|
+
o = {}
|
43
|
+
o[key] = @wrapObjects(if value instanceof Array then value else [value])
|
44
|
+
output.push o
|
45
|
+
else
|
46
|
+
o = {}
|
47
|
+
o[elem] = []
|
48
|
+
output.push o
|
49
|
+
output
|