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
data/lib/react/test/rspec.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'react/test/dsl'
|
2
|
-
require 'react/test/matchers/render_html_matcher'
|
3
|
-
|
4
|
-
RSpec.configure do |config|
|
5
|
-
config.include React::Test::DSL, type: :component
|
6
|
-
config.include React::Test::Matchers, type: :component
|
7
|
-
|
8
|
-
config.after do
|
9
|
-
React::Test.reset_session!
|
10
|
-
end
|
11
|
-
|
12
|
-
config.before do
|
13
|
-
# nothing yet
|
14
|
-
end
|
15
|
-
end
|
data/lib/react/test/session.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
module React
|
2
|
-
module Test
|
3
|
-
class Session
|
4
|
-
DSL_METHODS = %i[mount instance update_params html].freeze
|
5
|
-
|
6
|
-
def mount(component_klass, params = {})
|
7
|
-
@element = React.create_element(component_klass, params)
|
8
|
-
instance
|
9
|
-
end
|
10
|
-
|
11
|
-
def instance
|
12
|
-
unless @instance
|
13
|
-
@container = `document.createElement('div')`
|
14
|
-
@instance = React.render(@element, @container)
|
15
|
-
end
|
16
|
-
@instance
|
17
|
-
end
|
18
|
-
|
19
|
-
def update_params(params, &block)
|
20
|
-
cloned_element = React::Element.new(`React.cloneElement(#{@element.to_n}, #{params.to_n})`)
|
21
|
-
React.render(cloned_element, @container, &block)
|
22
|
-
nil
|
23
|
-
end
|
24
|
-
|
25
|
-
def html
|
26
|
-
html = `#@container.innerHTML`
|
27
|
-
%x{
|
28
|
-
var REGEX_REMOVE_ROOT_IDS = /\s?data-reactroot="[^"]*"/g;
|
29
|
-
var REGEX_REMOVE_IDS = /\s?data-reactid="[^"]+"/g;
|
30
|
-
html = html.replace(REGEX_REMOVE_ROOT_IDS, '');
|
31
|
-
html = html.replace(REGEX_REMOVE_IDS, '');
|
32
|
-
}
|
33
|
-
return html
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/lib/react/test/utils.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
module React
|
2
|
-
module Test
|
3
|
-
class Utils
|
4
|
-
def self.render_component_into_document(component, args = {})
|
5
|
-
element = React.create_element(component, args)
|
6
|
-
render_into_document(element)
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.render_into_document(element)
|
10
|
-
raise "You should pass a valid React::Element" unless React.is_valid_element?(element)
|
11
|
-
dom_el = `document.body.querySelector('div[data-react-class="React.TopLevelRailsComponent"]').appendChild(document.createElement('div'))`
|
12
|
-
React.render(element, dom_el)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.simulate_click(element)
|
16
|
-
# element must be a component or a dom node or a element
|
17
|
-
el = if `typeof element.nodeType !== "undefined"`
|
18
|
-
element
|
19
|
-
elsif element.respond_to? :dom_node
|
20
|
-
element.dom_node
|
21
|
-
elsif element.is_a? React::Element
|
22
|
-
`ReactDOM.findDOMNode(#{element.to_n}.native)`
|
23
|
-
else
|
24
|
-
element
|
25
|
-
end
|
26
|
-
%x{
|
27
|
-
var evob = new MouseEvent('click', {
|
28
|
-
view: window,
|
29
|
-
bubbles: true,
|
30
|
-
cancelable: true
|
31
|
-
});
|
32
|
-
el.dispatchEvent(evob);
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.simulate_keydown(element, key_name = "Enter")
|
37
|
-
# element must be a component or a dom node or a element
|
38
|
-
el = if `typeof element.nodeType !== "undefined"`
|
39
|
-
element
|
40
|
-
elsif element.respond_to? :dom_node
|
41
|
-
element.dom_node
|
42
|
-
elsif element.is_a? React::Element
|
43
|
-
`ReactDOM.findDOMNode(#{element.to_n}.native)`
|
44
|
-
else
|
45
|
-
element
|
46
|
-
end
|
47
|
-
%x{
|
48
|
-
var evob = new KeyboardEvent('keydown', { key: key_name, bubbles: true, cancelable: true });
|
49
|
-
el.dispatchEvent(evob);
|
50
|
-
}
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.simulate_submit(element)
|
54
|
-
# element must be a component or a dom node or a element
|
55
|
-
el = if `typeof element.nodeType !== "undefined"`
|
56
|
-
element
|
57
|
-
elsif element.respond_to? :dom_node
|
58
|
-
element.dom_node
|
59
|
-
elsif element.is_a? React::Element
|
60
|
-
`ReactDOM.findDOMNode(#{element.to_n}.native)`
|
61
|
-
else
|
62
|
-
element
|
63
|
-
end
|
64
|
-
%x{
|
65
|
-
var evob = new Event('submit', { bubbles: true, cancelable: true });
|
66
|
-
el.dispatchEvent(evob);
|
67
|
-
}
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
data/lib/react/top_level.rb
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
require "native"
|
2
|
-
require 'active_support/core_ext/object/try'
|
3
|
-
require 'react/component/tags'
|
4
|
-
require 'react/component/base'
|
5
|
-
|
6
|
-
module React
|
7
|
-
|
8
|
-
ATTRIBUTES = %w(accept acceptCharset accessKey action allowFullScreen allowTransparency alt
|
9
|
-
async autoComplete autoPlay cellPadding cellSpacing charSet checked classID
|
10
|
-
className cols colSpan content contentEditable contextMenu controls coords
|
11
|
-
crossOrigin data dateTime defer dir disabled download draggable encType form
|
12
|
-
formAction formEncType formMethod formNoValidate formTarget frameBorder height
|
13
|
-
hidden href hrefLang htmlFor httpEquiv icon id label lang list loop manifest
|
14
|
-
marginHeight marginWidth max maxLength media mediaGroup method min multiple
|
15
|
-
muted name noValidate open pattern placeholder poster preload radioGroup
|
16
|
-
readOnly rel required role rows rowSpan sandbox scope scrolling seamless
|
17
|
-
selected shape size sizes span spellCheck src srcDoc srcSet start step style
|
18
|
-
tabIndex target title type useMap value width wmode dangerouslySetInnerHTML) +
|
19
|
-
#SVG ATTRIBUTES
|
20
|
-
%w(clipPath cx cy d dx dy fill fillOpacity fontFamily
|
21
|
-
fontSize fx fy gradientTransform gradientUnits markerEnd
|
22
|
-
markerMid markerStart offset opacity patternContentUnits
|
23
|
-
patternUnits points preserveAspectRatio r rx ry spreadMethod
|
24
|
-
stopColor stopOpacity stroke strokeDasharray strokeLinecap
|
25
|
-
strokeOpacity strokeWidth textAnchor transform version
|
26
|
-
viewBox x1 x2 x xlinkActuate xlinkArcrole xlinkHref xlinkRole
|
27
|
-
xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace y1 y2 y)
|
28
|
-
HASH_ATTRIBUTES = %w(data aria)
|
29
|
-
HTML_TAGS = React::Component::Tags::HTML_TAGS
|
30
|
-
|
31
|
-
def self.html_tag?(name)
|
32
|
-
tags = HTML_TAGS
|
33
|
-
%x{
|
34
|
-
for(var i = 0; i < tags.length; i++) {
|
35
|
-
if(tags[i] === name)
|
36
|
-
return true;
|
37
|
-
}
|
38
|
-
return false;
|
39
|
-
}
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.html_attr?(name)
|
43
|
-
attrs = ATTRIBUTES
|
44
|
-
%x{
|
45
|
-
for(var i = 0; i < attrs.length; i++) {
|
46
|
-
if(attrs[i] === name)
|
47
|
-
return true;
|
48
|
-
}
|
49
|
-
return false;
|
50
|
-
}
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.create_element(type, properties = {}, &block)
|
54
|
-
React::API.create_element(type, properties, &block)
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.render(element, container)
|
58
|
-
%x{
|
59
|
-
console.error(
|
60
|
-
"Warning: Using deprecated behavior of `React.render`,",
|
61
|
-
"require \"react/top_level_render\" to get the correct behavior."
|
62
|
-
);
|
63
|
-
}
|
64
|
-
container = `container.$$class ? container[0] : container`
|
65
|
-
if !(`typeof ReactDOM === 'undefined'`)
|
66
|
-
component = Native(`ReactDOM.render(#{element.to_n}, container, function(){#{yield if block_given?}})`) # v0.15+
|
67
|
-
else
|
68
|
-
raise "render is not defined. In React >= v15 you must import it with ReactDOM"
|
69
|
-
end
|
70
|
-
|
71
|
-
component.class.include(React::Component::API)
|
72
|
-
component
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.is_valid_element(element)
|
76
|
-
%x{ console.error("Warning: `is_valid_element` is deprecated in favor of `is_valid_element?`."); }
|
77
|
-
element.kind_of?(React::Element) && `React.isValidElement(#{element.to_n})`
|
78
|
-
end
|
79
|
-
|
80
|
-
def self.is_valid_element?(element)
|
81
|
-
element.kind_of?(React::Element) && `React.isValidElement(#{element.to_n})`
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.render_to_string(element)
|
85
|
-
%x{ console.error("Warning: `React.render_to_string` is deprecated in favor of `React::Server.render_to_string`."); }
|
86
|
-
if !(`typeof ReactDOMServer === 'undefined'`)
|
87
|
-
React::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
|
88
|
-
else
|
89
|
-
raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.render_to_static_markup(element)
|
94
|
-
%x{ console.error("Warning: `React.render_to_static_markup` is deprecated in favor of `React::Server.render_to_static_markup`."); }
|
95
|
-
if !(`typeof ReactDOMServer === 'undefined'`)
|
96
|
-
React::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
|
97
|
-
else
|
98
|
-
raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.unmount_component_at_node(node)
|
103
|
-
if !(`typeof ReactDOM === 'undefined'`)
|
104
|
-
`ReactDOM.unmountComponentAtNode(node.$$class ? node[0] : node)` # v0.15+
|
105
|
-
else
|
106
|
-
raise "unmountComponentAtNode is not defined. In React >= v15 you must import it with ReactDOM"
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module React
|
2
|
-
def self.render(element, container)
|
3
|
-
raise "ReactDOM.render is not defined. In React >= v15 you must import it with ReactDOM" if (`typeof ReactDOM === 'undefined'`)
|
4
|
-
|
5
|
-
container = `container.$$class ? container[0] : container`
|
6
|
-
|
7
|
-
if block_given?
|
8
|
-
cb = %x{
|
9
|
-
function(){
|
10
|
-
setTimeout(function(){
|
11
|
-
#{yield}
|
12
|
-
}, 0)
|
13
|
-
}
|
14
|
-
}
|
15
|
-
native = `ReactDOM.render(#{element.to_n}, container, cb)`
|
16
|
-
else
|
17
|
-
native = `ReactDOM.render(#{element.to_n}, container)`
|
18
|
-
end
|
19
|
-
|
20
|
-
return unless `#{native} !== null`
|
21
|
-
|
22
|
-
if `#{native}.__opalInstance !== undefined && #{native}.__opalInstance !== null`
|
23
|
-
`#{native}.__opalInstance`
|
24
|
-
elsif `ReactDOM.findDOMNode !== undefined && #{native}.nodeType === undefined`
|
25
|
-
`ReactDOM.findDOMNode(#{native})`
|
26
|
-
else
|
27
|
-
native
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/lib/react/validator.rb
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
module React
|
2
|
-
class Validator
|
3
|
-
attr_accessor :errors
|
4
|
-
attr_reader :props_wrapper
|
5
|
-
private :errors, :props_wrapper
|
6
|
-
|
7
|
-
def initialize(props_wrapper = Class.new(Component::PropsWrapper))
|
8
|
-
@props_wrapper = props_wrapper
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.build(&block)
|
12
|
-
self.new.build(&block)
|
13
|
-
end
|
14
|
-
|
15
|
-
def build(&block)
|
16
|
-
instance_eval(&block)
|
17
|
-
self
|
18
|
-
end
|
19
|
-
|
20
|
-
def requires(name, options = {})
|
21
|
-
options[:required] = true
|
22
|
-
define_rule(name, options)
|
23
|
-
end
|
24
|
-
|
25
|
-
def optional(name, options = {})
|
26
|
-
options[:required] = false
|
27
|
-
define_rule(name, options)
|
28
|
-
end
|
29
|
-
|
30
|
-
def all_other_params(name)
|
31
|
-
@allow_undefined_props = true
|
32
|
-
props_wrapper.define_all_others(name) { |props| props.reject { |name, value| rules[name] } }
|
33
|
-
end
|
34
|
-
|
35
|
-
def validate(props)
|
36
|
-
self.errors = []
|
37
|
-
validate_undefined(props) unless allow_undefined_props?
|
38
|
-
props = coerce_native_hash_values(defined_props(props))
|
39
|
-
validate_required(props)
|
40
|
-
props.each do |name, value|
|
41
|
-
validate_types(name, value)
|
42
|
-
validate_allowed(name, value)
|
43
|
-
end
|
44
|
-
errors
|
45
|
-
end
|
46
|
-
|
47
|
-
def default_props
|
48
|
-
rules
|
49
|
-
.select {|key, value| value.keys.include?("default") }
|
50
|
-
.inject({}) {|memo, (k,v)| memo[k] = v[:default]; memo}
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def defined_props(props)
|
56
|
-
props.select { |name| rules.keys.include?(name) }
|
57
|
-
end
|
58
|
-
|
59
|
-
def allow_undefined_props?
|
60
|
-
!!@allow_undefined_props
|
61
|
-
end
|
62
|
-
|
63
|
-
def rules
|
64
|
-
@rules ||= { children: { required: false } }
|
65
|
-
end
|
66
|
-
|
67
|
-
def define_rule(name, options = {})
|
68
|
-
rules[name] = coerce_native_hash_values(options)
|
69
|
-
props_wrapper.define_param(name, options[:type])
|
70
|
-
end
|
71
|
-
|
72
|
-
def errors
|
73
|
-
@errors ||= []
|
74
|
-
end
|
75
|
-
|
76
|
-
def validate_types(prop_name, value)
|
77
|
-
return unless klass = rules[prop_name][:type]
|
78
|
-
if !klass.is_a?(Array)
|
79
|
-
allow_nil = !!rules[prop_name][:allow_nil]
|
80
|
-
type_check("`#{prop_name}`", value, klass, allow_nil)
|
81
|
-
elsif klass.length > 0
|
82
|
-
validate_value_array(prop_name, value)
|
83
|
-
else
|
84
|
-
allow_nil = !!rules[prop_name][:allow_nil]
|
85
|
-
type_check("`#{prop_name}`", value, Array, allow_nil)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def type_check(prop_name, value, klass, allow_nil)
|
90
|
-
return if allow_nil && value.nil?
|
91
|
-
return if value.is_a?(klass)
|
92
|
-
return if klass.respond_to?(:_react_param_conversion) &&
|
93
|
-
klass._react_param_conversion(value, :validate_only)
|
94
|
-
errors << "Provided prop #{prop_name} could not be converted to #{klass}"
|
95
|
-
end
|
96
|
-
|
97
|
-
def validate_allowed(prop_name, value)
|
98
|
-
return unless values = rules[prop_name][:values]
|
99
|
-
return if values.include?(value)
|
100
|
-
errors << "Value `#{value}` for prop `#{prop_name}` is not an allowed value"
|
101
|
-
end
|
102
|
-
|
103
|
-
def validate_required(props)
|
104
|
-
(rules.keys - props.keys).each do |name|
|
105
|
-
next unless rules[name][:required]
|
106
|
-
errors << "Required prop `#{name}` was not specified"
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def validate_undefined(props)
|
111
|
-
(props.keys - rules.keys).each do |prop_name|
|
112
|
-
errors << "Provided prop `#{prop_name}` not specified in spec"
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def validate_value_array(name, value)
|
117
|
-
klass = rules[name][:type]
|
118
|
-
allow_nil = !!rules[name][:allow_nil]
|
119
|
-
value.each_with_index do |item, index|
|
120
|
-
type_check("`#{name}`[#{index}]", Native(item), klass[0], allow_nil)
|
121
|
-
end
|
122
|
-
rescue NoMethodError
|
123
|
-
errors << "Provided prop `#{name}` was not an Array"
|
124
|
-
end
|
125
|
-
|
126
|
-
def coerce_native_hash_values(hash)
|
127
|
-
hash.each do |key, value|
|
128
|
-
hash[key] = Native(value)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
module ReactiveRuby
|
2
|
-
class ComponentLoader
|
3
|
-
attr_reader :v8_context
|
4
|
-
private :v8_context
|
5
|
-
|
6
|
-
def initialize(v8_context)
|
7
|
-
unless v8_context
|
8
|
-
raise ArgumentError.new('Could not obtain ExecJS runtime context')
|
9
|
-
end
|
10
|
-
@v8_context = v8_context
|
11
|
-
end
|
12
|
-
|
13
|
-
def load(file = components)
|
14
|
-
return true if loaded?
|
15
|
-
!!v8_context.eval(opal(file))
|
16
|
-
end
|
17
|
-
|
18
|
-
def load!(file = components)
|
19
|
-
return true if loaded?
|
20
|
-
self.load(file)
|
21
|
-
ensure
|
22
|
-
raise "No HyperReact components found in #{components}" unless loaded?
|
23
|
-
end
|
24
|
-
|
25
|
-
def loaded?
|
26
|
-
!!v8_context.eval('Opal.React !== undefined')
|
27
|
-
rescue ::ExecJS::Error
|
28
|
-
false
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def components
|
34
|
-
opts = ::Rails.configuration.react.server_renderer_options
|
35
|
-
return opts[:files].first.gsub(/.js$/,'') if opts && opts[:files]
|
36
|
-
'components'
|
37
|
-
end
|
38
|
-
|
39
|
-
def opal(file)
|
40
|
-
Opal::Sprockets.load_asset(file)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,233 +0,0 @@
|
|
1
|
-
require "react/config"
|
2
|
-
|
3
|
-
module React
|
4
|
-
module IsomorphicHelpers
|
5
|
-
def self.included(base)
|
6
|
-
base.extend(ClassMethods)
|
7
|
-
end
|
8
|
-
|
9
|
-
if RUBY_ENGINE != 'opal'
|
10
|
-
def self.load_context(ctx, controller, name = nil)
|
11
|
-
@context = Context.new("#{controller.object_id}-#{Time.now.to_i}", ctx, controller, name)
|
12
|
-
@context.load_opal_context
|
13
|
-
::Rails.logger.debug "************************** React Server Context Initialized #{name} #{Time.now.to_f} *********************************************"
|
14
|
-
@context
|
15
|
-
end
|
16
|
-
else
|
17
|
-
def self.load_context(unique_id = nil, name = nil)
|
18
|
-
# can be called on the client to force re-initialization for testing purposes
|
19
|
-
if !unique_id || !@context || @context.unique_id != unique_id
|
20
|
-
if on_opal_server?
|
21
|
-
`console.history = []` rescue nil
|
22
|
-
message = "************************ React Prerendering Context Initialized #{name} ***********************"
|
23
|
-
else
|
24
|
-
message = "************************ React Browser Context Initialized ****************************"
|
25
|
-
end
|
26
|
-
log(message)
|
27
|
-
@context = Context.new(unique_id)
|
28
|
-
end
|
29
|
-
@context
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.context
|
34
|
-
@context
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.log(message, message_type = :info)
|
38
|
-
message = [message] unless message.is_a? Array
|
39
|
-
|
40
|
-
if (message_type == :info || message_type == :warning) && Hyperloop.env.production?
|
41
|
-
return
|
42
|
-
end
|
43
|
-
|
44
|
-
if message_type == :info
|
45
|
-
if on_opal_server?
|
46
|
-
style = 'background: #00FFFF; color: red'
|
47
|
-
else
|
48
|
-
style = 'background: #222; color: #bada55'
|
49
|
-
end
|
50
|
-
message = ["%c" + message[0], style]+message[1..-1]
|
51
|
-
`console.log.apply(console, message)`
|
52
|
-
elsif message_type == :warning
|
53
|
-
`console.warn.apply(console, message)`
|
54
|
-
else
|
55
|
-
`console.error.apply(console, message)`
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
if RUBY_ENGINE != 'opal'
|
60
|
-
def self.on_opal_server?
|
61
|
-
false
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.on_opal_client?
|
65
|
-
false
|
66
|
-
end
|
67
|
-
else
|
68
|
-
def self.on_opal_server?
|
69
|
-
`typeof Opal.global.document === 'undefined'`
|
70
|
-
end
|
71
|
-
|
72
|
-
def self.on_opal_client?
|
73
|
-
!on_opal_server?
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def log(*args)
|
78
|
-
IsomorphicHelpers.log(*args)
|
79
|
-
end
|
80
|
-
|
81
|
-
def on_opal_server?
|
82
|
-
self.class.on_opal_server?
|
83
|
-
end
|
84
|
-
|
85
|
-
def on_opal_client?
|
86
|
-
self.class.on_opal_client?
|
87
|
-
end
|
88
|
-
|
89
|
-
def self.prerender_footers(controller = nil)
|
90
|
-
footer = Context.prerender_footer_blocks.collect { |block| block.call controller }.join("\n")
|
91
|
-
if RUBY_ENGINE != 'opal'
|
92
|
-
footer = (footer + @context.send_to_opal(:prerender_footers).to_s) if @context
|
93
|
-
footer = footer.html_safe
|
94
|
-
end
|
95
|
-
footer
|
96
|
-
end
|
97
|
-
|
98
|
-
class Context
|
99
|
-
attr_reader :controller
|
100
|
-
attr_reader :unique_id
|
101
|
-
|
102
|
-
def self.define_isomorphic_method(method_name, &block)
|
103
|
-
@@ctx_methods ||= {}
|
104
|
-
@@ctx_methods[method_name] = block
|
105
|
-
end
|
106
|
-
|
107
|
-
def self.before_first_mount_blocks
|
108
|
-
@before_first_mount_blocks ||= []
|
109
|
-
end
|
110
|
-
|
111
|
-
def self.prerender_footer_blocks
|
112
|
-
@prerender_footer_blocks ||= []
|
113
|
-
end
|
114
|
-
|
115
|
-
def initialize(unique_id, ctx = nil, controller = nil, cname = nil)
|
116
|
-
@unique_id = unique_id
|
117
|
-
@cname = cname
|
118
|
-
if RUBY_ENGINE != 'opal'
|
119
|
-
@controller = controller
|
120
|
-
@ctx = ctx
|
121
|
-
if defined? @@ctx_methods
|
122
|
-
@@ctx_methods.each do |method_name, block|
|
123
|
-
@ctx.attach("ServerSideIsomorphicMethod.#{method_name}", proc{|args| block.call(args.to_json)})
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
Hyperloop::Application::Boot.run(context: self)
|
128
|
-
self.class.before_first_mount_blocks.each { |block| block.call(self) }
|
129
|
-
end
|
130
|
-
|
131
|
-
def load_opal_context
|
132
|
-
send_to_opal(:load_context, @unique_id, @cname)
|
133
|
-
end
|
134
|
-
|
135
|
-
def eval(js)
|
136
|
-
@ctx.eval(js) if @ctx
|
137
|
-
end
|
138
|
-
|
139
|
-
def send_to_opal(method_name, *args)
|
140
|
-
return unless @ctx
|
141
|
-
args = [1] if args.length == 0
|
142
|
-
::ReactiveRuby::ComponentLoader.new(@ctx).load!
|
143
|
-
method_args = args.collect do |arg|
|
144
|
-
quarg = "#{arg}".tr('"', "'")
|
145
|
-
"\"#{quarg}\""
|
146
|
-
end.join(', ')
|
147
|
-
@ctx.eval("Opal.React.$const_get('IsomorphicHelpers').$#{method_name}(#{method_args})")
|
148
|
-
end
|
149
|
-
|
150
|
-
def self.register_before_first_mount_block(&block)
|
151
|
-
before_first_mount_blocks << block
|
152
|
-
end
|
153
|
-
|
154
|
-
def self.register_prerender_footer_block(&block)
|
155
|
-
prerender_footer_blocks << block
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
class IsomorphicProcCall
|
160
|
-
|
161
|
-
attr_reader :context
|
162
|
-
|
163
|
-
def result
|
164
|
-
@result.first if @result
|
165
|
-
end
|
166
|
-
|
167
|
-
def initialize(name, block, context, *args)
|
168
|
-
@name = name
|
169
|
-
@context = context
|
170
|
-
block.call(self, *args)
|
171
|
-
@result ||= send_to_server(*args)
|
172
|
-
end
|
173
|
-
|
174
|
-
def when_on_client(&block)
|
175
|
-
@result = [block.call] if IsomorphicHelpers.on_opal_client?
|
176
|
-
end
|
177
|
-
|
178
|
-
def send_to_server(*args)
|
179
|
-
if IsomorphicHelpers.on_opal_server?
|
180
|
-
method_string = "ServerSideIsomorphicMethod." + @name + "(" + args.to_json + ")"
|
181
|
-
@result = [JSON.parse(`eval(method_string)`)]
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
def when_on_server(&block)
|
186
|
-
@result = [block.call.to_json] unless IsomorphicHelpers.on_opal_client? || IsomorphicHelpers.on_opal_server?
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
module ClassMethods
|
191
|
-
def on_opal_server?
|
192
|
-
IsomorphicHelpers.on_opal_server?
|
193
|
-
end
|
194
|
-
|
195
|
-
def on_opal_client?
|
196
|
-
IsomorphicHelpers.on_opal_client?
|
197
|
-
end
|
198
|
-
|
199
|
-
def log(*args)
|
200
|
-
IsomorphicHelpers.log(*args)
|
201
|
-
end
|
202
|
-
|
203
|
-
def controller
|
204
|
-
IsomorphicHelpers.context.controller
|
205
|
-
end
|
206
|
-
|
207
|
-
def before_first_mount(&block)
|
208
|
-
React::IsomorphicHelpers::Context.register_before_first_mount_block(&block)
|
209
|
-
end
|
210
|
-
|
211
|
-
def prerender_footer(&block)
|
212
|
-
React::IsomorphicHelpers::Context.register_prerender_footer_block(&block)
|
213
|
-
end
|
214
|
-
|
215
|
-
if RUBY_ENGINE != 'opal'
|
216
|
-
def isomorphic_method(name, &block)
|
217
|
-
React::IsomorphicHelpers::Context.send(:define_isomorphic_method, name) do |args_as_json|
|
218
|
-
React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, self, *JSON.parse(args_as_json)).result
|
219
|
-
end
|
220
|
-
end
|
221
|
-
else
|
222
|
-
require 'json'
|
223
|
-
|
224
|
-
def isomorphic_method(name, &block)
|
225
|
-
self.class.send(:define_method, name) do | *args |
|
226
|
-
React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, self, *args).result
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|