hyper-mesh 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/Gemfile +7 -2
  4. data/README.md +112 -87
  5. data/Rakefile +6 -1
  6. data/docs/action_cable_quickstart.md +20 -16
  7. data/docs/activerecord_api.md +23 -17
  8. data/docs/authorization-policies.md +45 -35
  9. data/docs/client_side_scoping.md +5 -5
  10. data/docs/configuration_details.md +6 -46
  11. data/docs/pusher_faker_quickstart.md +7 -68
  12. data/docs/pusher_quickstart.md +7 -68
  13. data/docs/simple_poller_quickstart.md +6 -67
  14. data/docs/todo-example.md +2 -2
  15. data/docs/word_game.md +3 -1
  16. data/docs/words-example.md +2 -3
  17. data/examples/action-cable/Gemfile +2 -1
  18. data/examples/action-cable/Gemfile.lock +73 -54
  19. data/examples/action-cable/config/initializers/{hyper_mesh.rb → hyperloop.rb} +1 -1
  20. data/examples/action-cable/config/routes.rb +1 -1
  21. data/hyper-mesh.gemspec +10 -4
  22. data/lib/active_record_base.rb +3 -3
  23. data/{examples/action-cable-production-mode/log/.keep → lib/acts_as_string.rb} +0 -0
  24. data/lib/hyper-mesh.rb +11 -19
  25. data/lib/hyper_mesh/version.rb +3 -0
  26. data/lib/hypermesh/version.rb +1 -1
  27. data/lib/reactive_record/active_record/class_methods.rb +10 -3
  28. data/lib/reactive_record/active_record/instance_methods.rb +8 -0
  29. data/lib/reactive_record/active_record/public_columns_hash.rb +8 -2
  30. data/lib/reactive_record/active_record/reactive_record/collection.rb +0 -1
  31. data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +2 -1
  32. data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +55 -63
  33. data/lib/reactive_record/active_record/reactive_record/operations.rb +51 -0
  34. data/lib/reactive_record/active_record/reactive_record/reactive_set_relationship_helpers.rb +3 -3
  35. data/lib/reactive_record/active_record/reactive_record/while_loading.rb +93 -84
  36. data/lib/reactive_record/broadcast.rb +183 -0
  37. data/lib/reactive_record/permissions.rb +2 -2
  38. data/reactive_record_test_app/Gemfile +6 -2
  39. data/reactive_record_test_app/Gemfile.lock +120 -60
  40. data/reactive_record_test_app/app/assets/javascripts/application.rb +3 -5
  41. data/reactive_record_test_app/app/assets/javascripts/bigdecimal.rb +1 -0
  42. data/reactive_record_test_app/app/assets/javascripts/reactive_record_config.js +2 -2
  43. data/reactive_record_test_app/app/controllers/application_controller.rb +3 -3
  44. data/reactive_record_test_app/app/controllers/home_controller.rb +1 -1
  45. data/reactive_record_test_app/app/models/models.rb.erb +6 -0
  46. data/reactive_record_test_app/config/application.rb +2 -0
  47. data/reactive_record_test_app/config/environments/development.rb +1 -1
  48. data/reactive_record_test_app/config/routes.rb +1 -2
  49. data/reactive_record_test_app/db/seeds.rb +6 -0
  50. data/reactive_record_test_app/script/rails +0 -0
  51. data/reactive_record_test_app/spec-opal/active-record/rendering_spec.rb +11 -2
  52. data/reactive_record_test_app/spec-opal/active-record/save_spec.rb +3 -4
  53. data/reactive_record_test_app/spec-opal/spec_helper.js.rb +1 -1
  54. data/reactive_record_test_app/spec-opal/test_spec.rb +7 -0
  55. data/reactive_record_test_app/spec_dont_run/README.md +7 -0
  56. data/reactive_record_test_app/{spec-opal/active-record → spec_dont_run/active_record_broken}/permissions_spec.rb +0 -0
  57. data/reactive_record_test_app/{spec-opal/active-record → spec_dont_run/active_record_broken}/prerendering_spec.rb +1 -0
  58. data/spec/{synchromesh/aaa-unit_tests/connection_spec.rb → batch1/aaa-unit_tests/connection_movedspec.rb} +0 -0
  59. data/spec/{synchromesh → batch1}/aaa-unit_tests/dummy_value_spec.rb +2 -2
  60. data/spec/{synchromesh → batch1}/column_types/column_type_spec.rb +2 -2
  61. data/spec/{synchromesh → batch1}/crud_access_regulation/broadcast_controls_access_spec.rb +1 -1
  62. data/spec/{synchromesh → batch1}/crud_access_regulation/model_policies_spec.rb +6 -6
  63. data/spec/batch1/misc/access_like_hash_spec.rb +43 -0
  64. data/spec/batch1/misc/while_loading_spec.rb +196 -0
  65. data/spec/{synchromesh → batch1}/policies/regulate_all_broadcasts_spec.rb +12 -12
  66. data/spec/{synchromesh → batch1}/policies/regulate_broadcast_spec.rb +25 -25
  67. data/spec/{synchromesh/integration → batch2}/authorization_spec.rb +8 -7
  68. data/spec/{synchromesh/integration → batch2}/default_scope_spec.rb +2 -2
  69. data/spec/{synchromesh/integration → batch2}/has_many_through_spec.rb +2 -2
  70. data/spec/{synchromesh/integration → batch2}/relationships_spec.rb +3 -3
  71. data/spec/{reactive_record → batch3}/auto_load_itself_spec.rb +1 -1
  72. data/spec/{reactive_record → batch3}/edge_cases_spec.rb +1 -1
  73. data/spec/{reactive_record → batch3}/finder_method_spec.rb +1 -1
  74. data/spec/{reactive_record → batch3}/many_to_many_spec.rb +2 -2
  75. data/spec/{reactive_record → batch3}/pry_rescue_xspec.rb +0 -0
  76. data/{examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css → spec/batch3/readme.txt} +0 -0
  77. data/spec/{reactive_record → batch3}/revert_spec.rb +2 -2
  78. data/spec/{reactive_record → batch3}/save_while_loading_spec.rb +1 -1
  79. data/spec/{reactive_record → batch3}/update_associations_spec.rb +2 -2
  80. data/spec/{reactive_record → batch3}/update_scopes_spec.rb +2 -2
  81. data/spec/{synchromesh/integration → batch4}/saving_during_commit_spec.rb +2 -2
  82. data/spec/{synchromesh/integration → batch4}/scope_spec.rb +30 -2
  83. data/spec/{synchromesh/examples → batch4}/scoped_todos_spec.rb +3 -3
  84. data/spec/{synchromesh/integration → batch4}/synchromesh_spec.rb +2 -2
  85. data/spec/{synchromesh/examples → examples}/dictionary.rb +2 -2
  86. data/spec/{synchromesh/examples → examples}/dictionary_with_client_scopes.rb +2 -2
  87. data/spec/{synchromesh/examples → examples}/random_examples.rb +1 -1
  88. data/spec/{reactive_record/play.rb → play_ground.rb} +0 -0
  89. data/spec/{reactive_record/factory.rb → reactive_record_factory.rb} +0 -0
  90. data/spec/spec_helper.rb +3 -2
  91. data/spec/test_app/Gemfile +8 -3
  92. data/spec/test_app/Gemfile.lock +114 -64
  93. data/spec/test_app/app/views/components.rb +1 -2
  94. data/spec/test_app/config/application.rb +2 -0
  95. data/spec/test_app/config/routes.rb +1 -1
  96. data/spec/{synchromesh/integration/test_components.rb → test_components.rb} +0 -0
  97. metadata +144 -137
  98. data/app/controllers/reactive_record/application_controller.rb +0 -4
  99. data/app/controllers/reactive_record/reactive_record_controller.rb +0 -49
  100. data/config/routes.rb +0 -7
  101. data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js +0 -50
  102. data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js.gz +0 -0
  103. data/examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css.gz +0 -0
  104. data/examples/action-cable-production-mode/tmp/.keep +0 -0
  105. data/examples/action-cable/log/.keep +0 -0
  106. data/examples/action-cable/tmp/.keep +0 -0
  107. data/examples/pusher-fake/log/.keep +0 -0
  108. data/examples/pusher-fake/tmp/.keep +0 -0
  109. data/examples/pusher/log/.keep +0 -0
  110. data/examples/pusher/tmp/.keep +0 -0
  111. data/examples/simple-poller/log/.keep +0 -0
  112. data/examples/simple-poller/tmp/.keep +0 -0
  113. data/examples/word-game/log/.keep +0 -0
  114. data/examples/word-game/tmp/.keep +0 -0
  115. data/examples/words/log/.keep +0 -0
  116. data/examples/words/tmp/.keep +0 -0
  117. data/lib/reactive_record/version.rb +0 -3
  118. data/lib/sources/hyper-mesh/pusher.js +0 -98
  119. data/lib/synchromesh/action_cable.rb +0 -39
  120. data/lib/synchromesh/client_drivers.rb +0 -357
  121. data/lib/synchromesh/configuration.rb +0 -40
  122. data/lib/synchromesh/connection.rb +0 -170
  123. data/lib/synchromesh/policy.rb +0 -504
  124. data/lib/synchromesh/synchromesh.rb +0 -159
  125. data/lib/synchromesh/synchromesh_controller.rb +0 -162
  126. data/reactive_record_test_app/README.rdoc +0 -261
  127. data/reactive_record_test_app/app/assets/javascripts/components/another_component.rb +0 -24
  128. data/reactive_record_test_app/app/assets/javascripts/components/empty_component.rb +0 -6
  129. data/reactive_record_test_app/app/assets/javascripts/components/todo_item_component.js.rb +0 -16
  130. data/reactive_record_test_app/app/assets/javascripts/components/todos_component.js.rb +0 -42
  131. data/reactive_record_test_app/app/assets/javascripts/components/todos_main_component.rb +0 -49
  132. data/reactive_record_test_app/app/assets/javascripts/react_js_test_only.js +0 -21618
  133. data/reactive_record_test_app/app/assets/javascripts/spec/reactive_record_xspec.js.rb +0 -42
  134. data/reactive_record_test_app/app/controllers/test_controller.rb +0 -7
  135. data/reactive_record_test_app/app/mailers/.gitkeep +0 -0
  136. data/reactive_record_test_app/app/models/models.rb +0 -1
  137. data/reactive_record_test_app/app/policies/application_policy.rb +0 -5
  138. data/reactive_record_test_app/app/views/components.rb +0 -4
  139. data/reactive_record_test_app/app/views/components/test.rb +0 -18
  140. data/reactive_record_test_app/app/views/home/index.html.erb +0 -1
  141. data/reactive_record_test_app/app/views/layouts/application.html.erb +0 -17
  142. data/reactive_record_test_app/config/environments/production.rb +0 -70
  143. data/reactive_record_test_app/config/environments/test.rb +0 -41
  144. data/spec/synchromesh/integration/transports_spec.rb +0 -308
  145. data/spec/synchromesh/policies/auto_connect_spec.rb +0 -60
  146. data/spec/synchromesh/policies/auto_loader_spec.rb +0 -34
  147. data/spec/synchromesh/policies/policy_methods_spec.rb +0 -85
  148. data/spec/synchromesh/policies/regulate_class_connection_spec.rb +0 -50
  149. data/spec/synchromesh/policies/regulate_instance_connection_spec.rb +0 -66
  150. data/spec/test_app/log/.keep +0 -0
@@ -86,12 +86,12 @@ module ReactiveRecord
86
86
  if value.nil?
87
87
  current_value.attributes[inverse_attr].delete(@ar_instance) unless current_value.nil?
88
88
  else
89
- value.backing_record.push_onto_collection(association.inverse, @ar_instance)
89
+ value.backing_record.push_onto_collection(@model, association.inverse, @ar_instance)
90
90
  end
91
91
  end
92
92
 
93
- def push_onto_collection(association, ar_instance)
94
- attributes[association.attribute] ||= Collection.new(@model, @ar_instance, association)
93
+ def push_onto_collection(model, association, ar_instance)
94
+ attributes[association.attribute] ||= Collection.new(model, @ar_instance, association)
95
95
  attributes[association.attribute] << ar_instance
96
96
  end
97
97
 
@@ -83,12 +83,12 @@ module ReactiveRecord
83
83
  @while_loading_counter = 0
84
84
  end
85
85
 
86
- def get_next_while_loading_counter
86
+ def self.get_next_while_loading_counter
87
87
  @while_loading_counter += 1
88
88
  end
89
89
 
90
- def preload_css(css)
91
- @css_to_preload << css << "\n"
90
+ def self.preload_css(css)
91
+ @css_to_preload += "#{css}\n"
92
92
  end
93
93
 
94
94
  def self.has_observers?
@@ -103,14 +103,14 @@ module ReactiveRecord
103
103
 
104
104
  # I DONT THINK WE USE opal-jquery in this module anymore - require 'opal-jquery' if opal_client?
105
105
 
106
- include React::Component
106
+ include Hyperloop::Component::Mixin
107
107
 
108
108
  param :loading
109
109
  param :loaded_children
110
110
  param :loading_children
111
111
  param :element_type
112
112
  param :element_props
113
- param :display, default: ""
113
+ param :display, default: ''
114
114
 
115
115
  class << self
116
116
 
@@ -158,35 +158,37 @@ module ReactiveRecord
158
158
  before_mount do
159
159
  @uniq_id = WhileLoading.get_next_while_loading_counter
160
160
  WhileLoading.preload_css(
161
- ".reactive_record_while_loading_container_#{@uniq_id} > :nth-child(1n+#{loaded_children.count+1}) {\n"+
161
+ ".reactive_record_while_loading_container_#{@uniq_id} > :nth-child(1n+#{params.loaded_children.count+1}) {\n"+
162
162
  " display: none;\n"+
163
163
  "}\n"
164
164
  )
165
165
  end
166
166
 
167
167
  after_mount do
168
- @waiting_on_resources = loading
168
+ @waiting_on_resources = params.loading
169
169
  WhileLoading.add_style_sheet
170
170
  %x{
171
171
  var node = #{dom_node};
172
- $(node).children(':nth-child(-1n+'+#{loaded_children.count}+')').addClass('reactive_record_show_when_loaded');
173
- $(node).children(':nth-child(1n+'+#{loaded_children.count+1}+')').addClass('reactive_record_show_while_loading');
172
+ $(node).children(':nth-child(-1n+'+#{params.loaded_children.count}+')').addClass('reactive_record_show_when_loaded');
173
+ $(node).children(':nth-child(1n+'+#{params.loaded_children.count+1}+')').addClass('reactive_record_show_while_loading');
174
174
  }
175
175
  end
176
176
 
177
177
  after_update do
178
- @waiting_on_resources = loading
178
+ @waiting_on_resources = params.loading
179
179
  end
180
180
 
181
181
  def render
182
- props = element_props.dup
182
+ props = params.element_props.dup
183
183
  classes = [props[:class], props[:className], "reactive_record_while_loading_container_#{@uniq_id}"].compact.join(" ")
184
184
  props.merge!({
185
185
  "data-reactive_record_while_loading_container_id" => @uniq_id,
186
186
  "data-reactive_record_enclosing_while_loading_container_id" => @uniq_id,
187
187
  class: classes
188
188
  })
189
- React.create_element(element_type, props) { loaded_children + loading_children }
189
+ React.create_element(params.element_type[0], props) do
190
+ params.loaded_children + params.loading_children
191
+ end.tap { |e| e.waiting_on_resources = params.loading }
190
192
  end
191
193
 
192
194
  end
@@ -204,22 +206,28 @@ module React
204
206
  loaded_children = []
205
207
  loaded_children = block.call.dup if block
206
208
 
207
- loading_children = [display]
209
+ if display.respond_to? :as_node
210
+ display = display.as_node
211
+ loading_display_block = lambda { display.render }
212
+ elsif !loading_display_block
213
+ loading_display_block = lambda { display }
214
+ end
208
215
  loading_children = RenderingContext.build do |buffer|
209
216
  result = loading_display_block.call
210
- buffer << result.to_s if result.is_a? String
217
+ result = result.to_s if result.try :acts_as_string?
218
+ result.span.tap { |e| e.waiting_on_resources = RenderingContext.waiting_on_resources } if result.is_a? String
211
219
  buffer.dup
212
- end if loading_display_block
213
- RenderingContext.replace(
214
- self,
215
- React.create_element(
216
- ReactiveRecord::WhileLoading,
217
- loading: waiting_on_resources,
218
- loading_children: loading_children,
219
- loaded_children: loaded_children,
220
- element_type: type,
221
- element_props: properties)
222
- )
220
+ end
221
+
222
+ new_element = React.create_element(
223
+ ReactiveRecord::WhileLoading,
224
+ loading: waiting_on_resources,
225
+ loading_children: loading_children,
226
+ loaded_children: loaded_children,
227
+ element_type: [type],
228
+ element_props: properties)
229
+
230
+ RenderingContext.replace(self, new_element)
223
231
  end
224
232
 
225
233
  def hide_while_loading
@@ -228,77 +236,78 @@ module React
228
236
 
229
237
  end
230
238
 
231
- module Component
239
+ module ::Hyperloop
240
+ class Component
241
+ module Mixin
232
242
 
233
- alias_method :original_component_did_mount, :component_did_mount
243
+ alias_method :original_component_did_mount, :component_did_mount
234
244
 
235
- def component_did_mount(*args)
236
- original_component_did_mount(*args)
237
- reactive_record_link_to_enclosing_while_loading_container
238
- reactive_record_link_set_while_loading_container_class
239
- end
245
+ def component_did_mount(*args)
246
+ original_component_did_mount(*args)
247
+ reactive_record_link_to_enclosing_while_loading_container
248
+ reactive_record_link_set_while_loading_container_class
249
+ end
240
250
 
241
- alias_method :original_component_did_update, :component_did_update
251
+ alias_method :original_component_did_update, :component_did_update
242
252
 
243
- def component_did_update(*args)
244
- original_component_did_update(*args)
245
- reactive_record_link_set_while_loading_container_class
246
- end
253
+ def component_did_update(*args)
254
+ original_component_did_update(*args)
255
+ reactive_record_link_set_while_loading_container_class
256
+ end
247
257
 
248
- def reactive_record_link_to_enclosing_while_loading_container
249
- # Call after any component mounts - attaches the containers loading id to this component
250
- # Fyi, the while_loading container is responsible for setting its own link to itself
251
-
252
- %x{
253
- var node = #{dom_node};
254
- if (!$(node).is('[data-reactive_record_enclosing_while_loading_container_id]')) {
255
- var while_loading_container = $(node).closest('[data-reactive_record_while_loading_container_id]')
256
- if (while_loading_container.length > 0) {
257
- var container_id = $(while_loading_container).attr('data-reactive_record_while_loading_container_id')
258
- $(node).attr('data-reactive_record_enclosing_while_loading_container_id', container_id)
258
+ def reactive_record_link_to_enclosing_while_loading_container
259
+ # Call after any component mounts - attaches the containers loading id to this component
260
+ # Fyi, the while_loading container is responsible for setting its own link to itself
261
+
262
+ %x{
263
+ var node = #{dom_node};
264
+ if (!$(node).is('[data-reactive_record_enclosing_while_loading_container_id]')) {
265
+ var while_loading_container = $(node).closest('[data-reactive_record_while_loading_container_id]')
266
+ if (while_loading_container.length > 0) {
267
+ var container_id = $(while_loading_container).attr('data-reactive_record_while_loading_container_id')
268
+ $(node).attr('data-reactive_record_enclosing_while_loading_container_id', container_id)
269
+ }
270
+ }
259
271
  }
260
- }
261
- }
262
-
263
- end
264
-
265
- def reactive_record_link_set_while_loading_container_class
266
-
267
- %x{
268
-
269
- var node = #{dom_node};
270
- var while_loading_container_id = $(node).attr('data-reactive_record_enclosing_while_loading_container_id');
271
- if (while_loading_container_id) {
272
- var while_loading_container = $('[data-reactive_record_while_loading_container_id='+while_loading_container_id+']');
273
- var loading = (#{waiting_on_resources} == true);
274
- if (loading) {
275
- $(node).addClass('reactive_record_is_loading');
276
- $(node).removeClass('reactive_record_is_loaded');
277
- $(while_loading_container).addClass('reactive_record_is_loading');
278
- $(while_loading_container).removeClass('reactive_record_is_loaded');
272
+ end
279
273
 
280
- } else if (!$(node).hasClass('reactive_record_is_loaded')) {
274
+ def reactive_record_link_set_while_loading_container_class
275
+ %x{
281
276
 
282
- if (!$(node).attr('data-reactive_record_while_loading_container_id')) {
283
- $(node).removeClass('reactive_record_is_loading');
284
- $(node).addClass('reactive_record_is_loaded');
277
+ var node = #{dom_node};
278
+ var wl = #{!self.is_a?(ReactiveRecord::WhileLoading)}
279
+ if (#{!self.is_a?(ReactiveRecord::WhileLoading)} && $(node).is('[data-reactive_record_while_loading_container_id]')) {
280
+ return
285
281
  }
286
- if (!$(while_loading_container).hasClass('reactive_record_is_loaded')) {
287
- var loading_children = $(while_loading_container).
288
- find('[data-reactive_record_enclosing_while_loading_container_id='+while_loading_container_id+'].reactive_record_is_loading')
289
- if (loading_children.length == 0) {
290
- $(while_loading_container).removeClass('reactive_record_is_loading')
291
- $(while_loading_container).addClass('reactive_record_is_loaded')
282
+ var while_loading_container_id = $(node).attr('data-reactive_record_enclosing_while_loading_container_id');
283
+ if (while_loading_container_id) {
284
+ var while_loading_container = $('[data-reactive_record_while_loading_container_id='+while_loading_container_id+']');
285
+ var loading = #{!!waiting_on_resources == true};
286
+ if (loading) {
287
+ $(node).addClass('reactive_record_is_loading');
288
+ $(node).removeClass('reactive_record_is_loaded');
289
+ $(while_loading_container).addClass('reactive_record_is_loading');
290
+ $(while_loading_container).removeClass('reactive_record_is_loaded');
291
+
292
+ } else if (!$(node).hasClass('reactive_record_is_loaded')) {
293
+
294
+ if (!$(node).attr('data-reactive_record_while_loading_container_id')) {
295
+ $(node).removeClass('reactive_record_is_loading');
296
+ $(node).addClass('reactive_record_is_loaded');
297
+ }
298
+ if (!$(while_loading_container).hasClass('reactive_record_is_loaded')) {
299
+ var loading_children = $(while_loading_container).
300
+ find('[data-reactive_record_enclosing_while_loading_container_id='+while_loading_container_id+'].reactive_record_is_loading')
301
+ if (loading_children.length == 0) {
302
+ $(while_loading_container).removeClass('reactive_record_is_loading')
303
+ $(while_loading_container).addClass('reactive_record_is_loaded')
304
+ }
305
+ }
292
306
  }
293
307
  }
294
-
295
308
  }
296
-
297
- }
298
- }
299
-
309
+ end
310
+ end
300
311
  end
301
-
302
312
  end if RUBY_ENGINE == 'opal'
303
-
304
313
  end
@@ -0,0 +1,183 @@
1
+ module ReactiveRecord
2
+ class Broadcast
3
+
4
+ def self.after_commit(operation, model)
5
+ Hyperloop::InternalPolicy.regulate_broadcast(model) do |data|
6
+ if !Hyperloop.on_server? && Hyperloop::Connection.root_path
7
+ send_to_server(operation, data)
8
+ else
9
+ SendPacket.run(data, operation: operation)
10
+ end
11
+ end
12
+ rescue ActiveRecord::StatementInvalid => e
13
+ raise e unless e.message == "Could not find table 'hyperloop_connections'"
14
+ end unless RUBY_ENGINE == 'opal'
15
+
16
+ def self.send_to_server(operation, data)
17
+ salt = SecureRandom.hex
18
+ authorization = Hyperloop.authorization(salt, data[:channel], data[:broadcast_id])
19
+ raise 'no server running' unless Hyperloop::Connection.root_path
20
+ SendPacket.remote(
21
+ Hyperloop::Connection.root_path,
22
+ data,
23
+ operation: operation,
24
+ salt: salt,
25
+ authorization: authorization
26
+ )
27
+ end unless RUBY_ENGINE == 'opal'
28
+
29
+ class SendPacket < Hyperloop::ServerOp
30
+ param authorization: nil, nils: true
31
+ param salt: nil
32
+ param :operation
33
+ param :broadcast_id
34
+ param :channel
35
+ param :channels
36
+ param :klass
37
+ param :record
38
+ param :operation
39
+ param :previous_changes
40
+
41
+ unless RUBY_ENGINE == 'opal'
42
+ validate do
43
+ params.authorization.nil? ||
44
+ Hyperloop.authorization(
45
+ params.salt, params.channel, params.broadcast_id
46
+ ) == params.authorization
47
+ end
48
+ dispatch_to { params.channel }
49
+ end
50
+ end
51
+
52
+ SendPacket.on_dispatch do |params|
53
+ in_transit[params.broadcast_id].receive(params) do |broadcast|
54
+ if params.operation == :destroy
55
+ ReactiveRecord::Collection.sync_scopes broadcast
56
+ else
57
+ ReactiveRecord::Base.when_not_saving(broadcast.klass) do
58
+ ReactiveRecord::Collection.sync_scopes broadcast
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def self.to_self(record, data = {})
65
+ # simulate incoming packet after a local save
66
+ operation = if record.new?
67
+ :create
68
+ elsif record.destroyed?
69
+ :destroy
70
+ else
71
+ :change
72
+ end
73
+ dummy_broadcast = new.local(operation, record, data)
74
+ record.backing_record.sync! data unless operation == :destroy
75
+ ReactiveRecord::Collection.sync_scopes dummy_broadcast
76
+ end
77
+
78
+ def record_with_current_values
79
+ ReactiveRecord::Base.load_data do
80
+ backing_record = @backing_record || klass.find(record[:id]).backing_record
81
+ if destroyed?
82
+ backing_record.ar_instance
83
+ else
84
+ merge_current_values(backing_record)
85
+ end
86
+ end
87
+ end
88
+
89
+ def record_with_new_values
90
+ klass._react_param_conversion(record).tap do |ar_instance|
91
+ if destroyed?
92
+ ar_instance.backing_record.destroy_associations
93
+ elsif new?
94
+ ar_instance.backing_record.initialize_collections
95
+ end
96
+ end
97
+ end
98
+
99
+ def new?
100
+ @is_new
101
+ end
102
+
103
+ def destroyed?
104
+ @destroyed
105
+ end
106
+
107
+ def klass
108
+ Object.const_get(@klass)
109
+ end
110
+
111
+ def to_s
112
+ "klass: #{klass} record: #{record} new?: #{new?} destroyed?: #{destroyed?}"
113
+ end
114
+
115
+ # private
116
+
117
+ attr_reader :record
118
+
119
+ def self.open_channels
120
+ @open_channels ||= Set.new
121
+ end
122
+
123
+ def self.in_transit
124
+ @in_transit ||= Hash.new { |h, k| h[k] = new(k) }
125
+ end
126
+
127
+ def initialize(id)
128
+ @id = id
129
+ @received = Set.new
130
+ @record = {}
131
+ @previous_changes = {}
132
+ end
133
+
134
+ def local(operation, record, data)
135
+ @destroyed = operation == :destroy
136
+ @is_new = operation == :create
137
+ @klass = record.class.name
138
+ @record = data
139
+ record.backing_record.destroyed = false
140
+ @record.merge!(id: record.id) if record.id
141
+ record.backing_record.destroyed = @destroyed
142
+ @backing_record = record.backing_record
143
+ attributes = record.backing_record.attributes
144
+ data.each do |k, v|
145
+ next if klass.reflect_on_association(k) || attributes[k] == v
146
+ @previous_changes[k] = [attributes[k], v]
147
+ end
148
+ self
149
+ end
150
+
151
+ def receive(params)
152
+ @destroyed = params.operation == :destroy
153
+ @is_new = params.operation == :create
154
+ @channels ||= Hyperloop::IncomingBroadcast.open_channels.intersection params.channels
155
+ #raise 'synchromesh security violation' unless @channels.include? params.channels
156
+ @received << params.channel
157
+ @klass ||= params.klass
158
+ @record.merge! params.record
159
+ @previous_changes.merge! params.previous_changes
160
+ @backing_record = ReactiveRecord::Base.exists?(klass, params.record[:id])
161
+ yield complete! if @channels == @received
162
+ end
163
+
164
+ def complete!
165
+ self.class.in_transit.delete @id
166
+ end
167
+
168
+ def merge_current_values(br)
169
+ current_values = Hash[*@previous_changes.collect do |attr, values|
170
+ value = attr == :id ? record[:id] : values.first
171
+ if br.attributes.key?(attr) &&
172
+ br.attributes[attr] != br.convert(attr, value) &&
173
+ br.attributes[attr] != br.convert(attr, values.last)
174
+ puts "warning #{attr} has changed locally - will force a reload.\n"\
175
+ "local value: #{br.attributes[attr]} remote value: #{br.convert(attr, value)}->#{br.convert(attr, values.last)}"
176
+ return nil
177
+ end
178
+ [attr, value]
179
+ end.compact.flatten].merge(br.attributes)
180
+ klass._react_param_conversion(current_values)
181
+ end
182
+ end
183
+ end