live_record 0.2.7 → 0.2.8
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.
- checksums.yaml +4 -4
- data/README.md +15 -12
- data/app/channels/live_record/base_channel.rb +24 -5
- data/app/channels/live_record/changes_channel.rb +8 -10
- data/app/channels/live_record/publications_channel.rb +11 -9
- data/app/channels/live_record/publications_channel/search_adapters.rb +2 -2
- data/lib/live_record/generators/templates/model.rb.rb +2 -2
- data/lib/live_record/version.rb +1 -1
- data/spec/internal/app/models/post.rb +2 -21
- data/spec/internal/app/models/user.rb +1 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d08ed348f467c4dad7084528d36552775a03f365
|
4
|
+
data.tar.gz: 3698f07abf6f4e6edc1430870fb8eb7ad6862bad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 482a0751d39af81591ac4137fc2a8f12206fa4e687aeb5b1508c1ce60fb85fe1d92465ff853affad114017f1f97e19a859a2d39553f3d92d387d05ccc5840630
|
7
|
+
data.tar.gz: '081b8691a3f37984961031d230afa749603c9c61e402347d63a914ed854170cf6c2df561c68d8be8514e0bfc04cfc139cfbe1878573ff72fc5057a94bb0401b8'
|
data/README.md
CHANGED
@@ -101,9 +101,9 @@
|
|
101
101
|
# Add attributes to this array that you would like `current_user` to have access to when syncing this particular `book`
|
102
102
|
# empty array means not-authorised
|
103
103
|
if book.user == current_user
|
104
|
-
[:title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
104
|
+
[:id, :title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
105
105
|
elsif current_user.present?
|
106
|
-
[:title, :author, :created_at, :updated_at]
|
106
|
+
[:id ,:title, :author, :created_at, :updated_at]
|
107
107
|
else
|
108
108
|
[]
|
109
109
|
end
|
@@ -113,7 +113,7 @@
|
|
113
113
|
# Add attributes to this array that you would like `current_user` to be able to query upon on the `subscribe({where: {...}}) function`
|
114
114
|
# empty array means not-authorised
|
115
115
|
if current_user.isAdmin?
|
116
|
-
[:title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
116
|
+
[:id, :title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
117
117
|
else
|
118
118
|
[]
|
119
119
|
end
|
@@ -127,7 +127,7 @@
|
|
127
127
|
1. Add the following to your `Gemfile`:
|
128
128
|
|
129
129
|
```ruby
|
130
|
-
gem 'live_record', '~> 0.2.
|
130
|
+
gem 'live_record', '~> 0.2.8'
|
131
131
|
```
|
132
132
|
|
133
133
|
2. Run:
|
@@ -180,13 +180,13 @@
|
|
180
180
|
def self.live_record_whitelisted_attributes(book, current_user)
|
181
181
|
# Add attributes to this array that you would like current_user to have access to when syncing.
|
182
182
|
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
183
|
-
[:title, :author, :created_at, :updated_at]
|
183
|
+
[:id, :title, :author, :created_at, :updated_at]
|
184
184
|
end
|
185
185
|
|
186
186
|
def self.live_record_queryable_attributes(current_user)
|
187
187
|
# Add attributes to this array that you would like current_user to query upon when using `.subscribe({where: {...})`
|
188
188
|
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
189
|
-
[:title, :author, :created_at, :updated_at]
|
189
|
+
[:id, :title, :author, :created_at, :updated_at]
|
190
190
|
end
|
191
191
|
end
|
192
192
|
```
|
@@ -203,9 +203,9 @@
|
|
203
203
|
# Notice that from above, you also have access to `book` (the record currently requested by the client to be synced),
|
204
204
|
# and the `current_user`, the current user who is trying to sync the `book` record.
|
205
205
|
if book.user == current_user
|
206
|
-
[:title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
206
|
+
[:id, :title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
207
207
|
elsif current_user.present?
|
208
|
-
[:title, :author, :created_at, :updated_at]
|
208
|
+
[:id, :title, :author, :created_at, :updated_at]
|
209
209
|
else
|
210
210
|
[]
|
211
211
|
end
|
@@ -215,7 +215,7 @@
|
|
215
215
|
# this method should look like your `live_record_whitelisted_attributes` above, only except if you want to further customise this for flexibility
|
216
216
|
# or... that you may just simply return `[]` (empty array) if you do not want to allow users to use `subscribe()`
|
217
217
|
# also take note that this method only has `current_user` argument compared to `live_record_whitelisted_attributes` above which also has the `book` argument. This is intended for SQL performance reasons
|
218
|
-
[:title, :author, :created_at, :updated_at]
|
218
|
+
[:id, :title, :author, :created_at, :updated_at]
|
219
219
|
end
|
220
220
|
end
|
221
221
|
```
|
@@ -439,14 +439,14 @@
|
|
439
439
|
has_many :live_record_updates, as: :recordable, dependent: :destroy
|
440
440
|
|
441
441
|
def self.live_record_whitelisted_attributes(book, current_user)
|
442
|
-
[:title, :is_enabled]
|
442
|
+
[:id, :title, :is_enabled]
|
443
443
|
end
|
444
444
|
|
445
445
|
## this method will be invoked when `subscribe()` is called
|
446
446
|
## but, you should not use this method when using `ransack` gem!
|
447
447
|
## ransack's methods like `ransackable_attributes` below will be invoked instead
|
448
448
|
# def self.live_record_queryable_attributes(book, current_user)
|
449
|
-
# [:title, :is_enabled]
|
449
|
+
# [:id, :title, :is_enabled]
|
450
450
|
# end
|
451
451
|
|
452
452
|
private
|
@@ -659,11 +659,14 @@
|
|
659
659
|
* MIT
|
660
660
|
|
661
661
|
## Changelog
|
662
|
+
* 0.2.8
|
663
|
+
* You can now specify `:id` into `live_record_whitelisted_attributes` for verbosity; used to be automatically-included by default. Needed to do this otherwise there was this minor bug where `subscribe()` still receives records (having just `:id` attribute though) even when it is specified to be not-authorized.
|
664
|
+
* fixed minor bug when `live_record_whitelisted_attributes` is not returning anything, throwing a `NoMethodError`
|
662
665
|
* 0.2.7
|
663
666
|
* improved performance when using `subscribe({reload: true})`, but by doing so, I am forced to a conscious decision to have another separate model method for "queryable" attributes: `live_record_queryable_attributes`, which both has `pro` and `cons`
|
664
667
|
* pros:
|
665
668
|
* greatly sped up SQL for loading of data
|
666
|
-
* pro & con: now two methods in model: `live_record_whitelisted_attributes` and `live_record_queryable_attributes` which should have mostly the same code [(see some differences above)](
|
669
|
+
* pro & con: now two methods in model: `live_record_whitelisted_attributes` and `live_record_queryable_attributes` which should have mostly the same code [(see some differences above)](#example-1---simple-usage) which makes it repetitive to some degree, although this allows more flexibility.
|
667
670
|
* added `matches` and `does_not_match` filters
|
668
671
|
* 0.2.6
|
669
672
|
* fixed minor bug where `MODELINSTANCE.changes` do not accurately work on NULL values.
|
@@ -1,13 +1,32 @@
|
|
1
1
|
class LiveRecord::BaseChannel < ActionCable::Channel::Base
|
2
2
|
|
3
|
-
|
3
|
+
module Helpers
|
4
|
+
def self.whitelisted_attributes(record, current_user)
|
5
|
+
whitelisted_attributes = record.class.live_record_whitelisted_attributes(record, current_user)
|
6
|
+
|
7
|
+
unless whitelisted_attributes.is_a? Array
|
8
|
+
raise "#{record.class}.live_record_whitelisted_attributes should return an array"
|
9
|
+
end
|
10
|
+
|
11
|
+
whitelisted_attributes = whitelisted_attributes.map(&:to_s)
|
12
|
+
|
13
|
+
if !whitelisted_attributes.empty? && !whitelisted_attributes.include?('id')
|
14
|
+
raise "#{record.class}.live_record_whitelisted_attributes should return an array that also includes the :id attribute, as you are authorizing at least one other attribute along with it."
|
15
|
+
end
|
4
16
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
17
|
+
whitelisted_attributes.to_set
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.queryable_attributes(model_class, current_user)
|
21
|
+
queryable_attributes = model_class.live_record_queryable_attributes(current_user)
|
22
|
+
raise "#{model_class}.live_record_queryable_attributes should return an array" unless queryable_attributes.is_a? Array
|
23
|
+
queryable_attributes = queryable_attributes.map(&:to_s)
|
24
|
+
queryable_attributes.to_set
|
25
|
+
end
|
9
26
|
end
|
10
27
|
|
28
|
+
protected
|
29
|
+
|
11
30
|
def filtered_message(message, filters)
|
12
31
|
message['attributes'].slice!(*filters) if message['attributes'].present?
|
13
32
|
message
|
@@ -5,20 +5,19 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
|
|
5
5
|
|
6
6
|
def subscribed
|
7
7
|
find_record_from_params(params) do |record|
|
8
|
-
|
8
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
9
9
|
|
10
|
-
if
|
10
|
+
if whitelisted_attributes.present?
|
11
11
|
stream_for record, coder: ActiveSupport::JSON do |message|
|
12
12
|
begin
|
13
13
|
record.reload
|
14
14
|
rescue ActiveRecord::RecordNotFound
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
response = filtered_message(message, authorised_attributes)
|
19
|
+
if whitelisted_attributes.size > 0
|
20
|
+
response = filtered_message(message, whitelisted_attributes)
|
22
21
|
transmit response if response.present?
|
23
22
|
else
|
24
23
|
respond_with_error(:forbidden)
|
@@ -36,10 +35,9 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
|
|
36
35
|
params = data.symbolize_keys
|
37
36
|
|
38
37
|
find_record_from_params(params) do |record|
|
39
|
-
|
38
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
40
39
|
|
41
|
-
|
42
|
-
if authorised_attributes.size > 1
|
40
|
+
if whitelisted_attributes.size > 0
|
43
41
|
live_record_updates = nil
|
44
42
|
|
45
43
|
if params[:stale_since].present?
|
@@ -55,7 +53,7 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
|
|
55
53
|
# then we update the record in the client-side
|
56
54
|
if params[:stale_since].blank? || live_record_updates.exists?
|
57
55
|
message = { 'action' => 'update', 'attributes' => record.attributes }
|
58
|
-
response = filtered_message(message,
|
56
|
+
response = filtered_message(message, whitelisted_attributes)
|
59
57
|
transmit response if response.present?
|
60
58
|
end
|
61
59
|
else
|
@@ -14,7 +14,7 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
|
|
14
14
|
reject_subscription
|
15
15
|
end
|
16
16
|
|
17
|
-
if !(model_class &&
|
17
|
+
if !(model_class && Helpers.queryable_attributes(model_class, current_user).present?)
|
18
18
|
respond_with_error(:forbidden, 'You do not have privileges to query')
|
19
19
|
reject_subscription
|
20
20
|
end
|
@@ -29,11 +29,11 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
|
|
29
29
|
)
|
30
30
|
|
31
31
|
if active_record_relation.exists?(id: newly_created_record.id)
|
32
|
-
|
33
|
-
|
34
|
-
if
|
32
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(newly_created_record, current_user)
|
33
|
+
|
34
|
+
if whitelisted_attributes.size > 0
|
35
35
|
message = { 'action' => 'create', 'attributes' => message['attributes'] }
|
36
|
-
response = filtered_message(message,
|
36
|
+
response = filtered_message(message, whitelisted_attributes)
|
37
37
|
transmit response if response.present?
|
38
38
|
end
|
39
39
|
end
|
@@ -61,11 +61,13 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
|
|
61
61
|
# we `transmmit` a message back to client for each matching record
|
62
62
|
active_record_relation.find_each do |record|
|
63
63
|
# but first, check for the authorised attributes, if exists
|
64
|
-
|
64
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
if whitelisted_attributes.size > 0
|
67
|
+
message = { 'action' => 'create', 'attributes' => record.attributes }
|
68
|
+
response = filtered_message(message, whitelisted_attributes)
|
69
|
+
transmit response if response.present?
|
70
|
+
end
|
69
71
|
end
|
70
72
|
else
|
71
73
|
respond_with_error(:bad_request, 'Not a correct model name')
|
@@ -28,12 +28,12 @@ class LiveRecord::PublicationsChannel
|
|
28
28
|
current_user = args.fetch(:current_user)
|
29
29
|
|
30
30
|
current_active_record_relation = model_class.all
|
31
|
-
queryable_attributes =
|
31
|
+
queryable_attributes = LiveRecord::BaseChannel::Helpers.queryable_attributes(model_class, current_user)
|
32
32
|
|
33
33
|
conditions_hash.each do |key, value|
|
34
34
|
operator = key.split('_').last
|
35
35
|
# to get attribute_name, we subtract the end part of the string with size of operator substring; i.e.: created_at_lteq -> created_at
|
36
|
-
attribute_name = key[0..(-1 - operator.size - 1)]
|
36
|
+
attribute_name = key[0..(-1 - operator.size - 1)]
|
37
37
|
|
38
38
|
if queryable_attributes.include?(attribute_name)
|
39
39
|
case operator
|
@@ -20,7 +20,7 @@ class <%= class_name %> < <%= parent_class_name.classify %>
|
|
20
20
|
# then only these whitelisted attributes will be sent to this current_user client
|
21
21
|
# Empty array means unauthorized
|
22
22
|
# Example:
|
23
|
-
# [:email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
23
|
+
# [:id, :email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
24
24
|
[]
|
25
25
|
end
|
26
26
|
|
@@ -36,7 +36,7 @@ class <%= class_name %> < <%= parent_class_name.classify %>
|
|
36
36
|
# if you're using `ransack` gem, use `ransackable_attributes`
|
37
37
|
# Empty array means unauthorized
|
38
38
|
# Example:
|
39
|
-
# [:email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
39
|
+
# [:id, :email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
40
40
|
[]
|
41
41
|
end
|
42
42
|
end
|
data/lib/live_record/version.rb
CHANGED
@@ -5,29 +5,10 @@ class Post < ApplicationRecord
|
|
5
5
|
has_many :live_record_updates, as: :recordable, dependent: :destroy
|
6
6
|
|
7
7
|
def self.live_record_whitelisted_attributes(post, current_user)
|
8
|
-
|
9
|
-
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
10
|
-
# i.e. if this file is a User model, and that a User has been created/updated in the backend,
|
11
|
-
# then only these whitelisted attributes will be sent to this current_user client
|
12
|
-
# Empty array means unauthorized
|
13
|
-
# Example:
|
14
|
-
# [:email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
15
|
-
[:title, :is_enabled, :user_id, :created_at, :updated_at]
|
8
|
+
[:id, :title, :is_enabled, :user_id, :created_at, :updated_at]
|
16
9
|
end
|
17
10
|
|
18
11
|
def self.live_record_queryable_attributes(current_user)
|
19
|
-
|
20
|
-
# If you're using ransack gem, instead of this method, use one or more of the ransack methods:
|
21
|
-
# see https://github.com/activerecord-hackery/ransack#authorization-whitelistingblacklisting
|
22
|
-
#
|
23
|
-
# Add attributes to this array that you would like current_user client to be able to query upon when "subscribing"
|
24
|
-
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
25
|
-
# i.e. if a current_user client subscribes to "new records creation" using `.subscribe({where: {...}})`,
|
26
|
-
# then only these attributes will be considered in the "{where: ...}" argument
|
27
|
-
# if you're using `ransack` gem, use `ransackable_attributes`
|
28
|
-
# Empty array means unauthorized
|
29
|
-
# Example:
|
30
|
-
# [:email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
31
|
-
[:title, :is_enabled, :user_id, :created_at, :updated_at]
|
12
|
+
[:id, :title, :is_enabled, :user_id, :created_at, :updated_at]
|
32
13
|
end
|
33
14
|
end
|
@@ -5,9 +5,7 @@ class User < ApplicationRecord
|
|
5
5
|
has_many :posts
|
6
6
|
|
7
7
|
def self.live_record_whitelisted_attributes(user, current_user)
|
8
|
-
|
9
|
-
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
10
|
-
[:email, :created_at, :updated_at]
|
8
|
+
[:id, :email, :created_at, :updated_at]
|
11
9
|
end
|
12
10
|
|
13
11
|
def self.live_record_queryable_attributes(current_user)
|