hyper-component 0.99.6 → 1.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -3
- data/Gemfile +4 -3
- data/Gemfile.lock +51 -36
- data/{misc/how-component-name-lookup-works.md → how-component-name-lookup-works.md} +1 -1
- data/hyper-component.gemspec +9 -8
- data/lib/hyper-component.rb +31 -43
- data/lib/hyperstack/component.rb +145 -0
- data/lib/hyperstack/component/auto-import.rb +44 -0
- data/lib/hyperstack/component/children.rb +40 -0
- data/lib/hyperstack/component/element.rb +129 -0
- data/lib/hyperstack/component/event.rb +78 -0
- data/lib/hyperstack/component/haml.rb +18 -0
- data/lib/hyperstack/component/isomorphic_helpers.rb +235 -0
- data/lib/hyperstack/component/jquery.rb +2 -0
- data/lib/hyperstack/component/native_library.rb +92 -0
- data/lib/hyperstack/component/react_api.rb +142 -0
- data/lib/hyperstack/component/server.rb +21 -0
- data/lib/hyperstack/component/version.rb +5 -0
- data/lib/hyperstack/ext/component/boolean.rb +14 -0
- data/lib/{react/ext/opal-jquery → hyperstack/ext/component}/element.rb +17 -12
- data/lib/{react/ext → hyperstack/ext/component}/hash.rb +0 -0
- data/lib/{react/to_key.rb → hyperstack/ext/component/number.rb} +0 -12
- data/lib/hyperstack/ext/component/object.rb +32 -0
- data/lib/{reactive-ruby → hyperstack/ext/component}/serializers.rb +0 -0
- data/lib/{react/ext → hyperstack/ext/component}/string.rb +0 -0
- data/lib/hyperstack/internal/component.rb +16 -0
- data/lib/hyperstack/internal/component/class_methods.rb +212 -0
- data/lib/hyperstack/internal/component/haml.rb +56 -0
- data/lib/hyperstack/internal/component/instance_methods.rb +92 -0
- data/lib/hyperstack/internal/component/props_wrapper.rb +125 -0
- data/lib/hyperstack/internal/component/rails.rb +11 -0
- data/lib/hyperstack/internal/component/rails/component_loader.rb +49 -0
- data/lib/hyperstack/internal/component/rails/component_mount.rb +52 -0
- data/lib/{reactive-ruby → hyperstack/internal/component}/rails/controller_helper.rb +0 -0
- data/lib/hyperstack/internal/component/rails/railtie.rb +24 -0
- data/lib/hyperstack/internal/component/rails/server_rendering/contextual_renderer.rb +52 -0
- data/lib/hyperstack/internal/component/rails/server_rendering/hyper_asset_container.rb +52 -0
- data/lib/hyperstack/internal/component/react_wrapper.rb +308 -0
- data/lib/hyperstack/internal/component/rendering_context.rb +165 -0
- data/lib/hyperstack/internal/component/should_component_update.rb +101 -0
- data/lib/hyperstack/internal/component/tags.rb +109 -0
- data/lib/hyperstack/internal/component/top_level_rails_component.rb +83 -0
- data/lib/hyperstack/internal/component/validator.rb +149 -0
- data/lib/react/react-source.rb +2 -2
- data/unmounting-objects.md +78 -0
- metadata +73 -85
- data/DOCS.md +0 -1515
- data/LICENSE +0 -19
- data/README.md +0 -49
- data/lib/hyper-component/jquery.rb +0 -2
- data/lib/rails-helpers/top_level_rails_component.rb +0 -79
- data/lib/react/api.rb +0 -272
- data/lib/react/callbacks.rb +0 -42
- data/lib/react/children.rb +0 -38
- data/lib/react/component.rb +0 -189
- data/lib/react/component/api.rb +0 -70
- data/lib/react/component/base.rb +0 -13
- data/lib/react/component/class_methods.rb +0 -175
- data/lib/react/component/dsl_instance_methods.rb +0 -23
- data/lib/react/component/params.rb +0 -6
- data/lib/react/component/props_wrapper.rb +0 -90
- data/lib/react/component/should_component_update.rb +0 -99
- data/lib/react/component/tags.rb +0 -116
- data/lib/react/config.rb +0 -5
- data/lib/react/element.rb +0 -167
- data/lib/react/event.rb +0 -76
- data/lib/react/native_library.rb +0 -87
- data/lib/react/object.rb +0 -15
- data/lib/react/ref_callback.rb +0 -31
- data/lib/react/rendering_context.rb +0 -149
- data/lib/react/server.rb +0 -19
- data/lib/react/state_wrapper.rb +0 -23
- data/lib/react/test.rb +0 -16
- data/lib/react/test/dsl.rb +0 -17
- data/lib/react/test/matchers/render_html_matcher.rb +0 -56
- data/lib/react/test/rspec.rb +0 -15
- data/lib/react/test/session.rb +0 -37
- data/lib/react/test/utils.rb +0 -71
- data/lib/react/top_level.rb +0 -110
- data/lib/react/top_level_render.rb +0 -30
- data/lib/react/validator.rb +0 -132
- data/lib/reactive-ruby/component_loader.rb +0 -43
- data/lib/reactive-ruby/isomorphic_helpers.rb +0 -233
- data/lib/reactive-ruby/rails.rb +0 -8
- data/lib/reactive-ruby/rails/component_mount.rb +0 -48
- data/lib/reactive-ruby/rails/railtie.rb +0 -20
- data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +0 -46
- data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +0 -46
- data/lib/reactive-ruby/version.rb +0 -5
- data/lib/reactrb/auto-import.rb +0 -27
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +0 -3
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +0 -5
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +0 -2
- data/misc/generators/reactive_ruby/test_app/templates/boot.rb.erb +0 -6
- data/misc/generators/reactive_ruby/test_app/templates/script/rails +0 -5
- data/misc/generators/reactive_ruby/test_app/templates/test_application.rb.erb +0 -13
- data/misc/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +0 -11
- data/misc/generators/reactive_ruby/test_app/templates/views/components/todo.rb +0 -14
- data/misc/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
- data/misc/generators/reactive_ruby/test_app/test_app_generator.rb +0 -121
- data/misc/hyperloop-logo-small-pink.png +0 -0
- data/misc/logo1.png +0 -0
- data/misc/logo2.png +0 -0
- data/misc/logo3.png +0 -0
- data/path_release_steps.md +0 -9
@@ -0,0 +1,101 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
module Internal
|
3
|
+
module Component
|
4
|
+
#
|
5
|
+
# React assumes all components should update, unless a component explicitly overrides
|
6
|
+
# the shouldComponentUpdate method. Reactrb does an explicit check doing a shallow
|
7
|
+
# compare of params, and using a timestamp to determine if state has changed.
|
8
|
+
|
9
|
+
# If needed components can provide their own #needs_update? method which will be
|
10
|
+
# passed the next params and state opal hashes.
|
11
|
+
|
12
|
+
# Attached to these hashes is a #changed? method that returns whether the hash contains
|
13
|
+
# changes as calculated by the base mechanism. This way implementations of #needs_update?
|
14
|
+
# can use the base comparison mechanism as needed.
|
15
|
+
|
16
|
+
# For example
|
17
|
+
# def needs_update?(next_params, next_state)
|
18
|
+
# # use a special comparison method
|
19
|
+
# return false if next_state.changed? || next_params.changed?
|
20
|
+
# # do some other special checks
|
21
|
+
# end
|
22
|
+
|
23
|
+
# Note that beginning in 0.9 we will use standard ruby compare on all params further reducing
|
24
|
+
# the need for needs_update?
|
25
|
+
#
|
26
|
+
module ShouldComponentUpdate
|
27
|
+
def should_component_update?(next_props, next_state)
|
28
|
+
observing do
|
29
|
+
# rubocop:disable Style/DoubleNegation # we must return true/false to js land
|
30
|
+
if respond_to?(:needs_update?)
|
31
|
+
!!call_needs_update(next_props, next_state)
|
32
|
+
else
|
33
|
+
(props_changed?(next_props) || native_state_changed?(next_state))
|
34
|
+
end
|
35
|
+
# rubocop:enable Style/DoubleNegation
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# create opal hashes for next params and state, and attach
|
40
|
+
# the changed? method to each hash
|
41
|
+
|
42
|
+
def call_needs_update(next_params, next_state)
|
43
|
+
component = self
|
44
|
+
next_params.define_singleton_method(:changed?) do
|
45
|
+
component.props_changed?(self)
|
46
|
+
end
|
47
|
+
next_state.define_singleton_method(:changed?) do
|
48
|
+
component.native_state_changed?(next_state)
|
49
|
+
end
|
50
|
+
needs_update?(next_params, next_state)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Whenever state changes, reactrb updates a timestamp on the state object.
|
54
|
+
# We can rapidly check for state changes comparing the incoming state time_stamp
|
55
|
+
# with the current time stamp.
|
56
|
+
|
57
|
+
# we receive a Opal Ruby Hash here, always, so the Hash is either empty or filled
|
58
|
+
# Hash is converted to native object
|
59
|
+
# if the Hash was empty, the Object has no keys
|
60
|
+
|
61
|
+
# Different versions of react treat empty state differently, so we first
|
62
|
+
# convert anything that looks like an empty state to "false" for consistency.
|
63
|
+
|
64
|
+
# Then we test if one state is empty and the other is not, then we return false.
|
65
|
+
# Then we test if both states are empty we return true.
|
66
|
+
# If either state does not have a time stamp then we have to assume a change.
|
67
|
+
# Otherwise we check time stamps
|
68
|
+
|
69
|
+
# rubocop:disable Metrics/MethodLength # for effeciency we want this to be one method
|
70
|
+
def native_state_changed?(next_state_hash)
|
71
|
+
# next_state = next_state_hash.to_n
|
72
|
+
# %x{
|
73
|
+
# var current_state = #{@__hyperstack_component_native}.state
|
74
|
+
# var normalized_next_state =
|
75
|
+
# !next_state || Object.keys(next_state).length === 0 ? false : next_state
|
76
|
+
# var normalized_current_state =
|
77
|
+
# !current_state || Object.keys(current_state).length === 0 ? false : current_state
|
78
|
+
# if (!normalized_current_state != !normalized_next_state) return(true)
|
79
|
+
# if (!normalized_current_state && !normalized_next_state) return(false)
|
80
|
+
# if (!normalized_current_state['***_state_updated_at-***'] &&
|
81
|
+
# !normalized_next_state['***_state_updated_at-***']) return(false)
|
82
|
+
# if (!normalized_current_state['***_state_updated_at-***'] ||
|
83
|
+
# !normalized_next_state['***_state_updated_at-***']) return(true)
|
84
|
+
# return (normalized_current_state['***_state_updated_at-***'] !=
|
85
|
+
# normalized_next_state['***_state_updated_at-***'])
|
86
|
+
# }
|
87
|
+
state_hash = Hash.new(`#{@__hyperstack_component_native}.state`)
|
88
|
+
next_state_hash != state_hash
|
89
|
+
end
|
90
|
+
# rubocop:enable Metrics/MethodLength
|
91
|
+
|
92
|
+
# Do a shallow compare on the two hashes. Starting in 0.9 we will do a deep compare. ???
|
93
|
+
|
94
|
+
def props_changed?(next_props)
|
95
|
+
props = Hash.new(`#{@__hyperstack_component_native}.props`)
|
96
|
+
next_props != props
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
module Internal
|
3
|
+
module Component # contains the name of all HTML tags, and the mechanism to register a component
|
4
|
+
# class as a new tag
|
5
|
+
module Tags
|
6
|
+
HTML_TAGS = %w(a abbr address area article aside audio b base bdi bdo big blockquote body br
|
7
|
+
button canvas caption cite code col colgroup data datalist dd del details dfn
|
8
|
+
dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
|
9
|
+
h6 head header hr html i iframe img input ins kbd keygen label legend li link
|
10
|
+
main map mark menu menuitem meta meter nav noscript object ol optgroup option
|
11
|
+
output p param picture pre progress q rp rt ruby s samp script section select
|
12
|
+
small source span strong style sub summary sup table tbody td textarea tfoot th
|
13
|
+
thead time title tr track u ul var video wbr) +
|
14
|
+
# The SVG Tags
|
15
|
+
%w(circle clipPath defs ellipse g line linearGradient mask path pattern polygon polyline
|
16
|
+
radialGradient rect stop svg text tspan)
|
17
|
+
|
18
|
+
# the present method is retained as a legacy behavior
|
19
|
+
# def present(component, *params, &children)
|
20
|
+
# RenderingContext.render(component, *params, &children)
|
21
|
+
# end
|
22
|
+
|
23
|
+
# define each predefined tag (upcase) as an instance method and a constant
|
24
|
+
# deprecated: define each predefined tag (downcase) as the alias of the instance method
|
25
|
+
|
26
|
+
HTML_TAGS.each do |tag|
|
27
|
+
|
28
|
+
define_method(tag.upcase) do |*params, &children|
|
29
|
+
RenderingContext.render(tag, *params, &children)
|
30
|
+
end
|
31
|
+
|
32
|
+
const_set tag.upcase, tag
|
33
|
+
end
|
34
|
+
|
35
|
+
# this is used for haml style (i.e. DIV.foo.bar) class tags which is deprecated
|
36
|
+
def self.html_tag_class_for(tag)
|
37
|
+
downcased_tag = tag.downcase
|
38
|
+
if tag =~ /^[A-Z]+$/ && HTML_TAGS.include?(downcased_tag)
|
39
|
+
Object.const_set tag, ReactWrapper.create_element(downcased_tag)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# use method_missing to look up component names in the form of "Foo(..)"
|
44
|
+
# where there is no preceeding scope.
|
45
|
+
|
46
|
+
def method_missing(name, *params, &children)
|
47
|
+
component = find_component(name)
|
48
|
+
return RenderingContext.render(component, *params, &children) if component
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
# install methods with the same name as the component in the parent class/module
|
53
|
+
# thus component names in the form Foo::Bar(...) will work
|
54
|
+
|
55
|
+
class << self
|
56
|
+
def included(component)
|
57
|
+
name, parent = find_name_and_parent(component)
|
58
|
+
tag_names_module = Module.new do
|
59
|
+
define_method name do |*params, &children|
|
60
|
+
RenderingContext.render(component, *params, &children)
|
61
|
+
end
|
62
|
+
# handle deprecated _as_node style
|
63
|
+
define_method "#{name}_as_node" do |*params, &children|
|
64
|
+
RenderingContext.build_only(component, *params, &children)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
parent.extend(tag_names_module)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def find_name_and_parent(component)
|
73
|
+
split_name = component.name && component.name.split('::')
|
74
|
+
if split_name && split_name.length > 1
|
75
|
+
[split_name.last, split_name.inject([Module]) { |a, e| a + [a.last.const_get(e)] }[-2]]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def find_component(name)
|
83
|
+
component = lookup_const(name)
|
84
|
+
if component && !component.method_defined?(:render)
|
85
|
+
raise "#{name} does not appear to be a react component."
|
86
|
+
end
|
87
|
+
component || Object._reactrb_import_component_class(name)
|
88
|
+
end
|
89
|
+
|
90
|
+
def lookup_const(name)
|
91
|
+
return nil unless name =~ /^[A-Z]/
|
92
|
+
scopes = self.class.name.to_s.split('::').inject([Module]) do |nesting, next_const|
|
93
|
+
nesting + [nesting.last.const_get(next_const)]
|
94
|
+
end.reverse
|
95
|
+
scope = scopes.detect { |s| s.const_defined?(name) }
|
96
|
+
scope.const_get(name) if scope
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
unless Object.respond_to? :_reactrb_import_component_class
|
104
|
+
class Object
|
105
|
+
def self._reactrb_import_component_class(_name)
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
module Internal
|
3
|
+
module Component
|
4
|
+
class TopLevelRailsComponent
|
5
|
+
include Hyperstack::Component
|
6
|
+
|
7
|
+
def self.search_path
|
8
|
+
@search_path ||= [Object]
|
9
|
+
end
|
10
|
+
|
11
|
+
export_component
|
12
|
+
|
13
|
+
param :component_name
|
14
|
+
param :controller
|
15
|
+
param :render_params
|
16
|
+
|
17
|
+
backtrace :off
|
18
|
+
|
19
|
+
def render
|
20
|
+
top_level_render
|
21
|
+
end
|
22
|
+
|
23
|
+
def top_level_render
|
24
|
+
paths_searched = []
|
25
|
+
component = nil
|
26
|
+
if @ComponentName.start_with?('::')
|
27
|
+
# if absolute path of component is given, look it up and fail if not found
|
28
|
+
paths_searched << @ComponentName
|
29
|
+
component = begin
|
30
|
+
Object.const_get(@ComponentName)
|
31
|
+
rescue NameError
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
else
|
35
|
+
# if relative path is given, look it up like this
|
36
|
+
# 1) we check each path + controller-name + component-name
|
37
|
+
# 2) if we can't find it there we check each path + component-name
|
38
|
+
# if we can't find it we just try const_get
|
39
|
+
# so (assuming controller name is Home)
|
40
|
+
# ::Foo::Bar will only resolve to some component named ::Foo::Bar
|
41
|
+
# but Foo::Bar will check (in this order) ::Home::Foo::Bar, ::Components::Home::Foo::Bar, ::Foo::Bar, ::Components::Foo::Bar
|
42
|
+
self.class.search_path.each do |scope|
|
43
|
+
paths_searched << "#{scope.name}::#{@Controller}::#{@ComponentName}"
|
44
|
+
component = begin
|
45
|
+
scope.const_get(@Controller, false).const_get(@ComponentName, false)
|
46
|
+
rescue NameError
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
break if component != nil
|
50
|
+
end
|
51
|
+
unless component
|
52
|
+
self.class.search_path.each do |scope|
|
53
|
+
paths_searched << "#{scope.name}::#{@ComponentName}"
|
54
|
+
component = begin
|
55
|
+
scope.const_get(@ComponentName, false)
|
56
|
+
rescue NameError
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
break if component != nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
return RenderingContext.render(component, @RenderParams) if component && component.method_defined?(:render)
|
64
|
+
raise "Could not find component class '#{@ComponentName}' for @Controller '#{@Controller}' in any component directory. Tried [#{paths_searched.join(", ")}]"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Module
|
72
|
+
def add_to_react_search_path(replace_search_path = nil)
|
73
|
+
if replace_search_path
|
74
|
+
Hyperstack::Internal::Component::TopLevelRailsComponent.search_path = [self]
|
75
|
+
elsif !Hyperstack::Internal::Component::TopLevelRailsComponent.search_path.include? self
|
76
|
+
Hyperstack::Internal::Component::TopLevelRailsComponent.search_path << self
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module Components
|
82
|
+
add_to_react_search_path
|
83
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
module Internal
|
3
|
+
module Component
|
4
|
+
class Validator
|
5
|
+
|
6
|
+
attr_accessor :errors
|
7
|
+
attr_reader :props_wrapper
|
8
|
+
private :errors, :props_wrapper
|
9
|
+
|
10
|
+
def copy(new_props_wrapper)
|
11
|
+
Validator.new(new_props_wrapper).tap do |c|
|
12
|
+
%i[@allow_undefined_props @rules @errors].each do |var|
|
13
|
+
c.instance_variable_set(var, instance_variable_get(var).dup)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(props_wrapper = Class.new(PropsWrapper))
|
19
|
+
@props_wrapper = props_wrapper
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.build(&block)
|
23
|
+
new.build(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def build(&block)
|
27
|
+
instance_eval(&block)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def requires(name, options = {})
|
32
|
+
options[:required] = true
|
33
|
+
define_rule(name, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def optional(name, options = {})
|
37
|
+
options[:required] = false
|
38
|
+
define_rule(name, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def event(name)
|
42
|
+
rules[name] = coerce_native_hash_values(default: nil, type: Proc, allow_nil: true)
|
43
|
+
end
|
44
|
+
|
45
|
+
def all_other_params(name)
|
46
|
+
@allow_undefined_props = true
|
47
|
+
props_wrapper.define_all_others(name) { |props| props.reject { |name, value| rules[name] } }
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate(props)
|
51
|
+
self.errors = []
|
52
|
+
validate_undefined(props) unless allow_undefined_props?
|
53
|
+
props = coerce_native_hash_values(defined_props(props))
|
54
|
+
validate_required(props)
|
55
|
+
props.each do |name, value|
|
56
|
+
validate_types(name, value)
|
57
|
+
validate_allowed(name, value)
|
58
|
+
end
|
59
|
+
errors
|
60
|
+
end
|
61
|
+
|
62
|
+
def default_props
|
63
|
+
rules
|
64
|
+
.select {|key, value| value.keys.include?("default") }
|
65
|
+
.inject({}) {|memo, (k,v)| memo[k] = v[:default]; memo}
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def defined_props(props)
|
71
|
+
props.select { |name| rules.keys.include?(name) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def allow_undefined_props?
|
75
|
+
!!@allow_undefined_props
|
76
|
+
end
|
77
|
+
|
78
|
+
def rules
|
79
|
+
@rules ||= { children: { required: false } }
|
80
|
+
end
|
81
|
+
|
82
|
+
def define_rule(name, options = {})
|
83
|
+
rules[name] = coerce_native_hash_values(options)
|
84
|
+
props_wrapper.define_param(name, options[:type], options[:alias])
|
85
|
+
end
|
86
|
+
|
87
|
+
def errors
|
88
|
+
@errors ||= []
|
89
|
+
end
|
90
|
+
|
91
|
+
def validate_types(prop_name, value)
|
92
|
+
return unless klass = rules[prop_name][:type]
|
93
|
+
if !klass.is_a?(Array)
|
94
|
+
allow_nil = !!rules[prop_name][:allow_nil]
|
95
|
+
type_check("`#{prop_name}`", value, klass, allow_nil)
|
96
|
+
elsif klass.length > 0
|
97
|
+
validate_value_array(prop_name, value)
|
98
|
+
else
|
99
|
+
allow_nil = !!rules[prop_name][:allow_nil]
|
100
|
+
type_check("`#{prop_name}`", value, Array, allow_nil)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def type_check(prop_name, value, klass, allow_nil)
|
105
|
+
return if allow_nil && value.nil?
|
106
|
+
return if value.is_a?(klass)
|
107
|
+
return if klass.respond_to?(:_react_param_conversion) &&
|
108
|
+
klass._react_param_conversion(value, :validate_only)
|
109
|
+
errors << "Provided prop #{prop_name} could not be converted to #{klass}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def validate_allowed(prop_name, value)
|
113
|
+
return unless values = rules[prop_name][:values]
|
114
|
+
return if values.include?(value)
|
115
|
+
errors << "Value `#{value}` for prop `#{prop_name}` is not an allowed value"
|
116
|
+
end
|
117
|
+
|
118
|
+
def validate_required(props)
|
119
|
+
(rules.keys - props.keys).each do |name|
|
120
|
+
next unless rules[name][:required]
|
121
|
+
errors << "Required prop `#{name}` was not specified"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def validate_undefined(props)
|
126
|
+
(props.keys - rules.keys).each do |prop_name|
|
127
|
+
errors << "Provided prop `#{prop_name}` not specified in spec"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def validate_value_array(name, value)
|
132
|
+
klass = rules[name][:type]
|
133
|
+
allow_nil = !!rules[name][:allow_nil]
|
134
|
+
value.each_with_index do |item, index|
|
135
|
+
type_check("`#{name}`[#{index}]", Native(item), klass[0], allow_nil)
|
136
|
+
end
|
137
|
+
rescue NoMethodError
|
138
|
+
errors << "Provided prop `#{name}` was not an Array"
|
139
|
+
end
|
140
|
+
|
141
|
+
def coerce_native_hash_values(hash)
|
142
|
+
hash.each do |key, value|
|
143
|
+
hash[key] = Native(value)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/react/react-source.rb
CHANGED
@@ -9,9 +9,9 @@ if RUBY_ENGINE == 'opal'
|
|
9
9
|
require 'react.js'
|
10
10
|
require "react-server.js"
|
11
11
|
else
|
12
|
-
require "
|
12
|
+
require "hyperstack/internal/component"
|
13
13
|
require "react/rails/asset_variant"
|
14
|
-
variant =
|
14
|
+
variant = Hyperstack.env.production? ? 'production' : 'development'
|
15
15
|
react_directory = React::Rails::AssetVariant.new({environment: variant}).react_directory
|
16
16
|
Opal.append_path react_directory.untaint
|
17
17
|
end
|