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
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