hyperstack-org / hyperstack-org/website

Dumping exceptions in live editor

Aperta
#26 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
bug
Lingua principale
CSS
Stelle
5
Fork
11
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

The live editor can crash the whole page if the user adds one of the lifecycle methods (before_mount, etc). The only fix so far is the patch below which catches and dumps an exception. Each change has comment #barrie

We need to find a better solution and this needs to be removed before the rename.

```
module React
find_and_render_component
class NativeLibrary
class << self
def imports(native_name)
@native_prefix = "#{native_name}."
self
end

def rename(rename_list)
# rename_list is a hash in the form: native_name => ruby_name, native_name => ruby_name
rename_list.each do |js_name, ruby_name|
native_name = lookup_native_name(js_name)
if lookup_native_name(js_name)
create_component_wrapper(self, native_name, ruby_name) ||
create_library_wrapper(self, native_name, ruby_name)
else
raise "class #{name} < React::NativeLibrary could not import #{js_name}. "\
"Native value #{scope_native_name(js_name)} is undefined."
end
end
end

def import_const_from_native(klass, const_name, create_library)
begin
native_name = lookup_native_name(const_name) ||
lookup_native_name(const_name[0].downcase + const_name[1..-1])
native_name && (
create_component_wrapper(klass, native_name, const_name) || (
create_library &&
create_library_wrapper(klass, native_name, const_name)))
rescue Exception
# barrie
end
end

def const_missing(const_name)
import_const_from_native(self, const_name, true) || super
end

def method_missing(method, *args, &block)
component_class = const_get(method) if const_defined?(method, false)
component_class ||= import_const_from_native(self, method, false)
raise 'could not import a react component named: '\
"#{scope_native_name method}" unless component_class
React::RenderingContext.render(component_class, *args, &block)
end

private

def lookup_native_name(js_name)
native_name = scope_native_name(js_name)
`eval(#{native_name}) !== undefined && native_name`
# rubocop:disable Lint/RescueException # that is what eval raises in Opal >= 0.10.
rescue Exception
nil
# rubocop:enable Lint/RescueException
end

def scope_native_name(js_name)
"#{@native_prefix}#{js_name}"
end

def create_component_wrapper(klass, native_name, ruby_name)
if React::API.native_react_component?(native_name)
new_klass = klass.const_set ruby_name, Class.new
new_klass.class_eval do
include Hyperloop::Component::Mixin
imports native_name
end
new_klass
end
end

def create_library_wrapper(klass, native_name, ruby_name)
klass.const_set ruby_name, Class.new(React::NativeLibrary).imports(native_name)
end
end
end
end

module Hyperloop
class Component
class << self
def mounted_components
@mounted_components ||= Set.new
end

def force_update!
components = mounted_components.to_a
components.each do |comp|
next unless mounted_components.include? comp
comp.force_update!
end
end
end

module Mixin
def self.included(base)
base.include(Hyperloop::Store::Mixin)
base.include(React::Component::API)
base.include(React::Callbacks)
base.include(React::Component::Tags)
base.include(React::Component::DslInstanceMethods)
base.include(React::Component::ShouldComponentUpdate)
base.class_eval do
class_attribute :initial_state
define_callback :before_mount
define_callback :after_mount
define_callback :before_receive_props
define_callback :before_update
define_callback :after_update
define_callback :before_unmount
define_callback(:after_error) { React::API.add_after_error_hook(base) }
end
base.extend(React::Component::ClassMethods)
end

def self.deprecation_warning(message)
React::Component.deprecation_warning(name, message)
end

def deprecation_warning(message)
React::Component.deprecation_warning(self.class.name, message)
end

def initialize(native_element)
@native = native_element
init_store
end

def emit(event_name, *args)
if React::Event::BUILT_IN_EVENTS.include?(built_in_event_name = "on#{event_name.to_s.event_camelize}")
params[built_in_event_name].call(*args)
else
params["on_#{event_name}"].call(*args)
end
end

def component_will_mount
begin
React::IsomorphicHelpers.load_context(true) if React::IsomorphicHelpers.on_opal_client?
React::State.set_state_context_to(self) do
Hyperloop::Component.mounted_components << self
run_callback(:before_mount)
end
rescue Exception
# barrie
end
end

def component_did_mount
begin
React::State.set_state_context_to(self) do
run_callback(:after_mount)
React::State.update_states_to_observe
end
rescue Exception
# barrie
end
end

def component_will_receive_props(next_props)
# need to rethink how this works in opal-react, or if its actually that useful within the react.rb environment
# for now we are just using it to clear processed_params
begin
React::State.set_state_context_to(self) { run_callback(:before_receive_props, next_props) }
@_receiving_props = true
rescue Exception
# barrie
end
end

def component_will_update(next_props, next_state)
begin
React::State.set_state_context_to(self) { run_callback(:before_update, next_props, next_state) }
params._reset_all_others_cache if @_receiving_props
@_receiving_props = false
rescue Exception
# barrie
end
end

def component_did_update(prev_props, prev_state)
begin
React::State.set_state_context_to(self) do
run_callback(:after_update, prev_props, prev_state)
React::State.update_states_to_observe
end
rescue Exception
# barrie
end
end

def component_will_unmount
begin
React::State.set_state_context_to(self) do
run_callback(:before_unmount)
React::State.remove
Hyperloop::Component.mounted_components.delete self
end
rescue Exception
# barrie
end
end

def component_did_catch(error, info)
React::State.set_state_context_to(self) do
run_callback(:after_error, error, info)
end
end

attr_reader :waiting_on_resources

def update_react_js_state(object, name, value)
if object
name = "#{object.class}.#{name}" unless object == self
# Date.now() has only millisecond precision, if several notifications of
# observer happen within a millisecond, updates may get lost.
# to mitigate this the Math.random() appends some random number
# this way notifactions will happen as expected by the rest of hyperloop
set_state(
'***_state_updated_at-***' => `Date.now() + Math.random()`,
name => value
)
else
set_state name => value
end
end

def set_state_synchronously?
@native.JS[:__opalInstanceSyncSetState]
end

def render
raise 'no render defined'
end unless method_defined?(:render)

def _render_wrapper
React::State.set_state_context_to(self, true) do
element = React::RenderingContext.render(nil) { render || '' }
@waiting_on_resources =
element.waiting_on_resources if element.respond_to? :waiting_on_resources
element
end
end

def watch(value, &on_change)
Observable.new(value, on_change)
end

def define_state(*args, &block)
React::State.initialize_states(self, self.class.define_state(*args, &block))
end
end
end
end
```

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.