Skip to content

Commit 19618ca

Browse files
committed
merged not tested
1 parent 4088194 commit 19618ca

40 files changed

Lines changed: 896 additions & 170 deletions

File tree

docs/tutorial/todo.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -987,8 +987,8 @@ end
987987

988988
# app/hyperstack/components/edit_item.rb
989989
class EditItem < HyperComponent
990-
param :todo
991-
triggers :saved
990+
param :todo
991+
triggers :save
992992
triggers :cancel
993993
others :etc
994994
after_mount { DOM[dom_node].focus }

ruby/hyper-component/lib/hyper-component.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
require 'hyperstack/component/isomorphic_helpers'
2828
require 'hyperstack/component/react_api'
2929
require 'hyperstack/internal/component/top_level_rails_component'
30+
require 'hyperstack/component/while_loading'
31+
require 'hyperstack/internal/component/rescue_wrapper'
32+
require 'hyperstack/internal/component/while_loading_wrapper'
33+
3034
require 'hyperstack/component/version'
3135
else
3236
require 'opal'

ruby/hyper-component/lib/hyperstack/component.rb

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ def self.included(base)
2121
class_attribute :initial_state
2222
define_callback :before_mount
2323
define_callback :after_mount
24-
define_callback :before_receive_props
24+
define_callback :before_new_params
2525
define_callback :before_update
2626
define_callback :after_update
27+
define_callback :__hyperstack_component_after_render_hook
28+
define_callback :__hyperstack_component_rescue_hook
2729
#define_callback :before_unmount defined already by Async module
2830
define_callback(:after_error) { Hyperstack::Internal::Component::ReactWrapper.add_after_error_hook(base) }
2931
end
@@ -93,13 +95,14 @@ def component_will_mount
9395
def component_did_mount
9496
observing(update_objects: true) do
9597
run_callback(:after_mount)
98+
Hyperstack::Internal::Component::RenderingContext.quiet_test(self)
9699
end
97100
end
98101

99102
def component_will_receive_props(next_props)
100103
# need to rethink how this works in opal-react, or if its actually that useful within the react.rb environment
101104
# for now we are just using it to clear processed_params
102-
observing(immediate_update: true) { run_callback(:before_receive_props, next_props) }
105+
observing(immediate_update: true) { run_callback(:before_new_params, next_props) }
103106
@__hyperstack_component_receiving_props = true
104107
end
105108

@@ -112,7 +115,10 @@ def component_will_update(next_props, next_state)
112115
end
113116

114117
def component_did_update(prev_props, prev_state)
115-
observing(update_objects: true) { run_callback(:after_update, prev_props, prev_state) }
118+
observing(update_objects: true) do
119+
run_callback(:after_update, prev_props, prev_state)
120+
Hyperstack::Internal::Component::RenderingContext.quiet_test(self)
121+
end
116122
end
117123

118124
def component_will_unmount
@@ -124,7 +130,9 @@ def component_will_unmount
124130
end
125131

126132
def component_did_catch(error, info)
127-
observing { run_callback(:after_error, error, info) }
133+
observing do
134+
run_callback(:after_error, error, info)
135+
end
128136
end
129137

130138
def mutations(_objects)
@@ -156,9 +164,15 @@ def waiting_on_resources
156164
@__hyperstack_component_waiting_on_resources
157165
end
158166

167+
def __hyperstack_component_run_post_render_hooks(element)
168+
run_callback(:__hyperstack_component_after_render_hook, element) { |*args| args }.first
169+
end
170+
159171
def _render_wrapper
160172
observing(rendering: true) do
161-
element = Hyperstack::Internal::Component::RenderingContext.render(nil) { render || '' }
173+
element = Hyperstack::Internal::Component::RenderingContext.render(nil) do
174+
render || ''
175+
end
162176
@__hyperstack_component_waiting_on_resources =
163177
element.waiting_on_resources if element.respond_to? :waiting_on_resources
164178
element

ruby/hyper-component/lib/hyperstack/component/element.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def on(*event_names, &block)
4545
# Used for elements that are not yet in DOM, i.e. they are provided as children
4646
# or they have been explicitly removed from the rendering context using the delete method.
4747

48-
def render(props = {}, &new_block)
48+
def render(*props, &new_block)
4949
if props.empty?
5050
Hyperstack::Internal::Component::RenderingContext.render(self)
5151
else

ruby/hyper-component/lib/hyperstack/ext/component/string.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ def event_camelize
55
return capitalize ? word.substr(0,1).toUpperCase()+word.substr(1) : word;
66
})`
77
end
8-
end
8+
end
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
require 'hyperstack-config'
22

33
module Hyperstack
4-
54
define_setting :prerendering, :off if RUBY_ENGINE != 'opal'
65

6+
module Internal
7+
module Component
8+
class << self
9+
attr_accessor :after_error_args
10+
end
11+
end
12+
end
713
end

ruby/hyper-component/lib/hyperstack/internal/component/class_methods.rb

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,74 @@ def append_backtrace(message_array, backtrace)
4242
backtrace[1..-1].each { |line| message_array << line }
4343
end
4444

45+
def before_receive_props(*args, &block)
46+
deprecation_warning "'before_receive_props' is deprecated. Use the 'before_new_params' macro instead."
47+
before_new_params(*args, &block)
48+
end
49+
4550
def render(container = nil, params = {}, &block)
4651
Tags.included(self)
4752
if container
4853
container = container.type if container.is_a? Hyperstack::Component::Element
49-
define_method :__hyperstack_component_render do
50-
RenderingContext.render(container, params) { instance_eval(&block) if block }
54+
define_method(:__hyperstack_component_render) do
55+
__hyperstack_component_select_wrappers do
56+
RenderingContext.render(container, params) do
57+
instance_eval(&block) if block
58+
end
59+
end
5160
end
5261
else
53-
define_method(:__hyperstack_component_render) { instance_eval(&block) }
62+
define_method(:__hyperstack_component_render) do
63+
__hyperstack_component_select_wrappers do
64+
instance_eval(&block)
65+
end
66+
end
67+
end
68+
end
69+
70+
# def while_loading(*args, &block)
71+
# __hyperstack_component_after_render_hook do |element|
72+
# element.while_loading(*args) { |*aargs| instance_exec(*aargs, &block) }
73+
# end
74+
# end
75+
76+
def on(*args, &block)
77+
__hyperstack_component_after_render_hook do |element|
78+
element.on(*args) { |*aargs| instance_exec(*aargs, &block) }
79+
end
80+
end
81+
82+
def rescues(*klasses, &block)
83+
klasses = [StandardError] if klasses.empty?
84+
__hyperstack_component_rescue_hook do |found, *args|
85+
next [found, *args] if found || !klasses.detect { |klass| args[0].is_a? klass }
86+
instance_exec(*args, &block)
87+
[true, *args]
5488
end
5589
end
5690

91+
def before_render(*args, &block)
92+
before_mount(*args, &block)
93+
before_update(*args, &block)
94+
end
95+
96+
def after_render(*args, &block)
97+
after_mount(*args, &block)
98+
after_update(*args, &block)
99+
end
100+
101+
# [:while_loading, :on].each do |method|
102+
# define_method(method) do |*args, &block|
103+
# @@alias_id ||= 0
104+
# @@alias_id += 1
105+
# alias_name = "__hyperstack_attach_post_render_hook_#{@@alias_id}"
106+
# alias_method alias_name, :__hyperstack_attach_post_render_hook
107+
# define_method(:__hyperstack_attach_post_render_hook) do |element|
108+
# send(alias_name, element.while_loading(*args) { instance_eval(&block) })
109+
# end
110+
# end
111+
# end
112+
57113
# method missing will assume the method is a class name, and will treat this a render of
58114
# of the component, i.e. Foo::Bar.baz === Foo::Bar().baz
59115

@@ -147,8 +203,9 @@ def collect_other_params_as(name)
147203

148204
alias other_params collect_other_params_as
149205
alias others collect_other_params_as
206+
alias opts collect_other_params_as
150207

151-
def triggers(name, opts = {})
208+
def fires(name, opts = {})
152209
aka = opts[:alias] || "#{name}!"
153210
name = if name =~ /^<(.+)>$/
154211
name.gsub(/^<(.+)>$/, '\1')
@@ -161,6 +218,8 @@ def triggers(name, opts = {})
161218
define_method(aka) { |*args| props[name]&.call(*args) }
162219
end
163220

221+
alias triggers fires
222+
164223
def define_state(*states, &block)
165224
deprecation_warning "'define_state' is deprecated. Use the 'state' macro to declare states."
166225
default_initial_value = (block && block.arity == 0) ? yield : nil

ruby/hyper-component/lib/hyperstack/internal/component/instance_methods.rb

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def children
99
end
1010

1111
def params
12-
if @__hyperstack_component_params_wrapper.param_accessor_style == :hyperstack
12+
if [:hyperstack, :accessors].include? @__hyperstack_component_params_wrapper.param_accessor_style
1313
raise "params are now directly accessible via instance variables.\n"\
1414
' to access the legacy behavior add `param_accessor_style = :legacy` '\
1515
"to your component class\n"\
@@ -31,6 +31,16 @@ def mounted?
3131
`(#{self}.__hyperstack_component_is_mounted === undefined) ? false : #{self}.__hyperstack_component_is_mounted`
3232
end
3333

34+
def pluralize(count, singular, plural = nil)
35+
word = if (count == 1 || count =~ /^1(\.0+)?$/)
36+
singular
37+
else
38+
plural || singular.pluralize
39+
end
40+
41+
"#{count || 0} #{word}"
42+
end
43+
3444
def force_update!
3545
`#{self}.__hyperstack_component_native.forceUpdate()`
3646
self
@@ -47,6 +57,29 @@ def set_state!(state, &block)
4757

4858
private
4959

60+
# can be overriden by the Router include
61+
def __hyperstack_router_wrapper(&block)
62+
->() { instance_eval(&block) }
63+
end
64+
65+
# can be overriden by including WhileLoading include
66+
def __hyperstack_component_rescue_wrapper(child)
67+
if self.class.callbacks?(:__hyperstack_component_rescue_hook)
68+
Hyperstack::Internal::Component::RescueWrapper(child: self, children_elements: child)
69+
else
70+
child.call
71+
end
72+
end
73+
74+
def __hyperstack_component_select_wrappers(&block)
75+
RescueWrapper.after_error_args = nil
76+
__hyperstack_component_run_post_render_hooks(
77+
__hyperstack_component_rescue_wrapper(
78+
__hyperstack_router_wrapper(&block)
79+
)
80+
)
81+
end
82+
5083
def set_or_replace_state_or_prop(state_or_prop, method, &block)
5184
raise "No native ReactComponent associated" unless @__hyperstack_component_native
5285
`var state_prop_n = #{state_or_prop.shallow_to_n}`

ruby/hyper-component/lib/hyperstack/internal/component/props_wrapper.rb

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,25 @@ class << self
99
def instance_var_name_for(name)
1010
case Hyperstack.naming_convention
1111
when :camelize_params
12-
name.camelize
12+
fix_suffix(name.camelize)
1313
when :prefix_params
1414
"_#{name}"
1515
else
1616
name
1717
end
1818
end
1919

20+
def fix_suffix(name)
21+
return unless name
22+
if name =~ /\?$/
23+
name[0..-2] + '_q'
24+
elsif name =~ /\!$/
25+
name[0..-2] + '_b'
26+
else
27+
name
28+
end
29+
end
30+
2031
def param_accessor_style(style = nil)
2132
@param_accessor_style = style if style
2233
@param_accessor_style ||=
@@ -38,11 +49,14 @@ def param_definitions
3849

3950
def define_param(name, param_type, aka = nil)
4051
meth_name = aka || name
41-
var_name = aka || instance_var_name_for(name)
52+
var_name = fix_suffix(aka) || instance_var_name_for(name)
4253
param_definitions[name] = lambda do |props|
43-
@component.instance_variable_set :"@#{var_name}", fetch_from_cache(name, param_type, props)
54+
@component.instance_variable_set :"@#{var_name}", val = fetch_from_cache(name, param_type, props)
55+
next unless param_accessor_style == :accessors
56+
`#{@component}[#{"$#{meth_name}"}] = function() { return #{val} }`
57+
# @component.define_singleton_method(name) { val } if param_accessor_style == :accessors
4458
end
45-
return if param_accessor_style == :hyperstack
59+
return if %i[hyperstack accessors].include? param_accessor_style
4660
if param_type == Proc
4761
define_method(meth_name.to_sym) do |*args, &block|
4862
props[name].call(*args, &block) if props[name]
@@ -57,7 +71,10 @@ def define_param(name, param_type, aka = nil)
5771
def define_all_others(name)
5872
var_name = instance_var_name_for(name)
5973
param_definitions[name] = lambda do |props|
60-
@component.instance_variable_set :"@#{var_name}", yield(props)
74+
@component.instance_variable_set :"@#{var_name}", val = yield(props)
75+
next unless param_accessor_style == :accessors
76+
`#{@component}[#{"$#{name}"}] = function() { return #{val} }`
77+
# @component.define_singleton_method(name) { val } if param_accessor_style == :accessors
6178
end
6279
define_method(name.to_sym) do
6380
@_all_others_cache ||= yield(props)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,33 @@
11
require 'action_controller'
22

3+
module Hyperstack
4+
module Internal
5+
module Component
6+
class Redirect < StandardError
7+
attr_reader :url
8+
def initialize(url)
9+
@url = url
10+
super("redirect to #{url}")
11+
end
12+
end
13+
end
14+
end
15+
end
16+
317
module ActionController
418
# adds render_component helper to ActionControllers
519
class Base
620
def render_component(*args)
721
@component_name = (args[0].is_a? Hash) || args.empty? ? params[:action].camelize : args.shift
822
@render_params = args.shift || {}
923
options = args[0] || {}
24+
return if performed?
1025
render inline: '<%= react_component @component_name, @render_params %>',
1126
layout: options.key?(:layout) ? options[:layout].to_s : :default
27+
rescue Exception => e
28+
m = /^RuntimeError: Hyperstack::Internal::Component::Redirect (.+) status: (.+)$/.match(e.message)
29+
raise e unless m
30+
redirect_to m[1], status: m[2]
1231
end
1332
end
1433
end

0 commit comments

Comments
 (0)