hyper-mesh 0.5.3 → 0.5.4

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