rails-backbone-generator 0.0.2 → 0.0.3
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 +19 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +213 -0
- data/Rakefile +2 -0
- data/lib/generators/backbone/collection_generator.rb +70 -0
- data/lib/generators/backbone/model_generator.rb +56 -0
- data/lib/generators/backbone/namespace_generator.rb +62 -0
- data/lib/generators/backbone/setup_generator/templates/Guardfile +44 -0
- data/lib/generators/backbone/setup_generator/templates/app/assets/javascripts/application.js +14 -0
- data/lib/generators/backbone/setup_generator/templates/app/assets/javascripts/shared/core_extentions/collections_extentions.coffee +30 -0
- data/lib/generators/backbone/setup_generator/templates/app/assets/javascripts/shared/helpers/.gitkeep +0 -0
- data/lib/generators/backbone/setup_generator/templates/app/assets/javascripts/shared/utils/.gitkeep +0 -0
- data/lib/generators/backbone/setup_generator/templates/spec/javascripts/fixtures/.gitkeep +0 -0
- data/lib/generators/backbone/setup_generator/templates/spec/javascripts/helpers/association_helpers.coffee +3 -0
- data/lib/generators/backbone/setup_generator/templates/spec/javascripts/helpers/common_helpers.coffee +13 -0
- data/lib/generators/backbone/setup_generator/templates/spec/javascripts/helpers/fake_host.coffee +14 -0
- data/lib/generators/backbone/setup_generator/templates/spec/javascripts/helpers/headless_webkit_helper.coffee +9 -0
- data/lib/generators/backbone/setup_generator/templates/spec/javascripts/support/jasmine.yml +81 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/backbone/backbone-0.9.2.js +1431 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/backbone/backbone-bind-to-1.0.0.coffee +47 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/backbone/backbone-handlebars-1.0.0.js +121 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/backbone/backbone-query-0.2.3.coffee +314 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/backbone/backbone-relational-0.6.0.js +1687 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/backbone/handlebars-1.0.rc.1.js +1920 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/backbone/underscore-1.3.3.js +1059 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/browser_compatibility/json2.js +487 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/browser_compatibility/localstorage-polyfill.js +92 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/jquery/jquery-1.8.0.js +9227 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/jquery/jquery-ui.min.js +17 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/jquery/jquery.easing-1.3.js +205 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/jquery/jquery_ujs.js +368 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/testing/backbone-factory.js +53 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/testing/jasmine-jquery-1.3.1.js +288 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/testing/jasmine-sinon.js +43 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/testing/mock-ajax.js +207 -0
- data/lib/generators/backbone/setup_generator/templates/vendor/assets/javascripts/testing/sinon-1.4.2.js +4081 -0
- data/lib/generators/backbone/setup_generator.rb +90 -0
- data/lib/generators/backbone/templates/app/assets/javascripts/%namespace%/collections/%collection_name%.coffee.tt +4 -0
- data/lib/generators/backbone/templates/app/assets/javascripts/%namespace%/collections/.gitkeep +0 -0
- data/lib/generators/backbone/templates/app/assets/javascripts/%namespace%/models/%model_name%.coffee.tt +15 -0
- data/lib/generators/backbone/templates/app/assets/javascripts/%namespace%/models/.gitkeep +0 -0
- data/lib/generators/backbone/templates/app/assets/javascripts/%namespace%/routes/.gitkeep +0 -0
- data/lib/generators/backbone/templates/app/assets/javascripts/%namespace%/templates/.gitkeep +0 -0
- data/lib/generators/backbone/templates/app/assets/javascripts/%namespace%/views/.gitkeep +0 -0
- data/lib/generators/backbone/templates/app/assets/javascripts/%namespace%.coffee.tt +15 -0
- data/lib/generators/backbone/templates/spec/javascripts/%namespace%/collections/%collection_name%_spec.coffee.tt +8 -0
- data/lib/generators/backbone/templates/spec/javascripts/%namespace%/collections/.gitkeep +0 -0
- data/lib/generators/backbone/templates/spec/javascripts/%namespace%/factories/%model_name%_factory.coffee.tt +43 -0
- data/lib/generators/backbone/templates/spec/javascripts/%namespace%/factories/.gitkeep +0 -0
- data/lib/generators/backbone/templates/spec/javascripts/%namespace%/models/%model_name%_spec.coffee.tt +12 -0
- data/lib/generators/backbone/templates/spec/javascripts/%namespace%/models/.gitkeep +0 -0
- data/lib/generators/backbone/templates/spec/javascripts/%namespace%/views/.gitkeep +0 -0
- data/lib/generators/backbone_generator.rb +3 -0
- data/lib/rails_backbone_generator/version.rb +3 -0
- data/lib/rails_backbone_generator.rb +4 -0
- data/rails_backbone_generator.gemspec +20 -0
- metadata +61 -4
@@ -0,0 +1,47 @@
|
|
1
|
+
###
|
2
|
+
Backbone BindTo 1.0.0
|
3
|
+
|
4
|
+
Author: Radoslav Stankov
|
5
|
+
Project site: https://github.com/RStankov/backbone-bind-to
|
6
|
+
Licensed under the MIT License.
|
7
|
+
###
|
8
|
+
|
9
|
+
root = @
|
10
|
+
BackboneView = root.Backbone.View
|
11
|
+
|
12
|
+
class BindToView extends BackboneView
|
13
|
+
constructor: ->
|
14
|
+
super
|
15
|
+
|
16
|
+
@bindTo @model, eventName, methodName for eventName, methodName of @bindToModel if @model
|
17
|
+
@bindTo @collection, eventName, methodName for eventName, methodName of @bindToCollection if @collection
|
18
|
+
|
19
|
+
bindTo: (object, eventName, methodName) ->
|
20
|
+
unless object in [@model, @collection]
|
21
|
+
@_binded = []
|
22
|
+
@_binded.push object unless _.include @_binded, object
|
23
|
+
|
24
|
+
throw new Error "Method #{methodName} does not exists" unless @[methodName]
|
25
|
+
throw new Error "#{methodName} is not a function" unless typeof @[methodName] is 'function'
|
26
|
+
|
27
|
+
object.on eventName, @[methodName], @
|
28
|
+
|
29
|
+
remove: ->
|
30
|
+
@model.off null, null, @ if @model
|
31
|
+
@collection.off null, null, @ if @collection
|
32
|
+
|
33
|
+
_.invoke @_binded, 'off', null, null, @
|
34
|
+
delete @_binded
|
35
|
+
|
36
|
+
super
|
37
|
+
|
38
|
+
Backbone.BindTo =
|
39
|
+
VERSION: '1.0.0'
|
40
|
+
|
41
|
+
noConflict: ->
|
42
|
+
root.Backbone.View = BackboneView
|
43
|
+
BindToView
|
44
|
+
|
45
|
+
View: BindToView
|
46
|
+
|
47
|
+
root.Backbone.View = Backbone.BindTo.View
|
@@ -0,0 +1,121 @@
|
|
1
|
+
/*
|
2
|
+
Backbone Handlebars
|
3
|
+
|
4
|
+
Author: Radoslav Stankov
|
5
|
+
Project site: https://github.com/RStankov/backbone-handlebars
|
6
|
+
Licensed under the MIT License.
|
7
|
+
*/
|
8
|
+
|
9
|
+
|
10
|
+
(function() {
|
11
|
+
var BH, _compile, _remove;
|
12
|
+
|
13
|
+
BH = {
|
14
|
+
VERSION: '1.0.0',
|
15
|
+
postponed: {},
|
16
|
+
rendered: {},
|
17
|
+
postponeRender: function(name, options, parentView) {
|
18
|
+
var cid, view, viewClass, _base, _ref;
|
19
|
+
viewClass = _.inject((name || '').split('.'), (function(memo, fragment) {
|
20
|
+
return memo[fragment] || false;
|
21
|
+
}), window);
|
22
|
+
if (!viewClass) {
|
23
|
+
throw "Invalid view name - " + name;
|
24
|
+
}
|
25
|
+
view = new viewClass(options.hash);
|
26
|
+
if (options.fn !== null) {
|
27
|
+
view.template = options.fn;
|
28
|
+
}
|
29
|
+
cid = (parentView || options.data.view).cid;
|
30
|
+
if ((_ref = (_base = this.postponed)[cid]) === null) {
|
31
|
+
_base[cid] = [];
|
32
|
+
}
|
33
|
+
this.postponed[cid].push(view);
|
34
|
+
return '<div id="_' + view.cid + '"></div>';
|
35
|
+
},
|
36
|
+
renderPostponed: function(parentView) {
|
37
|
+
var cid;
|
38
|
+
cid = parentView.cid;
|
39
|
+
this.rendered[cid] = _.map(this.postponed[parentView.cid], function(view) {
|
40
|
+
view.render();
|
41
|
+
parentView.$("#_" + view.cid).replaceWith(view.el);
|
42
|
+
return view;
|
43
|
+
});
|
44
|
+
return delete this.postponed[cid];
|
45
|
+
},
|
46
|
+
clearRendered: function(parentView) {
|
47
|
+
var cid;
|
48
|
+
cid = parentView.cid;
|
49
|
+
if (this.rendered[cid]) {
|
50
|
+
_.invoke(this.rendered[cid], 'remove');
|
51
|
+
return delete this.rendered[cid];
|
52
|
+
}
|
53
|
+
}
|
54
|
+
};
|
55
|
+
|
56
|
+
Handlebars.registerHelper('view', function(name, options) {
|
57
|
+
return new Handlebars.SafeString(BH.postponeRender(name, options, this._parentView));
|
58
|
+
});
|
59
|
+
|
60
|
+
Handlebars.registerHelper('views', function(name, models, options) {
|
61
|
+
var callback, markers,
|
62
|
+
_this = this;
|
63
|
+
callback = function(model) {
|
64
|
+
options.hash.model = model;
|
65
|
+
return BH.postponeRender(name, options, _this._parentView);
|
66
|
+
};
|
67
|
+
markers = 'map' in models ? models.map(callback) : _.map(callback);
|
68
|
+
return new Handlebars.SafeString(markers.join(''));
|
69
|
+
});
|
70
|
+
|
71
|
+
_compile = Handlebars.compile;
|
72
|
+
|
73
|
+
Handlebars.compile = function(template, options) {
|
74
|
+
if (options === null) {
|
75
|
+
options = {};
|
76
|
+
}
|
77
|
+
options.data = true;
|
78
|
+
return _compile.call(this, template, options);
|
79
|
+
};
|
80
|
+
|
81
|
+
Backbone.View.prototype.renderTemplate = function(context) {
|
82
|
+
if (context === null) {
|
83
|
+
context = {};
|
84
|
+
}
|
85
|
+
BH.clearRendered(this);
|
86
|
+
context = _.clone(context);
|
87
|
+
context._parentView = this;
|
88
|
+
this.$el.html(this.template(context, {
|
89
|
+
data: {
|
90
|
+
view: this
|
91
|
+
}
|
92
|
+
}));
|
93
|
+
BH.renderPostponed(this);
|
94
|
+
return this;
|
95
|
+
};
|
96
|
+
|
97
|
+
Backbone.View.prototype.renderedSubViews = function() {
|
98
|
+
return BH.rendered[this.cid];
|
99
|
+
};
|
100
|
+
|
101
|
+
_remove = Backbone.View.prototype.remove;
|
102
|
+
|
103
|
+
Backbone.View.prototype.remove = function() {
|
104
|
+
BH.clearRendered(this);
|
105
|
+
return _remove.apply(this, arguments);
|
106
|
+
};
|
107
|
+
|
108
|
+
Backbone.View.prototype.render = function() {
|
109
|
+
if (this.template) {
|
110
|
+
this.renderTemplate(typeof this.templateData === 'function' ? this.templateData() : this.templateData);
|
111
|
+
}
|
112
|
+
return this;
|
113
|
+
};
|
114
|
+
|
115
|
+
Backbone.View.prototype.templateData = function() {
|
116
|
+
return {};
|
117
|
+
};
|
118
|
+
|
119
|
+
Backbone.Handlebars = BH;
|
120
|
+
|
121
|
+
}).call(this);
|
@@ -0,0 +1,314 @@
|
|
1
|
+
###
|
2
|
+
Backbone Query - A lightweight query API for Backbone Collections
|
3
|
+
(c)2012 - Dave Tonge
|
4
|
+
May be freely distributed according to MIT license.
|
5
|
+
###
|
6
|
+
((define) -> define 'backbone-query', (require, exports) ->
|
7
|
+
_ = require('underscore')
|
8
|
+
Backbone = require('backbone')
|
9
|
+
|
10
|
+
### UTILS ###
|
11
|
+
|
12
|
+
# Custom Filter / Reject methods faster than underscore methods as use for loops
|
13
|
+
# http://jsperf.com/filter-vs-for-loop2
|
14
|
+
filter = (array, test) -> (val for val in array when test val)
|
15
|
+
reject = (array, test) -> (val for val in array when not test val)
|
16
|
+
detect = (array, test) ->
|
17
|
+
for val in array
|
18
|
+
return true if test val
|
19
|
+
false
|
20
|
+
|
21
|
+
# Utility Function to turn a list of values into an object
|
22
|
+
makeObj = (args...)->
|
23
|
+
o = {}
|
24
|
+
current = o
|
25
|
+
while args.length
|
26
|
+
key = args.shift()
|
27
|
+
val = (if args.length is 1 then args.shift() else {})
|
28
|
+
current = current[key] = val
|
29
|
+
o
|
30
|
+
|
31
|
+
# Get the type as a string
|
32
|
+
getType = (item) ->
|
33
|
+
return "$regex" if _.isRegExp(item)
|
34
|
+
return "$date" if _.isDate(item)
|
35
|
+
return "object" if _.isObject(item) and not _.isArray(item)
|
36
|
+
return "array" if _.isArray(item)
|
37
|
+
return "string" if _.isString(item)
|
38
|
+
return "number" if _.isNumber(item)
|
39
|
+
return "boolean" if _.isBoolean(item)
|
40
|
+
return "function" if _.isFunction(item)
|
41
|
+
false
|
42
|
+
|
43
|
+
###
|
44
|
+
Function to parse raw queries
|
45
|
+
@param {mixed} raw query
|
46
|
+
@return {array} parsed query
|
47
|
+
|
48
|
+
Allows queries of the following forms:
|
49
|
+
query
|
50
|
+
name: "test"
|
51
|
+
id: $gte: 10
|
52
|
+
|
53
|
+
query [
|
54
|
+
{name:"test"}
|
55
|
+
{id:$gte:10}
|
56
|
+
]
|
57
|
+
###
|
58
|
+
parseSubQuery = (rawQuery) ->
|
59
|
+
|
60
|
+
if _.isArray(rawQuery)
|
61
|
+
queryArray = rawQuery
|
62
|
+
else
|
63
|
+
queryArray = (makeObj(key, val) for own key, val of rawQuery)
|
64
|
+
|
65
|
+
(for query in queryArray
|
66
|
+
for own key, queryParam of query
|
67
|
+
o = {key}
|
68
|
+
paramType = getType(queryParam)
|
69
|
+
switch paramType
|
70
|
+
# Test for Regexs and Dates as they can be supplied without an operator
|
71
|
+
when "$regex", "$date"
|
72
|
+
o.type = paramType
|
73
|
+
o.value = queryParam
|
74
|
+
|
75
|
+
# If the query paramater is an object then extract the key and value
|
76
|
+
when "object"
|
77
|
+
if key in ["$and", "$or", "$nor", "$not"]
|
78
|
+
o.value = parseSubQuery queryParam
|
79
|
+
o.type = key
|
80
|
+
o.key = null
|
81
|
+
else
|
82
|
+
for type, value of queryParam
|
83
|
+
# Before adding the query, its value is checked to make sure it is the right type
|
84
|
+
if testQueryValue type, value
|
85
|
+
o.type = type
|
86
|
+
switch type
|
87
|
+
when "$elemMatch", "$relationMatch"
|
88
|
+
o.value = parseQuery value
|
89
|
+
when "$computed"
|
90
|
+
q = makeObj(key,value)
|
91
|
+
o.value = parseSubQuery q
|
92
|
+
else
|
93
|
+
o.value = value
|
94
|
+
|
95
|
+
# If the query_param is not an object or a regexp then revert to the default operator: $equal
|
96
|
+
else
|
97
|
+
o.type = "$equal"
|
98
|
+
o.value = queryParam
|
99
|
+
|
100
|
+
# For "$equal" queries with arrays or objects we need to perform a deep equal
|
101
|
+
if (o.type is "$equal") and (paramType in ["object","array"])
|
102
|
+
o.type = "$oEqual"
|
103
|
+
o)
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
# Tests query value, to ensure that it is of the correct type
|
108
|
+
testQueryValue = (type, value) ->
|
109
|
+
switch type
|
110
|
+
when "$in","$nin","$all", "$any" then _(value).isArray()
|
111
|
+
when "$size" then _(value).isNumber()
|
112
|
+
when "$regex" then _(value).isRegExp()
|
113
|
+
when "$like", "$likeI" then _(value).isString()
|
114
|
+
when "$between" then _(value).isArray() and (value.length is 2)
|
115
|
+
when "$cb" then _(value).isFunction()
|
116
|
+
else true
|
117
|
+
|
118
|
+
# Test each attribute that is being tested to ensure that is of the correct type
|
119
|
+
testModelAttribute = (type, value) ->
|
120
|
+
switch type
|
121
|
+
when "$like", "$likeI", "$regex" then _(value).isString()
|
122
|
+
when "$contains", "$all", "$any", "$elemMatch" then _(value).isArray()
|
123
|
+
when "$size" then _(value).isArray() or _(value).isString()
|
124
|
+
when "$in", "$nin" then value?
|
125
|
+
when "$relationMatch" then value? and value.models
|
126
|
+
else true
|
127
|
+
|
128
|
+
# Perform the actual query logic for each query and each model/attribute
|
129
|
+
performQuery = (type, value, attr, model, key) ->
|
130
|
+
switch type
|
131
|
+
when "$equal"
|
132
|
+
# If the attribute is an array then search for the query value in the array the same as Mongo
|
133
|
+
if _(attr).isArray() then value in attr else attr is value
|
134
|
+
when "$oEqual" then _(attr).isEqual value
|
135
|
+
when "$contains" then value in attr
|
136
|
+
when "$ne" then attr isnt value
|
137
|
+
when "$lt" then attr < value
|
138
|
+
when "$gt" then attr > value
|
139
|
+
when "$lte" then attr <= value
|
140
|
+
when "$gte" then attr >= value
|
141
|
+
when "$between" then value[0] < attr < value[1]
|
142
|
+
when "$in" then attr in value
|
143
|
+
when "$nin" then attr not in value
|
144
|
+
when "$all" then _(value).all (item) -> item in attr
|
145
|
+
when "$any" then _(attr).any (item) -> item in value
|
146
|
+
when "$size" then attr.length is value
|
147
|
+
when "$exists", "$has" then attr? is value
|
148
|
+
when "$like" then attr.indexOf(value) isnt -1
|
149
|
+
when "$likeI" then attr.toLowerCase().indexOf(value.toLowerCase()) isnt -1
|
150
|
+
when "$regex" then value.test attr
|
151
|
+
when "$cb" then value.call model, attr
|
152
|
+
when "$elemMatch" then (runQuery(attr,value,"elemMatch")).length > 0
|
153
|
+
when "$relationMatch" then (runQuery(attr.models,value,"relationMatch")).length > 0
|
154
|
+
when "$computed" then iterator [model], value, false, detect, "computed"
|
155
|
+
when "$and", "$or", "$nor", "$not"
|
156
|
+
(processQuery[type]([model], value)).length is 1
|
157
|
+
else false
|
158
|
+
|
159
|
+
|
160
|
+
# The main iterator that actually applies the query
|
161
|
+
iterator = (models, query, andOr, filterFunction, itemType = false) ->
|
162
|
+
# The collections filter or reject method is used to iterate through each model in the collection
|
163
|
+
filterFunction models, (model) ->
|
164
|
+
# For each model in the collection, iterate through the supplied queries
|
165
|
+
for q in query
|
166
|
+
# Retrieve the attribute value from the model
|
167
|
+
attr = switch itemType
|
168
|
+
when "elemMatch" then model[q.key]
|
169
|
+
when "computed" then model[q.key]()
|
170
|
+
else model.get(q.key)
|
171
|
+
# Check if the attribute value is the right type (some operators need a string, or an array)
|
172
|
+
test = testModelAttribute(q.type, attr)
|
173
|
+
# If the attribute test is true, perform the query
|
174
|
+
if test then test = performQuery q.type, q.value, attr, model, q.key
|
175
|
+
# If the query is an "or" query than as soon as a match is found we return "true"
|
176
|
+
# Whereas if the query is an "and" query then we return "false" as soon as a match isn't found.
|
177
|
+
return andOr if andOr is test
|
178
|
+
|
179
|
+
# For an "or" query, if all the queries are false, then we return false
|
180
|
+
# For an "and" query, if all the queries are true, then we return true
|
181
|
+
not andOr
|
182
|
+
|
183
|
+
|
184
|
+
|
185
|
+
# An object with or, and, nor and not methods
|
186
|
+
processQuery =
|
187
|
+
$and: (models, query, itemType) -> iterator models, query, false, filter, itemType
|
188
|
+
$or: (models, query, itemType) -> iterator models, query, true, filter, itemType
|
189
|
+
$nor: (models, query, itemType) -> iterator models, query, true, reject, itemType
|
190
|
+
$not: (models, query, itemType) -> iterator models, query, false, reject, itemType
|
191
|
+
|
192
|
+
parseQuery = (query) ->
|
193
|
+
queryKeys = _(query).keys()
|
194
|
+
compoundKeys = ["$and", "$not", "$or", "$nor"]
|
195
|
+
compoundQuery = _.intersection compoundKeys, queryKeys
|
196
|
+
|
197
|
+
# If no compound methods are found then use the "and" iterator
|
198
|
+
if compoundQuery.length is 0
|
199
|
+
return [{type:"$and", parsedQuery:parseSubQuery(query)}]
|
200
|
+
else
|
201
|
+
# Detect if there is an implicit $and compundQuery operator
|
202
|
+
if compoundQuery.length isnt queryKeys.length
|
203
|
+
# Add the and compund query operator (with a sanity check that it doesn't exist)
|
204
|
+
if "$and" not in compoundQuery
|
205
|
+
query.$and = {}
|
206
|
+
compoundQuery.unshift "$and"
|
207
|
+
for own key, val of query when key not in compoundKeys
|
208
|
+
query.$and[key] = val
|
209
|
+
delete query[key]
|
210
|
+
return (for type in compoundQuery
|
211
|
+
{type, parsedQuery:parseSubQuery(query[type])})
|
212
|
+
|
213
|
+
|
214
|
+
runQuery = (items, query, itemType) ->
|
215
|
+
query = parseQuery(query) unless itemType
|
216
|
+
reduceIterator = (memo, queryItem) ->
|
217
|
+
processQuery[queryItem.type] memo, queryItem.parsedQuery, itemType
|
218
|
+
_.reduce query, reduceIterator, items
|
219
|
+
|
220
|
+
|
221
|
+
# This method attempts to retrieve the result from the cache.
|
222
|
+
# If no match is found in the cache, then the query is run and
|
223
|
+
# the results are saved in the cache
|
224
|
+
getCache = (collection, query, options) ->
|
225
|
+
# Convert the query to a string to use as a key in the cache
|
226
|
+
queryString = JSON.stringify query
|
227
|
+
# Create cache if doesn't exist
|
228
|
+
cache = collection._queryCache ?= {}
|
229
|
+
# Retrieve cached results
|
230
|
+
models = cache[queryString]
|
231
|
+
# If no results are retrieved then use the get_models method and cache the result
|
232
|
+
unless models
|
233
|
+
models = getSortedModels collection, query, options
|
234
|
+
cache[queryString] = models
|
235
|
+
# Return the results
|
236
|
+
models
|
237
|
+
|
238
|
+
|
239
|
+
# Gets the results and optionally sorts them
|
240
|
+
getSortedModels = (collection, query, options) ->
|
241
|
+
models = runQuery(collection.models, query)
|
242
|
+
if options.sortBy then models = sortModels models, options
|
243
|
+
models
|
244
|
+
|
245
|
+
# Sorts models either be a model attribute or with a callback
|
246
|
+
sortModels = (models, options) ->
|
247
|
+
# If the sortBy param is a string then we sort according to the model attribute with that string as a key
|
248
|
+
if _(options.sortBy).isString()
|
249
|
+
models = _(models).sortBy (model) -> model.get(options.sortBy)
|
250
|
+
# If a function is supplied then it is passed directly to the sortBy iterator
|
251
|
+
else if _(options.sortBy).isFunction()
|
252
|
+
models = _(models).sortBy(options.sortBy)
|
253
|
+
|
254
|
+
# If there is an order property of "desc" then the results can be reversed
|
255
|
+
# (sortBy provides result in ascending order by default)
|
256
|
+
if options.order is "desc" then models = models.reverse()
|
257
|
+
# The sorted models are returned
|
258
|
+
models
|
259
|
+
|
260
|
+
# Slices the results set according to the supplied options
|
261
|
+
pageModels = (models, options) ->
|
262
|
+
# Expects object in the form: {limit: num, offset: num, page: num, pager:callback}
|
263
|
+
if options.offset then start = options.offset
|
264
|
+
else if options.page then start = (options.page - 1) * options.limit
|
265
|
+
else start = 0
|
266
|
+
|
267
|
+
end = start + options.limit
|
268
|
+
|
269
|
+
# The results are sliced according to the calculated start and end params
|
270
|
+
sliced_models = models[start...end]
|
271
|
+
|
272
|
+
if options.pager and _.isFunction(options.pager)
|
273
|
+
total_pages = Math.ceil (models.length / options.limit)
|
274
|
+
options.pager total_pages, sliced_models
|
275
|
+
|
276
|
+
sliced_models
|
277
|
+
|
278
|
+
|
279
|
+
Backbone.QueryCollection = Backbone.Collection.extend
|
280
|
+
|
281
|
+
# The main query method
|
282
|
+
query: (query, options = {}) ->
|
283
|
+
|
284
|
+
# Retrieve matching models using the supplied query
|
285
|
+
if options.cache
|
286
|
+
models = getCache @, query, options
|
287
|
+
else
|
288
|
+
models = getSortedModels @, query, options
|
289
|
+
|
290
|
+
# If a limit param is specified than slice the results
|
291
|
+
if options.limit then models = pageModels models, options
|
292
|
+
|
293
|
+
# Return the results
|
294
|
+
models
|
295
|
+
|
296
|
+
findOne: (query) -> @query(query)[0]
|
297
|
+
|
298
|
+
# Where method wraps query and returns a new collection
|
299
|
+
whereBy: (params, options = {})->
|
300
|
+
new @constructor @query params, options
|
301
|
+
|
302
|
+
# Helper method to reset the query cache
|
303
|
+
# Defined as a separate method to make it easy to bind to collection's change/add/remove events
|
304
|
+
resetQueryCache: -> @_queryCache = {}
|
305
|
+
|
306
|
+
# On the server the new Query Collection is added to exports
|
307
|
+
exports.QueryCollection = Backbone.QueryCollection
|
308
|
+
).call this, if typeof define == 'function' and define.amd then define else (id, factory) ->
|
309
|
+
unless typeof exports is 'undefined'
|
310
|
+
factory ((id) -> require id), exports
|
311
|
+
else
|
312
|
+
# Load Underscore and backbone. No need to export QueryCollection in an module-less environment
|
313
|
+
factory ((id) -> this[if id == 'underscore' then '_' else 'Backbone']), {}
|
314
|
+
return
|