live_record 0.1.2 → 0.2.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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -2
  3. data/{lib/live_record/.rspec → .rspec} +0 -0
  4. data/.travis.yml +0 -1
  5. data/Gemfile +3 -1
  6. data/README.md +363 -202
  7. data/Rakefile +5 -0
  8. data/app/assets/javascripts/live_record.coffee +4 -0
  9. data/app/assets/javascripts/live_record/helpers.coffee +4 -0
  10. data/app/assets/javascripts/live_record/helpers/load_records.coffee +13 -7
  11. data/app/assets/javascripts/live_record/helpers/spaceship.coffee +13 -0
  12. data/app/assets/javascripts/live_record/model.coffee +4 -0
  13. data/app/assets/javascripts/live_record/model/create.coffee +96 -27
  14. data/app/assets/javascripts/live_record/plugins.coffee +3 -0
  15. data/app/assets/javascripts/live_record/plugins/live_dom.coffee +5 -19
  16. data/app/assets/javascripts/live_record/plugins/live_dom/apply_to_model.coffee +17 -0
  17. data/app/channels/live_record/base_channel.rb +41 -0
  18. data/app/channels/live_record/changes_channel.rb +59 -0
  19. data/app/channels/live_record/publications_channel.rb +134 -0
  20. data/{lib/live_record/config.ru → config.ru} +0 -0
  21. data/lib/live_record.rb +2 -0
  22. data/lib/live_record/action_view_extensions/view_helper.rb +22 -0
  23. data/lib/live_record/configure.rb +19 -0
  24. data/lib/live_record/generators/install_generator.rb +9 -4
  25. data/lib/live_record/generators/templates/create_live_record_updates.rb +1 -1
  26. data/lib/live_record/generators/templates/index.html.erb +1 -0
  27. data/lib/live_record/generators/templates/model.rb.rb +4 -4
  28. data/lib/live_record/model/callbacks.rb +8 -2
  29. data/lib/live_record/version.rb +1 -1
  30. data/live_record.gemspec +2 -2
  31. data/{lib/live_record/spec → spec}/factories/posts.rb +0 -0
  32. data/spec/features/live_record_syncing_spec.rb +184 -0
  33. data/spec/helpers/wait.rb +19 -0
  34. data/{lib/live_record/spec → spec}/internal/app/assets/config/manifest.js +0 -0
  35. data/{lib/live_record/spec → spec}/internal/app/assets/javascripts/application.js +0 -0
  36. data/{lib/live_record/spec → spec}/internal/app/assets/javascripts/cable.js +0 -0
  37. data/spec/internal/app/assets/javascripts/posts.coffee +8 -0
  38. data/{lib/live_record/spec → spec}/internal/app/channels/application_cable/channel.rb +0 -0
  39. data/{lib/live_record/spec → spec}/internal/app/channels/application_cable/connection.rb +4 -4
  40. data/{lib/live_record/spec → spec}/internal/app/controllers/application_controller.rb +0 -0
  41. data/{lib/live_record/spec → spec}/internal/app/controllers/posts_controller.rb +1 -0
  42. data/{lib/live_record/spec → spec}/internal/app/models/application_record.rb +0 -0
  43. data/{lib/live_record/spec → spec}/internal/app/models/live_record_update.rb +0 -0
  44. data/spec/internal/app/models/post.rb +11 -0
  45. data/{lib/live_record/spec → spec}/internal/app/views/layouts/application.html.erb +0 -0
  46. data/{lib/live_record/spec → spec}/internal/app/views/posts/_form.html.erb +0 -0
  47. data/{lib/live_record/spec → spec}/internal/app/views/posts/_post.json.jbuilder +0 -0
  48. data/{lib/live_record/spec → spec}/internal/app/views/posts/edit.html.erb +0 -0
  49. data/{lib/live_record/spec → spec}/internal/app/views/posts/index.html.erb +6 -5
  50. data/{lib/live_record/spec → spec}/internal/app/views/posts/index.json.jbuilder +0 -0
  51. data/{lib/live_record/spec → spec}/internal/app/views/posts/new.html.erb +0 -0
  52. data/{lib/live_record/spec → spec}/internal/app/views/posts/show.html.erb +0 -0
  53. data/{lib/live_record/spec → spec}/internal/app/views/posts/show.json.jbuilder +0 -0
  54. data/{lib/live_record/spec → spec}/internal/config/cable.yml +0 -0
  55. data/{lib/live_record/spec → spec}/internal/config/database.yml +0 -0
  56. data/{lib/live_record/spec → spec}/internal/config/routes.rb +0 -0
  57. data/{lib/live_record/spec → spec}/internal/db/schema.rb +2 -0
  58. data/{lib/live_record/spec → spec}/internal/public/favicon.ico +0 -0
  59. data/{lib/live_record/spec → spec}/rails_helper.rb +4 -2
  60. data/{lib/live_record/spec → spec}/spec_helper.rb +0 -0
  61. metadata +64 -56
  62. data/app/assets/javascripts/live_record.js +0 -4
  63. data/app/assets/javascripts/live_record/helpers.js +0 -4
  64. data/app/assets/javascripts/live_record/model.js +0 -4
  65. data/app/assets/javascripts/live_record/plugins.js +0 -3
  66. data/lib/live_record/channel/implement.rb +0 -100
  67. data/lib/live_record/spec/features/live_record_syncing_spec.rb +0 -60
  68. data/lib/live_record/spec/internal/app/assets/javascripts/posts.coffee +0 -14
  69. data/lib/live_record/spec/internal/app/channels/live_record_channel.rb +0 -4
  70. data/lib/live_record/spec/internal/app/models/post.rb +0 -11
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new('spec')
4
+
5
+ task default: :spec
@@ -0,0 +1,4 @@
1
+ #= require_self
2
+ #= require_directory ./live_record/
3
+
4
+ this.LiveRecord ||= {}
@@ -0,0 +1,4 @@
1
+ #= require_self
2
+ #= require_directory ./helpers/
3
+
4
+ LiveRecord.helpers ||= {}
@@ -8,23 +8,29 @@ LiveRecord.helpers.loadRecords = (args) ->
8
8
  args['url']
9
9
  ).done(
10
10
  (data) ->
11
+ record_or_records = undefined
12
+
11
13
  # Array JSON
12
14
  if $.isArray(data)
13
- records_attributes = data;
15
+ records_attributes = data
14
16
  records = []
15
17
 
16
18
  for record_attributes in records_attributes
17
- record = new LiveRecord.Model.all[args['modelName']](record_attributes);
18
- record.create();
19
- records << record
19
+ record = new LiveRecord.Model.all[args['modelName']](record_attributes)
20
+ record.create()
21
+ records.push(record)
22
+
23
+ record_or_records = records
20
24
 
21
25
  # Single-Record JSON
22
26
  else
23
27
  record_attributes = data
24
- record = new LiveRecord.Model.all[args['modelName']](record_attributes);
25
- record.create();
28
+ record = new LiveRecord.Model.all[args['modelName']](record_attributes)
29
+ record.create()
30
+
31
+ record_or_records = record
26
32
 
27
- args['onLoad'].call(this, records) if args['onLoad']
33
+ args['onLoad'].call(this, record_or_records) if args['onLoad']
28
34
  ).fail(
29
35
  (jqxhr, textStatus, error) ->
30
36
  args['onError'].call(this, jqxhr, textStatus, error) if args['onError']
@@ -0,0 +1,13 @@
1
+ # ref: https://stackoverflow.com/questions/34852855/combined-comparison-spaceship-operator-in-javascript
2
+
3
+ LiveRecord.helpers.spaceship = (val1, val2) ->
4
+ if val1 == null or val2 == null or typeof val1 != typeof val2
5
+ return null
6
+ if typeof val1 == 'string'
7
+ val1.localeCompare val2
8
+ else
9
+ if val1 > val2
10
+ return 1
11
+ else if val1 < val2
12
+ return -1
13
+ 0
@@ -0,0 +1,4 @@
1
+ #= require_self
2
+ #= require_directory ./model/
3
+
4
+ LiveRecord.Model ||= {}
@@ -6,18 +6,6 @@ LiveRecord.Model.create = (config) ->
6
6
  # NEW
7
7
  Model = (attributes) ->
8
8
  this.attributes = attributes
9
- # instance callbacks
10
- this._callbacks = {
11
- 'on:connect': [],
12
- 'on:disconnect': [],
13
- 'on:response_error': [],
14
- 'before:create': [],
15
- 'after:create': [],
16
- 'before:update': [],
17
- 'after:update': [],
18
- 'before:destroy': [],
19
- 'after:destroy': []
20
- }
21
9
 
22
10
  Object.keys(this.attributes).forEach (attribute_key) ->
23
11
  if Model.prototype[attribute_key] == undefined
@@ -27,18 +15,81 @@ LiveRecord.Model.create = (config) ->
27
15
 
28
16
  Model.modelName = config.modelName
29
17
 
30
- Model.prototype.modelName = ->
31
- Model.modelName
18
+ Model.store = {}
19
+
20
+ Model.all = {}
21
+
22
+ Model.subscriptions = []
23
+
24
+ Model.subscribe = (config) ->
25
+ config || (config = {})
26
+ config.callbacks || (config.callbacks = {})
27
+
28
+ subscription = App.cable.subscriptions.create(
29
+ {
30
+ channel: 'LiveRecord::PublicationsChannel'
31
+ model_name: Model.modelName
32
+ where: config.where
33
+ },
34
+
35
+ connected: ->
36
+ if this.liveRecord._staleSince != undefined
37
+ @syncRecords()
38
+
39
+ config.callbacks['on:connect'].call(this) if config.callbacks['on:connect']
40
+
41
+ disconnected: ->
42
+ this.liveRecord._staleSince = (new Date()).toISOString() unless this.liveRecord._staleSince
43
+ config.callbacks['on:disconnect'].call(this) if config.callbacks['on:disconnect']
44
+
45
+ received: (data) ->
46
+ @onAction[data.action].call(this, data)
47
+
48
+ onAction:
49
+ create: (data) ->
50
+ config.callbacks['before:create'].call(this, data) if config.callbacks['before:create']
51
+ record = new Model(data.attributes)
52
+ record.create()
53
+ config.callbacks['after:create'].call(this, data) if config.callbacks['after:create']
54
+
55
+ syncRecords: ->
56
+ @perform(
57
+ 'sync_records',
58
+ model_name: Model.modelName,
59
+ where: config.where,
60
+ stale_since: this.liveRecord._staleSince
61
+ )
62
+ this.liveRecord._staleSince = undefined
63
+ )
64
+
65
+ subscription.liveRecord = {}
66
+ subscription.liveRecord.modelName = config.modelName
67
+ subscription.liveRecord.where = config.where
68
+ subscription.liveRecord.callbacks = config.callbacks
69
+
70
+ this.subscriptions.push(subscription)
71
+ subscription
72
+
73
+ Model.unsubscribe = (subscription) ->
74
+ index = this.subscriptions.indexOf(subscription)
75
+ throw new Error('`subscription` argument does not exist in ' + this.modelName + ' subscriptions list') if index == -1
76
+
77
+ App.cable.subscriptions.remove(subscription)
78
+
79
+ this.subscriptions.splice(index, 1)
80
+ subscription
32
81
 
33
82
  Model.prototype.subscribe = ->
34
83
  return this.subscription if this.subscription != undefined
35
84
 
36
85
  # listen for record changes (update / destroy)
37
- subscription = App['live_record_' + this.modelName() + '_' + this.id()] = App.cable.subscriptions.create({
38
- channel: 'LiveRecordChannel'
39
- model_name: this.modelName()
40
- record_id: this.id()
41
- },
86
+ subscription = App['live_record_' + this.modelName() + '_' + this.id()] = App.cable.subscriptions.create(
87
+ {
88
+ channel: 'LiveRecord::ChangesChannel'
89
+ model_name: this.modelName()
90
+ record_id: this.id()
91
+ },
92
+
42
93
  record: ->
43
94
  return @_record if @_record
44
95
  identifier = JSON.parse(this.identifier)
@@ -94,6 +145,12 @@ LiveRecord.Model.create = (config) ->
94
145
 
95
146
  this.subscription = subscription
96
147
 
148
+ Model.prototype.model = ->
149
+ Model
150
+
151
+ Model.prototype.modelName = ->
152
+ Model.modelName
153
+
97
154
  Model.prototype.unsubscribe = ->
98
155
  return if this.subscription == undefined
99
156
  App.cable.subscriptions.remove(this.subscription)
@@ -102,14 +159,14 @@ LiveRecord.Model.create = (config) ->
102
159
  Model.prototype.isSubscribed = ->
103
160
  this.subscription != undefined
104
161
 
105
- # ALL
106
- Model.all = {}
107
-
108
162
  # CREATE
109
- Model.prototype.create = () ->
163
+ Model.prototype.create = (options) ->
164
+ throw new Error(Model.modelName+'('+this.id()+') is already in the store') if Model.all[this.attributes.id]
110
165
  this._callCallbacks('before:create', undefined)
111
166
 
112
167
  Model.all[this.attributes.id] = this
168
+ # because we do not know if this newly created object is statle, then we just set it to very long time ago, before we subscribe()
169
+ this._staleSince = (new Date(1900, 0, 1)).toISOString()
113
170
  this.subscribe()
114
171
 
115
172
  this._callCallbacks('after:create', undefined)
@@ -138,7 +195,6 @@ LiveRecord.Model.create = (config) ->
138
195
 
139
196
  # CALLBACKS
140
197
 
141
- ## class callbacks
142
198
  Model._callbacks = {
143
199
  'on:connect': [],
144
200
  'on:disconnect': [],
@@ -150,14 +206,28 @@ LiveRecord.Model.create = (config) ->
150
206
  'before:destroy': [],
151
207
  'after:destroy': []
152
208
  }
209
+
210
+ Model.prototype._callbacks = {
211
+ 'on:connect': [],
212
+ 'on:disconnect': [],
213
+ 'on:response_error': [],
214
+ 'before:create': [],
215
+ 'after:create': [],
216
+ 'before:update': [],
217
+ 'after:update': [],
218
+ 'before:destroy': [],
219
+ 'after:destroy': []
220
+ }
153
221
 
154
- Model.addCallback = Model.prototype.addCallback = (callbackKey, callbackFunction) ->
222
+ # adding new callbackd to the list
223
+ Model.prototype.addCallback = Model.addCallback = (callbackKey, callbackFunction) ->
155
224
  index = this._callbacks[callbackKey].indexOf(callbackFunction)
156
225
  if index == -1
157
226
  this._callbacks[callbackKey].push(callbackFunction)
158
227
  return callbackFunction
159
228
 
160
- Model.removeCallback = Model.prototype.removeCallback = (callbackKey, callbackFunction) ->
229
+ # removing a callback from the list
230
+ Model.prototype.removeCallback = Model.removeCallback = (callbackKey, callbackFunction) ->
161
231
  index = this._callbacks[callbackKey].indexOf(callbackFunction)
162
232
  if index != -1
163
233
  this._callbacks[callbackKey].splice(index, 1)
@@ -179,7 +249,6 @@ LiveRecord.Model.create = (config) ->
179
249
  for callbackFunction in callbackFunctions
180
250
  Model.addCallback(callbackKey, callbackFunction)
181
251
 
182
-
183
252
  # enable plugins from arguments
184
253
  for pluginKey, pluginValue of config.plugins
185
254
  if LiveRecord.plugins
@@ -0,0 +1,3 @@
1
+ #= require_self
2
+
3
+ LiveRecord.plugins ||= {}
@@ -1,21 +1,7 @@
1
- this.LiveRecord.plugins.LiveDOM || (this.LiveRecord.plugins.LiveDOM = {});
1
+ #= require_self
2
+ #= require_directory ./live_dom/
2
3
 
3
- if window.jQuery == undefined
4
- throw new Error('jQuery is not loaded yet, and is a dependency of LiveRecord')
5
-
6
- LiveRecord.plugins.LiveDOM.applyToModel = (Model, pluginValue) ->
7
- return if pluginValue != true
8
-
9
- # DOM callbacks
10
-
11
- Model._updateDomCallback = ->
12
- $updateableElements = $('[data-live-record-update-from]')
4
+ LiveRecord.plugins.LiveDOM ||= {}
13
5
 
14
- for key, value of this.attributes
15
- $updateableElements.filter('[data-live-record-update-from="' + Model.modelName + '-' + this.id() + '-' + key + '"]').text(this[key]())
16
-
17
- Model._destroyDomCallback = ->
18
- $('[data-live-record-destroy-from="' + Model.modelName + '-' + this.id() + '"]').remove()
19
-
20
- Model.addCallback('after:update', Model._updateDomCallback)
21
- Model.addCallback('after:destroy', Model._destroyDomCallback)
6
+ if window.jQuery == undefined
7
+ throw new Error('jQuery is not loaded yet, and is a dependency of LiveRecord')
@@ -0,0 +1,17 @@
1
+ LiveRecord.plugins.LiveDOM.applyToModel = (Model, pluginValue) ->
2
+ return if pluginValue != true
3
+
4
+ # DOM callbacks
5
+ Model._updateDomCallback = (domContext)->
6
+ domContext ||= $('body')
7
+
8
+ $updatableElements = domContext.find('[data-live-record-update-from]')
9
+
10
+ for key, value of this.attributes
11
+ $updatableElements.filter('[data-live-record-update-from="' + Model.modelName + '-' + this.id() + '-' + key + '"]').text(this[key]())
12
+
13
+ Model._destroyDomCallback = ->
14
+ $('[data-live-record-destroy-from="' + Model.modelName + '-' + this.id() + '"]').remove()
15
+
16
+ Model.addCallback('after:update', Model._updateDomCallback)
17
+ Model.addCallback('after:destroy', Model._destroyDomCallback)
@@ -0,0 +1,41 @@
1
+ class LiveRecord::BaseChannel < ActionCable::Channel::Base
2
+
3
+ protected
4
+
5
+ def authorised_attributes(record, current_user)
6
+ whitelisted_attributes = record.class.live_record_whitelisted_attributes(record, current_user)
7
+ raise "#{record.model}.live_record_whitelisted_attributes should return an array" unless whitelisted_attributes.is_a? Array
8
+ ([:id] + whitelisted_attributes).map(&:to_s).to_set
9
+ end
10
+
11
+ def filtered_message(message, filters)
12
+ message['attributes'].slice!(*filters) if message['attributes'].present?
13
+ message
14
+ end
15
+
16
+ def find_record_from_params(params)
17
+ model_class = params[:model_name].safe_constantize
18
+
19
+ if model_class && model_class < ApplicationRecord
20
+ record = model_class.find_by(id: params[:record_id])
21
+
22
+ if record.present?
23
+ yield record
24
+ else
25
+ transmit 'action' => 'destroy'
26
+ end
27
+ else
28
+ respond_with_error(:bad_request, 'Not a correct model name')
29
+ reject_subscription
30
+ end
31
+ end
32
+
33
+ def respond_with_error(type, message = nil)
34
+ case type
35
+ when :forbidden
36
+ transmit error: { 'code' => 'forbidden', 'message' => (message || 'You are not authorised') }
37
+ when :bad_request
38
+ transmit error: { 'code' => 'bad_request', 'message' => (message || 'Invalid request parameters') }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,59 @@
1
+ # This channel streams changes (update/destroy) from records to connected clients, through ActiveRecord callbacks
2
+ # This also supports syncing (old changes) when a client somehow got disconnected (i.e. network problems),
3
+ # through a separate cache `live_record_updates` table.
4
+ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
5
+
6
+ def subscribed
7
+ find_record_from_params(params) do |record|
8
+ authorised_attributes = authorised_attributes(record, current_user)
9
+
10
+ if authorised_attributes.present?
11
+ stream_for record, coder: ActiveSupport::JSON do |message|
12
+ begin
13
+ record.reload
14
+ rescue ActiveRecord::RecordNotFound
15
+ end
16
+
17
+ authorised_attributes = authorised_attributes(record, current_user)
18
+
19
+ # if not just :id
20
+ if authorised_attributes.size > 1
21
+ response = filtered_message(message, authorised_attributes)
22
+ transmit response if response.present?
23
+ else
24
+ respond_with_error(:forbidden)
25
+ reject_subscription
26
+ end
27
+ end
28
+ else
29
+ respond_with_error(:forbidden)
30
+ reject
31
+ end
32
+ end
33
+ end
34
+
35
+ def sync_record(data)
36
+ find_record_from_params(data.symbolize_keys) do |record|
37
+ authorised_attributes = authorised_attributes(record, current_user)
38
+
39
+ # if not just :id
40
+ if authorised_attributes.size > 1
41
+ live_record_update = LiveRecordUpdate.where(
42
+ recordable_type: record.class.name,
43
+ recordable_id: record.id
44
+ ).where(
45
+ 'created_at >= ?', DateTime.parse(data['stale_since']) - LiveRecord.configuration.sync_record_buffer_time
46
+ ).order(id: :asc)
47
+
48
+ if live_record_update.exists?
49
+ message = { 'action' => 'update', 'attributes' => record.attributes }
50
+ response = filtered_message(message, authorised_attributes)
51
+ transmit response if response.present?
52
+ end
53
+ else
54
+ respond_with_error(:forbidden)
55
+ reject_subscription
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,134 @@
1
+ # This channel streams new records to connected clients whenever the "where" condition supplied by the client matches
2
+ # This implementation can be quite inefficient because there's only one pub-sub queue used for each model, but because of
3
+ # constraints ( see https://github.com/jrpolidario/live_record/issues/2 ) and because Users are authorised-validated anyway
4
+ # in each stream, then there's already an overhead delay. I am prioritising development convenience (as Rails does), in order
5
+ # to achieve a simpler API; in this example, it would be something like in JS:
6
+ # `LiveRecord.Model.all.Book.subscribe({where: is_enabled: true})`
7
+ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
8
+
9
+ def subscribed
10
+ model_class = params[:model_name].safe_constantize
11
+
12
+ if !(model_class && model_class < ApplicationRecord)
13
+ respond_with_error(:bad_request, 'Not a correct model name')
14
+ reject_subscription
15
+ end
16
+
17
+ stream_from "live_record:publications:#{params[:model_name].underscore}", coder: ActiveSupport::JSON do |message|
18
+ newly_created_record = model_class.find(message['attributes']['id'])
19
+
20
+ @authorised_attributes ||= authorised_attributes(newly_created_record, current_user)
21
+
22
+ active_record_relation = SearchAdapters.mapped_active_record_relation(
23
+ model_class: model_class,
24
+ conditions_hash: params[:where].to_h,
25
+ current_user: current_user,
26
+ authorised_attributes: @authorised_attributes
27
+ )
28
+
29
+ if active_record_relation.exists?(id: newly_created_record.id)
30
+ # if not just :id
31
+ if @authorised_attributes.size > 1
32
+ message = { 'action' => 'create', 'attributes' => message['attributes'] }
33
+ response = filtered_message(message, @authorised_attributes)
34
+ transmit response if response.present?
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def sync_records(data)
41
+ params = data.symbolize_keys
42
+ model_class = params[:model_name].safe_constantize
43
+
44
+ if model_class && model_class < ApplicationRecord
45
+
46
+ records = model_class.where(
47
+ 'created_at >= ?', DateTime.parse(params[:stale_since]) - LiveRecord.configuration.sync_record_buffer_time
48
+ )
49
+
50
+ # we `transmmit` a message back to client for each matching record
51
+ records.find_each do |record|
52
+ # now we check each record if it is part of the "where" condition
53
+ current_authorised_attributes = authorised_attributes(record, current_user)
54
+
55
+ active_record_relation = SearchAdapters.mapped_active_record_relation(
56
+ model_class: model_class,
57
+ conditions_hash: params[:where].to_h,
58
+ current_user: current_user,
59
+ authorised_attributes: current_authorised_attributes
60
+ )
61
+
62
+ if active_record_relation.exists?(id: record)
63
+ message = { 'action' => 'create', 'attributes' => record.attributes }
64
+ response = filtered_message(message, current_authorised_attributes)
65
+ transmit response if response.present?
66
+ end
67
+ end
68
+ else
69
+ respond_with_error(:bad_request, 'Not a correct model name')
70
+ reject_subscription
71
+ end
72
+ end
73
+
74
+ module SearchAdapters
75
+ def self.mapped_active_record_relation(**args)
76
+ # if ransack is loaded, use ransack
77
+ if Gem.loaded_specs.has_key? 'ransack'
78
+ active_record_relation = RansackAdapter.mapped_active_record_relation(args)
79
+ else
80
+ active_record_relation = ActiveRecordDefaultAdapter.mapped_active_record_relation(args)
81
+ end
82
+ active_record_relation
83
+ end
84
+
85
+ module RansackAdapter
86
+ def self.mapped_active_record_relation(**args)
87
+ model_class = args.fetch(:model_class)
88
+ conditions_hash = args.fetch(:conditions_hash)
89
+ current_user = args.fetch(:current_user)
90
+
91
+ model_class.ransack(conditions_hash, auth_object: current_user).result
92
+ end
93
+ end
94
+
95
+ module ActiveRecordDefaultAdapter
96
+ def self.mapped_active_record_relation(**args)
97
+ model_class = args.fetch(:model_class)
98
+ conditions_hash = args.fetch(:conditions_hash)
99
+ authorised_attributes = args.fetch(:authorised_attributes)
100
+
101
+ current_active_record_relation = model_class.all
102
+
103
+ conditions_hash.each do |key, value|
104
+ operator = key.split('_').last
105
+ # to get attribute_name, we subtract the end part of the string with size of operator substring; i.e.: created_at_lteq -> created_at
106
+ attribute_name = key[0..(-1 - operator.size - 1)]
107
+
108
+ if authorised_attributes == :all || authorised_attributes.include?(attribute_name)
109
+ case operator
110
+ when 'eq'
111
+ current_active_record_relation = current_active_record_relation.where(attribute_name => value)
112
+ when 'not_eq'
113
+ current_active_record_relation = current_active_record_relation.where.not(attribute_name => value)
114
+ when 'gt'
115
+ current_active_record_relation = current_active_record_relation.where(model_class.arel_table[attribute_name].gt(value))
116
+ when 'gteq'
117
+ current_active_record_relation = current_active_record_relation.where(model_class.arel_table[attribute_name].gteq(value))
118
+ when 'lt'
119
+ current_active_record_relation = current_active_record_relation.where(model_class.arel_table[attribute_name].lt(value))
120
+ when 'lteq'
121
+ current_active_record_relation = current_active_record_relation.where(model_class.arel_table[attribute_name].lteq(value))
122
+ when 'in'
123
+ current_active_record_relation = current_active_record_relation.where(attribute_name => Array.wrap(value))
124
+ when 'not_in'
125
+ current_active_record_relation = current_active_record_relation.where.not(attribute_name => Array.wrap(value))
126
+ end
127
+ end
128
+ end
129
+
130
+ current_active_record_relation
131
+ end
132
+ end
133
+ end
134
+ end