Skip to content
This repository has been archived by the owner on Oct 19, 2018. It is now read-only.

Commit

Permalink
closes #95 #94 #93
Browse files Browse the repository at this point in the history
  • Loading branch information
catmando committed May 4, 2018
1 parent dfae482 commit 8147c1f
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 45 deletions.
4 changes: 2 additions & 2 deletions lib/active_record_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,11 @@ def __secure_remote_access_to_find_by(_self, _acting_user, *args)

%i[belongs_to has_one].each do |macro|
alias_method :"pre_syncromesh_#{macro}", macro
define_method(macro) do |name, scope = nil, opts = {}, &block|
define_method(macro) do |name, *aargs, &block|
define_method(:"__secure_remote_access_to_#{name}") do |this, _acting_user, *args|
this.send(name, *args)
end
send(:"pre_syncromesh_#{macro}", name, scope, opts, &block)
send(:"pre_syncromesh_#{macro}", name, *aargs, &block)
end
end
end
Expand Down
1 change: 0 additions & 1 deletion lib/reactive_record/active_record/instance_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ def initialize(hash = {})
h.each do |attribute, value|
next if attribute == primary_key
@ar_instance[attribute] = value
puts "#{self.class}.new : changed_attributes << #{attribute}"
changed_attributes << attribute
end
end
Expand Down
39 changes: 22 additions & 17 deletions lib/reactive_record/active_record/reactive_record/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def saving!
end

def errors!(hash)
@saving = false
notify_waiting_for_save
errors.clear && return unless hash
hash.each do |attribute, messages|
messages.each do |message|
Expand All @@ -303,7 +303,7 @@ def errors!(hash)
end

def saved!(save_only = nil) # sets saving to false AND notifies
@saving = false
notify_waiting_for_save
return self if save_only
if errors.empty?
React::State.set_state(self, self, :saved)
Expand All @@ -313,6 +313,26 @@ def saved!(save_only = nil) # sets saving to false AND notifies
self
end

def self.when_not_saving(model, &block)
if @records[model].detect(&:saving?)
wait_for_save(model, &block)
else
yield model
end
end

def notify_waiting_for_save
@saving = false
self.class.notify_waiting_for_save(model)
end

def self.notify_waiting_for_save(model)
waiters = waiting_for_save(model)
return if waiters.empty? || @records[model].detect(&:saving?)
waiters.each { |waiter| waiter.call model }
clear_waiting_for_save(model)
end

def saving?
React::State.get_state(self, self)
@saving
Expand Down Expand Up @@ -356,21 +376,6 @@ def add_to_outer_scopes(item)
@outer_scopes << item
end

# when_not_saving will wait until reactive-record is not saving a model.
# Currently there is no easy way to do this without polling.
def when_not_saving(model)
if @records[model].detect(&:saving?)
poller = every(0.1) do
unless @records[model].detect(&:saving?)
poller.stop
yield model
end
end
else
yield model
end
end

# While evaluating scopes we want to catch any requests
# to the server. Once we catch any requests to the server
# then all the further scopes in that chain will be made
Expand Down
13 changes: 13 additions & 0 deletions lib/reactive_record/active_record/reactive_record/lookup_tables.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,25 @@ def initialize_lookup_tables
@records_by_vector = `{}`
@records_by_object_id = `{}`
@class_scopes = Hash.new { |hash, key| hash[key] = {} }
@waiting_for_save = Hash.new { |hash, key| hash[key] = [] }
end

def class_scopes(model)
@class_scopes[model.base_class]
end

def waiting_for_save(model)
@waiting_for_save[model]
end

def wait_for_save(model, &block)
@waiting_for_save[model] << block
end

def clear_waiting_for_save(model)
@waiting_for_save[model] = []
end

def lookup_by_object_id(object_id)
`#{@records_by_object_id}[#{object_id}]`.ar_instance
end
Expand Down
14 changes: 7 additions & 7 deletions lib/reactive_record/broadcast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def self.send_to_server(operation, data)
operation: operation,
salt: salt,
authorization: authorization
)
).tap { |p| raise p.error if p.rejected? }
end unless RUBY_ENGINE == 'opal'

class SendPacket < Hyperloop::ServerOp
Expand Down Expand Up @@ -54,9 +54,7 @@ class SendPacket < Hyperloop::ServerOp
if params.operation == :destroy
ReactiveRecord::Collection.sync_scopes broadcast
else
ReactiveRecord::Base.when_not_saving(broadcast.klass) do
ReactiveRecord::Collection.sync_scopes broadcast.process_previous_changes
end
ReactiveRecord::Collection.sync_scopes broadcast.process_previous_changes
end
end
end
Expand Down Expand Up @@ -151,14 +149,16 @@ def local(operation, record, data)

def receive(params)
@destroyed = params.operation == :destroy
@is_new = params.operation == :create
@channels ||= Hyperloop::IncomingBroadcast.open_channels.intersection params.channels
@received << params.channel
@klass ||= params.klass
@record.merge! params.record
@previous_changes.merge! params.previous_changes
@backing_record = ReactiveRecord::Base.exists?(klass, params.record[:id])
yield complete! if @channels == @received
ReactiveRecord::Base.when_not_saving(klass) do
@backing_record = ReactiveRecord::Base.exists?(klass, params.record[:id])
@is_new = params.operation == :create && !@backing_record
yield complete! if @channels == @received
end
end

def complete!
Expand Down
7 changes: 4 additions & 3 deletions lib/reactive_record/permissions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@ class << self
alias belongs_to_without_reactive_record_add_is_method belongs_to

def belongs_to(attr_name, scope = nil, options = {})
define_method "#{attr_name}_is?".to_sym do |model|
send(options[:foreign_key] || "#{attr_name}_id") == model.id
belongs_to_without_reactive_record_add_is_method(attr_name, scope, options).tap do
define_method "#{attr_name}_is?".to_sym do |model|
self.class.reflections[attr_name].foreign_key == model.id
end
end
belongs_to_without_reactive_record_add_is_method(attr_name, scope, options)
end
end

Expand Down
2 changes: 1 addition & 1 deletion spec/batch1/policies/regulate_all_broadcasts_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def attribute_names
it "will broadcast to a single channel" do
stub_const "ApplicationPolicy", Class.new
ApplicationPolicy.class_eval do
regulate_all_broadcasts do | policy |
regulate_all_broadcasts do |policy|
policy.send_all
end
end
Expand Down
29 changes: 22 additions & 7 deletions spec/batch3/edge_cases_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
TestApplicationPolicy.class_eval do
always_allow_connection
regulate_all_broadcasts { |policy| policy.send_all }
allow_change(to: :all, on: [:create, :update, :destroy]) { true }
end
size_window(:small, :portrait)
end
Expand All @@ -51,21 +52,35 @@
end.to contain_exactly('0', '1', '2', '4')
end

it "does not double count local saves" do
expect_promise do
HyperMesh.load do
Todo.count
end.then do |count|
Todo.create(title: 'test todo')
end.then do
Todo.count
end
end.to eq(1)
end

xit "fetches data during prerendering" do # server_only not working!
# test for fix in prerendering fetch which was causing security violations
5.times do |i|
FactoryBot.create(:todo, title: "Todo #{i}")
end
mount "TestComponent2", {}, render_on: :server_only do
class TestComponent2 < React::Component::Base
mount "TestComponent77", {}, render_on: :both do
class TestComponent77 < Hyperloop::Component
render(UL) do
Todo.each do |todo|
# try Todo.find_by_title ... as well
LI { todo.title }
end
puts "Todo defined? #{defined? Todo} class? #{Todo.class}"
LI { "fred" }
#Todo.each do |todo|
# # try Todo.find_by_title ... as well
# LI { todo.title }
# end
end
end
end

binding.pry
end
end
26 changes: 22 additions & 4 deletions spec/support/component_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,12 @@ def build_test_url_for(controller)
@component_name = test_params[0]
@component_params = test_params[1]
render_params = test_params[2]
render_on = render_params.delete(:render_on) || :both
render_on = render_params.delete(:render_on) || :client_only
mock_time = render_params.delete(:mock_time)
style_sheet = render_params.delete(:style_sheet)
javascript = render_params.delete(:javascript)
code = render_params.delete(:code)
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
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
page = "<script type='text/javascript'>\n#{TOP_LEVEL_COMPONENT_PATCH}\n</script>\n#{page}"

if code
Expand Down Expand Up @@ -316,11 +316,29 @@ def render; end
opts[:code] = Opal.compile(block_with_helpers)
end
component_name ||= 'React::Component::HyperTestDummy'
Rails.cache.write(test_url, [component_name, params, opts])
::Rails.cache.write(test_url, [component_name, params, opts])

# this code copied from latest hyper-spec
test_code_key = "hyper_spec_prerender_test_code.js"
#::Rails.configuration.react.server_renderer_options[:files] ||= ['hyperloop-prerender-loader.js']
@@original_server_render_files ||= ::Rails.configuration.react.server_renderer_options[:files] || [] #'hyperloop-prerender-loader.js']
if opts[:render_on] == :both || opts[:render_on] == :server_only
unless opts[:code].blank?
::Rails.cache.write(test_code_key, opts[:code])
::Rails.configuration.react.server_renderer_options[:files] = @@original_server_render_files + [test_code_key]
::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
else
::Rails.cache.delete(test_code_key)
::Rails.configuration.react.server_renderer_options[:files] = @@original_server_render_files
::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
end
end
# end of copied code

visit test_url
wait_for_ajax unless opts[:no_wait]
page.instance_variable_set("@hyper_spec_mounted", true)
end
end

[:callback_history_for, :last_callback_for, :clear_callback_history_for, :event_history_for, :last_event_for, :clear_event_history_for].each do |method|
define_method(method) { |event_name| evaluate_script("Opal.React.TopLevelRailsComponent.$#{method}('#{event_name}')") }
Expand Down
4 changes: 3 additions & 1 deletion spec/test_app/app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//= require 'components'
//= require 'react'
//= require 'react_ujs'
//= require 'components'
//= require action_cable
//= require 'hyperloop/pusher'
Opal.load('components');
13 changes: 11 additions & 2 deletions spec/test_app/app/views/components.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
require 'opal'
require 'react/react-source-browser'
require 'hyper-component'
if React::IsomorphicHelpers.on_opal_client?
require 'browser'
require 'browser/delay'
require 'browser/interval'
require 'hyperloop/pusher'
#require 'hyperloop/pusher'
end
require 'hyper-mesh'
require '_react_public_models'
require_tree './components'


# require 'opal'
# require 'promise'
# require 'hyper-react'
# if React::IsomorphicHelpers.on_opal_client?
# require 'browser'
# require 'browser/delay'
# end
# require_tree './components'
6 changes: 6 additions & 0 deletions spec/test_app/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ class Application < Rails::Application

config.assets.cache_store = :null_store
config.hyperloop.auto_config = false

config.react.server_renderer_options = {
files: ['server_rendering.js']
}
config.react.server_renderer_directories = ['/app/assets/javascripts']

# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
Expand Down
9 changes: 9 additions & 0 deletions spec/test_app/config/initializers/synchromesh.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@
# config.channel_prefix = "synchromesh"
# config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
# end
class MiniRacer::Context
alias original_eval eval
def eval(str, options=nil)
original_eval str, options
rescue Exception => e
File.write('react_prerendering_src.js', str) rescue nil
raise e
end
end

0 comments on commit 8147c1f

Please sign in to comment.