From 6c885363cf35e293ffc149f2848f531e0cef9dd2 Mon Sep 17 00:00:00 2001 From: catmando Date: Wed, 7 Apr 2021 01:34:55 -0400 Subject: [PATCH] closes #351 and #403 --- docs/hyper-state/README.md | 3 +- ruby/hyper-model/lib/active_record_base.rb | 30 ++++++++++----- .../active_record/class_methods.rb | 12 ++++++ .../misc}/server_method_spec.rb | 38 +++++++++++++++++++ ruby/hyper-spec/lib/hyper-spec/helpers.rb | 6 ++- ruby/hyper-spec/spec/hyper_spec.rb | 14 +++++++ 6 files changed, 92 insertions(+), 11 deletions(-) rename ruby/hyper-model/spec/{batch6 => batch1/misc}/server_method_spec.rb (65%) diff --git a/docs/hyper-state/README.md b/docs/hyper-state/README.md index 4d044c6bb..0f3131d14 100644 --- a/docs/hyper-state/README.md +++ b/docs/hyper-state/README.md @@ -348,6 +348,7 @@ class DisplaySquare .on(:click) { Game.handle_click(id) } end end + class DisplayBoard < HyperComponent render(DIV) do (0..6).step(3) do |row| @@ -503,5 +504,5 @@ a component **unless** the state will be accessed outside the component. Howev would be better off to move the state into a separate store. > In addition components also act as the **Observers** in the system. What this means is -that current component that is running its render method is recording all stores that call `observe`, when +that the current component that is running its render method is recording all stores that call `observe`, when a store mutates, then all the components that recorded observations will be rerendered. diff --git a/ruby/hyper-model/lib/active_record_base.rb b/ruby/hyper-model/lib/active_record_base.rb index def9de439..151dd58db 100644 --- a/ruby/hyper-model/lib/active_record_base.rb +++ b/ruby/hyper-model/lib/active_record_base.rb @@ -129,19 +129,31 @@ def finder_method(name, &block) end end + def allow_remote_access_to(*methods, &block) + methods = methods.collect { |meth| meth.is_a?(Hash) ? meth.keys : meth }.flatten + methods.each do |name| + define_method("__secure_remote_access_to_#{name}") do |_self, acting_user, *args| + begin + old = self.acting_user + self.acting_user = acting_user + allowed = !block || instance_eval(&block) rescue nil + return send(name, *args) if allowed + + Hyperstack::InternalPolicy.raise_operation_access_violation( + :illegal_remote_access, "Access denied to #{name}" + ) + ensure + self.acting_user = old + end + end + end + end + def server_method(name, _opts = {}, &block) # callable from the server internally define_method(name, &block) # callable remotely from the client - define_method("__secure_remote_access_to_#{name}") do |_self, acting_user, *args| - begin - old = self.acting_user - self.acting_user = acting_user - send(name, *args) - ensure - self.acting_user = old - end - end + allow_remote_access_to(name) end # relationships (and scopes) are regulated using a tri-state system. Each diff --git a/ruby/hyper-model/lib/reactive_record/active_record/class_methods.rb b/ruby/hyper-model/lib/reactive_record/active_record/class_methods.rb index 4e7a75b79..079313938 100644 --- a/ruby/hyper-model/lib/reactive_record/active_record/class_methods.rb +++ b/ruby/hyper-model/lib/reactive_record/active_record/class_methods.rb @@ -364,6 +364,18 @@ def server_method(name, default: nil) end end + def allow_remote_access_to(*methods) + methods.each do |meth| + if meth.is_a? Hash + puts "defining these guys: #{meth}" + meth.each { |name, default| server_method(name, default: default) } + else + puts "defining this guy: #{meth}" + server_method(meth) + end + end + end + # define all the methods for each column. To allow overriding the methods they will NOT # be defined if already defined (i.e. by the model) See the instance_methods module for how # super calls are handled in this case. The _hyperstack_internal_setter_... methods diff --git a/ruby/hyper-model/spec/batch6/server_method_spec.rb b/ruby/hyper-model/spec/batch1/misc/server_method_spec.rb similarity index 65% rename from ruby/hyper-model/spec/batch6/server_method_spec.rb rename to ruby/hyper-model/spec/batch1/misc/server_method_spec.rb index 04bdba059..2889318a9 100644 --- a/ruby/hyper-model/spec/batch6/server_method_spec.rb +++ b/ruby/hyper-model/spec/batch1/misc/server_method_spec.rb @@ -88,4 +88,42 @@ class ServerMethodTester < HyperComponent expect(TestModel.count).to be_zero expect(ChildModel.count).to be_zero end + + it "will allow remote access to methods" do + TodoItem.class_eval do + def foo + "foo" + end + + def bar + "bar" + end + + def broken + "broken" + end + + def defaulted + "defaulted" + end + end + isomorphic do + TodoItem.class_eval do + allow_remote_access_to(:foo, :bar) { acting_user.nil? } + allow_remote_access_to(:broken) { acting_user.admin? } + allow_remote_access_to(:dontcallme, defaulted: "loading") { true } + end + end + client_option raise_on_js_errors: :off + expect { TodoItem.last.foo }.on_client_to be_nil + expect { Hyperstack::Model.load { TodoItem.last.foo } }.on_client_to eq("foo") + expect { TodoItem.last.bar }.on_client_to be_nil + expect { Hyperstack::Model.load { TodoItem.last.bar } }.on_client_to eq("bar") + expect { Hyperstack::Model.load { TodoItem.last.broken } }.on_client_to be_nil + expect { TodoItem.last.defaulted }.on_client_to eq "loading" + expect { Hyperstack::Model.load { TodoItem.last.defaulted } }.on_client_to eq("defaulted") + errors = page.driver.browser.manage.logs.get(:browser).select { |m| m.level == "SEVERE" } + expect(errors.count).to eq(2) + expect(errors.first.message).to match(/the server responded with a status of 403 \(Forbidden\)/) + end end diff --git a/ruby/hyper-spec/lib/hyper-spec/helpers.rb b/ruby/hyper-spec/lib/hyper-spec/helpers.rb index c5d05df04..e86104f20 100644 --- a/ruby/hyper-spec/lib/hyper-spec/helpers.rb +++ b/ruby/hyper-spec/lib/hyper-spec/helpers.rb @@ -82,7 +82,11 @@ def before_mount(&block) def isomorphic(&block) yield - before_mount(&block) + if page.instance_variable_get('@hyper_spec_mounted') + internal_evaluate_ruby(&block) + else + before_mount(&block) + end end # Allows options to the mount method to be specified globally diff --git a/ruby/hyper-spec/spec/hyper_spec.rb b/ruby/hyper-spec/spec/hyper_spec.rb index beff868f9..d1e2af1d6 100644 --- a/ruby/hyper-spec/spec/hyper_spec.rb +++ b/ruby/hyper-spec/spec/hyper_spec.rb @@ -108,6 +108,20 @@ def factorial(n) expect(evaluate_ruby('factorial(5)')).to eq(factorial(5)) end + it "can load isomorphic code after loading" do + on_client do + CONSTANT = 1 + end + CONSTANT = 1 + isomorphic do + def factorial(n) + n==CONSTANT ? CONSTANT : n * factorial(n-CONSTANT) + end + nil + end + expect(evaluate_ruby('factorial(5)')).to eq(factorial(5)) + end + context 'promise helpers' do # just to demonstrate a few things: # 1 - You can use methods like mount, isomorphic, on_client in before(:each) blocks