live_record 0.2.8 → 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.
- checksums.yaml +4 -4
- data/README.md +154 -59
- data/app/assets/javascripts/live_record/model/create.coffee +78 -0
- data/app/channels/live_record/autoloads_channel.rb +92 -0
- data/app/channels/live_record/base_channel.rb +0 -25
- data/app/channels/live_record/base_channel/helpers.rb +27 -0
- data/app/channels/live_record/{publications_channel → base_channel}/search_adapters.rb +1 -1
- data/app/channels/live_record/changes_channel.rb +3 -3
- data/app/channels/live_record/publications_channel.rb +5 -5
- data/config/puma.rb +2 -0
- data/lib/live_record/model/callbacks.rb +29 -2
- data/lib/live_record/version.rb +1 -1
- data/live_record.gemspec +10 -6
- data/spec/features/live_record_syncing_spec.rb +219 -35
- data/spec/internal/app/views/posts/index.html.erb +0 -5
- data/spec/internal/db/schema.rb +4 -0
- data/spec/rails_helper.rb +1 -1
- metadata +53 -36
@@ -1,30 +1,5 @@
|
|
1
1
|
class LiveRecord::BaseChannel < ActionCable::Channel::Base
|
2
2
|
|
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
|
16
|
-
|
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
|
26
|
-
end
|
27
|
-
|
28
3
|
protected
|
29
4
|
|
30
5
|
def filtered_message(message, filters)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class LiveRecord::BaseChannel
|
2
|
+
|
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
|
16
|
+
|
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
|
26
|
+
end
|
27
|
+
end
|
@@ -5,7 +5,7 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
|
|
5
5
|
|
6
6
|
def subscribed
|
7
7
|
find_record_from_params(params) do |record|
|
8
|
-
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
8
|
+
whitelisted_attributes = LiveRecord::BaseChannel::Helpers.whitelisted_attributes(record, current_user)
|
9
9
|
|
10
10
|
if whitelisted_attributes.present?
|
11
11
|
stream_for record, coder: ActiveSupport::JSON do |message|
|
@@ -14,7 +14,7 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
|
|
14
14
|
rescue ActiveRecord::RecordNotFound
|
15
15
|
end
|
16
16
|
|
17
|
-
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
17
|
+
whitelisted_attributes = LiveRecord::BaseChannel::Helpers.whitelisted_attributes(record, current_user)
|
18
18
|
|
19
19
|
if whitelisted_attributes.size > 0
|
20
20
|
response = filtered_message(message, whitelisted_attributes)
|
@@ -35,7 +35,7 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
|
|
35
35
|
params = data.symbolize_keys
|
36
36
|
|
37
37
|
find_record_from_params(params) do |record|
|
38
|
-
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
38
|
+
whitelisted_attributes = LiveRecord::BaseChannel::Helpers.whitelisted_attributes(record, current_user)
|
39
39
|
|
40
40
|
if whitelisted_attributes.size > 0
|
41
41
|
live_record_updates = nil
|
@@ -14,7 +14,7 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
|
|
14
14
|
reject_subscription
|
15
15
|
end
|
16
16
|
|
17
|
-
if !(model_class && Helpers.queryable_attributes(model_class, current_user).present?)
|
17
|
+
if !(model_class && LiveRecord::BaseChannel::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
|
@@ -22,14 +22,14 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
|
|
22
22
|
stream_from "live_record:publications:#{params[:model_name].underscore}", coder: ActiveSupport::JSON do |message|
|
23
23
|
newly_created_record = model_class.find(message['attributes']['id'])
|
24
24
|
|
25
|
-
active_record_relation = SearchAdapters.mapped_active_record_relation(
|
25
|
+
active_record_relation = LiveRecord::BaseChannel::SearchAdapters.mapped_active_record_relation(
|
26
26
|
model_class: model_class,
|
27
27
|
conditions_hash: params[:where].to_h,
|
28
28
|
current_user: current_user
|
29
29
|
)
|
30
30
|
|
31
31
|
if active_record_relation.exists?(id: newly_created_record.id)
|
32
|
-
whitelisted_attributes = Helpers.whitelisted_attributes(newly_created_record, current_user)
|
32
|
+
whitelisted_attributes = LiveRecord::BaseChannel::Helpers.whitelisted_attributes(newly_created_record, current_user)
|
33
33
|
|
34
34
|
if whitelisted_attributes.size > 0
|
35
35
|
message = { 'action' => 'create', 'attributes' => message['attributes'] }
|
@@ -46,7 +46,7 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
|
|
46
46
|
|
47
47
|
if model_class && model_class < ApplicationRecord
|
48
48
|
|
49
|
-
active_record_relation = SearchAdapters.mapped_active_record_relation(
|
49
|
+
active_record_relation = LiveRecord::BaseChannel::SearchAdapters.mapped_active_record_relation(
|
50
50
|
model_class: model_class,
|
51
51
|
conditions_hash: params[:where].to_h,
|
52
52
|
current_user: current_user,
|
@@ -61,7 +61,7 @@ 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
|
-
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
64
|
+
whitelisted_attributes = LiveRecord::BaseChannel::Helpers.whitelisted_attributes(record, current_user)
|
65
65
|
|
66
66
|
if whitelisted_attributes.size > 0
|
67
67
|
message = { 'action' => 'create', 'attributes' => record.attributes }
|
data/config/puma.rb
ADDED
@@ -4,10 +4,12 @@ module LiveRecord
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
included do
|
7
|
+
after_update_commit :__live_record_dereference_changed_attributes__
|
7
8
|
before_update :__live_record_reference_changed_attributes__
|
8
9
|
after_update_commit :__live_record_broadcast_record_update__
|
9
10
|
after_destroy_commit :__live_record_broadcast_record_destroy__
|
10
11
|
after_create_commit :__live_record_broadcast_record_create__
|
12
|
+
after_commit :__live_record_broadcast_record_autoload__, on: [:update, :create]
|
11
13
|
|
12
14
|
def self.live_record_whitelisted_attributes(record, current_user)
|
13
15
|
[]
|
@@ -19,9 +21,12 @@ module LiveRecord
|
|
19
21
|
@_live_record_changed_attributes = changed
|
20
22
|
end
|
21
23
|
|
24
|
+
def __live_record_dereference_changed_attributes__
|
25
|
+
@_live_record_changed_attributes = nil
|
26
|
+
end
|
27
|
+
|
22
28
|
def __live_record_broadcast_record_update__
|
23
29
|
included_attributes = attributes.slice(*@_live_record_changed_attributes)
|
24
|
-
@_live_record_changed_attributes = nil
|
25
30
|
message_data = { 'action' => 'update', 'attributes' => included_attributes }
|
26
31
|
LiveRecord::ChangesChannel.broadcast_to(self, message_data)
|
27
32
|
LiveRecordUpdate.create!(recordable_type: self.class, recordable_id: self.id, created_at: DateTime.now)
|
@@ -36,7 +41,29 @@ module LiveRecord
|
|
36
41
|
message_data = { 'action' => 'create', 'attributes' => attributes }
|
37
42
|
ActionCable.server.broadcast "live_record:publications:#{self.class.name.underscore}", message_data
|
38
43
|
end
|
44
|
+
|
45
|
+
def __live_record_broadcast_record_autoload__
|
46
|
+
included_attributes = nil
|
47
|
+
|
48
|
+
changed_attributes = attributes.slice(*@_live_record_changed_attributes)
|
49
|
+
|
50
|
+
# if after_update
|
51
|
+
if changed_attributes.present?
|
52
|
+
included_attributes = changed_attributes
|
53
|
+
# else if after_create_commit
|
54
|
+
else
|
55
|
+
included_attributes = attributes
|
56
|
+
end
|
57
|
+
|
58
|
+
message_data = {
|
59
|
+
'action' => 'create_or_update',
|
60
|
+
'model_name' => self.class.to_s,
|
61
|
+
'record_id' => id,
|
62
|
+
'attributes' => included_attributes
|
63
|
+
}
|
64
|
+
ActionCable.server.broadcast 'live_record:autoloads', message_data
|
65
|
+
end
|
39
66
|
end
|
40
67
|
end
|
41
68
|
end
|
42
|
-
end
|
69
|
+
end
|
data/lib/live_record/version.rb
CHANGED
data/live_record.gemspec
CHANGED
@@ -17,19 +17,23 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.add_dependency 'rails', '>= 5.0.0', '< 5.2'
|
18
18
|
|
19
19
|
s.add_development_dependency 'rails', '~> 5.1'
|
20
|
+
s.add_development_dependency 'puma', '~> 3.10'
|
20
21
|
s.add_development_dependency 'bundler', '~> 1.3'
|
21
|
-
s.add_development_dependency 'rspec-rails', '~> 3.6'
|
22
|
-
s.add_development_dependency 'combustion', '~> 0.7'
|
23
22
|
s.add_development_dependency 'byebug', '~> 9.0'
|
24
|
-
|
25
|
-
s.add_development_dependency 'redis', '~> 3.3'
|
26
|
-
s.add_development_dependency 'puma', '~> 3.10'
|
27
|
-
s.add_development_dependency 'jquery-rails', '~> 4.3'
|
23
|
+
|
28
24
|
s.add_development_dependency 'sprockets-rails', '~> 3.2'
|
29
25
|
s.add_development_dependency 'coffee-rails', '~> 4.2'
|
26
|
+
s.add_development_dependency 'jquery-rails', '~> 4.3'
|
30
27
|
s.add_development_dependency 'jbuilder', '~> 2.7'
|
28
|
+
|
29
|
+
s.add_development_dependency 'sqlite3', '~> 1.3'
|
30
|
+
s.add_development_dependency 'redis', '~> 3.3'
|
31
|
+
|
32
|
+
s.add_development_dependency 'rspec-rails', '~> 3.6'
|
33
|
+
s.add_development_dependency 'combustion', '~> 0.7'
|
31
34
|
s.add_development_dependency 'chromedriver-helper', '~> 1.1'
|
32
35
|
s.add_development_dependency 'selenium-webdriver', '~> 3.5'
|
33
36
|
s.add_development_dependency 'faker', '~> 1.8'
|
34
37
|
s.add_development_dependency 'database_cleaner', '~> 1.6'
|
38
|
+
s.add_development_dependency 'timecop', '0.9.1'
|
35
39
|
end
|
@@ -9,9 +9,15 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
9
9
|
let!(:users) { [user1, user2] }
|
10
10
|
let!(:posts) { [post1, post2, post3] }
|
11
11
|
|
12
|
+
# LiveRecord.helpers.loadRecords({ modelName: 'Post' });
|
13
|
+
# LiveRecord.helpers.loadRecords({ modelName: 'User', url: '<%= j users_path %>' });
|
14
|
+
# LiveRecord.Model.all.Post.subscribe({ where: { is_enabled_eq: true, content_eq: 'somecontent' }});
|
15
|
+
|
12
16
|
scenario 'User sees live changes (updates) of post records', js: true do
|
13
17
|
visit '/posts'
|
14
18
|
|
19
|
+
execute_script("LiveRecord.helpers.loadRecords({ modelName: 'Post' });")
|
20
|
+
|
15
21
|
post1_title_td = find('td', text: post1.title, wait: 10)
|
16
22
|
post2_title_td = find('td', text: post2.title, wait: 10)
|
17
23
|
post3_title_td = find('td', text: post3.title, wait: 10)
|
@@ -27,6 +33,8 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
27
33
|
scenario 'User sees live changes (destroy) of post records', js: true do
|
28
34
|
visit '/posts'
|
29
35
|
|
36
|
+
execute_script("LiveRecord.helpers.loadRecords({ modelName: 'Post' });")
|
37
|
+
|
30
38
|
expect{find('td', text: post1.title, wait: 10)}.to_not raise_error
|
31
39
|
expect{find('td', text: post2.title, wait: 10)}.to_not raise_error
|
32
40
|
expect{find('td', text: post3.title, wait: 10)}.to_not raise_error
|
@@ -43,6 +51,8 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
43
51
|
scenario 'User sees live changes (updates) of post records, but only changes from whitelisted authorised attributes', js: true do
|
44
52
|
visit '/posts'
|
45
53
|
|
54
|
+
execute_script("LiveRecord.helpers.loadRecords({ modelName: 'Post' });")
|
55
|
+
|
46
56
|
post1_title_td = find('td', text: post1.title, wait: 10)
|
47
57
|
post1_content_td = find('td', text: post1.content, wait: 10)
|
48
58
|
post2_title_td = find('td', text: post2.title, wait: 10)
|
@@ -65,6 +75,13 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
65
75
|
scenario 'JS-Client can access Model associations record objects in its current store', js: true do
|
66
76
|
visit '/posts'
|
67
77
|
|
78
|
+
execute_script(
|
79
|
+
<<-eos
|
80
|
+
LiveRecord.helpers.loadRecords({ modelName: 'Post' });
|
81
|
+
LiveRecord.helpers.loadRecords({ modelName: 'User', url: '#{users_path}' });
|
82
|
+
eos
|
83
|
+
)
|
84
|
+
|
68
85
|
# let's wait for all records first before checking correct associations
|
69
86
|
wait before: -> { evaluate_script('Object.keys( LiveRecord.Model.all.User.all ).length') }, becomes: -> (value) { value == users.size }, duration: 10.seconds
|
70
87
|
expect(evaluate_script('Object.keys( LiveRecord.Model.all.User.all ).length')).to eq users.size
|
@@ -94,35 +111,35 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
94
111
|
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post3.id}].user()")).to eq nil
|
95
112
|
end
|
96
113
|
|
97
|
-
# see spec/internal/app/views/posts/index.html.erb to see the subscribe "conditions"
|
98
114
|
scenario 'JS-Client receives live new (create) post records where specified "conditions" matched', js: true do
|
99
115
|
visit '/posts'
|
100
116
|
|
101
|
-
|
102
|
-
wait before: -> { evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length") }, becomes: -> (value) { value == Post.all.count }, duration: 10.seconds
|
103
|
-
expect(evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length")).to be Post.all.count
|
117
|
+
execute_script("LiveRecord.Model.all.Post.subscribe({ where: { is_enabled_eq: true }});")
|
104
118
|
|
105
|
-
|
106
|
-
|
119
|
+
sleep(1)
|
120
|
+
|
121
|
+
post4 = create(:post, is_enabled: true)
|
122
|
+
post5 = create(:post, is_enabled: false)
|
123
|
+
post6 = create(:post, is_enabled: true)
|
107
124
|
|
108
125
|
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}] == undefined") }, becomes: -> (value) { value == false }
|
109
126
|
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}] == undefined")).to be false
|
110
127
|
|
111
128
|
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post5.id}] == undefined") }, becomes: -> (value) { value == true }
|
112
129
|
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post5.id}] == undefined")).to be true
|
130
|
+
|
131
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post6.id}] == undefined") }, becomes: -> (value) { value == false }
|
132
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post6.id}] == undefined")).to be false
|
113
133
|
end
|
114
134
|
|
115
|
-
# see spec/internal/app/views/posts/index.html.erb to see the subscribe "conditions"
|
116
135
|
scenario 'JS-Client receives live new (create) post records where only considered "conditions" are the whitelisted authorised attributes', js: true do
|
117
136
|
visit '/posts'
|
118
137
|
|
119
|
-
|
120
|
-
wait before: -> { evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length") }, becomes: -> (value) { value == Post.all.count }, duration: 10.seconds
|
121
|
-
expect(evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length")).to be Post.all.count
|
138
|
+
execute_script("LiveRecord.Model.all.Post.subscribe({ where: { is_enabled_eq: true, content_eq: 'somecontent' }});")
|
122
139
|
|
123
|
-
|
124
|
-
|
125
|
-
# because `content` is not whitelisted, therefore the `content` condition
|
140
|
+
sleep(1)
|
141
|
+
|
142
|
+
# because `content` is not whitelisted in models/post.rb, therefore the `content` condition is disregarded from above
|
126
143
|
post4 = create(:post, is_enabled: true, content: 'somecontent')
|
127
144
|
post5 = create(:post, is_enabled: true, content: 'contentisnotwhitelistedthereforewontbeconsidered')
|
128
145
|
|
@@ -137,20 +154,105 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
137
154
|
scenario 'JS-Client receives live new (create) post records having only the whitelisted authorised attributes', js: true do
|
138
155
|
visit '/posts'
|
139
156
|
|
140
|
-
|
141
|
-
|
142
|
-
|
157
|
+
execute_script("LiveRecord.Model.all.Post.subscribe();")
|
158
|
+
|
159
|
+
sleep(1)
|
143
160
|
|
144
161
|
post4 = create(:post, is_enabled: true, title: 'sometitle', content: 'somecontent')
|
145
162
|
|
146
163
|
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}] == undefined") }, becomes: -> (value) { value == false }
|
164
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}] == undefined")).to be false
|
147
165
|
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}].title()")).to eq post4.title
|
148
|
-
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}].content
|
166
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}].attributes.content")).to eq nil
|
167
|
+
end
|
168
|
+
|
169
|
+
scenario 'JS-Client receives live autoloaded (create or update) post records where specified "conditions" matched', js: true do
|
170
|
+
visit '/posts'
|
171
|
+
|
172
|
+
# prepopulate
|
173
|
+
disabled_post_that_will_be_enabled = create(:post, is_enabled: false)
|
174
|
+
|
175
|
+
execute_script("LiveRecord.Model.all.Post.autoload({ where: { is_enabled_eq: true }});")
|
176
|
+
|
177
|
+
sleep(1)
|
178
|
+
|
179
|
+
disabled_post = create(:post, is_enabled: false)
|
180
|
+
enabled_post = create(:post, is_enabled: true)
|
181
|
+
|
182
|
+
sleep(5)
|
183
|
+
|
184
|
+
disabled_post_that_will_be_enabled.update!(is_enabled: true)
|
185
|
+
|
186
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{disabled_post.id}] == undefined") }, becomes: -> (value) { value == true }
|
187
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{disabled_post.id}] == undefined")).to be true
|
188
|
+
|
189
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{enabled_post.id}] == undefined") }, becomes: -> (value) { value == false }
|
190
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{enabled_post.id}] == undefined")).to be false
|
191
|
+
|
192
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{disabled_post_that_will_be_enabled.id}] == undefined") }, becomes: -> (value) { value == false }
|
193
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{disabled_post_that_will_be_enabled.id}] == undefined")).to be false
|
194
|
+
end
|
195
|
+
|
196
|
+
scenario 'JS-Client receives live autoloaded (create or update) post records where only considered "conditions" are the whitelisted authorised attributes', js: true do
|
197
|
+
visit '/posts'
|
198
|
+
|
199
|
+
# prepopulate
|
200
|
+
updated_post1 = create(:post, is_enabled: false, content: 'somecontent')
|
201
|
+
updated_post2 = create(:post, is_enabled: false, content: 'contentisnotwhitelistedthereforewontbeconsidered')
|
202
|
+
|
203
|
+
execute_script("LiveRecord.Model.all.Post.autoload({ where: { is_enabled_eq: true, content_eq: 'somecontent' }});")
|
204
|
+
|
205
|
+
sleep(1)
|
206
|
+
|
207
|
+
# because `content` is not whitelisted in models/post.rb, therefore the `content` condition is disregarded from above
|
208
|
+
created_post1 = create(:post, is_enabled: true, content: 'somecontent')
|
209
|
+
created_post2 = create(:post, is_enabled: true, content: 'contentisnotwhitelistedthereforewontbeconsidered')
|
210
|
+
updated_post1.update!(is_enabled: true)
|
211
|
+
updated_post2.update!(is_enabled: true)
|
212
|
+
|
213
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{created_post1.id}] == undefined") }, becomes: -> (value) { value == false }
|
214
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{created_post1.id}] == undefined")).to be false
|
215
|
+
|
216
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{created_post2.id}] == undefined") }, becomes: -> (value) { value == false }
|
217
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{created_post2.id}] == undefined")).to be false
|
218
|
+
|
219
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{updated_post1.id}] == undefined") }, becomes: -> (value) { value == false }
|
220
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{updated_post1.id}] == undefined")).to be false
|
221
|
+
|
222
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{updated_post2.id}] == undefined") }, becomes: -> (value) { value == false }
|
223
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{updated_post2.id}] == undefined")).to be false
|
224
|
+
end
|
225
|
+
|
226
|
+
# see spec/internal/app/models/post.rb to view specified whitelisted attributes
|
227
|
+
scenario 'JS-Client receives live autoloaded (create or update) post records having only the whitelisted authorised attributes', js: true do
|
228
|
+
visit '/posts'
|
229
|
+
|
230
|
+
# prepopulate
|
231
|
+
updated_post = create(:post, is_enabled: true, title: 'sometitle', content: 'somecontent')
|
232
|
+
|
233
|
+
execute_script("LiveRecord.Model.all.Post.autoload();")
|
234
|
+
|
235
|
+
sleep(1)
|
236
|
+
|
237
|
+
created_post = create(:post, is_enabled: true, title: 'sometitle', content: 'somecontent')
|
238
|
+
updated_post = create(:post, is_enabled: false, title: 'sometitle', content: 'somecontent')
|
239
|
+
|
240
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{created_post.id}] == undefined") }, becomes: -> (value) { value == false }
|
241
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{created_post.id}] == undefined")).to be false
|
242
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{created_post.id}].title()")).to eq created_post.title
|
243
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{created_post.id}].attributes.content")).to eq nil
|
244
|
+
|
245
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{updated_post.id}] == undefined") }, becomes: -> (value) { value == false }
|
246
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{updated_post.id}] == undefined")).to be false
|
247
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{updated_post.id}].title()")).to eq updated_post.title
|
248
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{updated_post.id}].attributes.content")).to eq nil
|
149
249
|
end
|
150
250
|
|
151
251
|
scenario 'JS-Client should not have shared callbacks for those callbacks defined only for a particular post record', js: true do
|
152
252
|
visit '/posts'
|
153
253
|
|
254
|
+
execute_script("LiveRecord.helpers.loadRecords({ modelName: 'Post' });")
|
255
|
+
|
154
256
|
# wait first for all posts to be loaded
|
155
257
|
wait before: -> { evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length") }, becomes: -> (value) { value == Post.all.count }, duration: 10.seconds
|
156
258
|
expect(evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length")).to be Post.all.count
|
@@ -169,9 +271,7 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
169
271
|
scenario 'JS-Client should receive response :forbidden error when using `subscribe()` but that the current_user is forbidden to do so', js: true do
|
170
272
|
visit '/posts'
|
171
273
|
|
172
|
-
|
173
|
-
wait before: -> { evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length") }, becomes: -> (value) { value == Post.all.count }, duration: 10.seconds
|
174
|
-
expect(evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length")).to be Post.all.count
|
274
|
+
execute_script("LiveRecord.Model.all.Post.subscribe();")
|
175
275
|
|
176
276
|
expect_any_instance_of(LiveRecord::BaseChannel).to(
|
177
277
|
receive(:respond_with_error).with(:forbidden, 'You do not have privileges to query').once
|
@@ -194,12 +294,17 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
194
294
|
|
195
295
|
visit '/posts'
|
196
296
|
|
297
|
+
execute_script(
|
298
|
+
<<-eos
|
299
|
+
LiveRecord.helpers.loadRecords({ modelName: 'Post' });
|
300
|
+
LiveRecord.Model.all.Post.subscribe();
|
301
|
+
eos
|
302
|
+
)
|
303
|
+
|
197
304
|
# wait first for all posts to be loaded
|
198
305
|
wait before: -> { evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length") }, becomes: -> (value) { value == Post.all.count }, duration: 10.seconds
|
199
306
|
expect(evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length")).to be Post.all.count
|
200
307
|
|
201
|
-
disconnection_time = 10 # seconds
|
202
|
-
|
203
308
|
Thread.new do
|
204
309
|
sleep(2)
|
205
310
|
|
@@ -213,7 +318,7 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
213
318
|
post_that_will_be_updated_while_disconnected_2.update!(title: 'newtitle')
|
214
319
|
end
|
215
320
|
|
216
|
-
|
321
|
+
disconnection_time = 10
|
217
322
|
sleep(disconnection_time)
|
218
323
|
|
219
324
|
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_that_will_be_updated_while_disconnected_1.id}] == undefined") }, becomes: -> (value) { value == false }, duration: 10.seconds
|
@@ -223,18 +328,22 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
223
328
|
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_that_will_be_updated_while_disconnected_2.id}].title()")).to eq 'newtitle'
|
224
329
|
end
|
225
330
|
|
226
|
-
scenario 'JS-Client receives all post records created during the time it got disconnected', js: true do
|
331
|
+
scenario 'JS-Client receives all post records created (matching the conditions) during the time it got disconnected', js: true do
|
227
332
|
visit '/posts'
|
228
333
|
|
229
|
-
# wait first for all posts to be loaded
|
230
|
-
wait before: -> { evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length") }, becomes: -> (value) { value == Post.all.count }, duration: 10.seconds
|
231
|
-
expect(evaluate_script("Object.keys( LiveRecord.Model.all.Post.all ).length")).to be Post.all.count
|
232
|
-
|
233
|
-
disconnection_time = 10 # seconds
|
234
|
-
|
235
334
|
post_created_before_disconnection = nil
|
236
335
|
post_created_while_disconnected_1 = nil
|
237
336
|
post_created_while_disconnected_2 = nil
|
337
|
+
post_created_while_disconnected_but_does_not_match = nil
|
338
|
+
|
339
|
+
# this needs to be before execute_script, otherwise it gets loaded by the subscription
|
340
|
+
Timecop.freeze((DateTime.now - LiveRecord.configuration.sync_record_buffer_time) - 30.seconds) do
|
341
|
+
post_created_before_disconnection = create(:post, is_enabled: true)
|
342
|
+
end
|
343
|
+
|
344
|
+
execute_script("LiveRecord.Model.all.Post.subscribe({where: {is_enabled_eq: true}});")
|
345
|
+
|
346
|
+
sleep(1)
|
238
347
|
|
239
348
|
Thread.new do
|
240
349
|
sleep(2)
|
@@ -245,13 +354,85 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
245
354
|
end
|
246
355
|
|
247
356
|
# then we create records while disconnected
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
357
|
+
Timecop.freeze((DateTime.now - LiveRecord.configuration.sync_record_buffer_time) + 30.seconds) do
|
358
|
+
post_created_while_disconnected_1 = create(:post, is_enabled: true)
|
359
|
+
end
|
360
|
+
|
361
|
+
Timecop.freeze((DateTime.now - LiveRecord.configuration.sync_record_buffer_time) + 99.hours) do
|
362
|
+
post_created_while_disconnected_2 = create(:post, is_enabled: true)
|
363
|
+
end
|
364
|
+
|
365
|
+
Timecop.freeze((DateTime.now - LiveRecord.configuration.sync_record_buffer_time) + 99.hours) do
|
366
|
+
post_created_while_disconnected_but_does_not_match = create(:post, is_enabled: false)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
disconnection_time = 10
|
371
|
+
sleep(disconnection_time)
|
372
|
+
|
373
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_1.id}] == undefined") }, becomes: -> (value) { value == false }, duration: 10.seconds
|
374
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_1.id}] == undefined")).to be false
|
375
|
+
|
376
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_2.id}] == undefined") }, becomes: -> (value) { value == false }
|
377
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_2.id}] == undefined")).to be false
|
378
|
+
|
379
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_but_does_not_match.id}] == undefined") }, becomes: -> (value) { value == true }
|
380
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_but_does_not_match.id}] == undefined")).to be true
|
381
|
+
|
382
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_before_disconnection.id}] == undefined") }, becomes: -> (value) { value == false }
|
383
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_before_disconnection.id}] == undefined")).to be true
|
384
|
+
end
|
385
|
+
|
386
|
+
scenario 'JS-Client receives all post records created/updated (matching the conditions), during the time it got disconnected', js: true do
|
387
|
+
post_created_before_disconnection = nil
|
388
|
+
post_created_while_disconnected_1 = nil
|
389
|
+
post_created_while_disconnected_2 = nil
|
390
|
+
post_created_while_disconnected_but_does_not_match = nil
|
391
|
+
|
392
|
+
# prepopulate before loading of page/script
|
393
|
+
post_updated_before_disconnection = create(:post, is_enabled: false)
|
394
|
+
post_updated_while_disconnected_1 = create(:post, is_enabled: false)
|
395
|
+
post_updated_while_disconnected_2 = create(:post, is_enabled: false)
|
396
|
+
post_updated_while_disconnected_but_does_not_match = create(:post, is_enabled: true)
|
397
|
+
|
398
|
+
# this needs to be before execute_script, otherwise it gets loaded by the subscription
|
399
|
+
Timecop.freeze((DateTime.now - LiveRecord.configuration.sync_record_buffer_time) - 30.seconds) do
|
400
|
+
post_created_before_disconnection = create(:post, is_enabled: true)
|
401
|
+
post_updated_before_disconnection.update!(is_enabled: true)
|
402
|
+
end
|
403
|
+
|
404
|
+
visit '/posts'
|
405
|
+
|
406
|
+
execute_script("LiveRecord.Model.all.Post.autoload({where: {is_enabled_eq: true}});")
|
407
|
+
|
408
|
+
sleep(1)
|
409
|
+
|
410
|
+
Thread.new do
|
411
|
+
sleep(2)
|
412
|
+
|
413
|
+
# temporarily stop all current autoloads_channel connections
|
414
|
+
ObjectSpace.each_object(LiveRecord::AutoloadsChannel) do |autoloads_channel|
|
415
|
+
autoloads_channel.connection.close
|
416
|
+
end
|
417
|
+
|
418
|
+
# then we create/update records while disconnected
|
419
|
+
Timecop.freeze((DateTime.now - LiveRecord.configuration.sync_record_buffer_time) + 30.seconds) do
|
420
|
+
post_created_while_disconnected_1 = create(:post, is_enabled: true)
|
421
|
+
post_updated_while_disconnected_1.update!(is_enabled: true)
|
422
|
+
end
|
423
|
+
|
424
|
+
Timecop.freeze((DateTime.now - LiveRecord.configuration.sync_record_buffer_time) + 99.hours) do
|
425
|
+
post_created_while_disconnected_2 = create(:post, is_enabled: true)
|
426
|
+
post_updated_while_disconnected_2.update!(is_enabled: true)
|
427
|
+
end
|
428
|
+
|
429
|
+
Timecop.freeze((DateTime.now - LiveRecord.configuration.sync_record_buffer_time) + 99.hours) do
|
430
|
+
post_created_while_disconnected_but_does_not_match = create(:post, is_enabled: false, created_at: (DateTime.now - LiveRecord.configuration.sync_record_buffer_time) + 99.hours)
|
431
|
+
post_updated_while_disconnected_but_does_not_match.update!(is_enabled: false)
|
432
|
+
end
|
252
433
|
end
|
253
434
|
|
254
|
-
|
435
|
+
disconnection_time = 10
|
255
436
|
sleep(disconnection_time)
|
256
437
|
|
257
438
|
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_1.id}] == undefined") }, becomes: -> (value) { value == false }, duration: 10.seconds
|
@@ -260,6 +441,9 @@ RSpec.feature 'LiveRecord Syncing', type: :feature do
|
|
260
441
|
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_2.id}] == undefined") }, becomes: -> (value) { value == false }
|
261
442
|
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_2.id}] == undefined")).to be false
|
262
443
|
|
444
|
+
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_but_does_not_match.id}] == undefined") }, becomes: -> (value) { value == true }
|
445
|
+
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_but_does_not_match.id}] == undefined")).to be true
|
446
|
+
|
263
447
|
wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_before_disconnection.id}] == undefined") }, becomes: -> (value) { value == false }
|
264
448
|
expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_before_disconnection.id}] == undefined")).to be true
|
265
449
|
end
|