Skip to content

Commit 8147c1f

Browse files
committed
1 parent dfae482 commit 8147c1f

File tree

13 files changed

+122
-45
lines changed

13 files changed

+122
-45
lines changed

lib/active_record_base.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,11 @@ def __secure_remote_access_to_find_by(_self, _acting_user, *args)
265265

266266
%i[belongs_to has_one].each do |macro|
267267
alias_method :"pre_syncromesh_#{macro}", macro
268-
define_method(macro) do |name, scope = nil, opts = {}, &block|
268+
define_method(macro) do |name, *aargs, &block|
269269
define_method(:"__secure_remote_access_to_#{name}") do |this, _acting_user, *args|
270270
this.send(name, *args)
271271
end
272-
send(:"pre_syncromesh_#{macro}", name, scope, opts, &block)
272+
send(:"pre_syncromesh_#{macro}", name, *aargs, &block)
273273
end
274274
end
275275
end

lib/reactive_record/active_record/instance_methods.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ def initialize(hash = {})
4040
h.each do |attribute, value|
4141
next if attribute == primary_key
4242
@ar_instance[attribute] = value
43-
puts "#{self.class}.new : changed_attributes << #{attribute}"
4443
changed_attributes << attribute
4544
end
4645
end

lib/reactive_record/active_record/reactive_record/base.rb

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ def saving!
293293
end
294294

295295
def errors!(hash)
296-
@saving = false
296+
notify_waiting_for_save
297297
errors.clear && return unless hash
298298
hash.each do |attribute, messages|
299299
messages.each do |message|
@@ -303,7 +303,7 @@ def errors!(hash)
303303
end
304304

305305
def saved!(save_only = nil) # sets saving to false AND notifies
306-
@saving = false
306+
notify_waiting_for_save
307307
return self if save_only
308308
if errors.empty?
309309
React::State.set_state(self, self, :saved)
@@ -313,6 +313,26 @@ def saved!(save_only = nil) # sets saving to false AND notifies
313313
self
314314
end
315315

316+
def self.when_not_saving(model, &block)
317+
if @records[model].detect(&:saving?)
318+
wait_for_save(model, &block)
319+
else
320+
yield model
321+
end
322+
end
323+
324+
def notify_waiting_for_save
325+
@saving = false
326+
self.class.notify_waiting_for_save(model)
327+
end
328+
329+
def self.notify_waiting_for_save(model)
330+
waiters = waiting_for_save(model)
331+
return if waiters.empty? || @records[model].detect(&:saving?)
332+
waiters.each { |waiter| waiter.call model }
333+
clear_waiting_for_save(model)
334+
end
335+
316336
def saving?
317337
React::State.get_state(self, self)
318338
@saving
@@ -356,21 +376,6 @@ def add_to_outer_scopes(item)
356376
@outer_scopes << item
357377
end
358378

359-
# when_not_saving will wait until reactive-record is not saving a model.
360-
# Currently there is no easy way to do this without polling.
361-
def when_not_saving(model)
362-
if @records[model].detect(&:saving?)
363-
poller = every(0.1) do
364-
unless @records[model].detect(&:saving?)
365-
poller.stop
366-
yield model
367-
end
368-
end
369-
else
370-
yield model
371-
end
372-
end
373-
374379
# While evaluating scopes we want to catch any requests
375380
# to the server. Once we catch any requests to the server
376381
# then all the further scopes in that chain will be made

lib/reactive_record/active_record/reactive_record/lookup_tables.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,25 @@ def initialize_lookup_tables
66
@records_by_vector = `{}`
77
@records_by_object_id = `{}`
88
@class_scopes = Hash.new { |hash, key| hash[key] = {} }
9+
@waiting_for_save = Hash.new { |hash, key| hash[key] = [] }
910
end
1011

1112
def class_scopes(model)
1213
@class_scopes[model.base_class]
1314
end
1415

16+
def waiting_for_save(model)
17+
@waiting_for_save[model]
18+
end
19+
20+
def wait_for_save(model, &block)
21+
@waiting_for_save[model] << block
22+
end
23+
24+
def clear_waiting_for_save(model)
25+
@waiting_for_save[model] = []
26+
end
27+
1528
def lookup_by_object_id(object_id)
1629
`#{@records_by_object_id}[#{object_id}]`.ar_instance
1730
end

lib/reactive_record/broadcast.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def self.send_to_server(operation, data)
2323
operation: operation,
2424
salt: salt,
2525
authorization: authorization
26-
)
26+
).tap { |p| raise p.error if p.rejected? }
2727
end unless RUBY_ENGINE == 'opal'
2828

2929
class SendPacket < Hyperloop::ServerOp
@@ -54,9 +54,7 @@ class SendPacket < Hyperloop::ServerOp
5454
if params.operation == :destroy
5555
ReactiveRecord::Collection.sync_scopes broadcast
5656
else
57-
ReactiveRecord::Base.when_not_saving(broadcast.klass) do
58-
ReactiveRecord::Collection.sync_scopes broadcast.process_previous_changes
59-
end
57+
ReactiveRecord::Collection.sync_scopes broadcast.process_previous_changes
6058
end
6159
end
6260
end
@@ -151,14 +149,16 @@ def local(operation, record, data)
151149

152150
def receive(params)
153151
@destroyed = params.operation == :destroy
154-
@is_new = params.operation == :create
155152
@channels ||= Hyperloop::IncomingBroadcast.open_channels.intersection params.channels
156153
@received << params.channel
157154
@klass ||= params.klass
158155
@record.merge! params.record
159156
@previous_changes.merge! params.previous_changes
160-
@backing_record = ReactiveRecord::Base.exists?(klass, params.record[:id])
161-
yield complete! if @channels == @received
157+
ReactiveRecord::Base.when_not_saving(klass) do
158+
@backing_record = ReactiveRecord::Base.exists?(klass, params.record[:id])
159+
@is_new = params.operation == :create && !@backing_record
160+
yield complete! if @channels == @received
161+
end
162162
end
163163

164164
def complete!

lib/reactive_record/permissions.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,11 @@ class << self
8888
alias belongs_to_without_reactive_record_add_is_method belongs_to
8989

9090
def belongs_to(attr_name, scope = nil, options = {})
91-
define_method "#{attr_name}_is?".to_sym do |model|
92-
send(options[:foreign_key] || "#{attr_name}_id") == model.id
91+
belongs_to_without_reactive_record_add_is_method(attr_name, scope, options).tap do
92+
define_method "#{attr_name}_is?".to_sym do |model|
93+
self.class.reflections[attr_name].foreign_key == model.id
94+
end
9395
end
94-
belongs_to_without_reactive_record_add_is_method(attr_name, scope, options)
9596
end
9697
end
9798

spec/batch1/policies/regulate_all_broadcasts_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def attribute_names
3838
it "will broadcast to a single channel" do
3939
stub_const "ApplicationPolicy", Class.new
4040
ApplicationPolicy.class_eval do
41-
regulate_all_broadcasts do | policy |
41+
regulate_all_broadcasts do |policy|
4242
policy.send_all
4343
end
4444
end

spec/batch3/edge_cases_spec.rb

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
TestApplicationPolicy.class_eval do
3434
always_allow_connection
3535
regulate_all_broadcasts { |policy| policy.send_all }
36+
allow_change(to: :all, on: [:create, :update, :destroy]) { true }
3637
end
3738
size_window(:small, :portrait)
3839
end
@@ -51,21 +52,35 @@
5152
end.to contain_exactly('0', '1', '2', '4')
5253
end
5354

55+
it "does not double count local saves" do
56+
expect_promise do
57+
HyperMesh.load do
58+
Todo.count
59+
end.then do |count|
60+
Todo.create(title: 'test todo')
61+
end.then do
62+
Todo.count
63+
end
64+
end.to eq(1)
65+
end
66+
5467
xit "fetches data during prerendering" do # server_only not working!
5568
# test for fix in prerendering fetch which was causing security violations
5669
5.times do |i|
5770
FactoryBot.create(:todo, title: "Todo #{i}")
5871
end
59-
mount "TestComponent2", {}, render_on: :server_only do
60-
class TestComponent2 < React::Component::Base
72+
mount "TestComponent77", {}, render_on: :both do
73+
class TestComponent77 < Hyperloop::Component
6174
render(UL) do
62-
Todo.each do |todo|
63-
# try Todo.find_by_title ... as well
64-
LI { todo.title }
65-
end
75+
puts "Todo defined? #{defined? Todo} class? #{Todo.class}"
76+
LI { "fred" }
77+
#Todo.each do |todo|
78+
# # try Todo.find_by_title ... as well
79+
# LI { todo.title }
80+
# end
6681
end
6782
end
6883
end
69-
84+
binding.pry
7085
end
7186
end

spec/support/component_helpers.rb

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,12 @@ def build_test_url_for(controller)
124124
@component_name = test_params[0]
125125
@component_params = test_params[1]
126126
render_params = test_params[2]
127-
render_on = render_params.delete(:render_on) || :both
127+
render_on = render_params.delete(:render_on) || :client_only
128128
mock_time = render_params.delete(:mock_time)
129129
style_sheet = render_params.delete(:style_sheet)
130130
javascript = render_params.delete(:javascript)
131131
code = render_params.delete(:code)
132-
page = "<%= react_component @component_name, @component_params, { prerender: false } %>" # false should be: "#{render_on != :client_only} } %>" but its not working in the gem testing harness
132+
page = "<%= react_component @component_name, @component_params, { prerender: #{render_on != :client_only} } %>" # false should be: "#{render_on != :client_only} } %>" but its not working in the gem testing harness
133133
page = "<script type='text/javascript'>\n#{TOP_LEVEL_COMPONENT_PATCH}\n</script>\n#{page}"
134134

135135
if code
@@ -316,11 +316,29 @@ def render; end
316316
opts[:code] = Opal.compile(block_with_helpers)
317317
end
318318
component_name ||= 'React::Component::HyperTestDummy'
319-
Rails.cache.write(test_url, [component_name, params, opts])
319+
::Rails.cache.write(test_url, [component_name, params, opts])
320+
321+
# this code copied from latest hyper-spec
322+
test_code_key = "hyper_spec_prerender_test_code.js"
323+
#::Rails.configuration.react.server_renderer_options[:files] ||= ['hyperloop-prerender-loader.js']
324+
@@original_server_render_files ||= ::Rails.configuration.react.server_renderer_options[:files] || [] #'hyperloop-prerender-loader.js']
325+
if opts[:render_on] == :both || opts[:render_on] == :server_only
326+
unless opts[:code].blank?
327+
::Rails.cache.write(test_code_key, opts[:code])
328+
::Rails.configuration.react.server_renderer_options[:files] = @@original_server_render_files + [test_code_key]
329+
::React::ServerRendering.reset_pool # make sure contexts are reloaded so they dont use code from cache, as the rails filewatcher doesnt look for cache changes
330+
else
331+
::Rails.cache.delete(test_code_key)
332+
::Rails.configuration.react.server_renderer_options[:files] = @@original_server_render_files
333+
::React::ServerRendering.reset_pool # make sure contexts are reloaded so they dont use code from cache, as the rails filewatcher doesnt look for cache changes
334+
end
335+
end
336+
# end of copied code
337+
320338
visit test_url
321339
wait_for_ajax unless opts[:no_wait]
322340
page.instance_variable_set("@hyper_spec_mounted", true)
323-
end
341+
end
324342

325343
[:callback_history_for, :last_callback_for, :clear_callback_history_for, :event_history_for, :last_event_for, :clear_event_history_for].each do |method|
326344
define_method(method) { |event_name| evaluate_script("Opal.React.TopLevelRailsComponent.$#{method}('#{event_name}')") }
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//= require 'components'
1+
//= require 'react'
22
//= require 'react_ujs'
3+
//= require 'components'
34
//= require action_cable
5+
//= require 'hyperloop/pusher'
46
Opal.load('components');

0 commit comments

Comments
 (0)