live_record 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
File without changes
data/lib/live_record.rb CHANGED
@@ -6,6 +6,8 @@ Dir[__dir__ + '/live_record/*.rb'].each {|file| require file }
6
6
  Dir[__dir__ + '/live_record/model/*.rb'].each {|file| require file }
7
7
  Dir[__dir__ + '/live_record/channel/*.rb'].each {|file| require file }
8
8
  Dir[__dir__ + '/live_record/generators/*.rb'].each {|file| require file }
9
+ Dir[__dir__ + '/live_record/overrides/*.rb'].each {|file| require file }
10
+ Dir[__dir__ + '/live_record/action_view_extensions/*.rb'].each {|file| require file }
9
11
 
10
12
  module LiveRecord
11
13
  end
@@ -0,0 +1,22 @@
1
+ module LiveRecord
2
+ module ActionViewExtensions
3
+ module ViewHelper
4
+ def live_record_element(record)
5
+ raw " data-live-record-element='#{record.class.name}-#{record.id}' "
6
+ end
7
+
8
+ def live_record_updatable(record, attribute)
9
+ raise ArgumentError, "[#{record.class}] does not have an attribute named [#{attribute}]" unless record.attribute_names.include? attribute.to_s
10
+ raw " data-live-record-update-from='#{record.class.name}-#{record.id}-#{attribute}' "
11
+ end
12
+
13
+ def live_record_destroyable(record)
14
+ raw " data-live-record-destroy-from='#{record.class.name}-#{record.id}' "
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ ActiveSupport.on_load(:action_view) do
21
+ include LiveRecord::ActionViewExtensions::ViewHelper
22
+ end
@@ -0,0 +1,19 @@
1
+ require 'ostruct'
2
+
3
+ module LiveRecord
4
+
5
+ class Configuration < OpenStruct
6
+ end
7
+
8
+ @configuration = Configuration.new(
9
+ sync_record_buffer_time: 1.minute
10
+ )
11
+
12
+ class << self
13
+ attr_accessor :configuration
14
+
15
+ def configure(&block)
16
+ block.call(@configuration)
17
+ end
18
+ end
19
+ end
@@ -32,10 +32,15 @@ module LiveRecord
32
32
  migration_template 'create_live_record_updates.rb', 'db/migrate/create_live_record_updates.rb'
33
33
  end
34
34
 
35
- def copy_live_record_channel_template
36
- class_collisions 'LiveRecordChannel'
37
- template 'live_record_channel.rb', File.join('app/channels', 'live_record_channel.rb')
38
- end
35
+ # def copy_live_record_changes_channel_template
36
+ # class_collisions 'LiveRecordChangesChannel'
37
+ # template 'live_record_changes_channel.rb', File.join('app/channels', 'live_record_changes_channel.rb')
38
+ # end
39
+
40
+ # def copy_live_record_publication_channel_template
41
+ # class_collisions 'LiveRecordPublicationChannel'
42
+ # template 'live_record_publication_channel.rb', File.join('app/channels', 'live_record_publication_channel.rb')
43
+ # end
39
44
 
40
45
  def update_application_javascript
41
46
  in_root do
@@ -1,5 +1,5 @@
1
1
  class CreateLiveRecordUpdates < ActiveRecord::Migration[5.0]
2
- def change
2
+ def change
3
3
  create_table :live_record_updates do |t|
4
4
  t.references :recordable, polymorphic: true
5
5
  t.datetime :created_at, null: false, index: true
@@ -1,5 +1,6 @@
1
1
  <script>
2
2
  LiveRecord.helpers.loadRecords({modelName: '<%= singular_table_name.camelcase %>'})
3
+ LiveRecord.Model.all.<%= singular_table_name.camelcase %>.subscribe();
3
4
  </script>
4
5
  <p id="notice"><%%= notice %></p>
5
6
 
@@ -11,12 +11,12 @@ class <%= class_name %> < <%= parent_class_name.classify %>
11
11
  <% end -%>
12
12
 
13
13
  include LiveRecord::Model::Callbacks
14
- has_many :live_record_updates, as: :recordable
14
+ has_many :live_record_updates, as: :recordable, dependent: :destroy
15
15
 
16
16
  def self.live_record_whitelisted_attributes(<%= class_name.underscore %>, current_user)
17
- # Add attributes to this array that you would like current_user to have access to.
18
- # Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
19
- []
17
+ # Add attributes to this array that you would like current_user to have access to.
18
+ # Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
19
+ []
20
20
  end
21
21
  end
22
22
  <% end -%>
@@ -7,6 +7,7 @@ module LiveRecord
7
7
  before_update :__live_record_reference_changed_attributes__
8
8
  after_update_commit :__live_record_broadcast_record_update__
9
9
  after_destroy_commit :__live_record_broadcast_record_destroy__
10
+ after_create_commit :__live_record_broadcast_record_create__
10
11
 
11
12
  def self.live_record_whitelisted_attributes(record, current_user)
12
13
  []
@@ -22,13 +23,18 @@ module LiveRecord
22
23
  included_attributes = attributes.slice(*@_live_record_changed_attributes)
23
24
  @_live_record_changed_attributes = nil
24
25
  message_data = { 'action' => 'update', 'attributes' => included_attributes }
25
- LiveRecordChannel.broadcast_to(self, message_data)
26
+ LiveRecord::ChangesChannel.broadcast_to(self, message_data)
26
27
  LiveRecordUpdate.create!(recordable_type: self.class, recordable_id: self.id, created_at: DateTime.now)
27
28
  end
28
29
 
29
30
  def __live_record_broadcast_record_destroy__
30
31
  message_data = { 'action' => 'destroy' }
31
- LiveRecordChannel.broadcast_to(self, message_data)
32
+ LiveRecord::ChangesChannel.broadcast_to(self, message_data)
33
+ end
34
+
35
+ def __live_record_broadcast_record_create__
36
+ message_data = { 'action' => 'create', 'attributes' => attributes }
37
+ ActionCable.server.broadcast "live_record:publications:#{self.class.name.underscore}", message_data
32
38
  end
33
39
  end
34
40
  end
@@ -1,3 +1,3 @@
1
1
  module LiveRecord
2
- VERSION = '0.1.2'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
data/live_record.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.name = 'live_record'
7
7
  s.version = LiveRecord::VERSION
8
8
  s.summary = 'Rails 5 ActionCable Live JS Objects and DOM Elements'
9
- s.description = "Auto-syncs records in client-side JS (through a Model DSL) from changes in the backend Rails server through ActionCable.\nAuto-updates DOM elements mapped to a record attribute, from changes.\nAutomatically resyncs after client-side reconnection."
9
+ s.description = "Auto-syncs records in client-side JS (through a Model DSL) from changes (updates/destroy) in the backend Rails server through ActionCable.\n Also supports streaming newly created records to client-side JS.\n Supports lost connection restreaming for both new records (create), and record-changes (updates/destroy).\n Auto-updates DOM elements mapped to a record attribute, from changes (updates/destroy)."
10
10
  s.authors = ['Jules Roman B. Polidario']
11
11
  s.email = 'jrpolidario@gmail.com'
12
12
  s.files = `git ls-files`.split("\n")
@@ -16,6 +16,7 @@ Gem::Specification.new do |s|
16
16
 
17
17
  s.add_dependency 'rails', '>= 5.0.0', '< 5.2'
18
18
 
19
+ s.add_development_dependency 'rails', '~> 5.1'
19
20
  s.add_development_dependency 'bundler', '~> 1.3'
20
21
  s.add_development_dependency 'rspec-rails', '~> 3.6'
21
22
  s.add_development_dependency 'combustion', '~> 0.7'
@@ -27,7 +28,6 @@ Gem::Specification.new do |s|
27
28
  s.add_development_dependency 'sprockets-rails', '~> 3.2'
28
29
  s.add_development_dependency 'coffee-rails', '~> 4.2'
29
30
  s.add_development_dependency 'jbuilder', '~> 2.7'
30
- s.add_development_dependency 'capybara', '~> 2.15'
31
31
  s.add_development_dependency 'chromedriver-helper', '~> 1.1'
32
32
  s.add_development_dependency 'selenium-webdriver', '~> 3.5'
33
33
  s.add_development_dependency 'faker', '~> 1.8'
File without changes
@@ -0,0 +1,184 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.feature 'LiveRecord Syncing', type: :feature do
4
+ let(:post1) { create(:post) }
5
+ let(:post2) { create(:post) }
6
+ let(:post3) { create(:post) }
7
+ let!(:posts) { [post1, post2, post3] }
8
+
9
+ scenario 'User sees live changes (updates) of post records', js: true do
10
+ visit '/posts'
11
+
12
+ post1_title_td = find('td', text: post1.title, wait: 10)
13
+ post2_title_td = find('td', text: post2.title, wait: 10)
14
+ post3_title_td = find('td', text: post3.title, wait: 10)
15
+
16
+ post1.update!(title: 'post1newtitle')
17
+ post2.update!(title: 'post2newtitle')
18
+
19
+ expect(post1_title_td).to have_content('post1newtitle', wait: 10)
20
+ expect(post2_title_td).to have_content('post2newtitle', wait: 10)
21
+ expect(post3_title_td).to have_content(post3.title, wait: 10)
22
+ end
23
+
24
+ scenario 'User sees live changes (destroy) of post records', js: true do
25
+ visit '/posts'
26
+
27
+ expect{find('td', text: post1.title, wait: 10)}.to_not raise_error
28
+ expect{find('td', text: post2.title, wait: 10)}.to_not raise_error
29
+ expect{find('td', text: post3.title, wait: 10)}.to_not raise_error
30
+
31
+ post1.destroy
32
+ post2.destroy
33
+
34
+ expect{find('td', text: post1.title)}.to raise_error Capybara::ElementNotFound
35
+ expect{find('td', text: post2.title)}.to raise_error Capybara::ElementNotFound
36
+ expect{find('td', text: post3.title)}.to_not raise_error
37
+ end
38
+
39
+ # see spec/internal/app/models/post.rb to view specified whitelisted attributes
40
+ scenario 'User sees live changes (updates) of post records, but only changes from whitelisted authorised attributes', js: true do
41
+ visit '/posts'
42
+
43
+ post1_title_td = find('td', text: post1.title, wait: 10)
44
+ post1_content_td = find('td', text: post1.content, wait: 10)
45
+ post2_title_td = find('td', text: post2.title, wait: 10)
46
+ post2_content_td = find('td', text: post2.content, wait: 10)
47
+ post3_title_td = find('td', text: post3.title, wait: 10)
48
+ post3_content_td = find('td', text: post3.content, wait: 10)
49
+
50
+ post1.update!(title: 'post1newtitle', content: 'post1newcontent')
51
+ post2.update!(title: 'post2newtitle', content: 'post2newcontent')
52
+ post3.update!(title: 'post3newtitle', content: 'post3newcontent')
53
+
54
+ expect(post1_title_td).to have_content('post1newtitle', wait: 10)
55
+ expect(post1_content_td).to_not have_content('post1newcontent')
56
+ expect(post2_title_td).to have_content('post2newtitle', wait: 10)
57
+ expect(post2_content_td).to_not have_content('post2newcontent')
58
+ expect(post3_title_td).to have_content('post3newtitle', wait: 10)
59
+ expect(post3_content_td).to_not have_content('post3newcontent')
60
+ end
61
+
62
+ # see spec/internal/app/views/posts/index.html.erb to see the subscribe "conditions"
63
+ scenario 'JS-Client receives live new (create) post records where specified "conditions" matched', js: true do
64
+ visit '/posts'
65
+
66
+ sleep(5)
67
+
68
+ post4 = create(:post, id: 98, is_enabled: true)
69
+ post5 = create(:post, id: 99, is_enabled: false)
70
+
71
+ wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}] == undefined") }, becomes: -> (value) { value == false }
72
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}] == undefined")).to be false
73
+
74
+ wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post5.id}] == undefined") }, becomes: -> (value) { value == true }
75
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post5.id}] == undefined")).to be true
76
+ end
77
+
78
+ # see spec/internal/app/views/posts/index.html.erb to see the subscribe "conditions"
79
+ scenario 'JS-Client receives live new (create) post records where only considered "conditions" are the whitelisted authorised attributes', js: true do
80
+ visit '/posts'
81
+
82
+ sleep(5)
83
+
84
+ # in index.html.erb:
85
+ # LiveRecord.Model.all.Post.subscribe({ where: { is_enabled: true, content: 'somecontent' }});
86
+ # because `content` is not whitelisted, therefore the `content` condition above is disregarded
87
+ post4 = create(:post, is_enabled: true, content: 'somecontent')
88
+ post5 = create(:post, is_enabled: true, content: 'contentisnotwhitelistedthereforewontbeconsidered')
89
+
90
+ wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}] == undefined") }, becomes: -> (value) { value == false }
91
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}] == undefined")).to be false
92
+
93
+ wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post5.id}] == undefined") }, becomes: -> (value) { value == false }
94
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post5.id}] == undefined")).to be false
95
+ end
96
+
97
+ # see spec/internal/app/models/post.rb to view specified whitelisted attributes
98
+ scenario 'JS-Client receives live new (create) post records having only the whitelisted authorised attributes', js: true do
99
+ visit '/posts'
100
+
101
+ sleep(5)
102
+
103
+ post4 = create(:post, is_enabled: true, title: 'sometitle', content: 'somecontent')
104
+
105
+ wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}] == undefined") }, becomes: -> (value) { value == false }
106
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}].title()")).to eq post4.title
107
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post4.id}].content()")).to eq nil
108
+ end
109
+
110
+ # see spec/internal/app/models/post.rb to view specified whitelisted attributes
111
+ context 'when client got disconnected, and then reconnected' do
112
+ scenario 'JS-Client resyncs stale records', js: true do
113
+ post_that_will_be_updated_while_disconnected_1 = create(:post, is_enabled: true)
114
+ post_that_will_be_updated_while_disconnected_2 = create(:post, is_enabled: true)
115
+
116
+ visit '/posts'
117
+
118
+ sleep(5)
119
+
120
+ disconnection_time = 20 # seconds
121
+
122
+ Thread.new do
123
+ sleep(2)
124
+
125
+ # temporarily stop all current publication_channel connections
126
+ ObjectSpace.each_object(LiveRecord::ChangesChannel) do |changes_channel|
127
+ changes_channel.connection.close
128
+ end
129
+
130
+ # then we update records while disconnected
131
+ post_that_will_be_updated_while_disconnected_1.update!(title: 'newtitle')
132
+ post_that_will_be_updated_while_disconnected_2.update!(title: 'newtitle')
133
+ end
134
+
135
+ # LiveRecord.stop_all_streams
136
+ sleep(disconnection_time)
137
+
138
+ 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
139
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_that_will_be_updated_while_disconnected_1.id}].title()")).to eq 'newtitle'
140
+
141
+ wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_that_will_be_updated_while_disconnected_2.id}] == undefined") }, becomes: -> (value) { value == false }
142
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_that_will_be_updated_while_disconnected_2.id}].title()")).to eq 'newtitle'
143
+ end
144
+
145
+ scenario 'JS-Client receives all post records created during the time it got disconnected', js: true do
146
+ visit '/posts'
147
+
148
+ sleep(5)
149
+
150
+ disconnection_time = 20 # seconds
151
+
152
+ post_created_before_disconnection = nil
153
+ post_created_while_disconnected_1 = nil
154
+ post_created_while_disconnected_2 = nil
155
+
156
+ Thread.new do
157
+ sleep(2)
158
+
159
+ # temporarily stop all current publication_channel connections
160
+ ObjectSpace.each_object(LiveRecord::PublicationsChannel) do |publication_channel|
161
+ publication_channel.connection.close
162
+ end
163
+
164
+ # then we create records while disconnected
165
+ post_created_while_disconnected_1 = create(:post, is_enabled: true, created_at: (DateTime.now - LiveRecord.configuration.sync_record_buffer_time) + 30.seconds)
166
+ post_created_while_disconnected_2 = create(:post, is_enabled: true, created_at: (DateTime.now - LiveRecord.configuration.sync_record_buffer_time) + 99.hours)
167
+ # we need to create this here, otherwise it will be loaded on the Page immediately by the .loadRecords() in JS
168
+ post_created_before_disconnection = create(:post, is_enabled: true, created_at: (DateTime.now - LiveRecord.configuration.sync_record_buffer_time) - 30.seconds)
169
+ end
170
+
171
+ # LiveRecord.stop_all_streams
172
+ sleep(disconnection_time)
173
+
174
+ wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_1.id}] == undefined") }, becomes: -> (value) { value == false }, duration: 10.seconds
175
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_1.id}] == undefined")).to be false
176
+
177
+ wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_2.id}] == undefined") }, becomes: -> (value) { value == false }
178
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_while_disconnected_2.id}] == undefined")).to be false
179
+
180
+ wait before: -> { evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_before_disconnection.id}] == undefined") }, becomes: -> (value) { value == false }
181
+ expect(evaluate_script("LiveRecord.Model.all.Post.all[#{post_created_before_disconnection.id}] == undefined")).to be true
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,19 @@
1
+ module SpecHelpers
2
+ module Wait
3
+ # Wait before the value of the "before" Proc becomes "becomes",
4
+ # but only wait until "duration" number of seconds,
5
+ # while checking the value each "interval" seconds interval.
6
+ def wait(duration: 5.seconds, interval: (0.5).second, before:, becomes:)
7
+ # use a different thread to prevent blocking the main thread due to sleep()
8
+ Thread.new do
9
+ start = Time.now
10
+
11
+ loop do
12
+ break if becomes.call(before.call)
13
+ sleep interval.seconds
14
+ break if (Time.now - start).seconds > duration
15
+ end
16
+ end.join
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ LiveRecord.Model.create(
2
+ {
3
+ modelName: 'Post',
4
+ plugins: {
5
+ LiveDOM: true
6
+ }
7
+ }
8
+ )
@@ -1,8 +1,8 @@
1
1
  module ApplicationCable
2
2
  class Connection < ActionCable::Connection::Base
3
- identified_by :current_user
4
-
5
- def current_user
6
- end
3
+ identified_by :current_user
4
+
5
+ def current_user
6
+ end
7
7
  end
8
8
  end
@@ -4,6 +4,7 @@ class PostsController < ApplicationController
4
4
  # GET /posts
5
5
  # GET /posts.json
6
6
  def index
7
+ request.session['init'] = true if request.session.id.nil?
7
8
  @posts = Post.all
8
9
  end
9
10
 
@@ -0,0 +1,11 @@
1
+ class Post < ApplicationRecord
2
+ include LiveRecord::Model::Callbacks
3
+
4
+ has_many :live_record_updates, as: :recordable, dependent: :destroy
5
+
6
+ def self.live_record_whitelisted_attributes(post, current_user)
7
+ # Add attributes to this array that you would like current_user to have access to.
8
+ # Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
9
+ [:title, :is_enabled, :created_at, :updated_at]
10
+ end
11
+ end