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

Commit 85efd52

Browse files
committed
working, adding nicer outer most rendering routines
1 parent 87a07b6 commit 85efd52

13 files changed

Lines changed: 449 additions & 81 deletions

lib/rails-helpers/top_level_rails_component.rb

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,33 @@ def self.search_path
88

99
export_component
1010

11-
required_param :component_name
12-
required_param :controller
13-
required_param :render_params
11+
param :component_name
12+
param :controller
13+
param :render_params
1414

1515
backtrace :on
1616

1717
def render
1818
paths_searched = []
19-
if component_name.start_with? "::"
20-
paths_searched << component_name.gsub(/^\:\:/,"")
21-
component = component_name.gsub(/^\:\:/,"").split("::").inject(Module) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
22-
return present component, render_params if component && component.method_defined?(:render)
19+
if params.component_name.start_with? "::"
20+
paths_searched << params.component_name.gsub(/^\:\:/,"")
21+
component = params.component_name.gsub(/^\:\:/,"").split("::").inject(Module) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
22+
return present component, params.render_params if component && component.method_defined?(:render)
2323
else
2424
self.class.search_path.each do |path|
25-
# try each path + controller + component_name
26-
paths_searched << "#{path.name + '::' unless path == Module}#{controller}::#{component_name}"
27-
component = "#{controller}::#{component_name}".split("::").inject(path) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
28-
return present component, render_params if component && component.method_defined?(:render)
25+
# try each path + params.controller + params.component_name
26+
paths_searched << "#{path.name + '::' unless path == Module}#{params.controller}::#{params.component_name}"
27+
component = "#{params.controller}::#{params.component_name}".split("::").inject(path) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
28+
return present component, params.render_params if component && component.method_defined?(:render)
2929
end
3030
self.class.search_path.each do |path|
31-
# then try each path + component_name
32-
paths_searched << "#{path.name + '::' unless path == Module}#{component_name}"
33-
component = "#{component_name}".split("::").inject(path) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
34-
return present component, render_params if component && component.method_defined?(:render)
31+
# then try each path + params.component_name
32+
paths_searched << "#{path.name + '::' unless path == Module}#{params.component_name}"
33+
component = "#{params.component_name}".split("::").inject(path) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
34+
return present component, params.render_params if component && component.method_defined?(:render)
3535
end
3636
end
37-
raise "Could not find component class '#{component_name}' for controller '#{controller}' in any component directory. Tried [#{paths_searched.join(", ")}]"
37+
raise "Could not find component class '#{params.component_name}' for params.controller '#{params.controller}' in any component directory. Tried [#{paths_searched.join(", ")}]"
3838
end
3939
end
4040
end

lib/react/callbacks.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ def self.included(base)
77
end
88

99
def run_callback(name, *args)
10-
attribute_name = "_#{name}_callbacks"
11-
callbacks = self.class.send(attribute_name)
12-
callbacks.each do |callback|
10+
self.class.callbacks_for(name).each do |callback|
1311
if callback.is_a?(Proc)
1412
instance_exec(*args, &callback)
1513
else
@@ -30,6 +28,15 @@ def define_callback(callback_name)
3028
self.send("#{attribute_name}=", callbacks)
3129
end
3230
end
31+
32+
def callbacks_for(callback_name)
33+
attribute_name = "_#{callback_name}_callbacks"
34+
if superclass.respond_to? :callbacks_for
35+
superclass.callbacks_for(callback_name)
36+
else
37+
[]
38+
end + self.send(attribute_name)
39+
end
3340
end
3441
end
3542
end

lib/react/component.rb

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ module Component
1414
def self.included(base)
1515
base.include(API)
1616
base.include(React::Callbacks)
17+
base.instance_eval do
18+
class PropsWrapper
19+
20+
def initialize(props_hash)
21+
@props_hash = props_hash
22+
@processed_params = {}
23+
end
24+
25+
def [](prop)
26+
return unless @props_hash
27+
@props_hash[prop]
28+
end
29+
30+
end
31+
end
1732
base.class_eval do
1833
class_attribute :initial_state
1934
define_callback :before_mount
@@ -23,6 +38,12 @@ def self.included(base)
2338
define_callback :after_update
2439
define_callback :before_unmount
2540

41+
def deprecated_params_method(name, *args, &block)
42+
puts "deprecated_params_method for #{self.class.name}.#{name}"
43+
# TODO display deprecation message, but only once per access to a method
44+
params.send(name, *args, &block)
45+
end
46+
2647
def render
2748
raise "no render defined"
2849
end unless method_defined?(:render)
@@ -97,6 +118,11 @@ def initialize(native_element)
97118
end
98119

99120
def params
121+
@props_wrapper
122+
#Hash.new(`#{@native}.props`)
123+
end
124+
125+
def props
100126
Hash.new(`#{@native}.props`)
101127
end
102128

@@ -105,8 +131,8 @@ def refs
105131
end
106132

107133
def state
108-
raise "No native ReactComponent associated" unless @native
109-
Hash.new(`#{@native}.state`)
134+
#raise "No native ReactComponent associated" unless @native
135+
@state_wrapper ||= StateWrapper.new(@native, self)
110136
end
111137

112138
def update_react_js_state(object, name, value)
@@ -123,7 +149,7 @@ def emit(event_name, *args)
123149

124150
def component_will_mount
125151
IsomorphicHelpers.load_context(true) if IsomorphicHelpers.on_opal_client?
126-
@processed_params = {}
152+
@props_wrapper = self.class.const_get("PropsWrapper").new(Hash.new(`#{@native}.props`))
127153
set_state! initial_state if initial_state
128154
React::State.initialize_states(self, initial_state)
129155
React::State.set_state_context_to(self) { self.run_callback(:before_mount) }
@@ -144,14 +170,13 @@ def component_will_receive_props(next_props)
144170
# need to rethink how this works in opal-react, or if its actually that useful within the react.rb environment
145171
# for now we are just using it to clear processed_params
146172
React::State.set_state_context_to(self) { self.run_callback(:before_receive_props, Hash.new(next_props)) }
147-
@processed_params = {}
148173
rescue Exception => e
149174
self.class.process_exception(e, self)
150175
end
151176

152177
def props_changed?(next_props)
153-
return true unless params.keys.sort == next_props.keys.sort
154-
params.detect { |k, v| `#{next_props[k]} != #{params[k]}`}
178+
return true unless props.keys.sort == next_props.keys.sort
179+
props.detect { |k, v| `#{next_props[k]} != #{params[k]}`}
155180
end
156181

157182
def should_component_update?(next_props, next_state)
@@ -177,6 +202,7 @@ def should_component_update?(next_props, next_state)
177202

178203
def component_will_update(next_props, next_state)
179204
React::State.set_state_context_to(self) { self.run_callback(:before_update, Hash.new(next_props), Hash.new(next_state)) }
205+
@props_wrapper = self.class.const_get("PropsWrapper").new(Hash.new(next_props))
180206
rescue Exception => e
181207
self.class.process_exception(e, self)
182208
end
@@ -220,7 +246,7 @@ def component?(name)
220246
end
221247

222248
def method_missing(n, *args, &block)
223-
return params[n] if params.key? n
249+
return props[n] if props.key? n # TODO deprecate and remove - done so that params shadow tags, no longer needed
224250
name = n
225251
if name =~ /_as_node$/
226252
node_only = true

lib/react/component/base.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module React
2+
module Component
3+
class Base
4+
def self.inherited(child)
5+
child.send(:include, React::Component)
6+
end
7+
end
8+
end
9+
end

lib/react/component/class_methods.rb

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def process_exception(e, component, reraise = nil)
1919
end
2020

2121
def validator
22-
@validator ||= React::Validator.new
22+
@validator ||= React::Validator.new(self)
2323
end
2424

2525
def prop_types
@@ -47,50 +47,71 @@ def params(&block)
4747
end
4848

4949
def define_param_method(name, param_type)
50+
wrapper = const_get("PropsWrapper")
5051
if param_type == React::Observable
51-
(@two_way_params ||= []) << name
52-
define_method("#{name}") do
53-
params[name].instance_variable_get("@value") if params[name]
52+
#(@two_way_params ||= []) << name
53+
wrapper.define_method("#{name}") do
54+
@props_hash[name].instance_variable_get("@value") if @props_hash[name]
5455
end
55-
define_method("#{name}!") do |*args|
56-
return unless params[name]
56+
wrapper.define_method("#{name}!") do |*args|
57+
return unless @props_hash[name]
5758
if args.count > 0
58-
current_value = params[name].instance_variable_get("@value")
59-
params[name].call args[0]
59+
current_value = @props_hash[name].instance_variable_get("@value")
60+
@props_hash[name].call args[0]
6061
current_value
6162
else
62-
current_value = params[name].instance_variable_get("@value")
63-
params[name].call current_value unless @dont_update_state rescue nil # rescue in case we in middle of render
64-
params[name]
63+
current_value = @props_hash[name].instance_variable_get("@value")
64+
@props_hash[name].call current_value unless @dont_update_state rescue nil # rescue in case we in middle of render
65+
@props_hash[name]
6566
end
6667
end
68+
define_method("#{name}") { deprecated_params_method("#{name}") }
69+
define_method("#{name}!") { |*args| deprecated_params_method("#{name}!", *args) }
6770
elsif param_type == Proc
68-
define_method("#{name}") do |*args, &block|
69-
params[name].call(*args, &block) if params[name]
71+
wrapper.define_method("#{name}") do |*args, &block|
72+
@props_hash[name].call(*args, &block) if @props_hash[name]
7073
end
74+
define_method("#{name}") { deprecated_params_method("#{name}", *args, &block) }
7175
else
72-
define_method("#{name}") do
76+
wrapper.define_method("#{name}") do
7377
@processed_params[name] ||= if param_type.respond_to? :_react_param_conversion
74-
param_type._react_param_conversion params[name]
78+
param_type._react_param_conversion @props_hash[name]
7579
elsif param_type.is_a?(Array) && param_type[0].respond_to?(:_react_param_conversion)
76-
params[name].collect { |param| param_type[0]._react_param_conversion param }
80+
@props_hash[name].collect { |param| param_type[0]._react_param_conversion param }
7781
else
78-
params[name]
82+
@props_hash[name]
7983
end
8084
end
85+
define_method("#{name}") { deprecated_params_method("#{name}") }
86+
end
87+
end
88+
89+
def param(*args)
90+
if args[0].is_a? Hash
91+
options = args[0]
92+
name = options.first[0]
93+
default = options.first[1]
94+
options.delete(name)
95+
options.merge!({default: default})
96+
else
97+
name = args[0]
98+
options = args[1] || {}
99+
end
100+
if options[:default]
101+
validator.optional(name, options)
102+
else
103+
validator.requires(name, options)
81104
end
82105
end
83106

84107
def required_param(name, options = {})
85108
validator.requires(name, options)
86-
define_param_method(name, options[:type])
87109
end
88110

89111
alias_method :require_param, :required_param
90112

91113
def optional_param(name, options = {})
92114
validator.optional(name, options)
93-
define_param_method(name, options[:type]) unless name == :params
94115
end
95116

96117
def collect_other_params_as(name)
@@ -130,7 +151,6 @@ def define_state_methods(this, name, from = nil, &block)
130151
React::State.set_state(from || self, name, new_state)
131152
end
132153
this.define_method("#{name}!") do |*args|
133-
#return unless @native
134154
if args.count > 0
135155
yield name, React::State.get_state(from || self, name), args[0] if block && block.arity > 0
136156
current_value = React::State.get_state(from || self, name)

lib/react/state.rb

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,39 @@
11
module React
2+
class StateWrapper
3+
4+
def initialize(native, from)
5+
@state_hash = Hash.new(`#{native}.state`)
6+
@from = from
7+
end
8+
9+
def [](state)
10+
@state_hash[state]
11+
end
12+
13+
def []=(state, new_value)
14+
@state_hash[state] = new_value
15+
end
16+
17+
def method_missing(method, *args)
18+
if match = method.match(/^(.+)\!$/)
19+
if args.count > 0
20+
current_value = React::State.get_state(@from, match[1])
21+
React::State.set_state(@from, $1, args[0])
22+
current_value
23+
else
24+
current_state = React::State.get_state(@from, match[1])
25+
React::State.set_state(@from, $1, current_state)
26+
React::Observable.new(current_state) do |update|
27+
React::State.set_state(@from, $1, update)
28+
end
29+
end
30+
else
31+
React::State.get_state(@from, method)
32+
end
33+
end
34+
end
35+
36+
237
class State
338
class << self
439
attr_reader :current_observer
@@ -80,14 +115,10 @@ def set_state_context_to(observer) # wrap all execution that may set or get stat
80115
@nesting_level = (@nesting_level || 0) + 1
81116
start_time = Time.now.to_f
82117
observer_name = (observer.class.respond_to?(:name) ? observer.class.name : observer.to_s) rescue "object:#{observer.object_id}"
83-
puts "#{' ' * @nesting_level} Timing #{observer_name} Execution"
84118
end
85119
saved_current_observer = @current_observer
86120
@current_observer = observer
87121
return_value = yield
88-
if `typeof window.reactive_ruby_timing !== 'undefined'`
89-
puts "#{' ' * @nesting_level} Timing #{observer_name} Completed in #{'%.04f' % (Time.now.to_f-start_time)} seconds"
90-
end
91122
return_value
92123
ensure
93124
@current_observer = saved_current_observer

lib/react/validator.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
module React
2+
23
class Validator
34
attr_accessor :errors
45
private :errors
56

7+
def initialize(component_class)
8+
@component_class = component_class
9+
end
10+
611
def self.build(&block)
712
self.new.build(&block)
813
end
@@ -15,11 +20,13 @@ def build(&block)
1520
def requires(name, options = {})
1621
options[:required] = true
1722
define_rule(name, options)
23+
@component_class.define_param_method(name, options[:type])
1824
end
1925

2026
def optional(name, options = {})
2127
options[:required] = false
2228
define_rule(name, options)
29+
@component_class.define_param_method(name, options[:type]) unless name == :params
2330
end
2431

2532
def allow_undefined_props=(allow)
@@ -73,11 +80,14 @@ def errors
7380

7481
def validate_types(prop_name, value)
7582
return unless klass = rules[prop_name][:type]
76-
if klass.is_a?(Array) && klass.length > 0
83+
if !klass.is_a?(Array)
84+
allow_nil = !!rules[prop_name][:allow_nil]
85+
type_check("`#{prop_name}`", value, klass, allow_nil)
86+
elsif klass.length > 0
7787
validate_value_array(prop_name, value)
7888
else
7989
allow_nil = !!rules[prop_name][:allow_nil]
80-
type_check("`#{prop_name}`", value, klass, allow_nil)
90+
type_check("`#{prop_name}`", value, Array, allow_nil)
8191
end
8292
end
8393

0 commit comments

Comments
 (0)