pinkman 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +41 -0
  8. data/Rakefile +28 -0
  9. data/app/assets/javascripts/pinkman.js +10 -0
  10. data/app/assets/javascripts/pinkman_base/ajax.coffee +90 -0
  11. data/app/assets/javascripts/pinkman_base/collection.coffee +387 -0
  12. data/app/assets/javascripts/pinkman_base/common.coffee +56 -0
  13. data/app/assets/javascripts/pinkman_base/controller.coffee +164 -0
  14. data/app/assets/javascripts/pinkman_base/glue.coffee +15 -0
  15. data/app/assets/javascripts/pinkman_base/handlebars.js +4608 -0
  16. data/app/assets/javascripts/pinkman_base/hogan.js +576 -0
  17. data/app/assets/javascripts/pinkman_base/markup.js +483 -0
  18. data/app/assets/javascripts/pinkman_base/mixins.coffee +37 -0
  19. data/app/assets/javascripts/pinkman_base/object.js.coffee.erb +195 -0
  20. data/app/assets/javascripts/pinkman_base/pinkman.coffee +131 -0
  21. data/app/assets/javascripts/pinkman_base/render.coffee.erb +80 -0
  22. data/app/assets/javascripts/pinkman_base/tools.coffee +4 -0
  23. data/app/helpers/pinkman_helper.rb +48 -0
  24. data/bin/console +14 -0
  25. data/bin/setup +8 -0
  26. data/lib/generators/pinkman/USAGE +16 -0
  27. data/lib/generators/pinkman/api_generator.rb +60 -0
  28. data/lib/generators/pinkman/install_generator.rb +25 -0
  29. data/lib/generators/pinkman/model_generator.rb +41 -0
  30. data/lib/generators/pinkman/resource_generator.rb +15 -0
  31. data/lib/generators/pinkman/serializer_generator.rb +33 -0
  32. data/lib/generators/pinkman/templates/api.rb.erb +87 -0
  33. data/lib/generators/pinkman/templates/api_controller.rb +2 -0
  34. data/lib/generators/pinkman/templates/collection.js.erb +5 -0
  35. data/lib/generators/pinkman/templates/object.js.erb +3 -0
  36. data/lib/generators/pinkman/templates/serializer.rb.erb +15 -0
  37. data/lib/pinkman.rb +57 -0
  38. data/lib/pinkman/serializer.rb +6 -0
  39. data/lib/pinkman/serializer/base.rb +42 -0
  40. data/lib/pinkman/serializer/scope.rb +48 -0
  41. data/lib/pinkman/version.rb +3 -0
  42. data/pinkman.gemspec +46 -0
  43. data/public/javascripts/pinkman.min.js +1 -0
  44. data/public/jquery.pinkman.min.js +0 -0
  45. data/public/pinkman.min.js +29 -0
  46. metadata +242 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c608ca91577bd19a8d7014ddd49c49d58f9ef12a
4
+ data.tar.gz: 14851ec0f71e7a62df16622c525ad42efdb32114
5
+ SHA512:
6
+ metadata.gz: 2f0636432c7af59425a2f87d803798b837eb745de32200f71cd05d58bef06aff4bb8369c2c6955de367db62b75e3b8da5927c6810650566d1444543f5e5d2fe8
7
+ data.tar.gz: c957e9f96a1c2d4c1fd41ebb3463059075cf182d2011444643be516931ec9f339222184eaff8a99164662389c9d13b271d4b4b1b86ef72a53d41a903e27edda4
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /dummy/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.13.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pinkman.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Agilso Oliveira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,41 @@
1
+ # Pinkman
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/pinkman`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'pinkman'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install pinkman
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/pinkman.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
@@ -0,0 +1,28 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require 'pinkman'
4
+ require Pinkman.root.join('spec/support/sprockets')
5
+
6
+ task :default => :spec
7
+ require 'jasmine'
8
+ load 'jasmine/tasks/jasmine.rake'
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ namespace :pinkman do
12
+ desc 'Precompile pinkman.js'
13
+ task :precompile do
14
+ f = File.open(Pinkman.root.join('public','pinkman.min.js'),'w+') {|f| f.write Assets.app_environment['pinkman'].to_s }
15
+ end
16
+ end
17
+
18
+ namespace :spec do
19
+
20
+ desc 'Compile spec.js and coffeescripts specs to js'
21
+ task precompile: ['pinkman:precompile'] do
22
+ f = File.open(Pinkman.root.join('spec','javascripts','spec.js'),'w+') {|f| f.write Assets.spec_environment['support/spec_helper'].to_s }
23
+ end
24
+
25
+ desc 'Run JS (Jasmine) Specs'
26
+ task :js => ['spec:precompile', 'jasmine']
27
+
28
+ end
@@ -0,0 +1,10 @@
1
+ //= require pinkman_base/handlebars
2
+ //= require pinkman_base/tools
3
+ //= require pinkman_base/pinkman
4
+ //= require pinkman_base/mixins
5
+ //= require pinkman_base/common
6
+ //= require pinkman_base/object
7
+ //= require pinkman_base/collection
8
+ //= require pinkman_base/render
9
+ //= require pinkman_base/controller
10
+ //= require pinkman_base/ajax
@@ -0,0 +1,90 @@
1
+ # @ajax:
2
+ # get: (options) ->
3
+ # if options.url?
4
+ # ajax = jQuery.ajax options.url,
5
+ # type: "GET"
6
+ # dataType: 'json'
7
+ # ajax.done (response) =>
8
+ # if response.errors?
9
+ # options.error(response) if options.error? and typeof options.error == 'function'
10
+ # return false
11
+ # else
12
+ # options.success(response) if options.success? and typeof options.success == 'function'
13
+ # options.complete(response) if options.complete? and typeof options.complete == 'function'
14
+ # return this
15
+ # else
16
+ # return false
17
+
18
+ # post: (options) ->
19
+ # if options.url?
20
+ # ajax = jQuery.ajax options.url,
21
+ # beforeSend: (xhr) ->
22
+ # xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
23
+ # type: "POST"
24
+ # dataType: 'json'
25
+ # data: options.data
26
+ # ajax.done (response) =>
27
+ # if response.errors?
28
+ # options.error(this) if options.error? and typeof options.error == 'function'
29
+ # return false
30
+ # else
31
+ # options.success(response) if options.success? and typeof options.success == 'function'
32
+ # options.complete(response) if options.complete? and typeof options.complete == 'function'
33
+ # return this
34
+ # else
35
+ # return false
36
+
37
+ # put: (options) ->
38
+ # if options.url?
39
+ # ajax = jQuery.ajax options.url,
40
+ # beforeSend: (xhr) ->
41
+ # xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
42
+ # type: "PUT"
43
+ # dataType: 'json'
44
+ # data: options.data
45
+ # ajax.done (response) =>
46
+ # if response.errors?
47
+ # options.error(this) if options.error? and typeof options.error == 'function'
48
+ # return false
49
+ # else
50
+ # options.success(response) if options.success? and typeof options.success == 'function'
51
+ # options.complete(response) if options.complete? and typeof options.complete == 'function'
52
+ # return this
53
+ # else
54
+ # return false
55
+
56
+
57
+ # file: (options) ->
58
+ # if options.url?
59
+ # ajax = jQuery.ajax options.url,
60
+ # beforeSend: (xhr) ->
61
+ # xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
62
+ # xhr: ->
63
+ # myXhr = $.ajaxSettings.xhr()
64
+ # myXhr.upload.addEventListener 'progress', (e) ->
65
+ # if e.lengthComputable
66
+ # options.progress e.loaded/e.total if options.progress?
67
+ # , false
68
+ # myXhr.addEventListener 'progress', (e) ->
69
+ # if e.lengthComputable
70
+ # options.progress e.loaded/e.total if options.progress?
71
+ # , false
72
+ # return myXhr
73
+ # type: "POST"
74
+ # dataType: 'json'
75
+ # data: options.data
76
+ # processData: false
77
+ # contentType: false
78
+ # ajax.done (response) =>
79
+ # if response? and response.errors?
80
+ # options.error(this) if options.error? and typeof options.error == 'function'
81
+ # return false
82
+ # else
83
+ # options.success(response) if options.success? and typeof options.success == 'function'
84
+ # options.complete(response) if options.complete? and typeof options.complete == 'function'
85
+ # return this
86
+ # else
87
+ # return false
88
+
89
+ # upload: (options...) ->
90
+ # @file(options...)
@@ -0,0 +1,387 @@
1
+ class window.PinkmanCollection extends window.PinkmanCommon
2
+
3
+ @pinkmanType = 'collection'
4
+
5
+ config:
6
+ memberClass: PinkmanObject
7
+
8
+ constructor: () ->
9
+
10
+ @isPink = true
11
+ @isCollection = true
12
+ @pinkmanType = 'collection'
13
+
14
+ @collection = []
15
+ @pinkey = Pinkman.all.length
16
+
17
+ Pinkman.collections.push(this)
18
+ Pinkman.all.push(this)
19
+
20
+ # Desc: return an array of all members
21
+ # this behaviour makes life easier... trust me
22
+ attributes: ->
23
+ return @collection
24
+
25
+ # Desc: collection size
26
+ count: (criteria='') ->
27
+ if criteria? and typeof criteria == 'function'
28
+ count = 0
29
+ (count++ if criteria(object)) for object in @collection
30
+ count
31
+ else
32
+ @collection.length
33
+
34
+ # Desc: Alias for count
35
+ size: (criteria='') ->
36
+ @count(criteria)
37
+
38
+ # Desc: Alias for count
39
+ length: (criteria='') ->
40
+ @count(criteria)
41
+
42
+ # rails/ruby equivalent: each
43
+ # Desc: receive a function and apply it to all members
44
+ # tip: you can chain functions. Example: collection.each(transform).first()
45
+ each: (transformation='') ->
46
+ if transformation? and typeof transformation=='function'
47
+ transformation(o) for o in @collection
48
+ return this
49
+
50
+ # rails/ruby equivalent: where/select
51
+ # Desc: returns a new collection of all members that satisfies a criteria (criteria(obj) returns true)
52
+ # new version: accepts a object to match against the object attributes
53
+ select: (criteria,callback='') ->
54
+ selection = new @constructor
55
+ # function version
56
+ if typeof criteria == 'function'
57
+ @each (object) ->
58
+ selection.push(object) if criteria(object)
59
+
60
+ # object version
61
+ else if typeof criteria == 'object'
62
+ @each (object) ->
63
+ value = true
64
+ (value = false if object[k] != v) for k,v of criteria
65
+ selection.push(object) if value
66
+
67
+ callback(selection) if typeof callback == 'function'
68
+ return(selection)
69
+
70
+ # Desc: insert in last position
71
+ push: (arg) ->
72
+ if Pinkman.isArray(arg)
73
+ @pushIndividually(item) for item in arg
74
+ return(@include(arg))
75
+ else
76
+ @pushIndividually(arg)
77
+
78
+ # Desc: insert in first position
79
+ unshift: (object) ->
80
+ if Pinkman.isArray(object)
81
+ @unshiftIndividually(item) for item in object
82
+ return(@include(object))
83
+ else
84
+ @unshiftIndividually(object)
85
+
86
+ pushIndividually: (object) ->
87
+ if object? and typeof object == 'object' and not @include(object)
88
+ @beforeInsertionPrep object, (object) =>
89
+ @collection.push(object)
90
+ object.collections.push(this) if object.isObject and object.collections?
91
+ return true
92
+ else
93
+ return false
94
+
95
+ directPush: (object) ->
96
+ @collection.push(object) unless @include(object)
97
+
98
+ forcePush: (object) ->
99
+ @collection.push(object)
100
+
101
+ unshiftIndividually: (object) ->
102
+ if object? and typeof object == 'object' and not @include(object)
103
+ @beforeInsertionPrep object, (object) =>
104
+ @collection.unshift(object)
105
+ object.collections.push(this) if object.isObject and object.collections?
106
+ return true
107
+ else
108
+ return false
109
+
110
+ beforeInsertionPrep: (object,callback='') ->
111
+ unless object.isPink
112
+ pinkObject = @new()
113
+ pinkObject.assign(object)
114
+ object = pinkObject
115
+ callback(object) if typeof callback == 'function'
116
+ return(object)
117
+
118
+ # Desc: removes from last position and returns it
119
+ pop: ->
120
+ @remove(@last())
121
+
122
+ # Desc: removes from first position and returns it
123
+ shift: ->
124
+ @remove(@first())
125
+
126
+ # Desc: remove a object from the collection
127
+ remove: (object) ->
128
+ if object?
129
+ i = @collection.indexOf object
130
+ @collection.splice(i,1)
131
+ return object
132
+
133
+ # Desc: remove the first object that matches
134
+ removeBy: (attribute,value) ->
135
+ if attribute? and value?
136
+ @remove(@getBy(attribute,value))
137
+
138
+ # Desc: remove everyone from this collection
139
+ removeAll: ->
140
+ if @any()
141
+ @remove(@first())
142
+ @removeAll()
143
+ else
144
+ return(true)
145
+
146
+ # Desc: return true if object is in this collection and false if anything else.
147
+ # Also accepts an array as argument. Return true if
148
+ include: (args) ->
149
+ if args? and Pinkman.isArray(args)
150
+ value = true
151
+ for item in args
152
+ value=false unless @include(item)
153
+ return(value)
154
+ else if args? and typeof args == 'object'
155
+ if args.id?
156
+ @any (o) ->
157
+ args.id == o.id
158
+ else
159
+ @collection.indexOf(args) isnt -1
160
+ else
161
+ false
162
+
163
+ first: (n=1) ->
164
+ if n==1
165
+ return @collection[0]
166
+ else
167
+ return @collection[0..(n-1)]
168
+
169
+ last: (n=1) ->
170
+ if n==1
171
+ return @collection[@collection.length - 1]
172
+ else
173
+ return @collection[(@collection.length - n - 1)..]
174
+
175
+ # Desc: return trues if has at least one member. If a criteria is specificied, returns true if at least one member satisfies it.
176
+ any: (criteria='') ->
177
+ if criteria? and typeof criteria == 'function'
178
+ @select(criteria).count() > 0
179
+ else
180
+ @count() > 0
181
+
182
+ # Desc: return the first object that matches
183
+ getBy: (attribute, value) ->
184
+ if attribute? and value?
185
+ object = new Object
186
+ object[attribute] = value
187
+ return(@getByAttributes(object))
188
+
189
+ # Desc: return the first that matches
190
+ getByAttributes: (object) ->
191
+ if object? and typeof object == 'object' and @any()
192
+ for member in @collection
193
+ match = true
194
+ for key,value of object
195
+ match = false if member[key]!=value
196
+ return(member) if match
197
+ else
198
+ return null
199
+
200
+ # Desc: get by pinkey
201
+ getByPinkey: (pinkey) ->
202
+ @getBy('pinkey',pinkey)
203
+
204
+ # Desc: sinthetize getByPinkey, getBy, and getByAttributes in one method
205
+ get: (args...) ->
206
+ if args.length > 0
207
+ if Pinkman.isNumber(args[0])
208
+ @getByPinkey(args[0])
209
+ else if args.length == 2
210
+ @getBy(args...)
211
+ else if typeof args[0] == 'object'
212
+ @getByAttributes(args[0])
213
+
214
+ # Desc: find by id
215
+ find: (id) ->
216
+ if id?
217
+ object = @getBy('id',id)
218
+ return(object)
219
+
220
+ # Desc: return the next (after) object
221
+ next: (object) ->
222
+ i = @collection.indexOf object
223
+ @collection[i+1]
224
+
225
+ # Desc: return the previous (before) object
226
+ prev: (object) ->
227
+ i = @collection.indexOf object
228
+ @collection[i-1]
229
+
230
+ # Desc: log every member pinkey in the console
231
+ logPinkeys: () ->
232
+ @each (object) ->
233
+ console.log object.pinkey
234
+
235
+ # Desc: removes duplicated records (same id or same pinkey)
236
+ uniq: (callback='') ->
237
+ duplicated = []
238
+ @each (object) =>
239
+ @each (matching) ->
240
+ duplicated.push(matching) if object.pinkey? and (object.pinkey == matching.pinkey) and (object isnt matching)
241
+ duplicated.push(matching) if object.id? and (object.id == matching.id) and (object isnt matching)
242
+ for d in duplicated
243
+ @remove(d)
244
+ return(this)
245
+
246
+ # Desc: fetch from array ... T_T
247
+ fetchFromArray: (array) ->
248
+ for a in array
249
+ object = @beforeInsertionPrep(a)
250
+ # if object already is in this collection, overwrite its values
251
+ if object? and @find(object.id)?
252
+ @find(object.id).assign(object.attributes())
253
+ # else, inserts it
254
+ else
255
+ @push(object)
256
+ return(this)
257
+
258
+ # Desc: merges this collection with another
259
+ merge: (collection) ->
260
+ if Pinkman.isArray(collection)
261
+ @fetchFromArray(collection)
262
+ else if typeof collection == 'object' and collection.isPink and collection.isCollection
263
+ @fetchFromArray(collection.collection)
264
+
265
+ # Desc: returns a new object associated with this collection
266
+ new: (attributes) ->
267
+ object = new (@config.memberClass)
268
+ object.initialize(attributes)
269
+ object
270
+
271
+ # Desc: reload every object in this collection
272
+ reload: (callback='') ->
273
+ if @any()
274
+ @each (object) =>
275
+ if object.id?
276
+ object.reload (object) =>
277
+ if object.pinkey == @last().pinkey and callback == 'function'
278
+ callback(this)
279
+
280
+ # Desc: create a index for objects
281
+ makeIndex: ->
282
+ if @any()
283
+ i = 1
284
+ for object in @collection
285
+ object.set("index",i)
286
+ i++
287
+ return true
288
+ else
289
+ return false
290
+
291
+ # Desc: exactly what it suggests
292
+ shuffle: () ->
293
+ if @any?
294
+ currentIndex = @collection.length
295
+
296
+ # // While there remain elements to shuffle...
297
+ while (0 != currentIndex)
298
+ # // Pick a remaining element...
299
+ randomIndex = Math.floor(Math.random() * currentIndex)
300
+ currentIndex = currentIndex - 1
301
+
302
+ # // And swap it with the current element.
303
+ temporaryValue = @collection[currentIndex]
304
+ @collection[currentIndex] = @collection[randomIndex]
305
+ @collection[randomIndex] = temporaryValue
306
+
307
+ # Desc: filters collection whose attribute matches the query
308
+ filter: (attribute,query,callback) ->
309
+ if attribute? and query? and query!="" and (typeof(attribute) == "string")
310
+ if @any()
311
+ filter = new @constructor
312
+ for obj in @collection
313
+ if obj[attribute] == query
314
+ filter.push obj
315
+ else if obj[attribute]? and (typeof obj[attribute] == "string") and (obj[attribute]!="") and (typeof query == "string") and (query!="") and (obj[field].toLowerCase().indexOf(query.toLowerCase()) > -1)
316
+ filter.push obj
317
+ callback(filter) if callback? and typeof callback == 'function'
318
+ return filter
319
+ else
320
+ return false
321
+
322
+
323
+ # --- Ajax related --- #
324
+
325
+ # Desc: Fetch records from API_URL
326
+ # request: get /api/API_URL/
327
+ # in rails: api::controller#index
328
+ fetch: (callback = '') ->
329
+ @fetchFromUrl url: @api(), callback: callback
330
+
331
+ # Desc: Fetch records from another action of this model api
332
+ # request: get /api/API_URL/:action/#id
333
+ # in rails: api::controller#action
334
+
335
+ # Example: fetching all orders of a user
336
+ # coffee: orders.fetchFrom 'user', user.id
337
+ # api::controler#order: render json: User.find(params[:id]).orders.to_json
338
+ fetchFrom: (action,id,callback = '') ->
339
+ if action? and id?
340
+ @fetchFromUrl(@api() + "#{action}/#{id}", callback)
341
+ else
342
+ return false
343
+
344
+
345
+ # Desc: Fetch records from URL
346
+ # request: get /api/API_URL/
347
+ fetchFromUrl: (options) ->
348
+ if options? and typeof options == 'object' and options.url?
349
+ limit = if options.limit? then options.limit else 1000
350
+ offset = if options.offset? then options.offset else 0
351
+ @fetchingFrom = options.url
352
+ Pinkman.ajax.get
353
+ url: options.url + "?limit=#{limit}&offset=#{offset}"
354
+ complete: (response) =>
355
+ if response.errors? or response.error?
356
+ [@errors, @error] = [response.errors, response.error]
357
+ if response.errors? then (throw new Error('Oh no... I could not fetch your records, bitch. (jk... about the "bitch" part)')) else (throw new Error(response.error))
358
+ return false
359
+ else
360
+ @fetchFromArray(response).emptyResponse = response.length == 0
361
+ options.callback(this) if options.callback? and typeof options.callback == 'function'
362
+ return(this)
363
+
364
+ # Desc: Fetch next records from last fetched URL or main API_URl
365
+ # request: get /api/API_URL/?offset="COLLECTION_SIZE"&limit=n
366
+ # in rails: api::controller#index
367
+ fetchMore: (n=10,callback='') ->
368
+ if @fetchingFrom?
369
+ @fetchFromUrl url: @fetchingFrom, limit: n, offset: @count(), callback: callback
370
+ else
371
+ @fetchFromUrl url: @api(), limit: n, offset: @count(), callback: callback
372
+
373
+ # Desc: Fetch next n records from a action
374
+ # request: get /api/API_URL/:action/?offset="COLLECTION_SIZE"&limit=n
375
+ # in rails: api::controller#action
376
+ fetchMoreFrom: (n,action,id,callback='') ->
377
+ @fetchFromUrl {url: @api() + "#{action}/#{id}", limit: n, offset: @count(), callback: callback}
378
+
379
+ # Desc: Connect to api to search in this model a query
380
+ # request: get /api/API_URL/search?query=YOUR_QUERY
381
+ # in rails: api::controller#search
382
+ # assume models to have a Model.search("YOUR_QUERY") method.
383
+ search: (query,callback='') ->
384
+ @removeAll()
385
+ @fetchFromUrl { url: @api() + "search?query=#{query}", callback: callback }
386
+
387
+ window.Pinkman.collection = window.PinkmanCollection