hyper-react 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.codeclimate.yml +27 -0
- data/.gitignore +36 -0
- data/.rubocop.yml +1159 -0
- data/.travis.yml +29 -0
- data/Appraisals +20 -0
- data/CHANGELOG.md +93 -0
- data/Gemfile +6 -0
- data/LICENSE +19 -0
- data/README.md +121 -0
- data/Rakefile +33 -0
- data/UPGRADING.md +24 -0
- data/component-name-lookup.md +145 -0
- data/config.ru +25 -0
- data/gemfiles/opal_0.8_react_13.gemfile +13 -0
- data/gemfiles/opal_0.8_react_14.gemfile +13 -0
- data/gemfiles/opal_0.8_react_15.gemfile +13 -0
- data/gemfiles/opal_0.9_react_13.gemfile +13 -0
- data/gemfiles/opal_0.9_react_14.gemfile +13 -0
- data/gemfiles/opal_0.9_react_15.gemfile +13 -0
- data/hyper-react.gemspec +43 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +4 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
- data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
- data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
- data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
- data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +109 -0
- data/lib/hyper-react.rb +52 -0
- data/lib/rails-helpers/top_level_rails_component.rb +54 -0
- data/lib/react-sources/react-server.js +2 -0
- data/lib/react/api.rb +162 -0
- data/lib/react/callbacks.rb +42 -0
- data/lib/react/children.rb +30 -0
- data/lib/react/component.rb +139 -0
- data/lib/react/component/api.rb +50 -0
- data/lib/react/component/base.rb +9 -0
- data/lib/react/component/class_methods.rb +214 -0
- data/lib/react/component/dsl_instance_methods.rb +27 -0
- data/lib/react/component/params.rb +6 -0
- data/lib/react/component/props_wrapper.rb +83 -0
- data/lib/react/component/should_component_update.rb +98 -0
- data/lib/react/component/tags.rb +144 -0
- data/lib/react/element.rb +168 -0
- data/lib/react/event.rb +76 -0
- data/lib/react/ext/hash.rb +9 -0
- data/lib/react/ext/string.rb +8 -0
- data/lib/react/hash.rb +13 -0
- data/lib/react/native_library.rb +92 -0
- data/lib/react/object.rb +15 -0
- data/lib/react/observable.rb +29 -0
- data/lib/react/react-source.rb +9 -0
- data/lib/react/rendering_context.rb +142 -0
- data/lib/react/state.rb +190 -0
- data/lib/react/test.rb +16 -0
- data/lib/react/test/dsl.rb +17 -0
- data/lib/react/test/matchers/render_html_matcher.rb +49 -0
- data/lib/react/test/rspec.rb +15 -0
- data/lib/react/test/session.rb +46 -0
- data/lib/react/top_level.rb +132 -0
- data/lib/react/validator.rb +136 -0
- data/lib/reactive-ruby/component_loader.rb +49 -0
- data/lib/reactive-ruby/isomorphic_helpers.rb +197 -0
- data/lib/reactive-ruby/rails.rb +7 -0
- data/lib/reactive-ruby/rails/component_mount.rb +46 -0
- data/lib/reactive-ruby/rails/controller_helper.rb +15 -0
- data/lib/reactive-ruby/rails/railtie.rb +14 -0
- data/lib/reactive-ruby/serializers.rb +15 -0
- data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +42 -0
- data/lib/reactive-ruby/version.rb +3 -0
- data/lib/reactrb/auto-import.rb +32 -0
- data/lib/reactrb/deep-compare.rb +24 -0
- data/lib/reactrb/new-event-name-convention.rb +11 -0
- data/lib/sources/react-latest.js +21169 -0
- data/lib/sources/react-v13.js +21645 -0
- data/lib/sources/react-v14.js +20821 -0
- data/lib/sources/react-v15.js +21170 -0
- data/logo1.png +0 -0
- data/logo2.png +0 -0
- data/logo3.png +0 -0
- data/path_release_steps.md +9 -0
- data/spec/controller_helper_spec.rb +34 -0
- data/spec/index.html.erb +10 -0
- data/spec/react/callbacks_spec.rb +106 -0
- data/spec/react/children_spec.rb +76 -0
- data/spec/react/component/base_spec.rb +32 -0
- data/spec/react/component_spec.rb +872 -0
- data/spec/react/dsl_spec.rb +296 -0
- data/spec/react/element_spec.rb +136 -0
- data/spec/react/event_spec.rb +24 -0
- data/spec/react/native_library_spec.rb +344 -0
- data/spec/react/observable_spec.rb +7 -0
- data/spec/react/opal_jquery_extensions_spec.rb +66 -0
- data/spec/react/param_declaration_spec.rb +258 -0
- data/spec/react/react_spec.rb +209 -0
- data/spec/react/state_spec.rb +55 -0
- data/spec/react/test/dsl_spec.rb +43 -0
- data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
- data/spec/react/test/rspec_spec.rb +62 -0
- data/spec/react/test/session_spec.rb +100 -0
- data/spec/react/test/utils_spec.rb +45 -0
- data/spec/react/top_level_component_spec.rb +96 -0
- data/spec/react/tutorial/tutorial_spec.rb +36 -0
- data/spec/react/validator_spec.rb +124 -0
- data/spec/reactive-ruby/component_loader_spec.rb +71 -0
- data/spec/reactive-ruby/isomorphic_helpers_spec.rb +155 -0
- data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +10 -0
- data/spec/reactive-ruby/rails/component_mount_spec.rb +66 -0
- data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +35 -0
- data/spec/spec_helper.rb +115 -0
- data/spec/support/react/spec_helpers.rb +64 -0
- data/spec/vendor/es5-shim.min.js +6 -0
- data/spec/vendor/jquery-2.2.4.min.js +4 -0
- metadata +387 -0
data/lib/react/test.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'react/test'
|
|
2
|
+
|
|
3
|
+
module React
|
|
4
|
+
module Test
|
|
5
|
+
module DSL
|
|
6
|
+
def component
|
|
7
|
+
React::Test.current_session
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
Session::DSL_METHODS.each do |method|
|
|
11
|
+
define_method method do |*args, &block|
|
|
12
|
+
component.public_send(method, *args, &block)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module React
|
|
2
|
+
module Test
|
|
3
|
+
module Matchers
|
|
4
|
+
class RenderHTMLMatcher
|
|
5
|
+
def initialize(expected)
|
|
6
|
+
@expected = expected
|
|
7
|
+
@params = {}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def with_params(params)
|
|
11
|
+
@params = params
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def matches?(component)
|
|
16
|
+
@component = component
|
|
17
|
+
@actual = render_to_html
|
|
18
|
+
@expected == @actual
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def failure_message
|
|
22
|
+
failure_string
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def negative_failure_message
|
|
26
|
+
failure_string(:negative)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def render_to_html
|
|
32
|
+
element = React.create_element(@component, @params)
|
|
33
|
+
React.render_to_static_markup(element)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def failure_string(negative = false)
|
|
37
|
+
str = "expected '#{@component.name}' with params '#{@params}' to "
|
|
38
|
+
str = str + "not " if negative
|
|
39
|
+
str = str + "render '#{@expected}', but '#{@actual}' was rendered."
|
|
40
|
+
str
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def render(*args)
|
|
45
|
+
RenderHTMLMatcher.new(*args)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module React
|
|
2
|
+
module Test
|
|
3
|
+
class Session
|
|
4
|
+
DSL_METHODS = %i[mount instance native element update_params
|
|
5
|
+
force_update! html].freeze
|
|
6
|
+
|
|
7
|
+
def mount(component_klass, params = {})
|
|
8
|
+
@element = React.create_element(component_klass, params)
|
|
9
|
+
instance
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def instance
|
|
13
|
+
unless @instance
|
|
14
|
+
@native = Native(`React.addons.TestUtils.renderIntoDocument(#{element.to_n})`)
|
|
15
|
+
@instance = `#{@native.to_n}._getOpalInstance()`
|
|
16
|
+
end
|
|
17
|
+
@instance
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def native
|
|
21
|
+
@native
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def element
|
|
25
|
+
@element
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def update_params(params)
|
|
29
|
+
cloned_element = React::Element.new(`React.cloneElement(#{self.element.to_n}, #{params.to_n})`)
|
|
30
|
+
prev_container = `#{self.instance.dom_node}.parentNode`
|
|
31
|
+
React.render(cloned_element, prev_container)
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def force_update!
|
|
36
|
+
native.force_update!
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def html
|
|
40
|
+
# How can we get the current ReactElement w/o violating private APIs?
|
|
41
|
+
elem = Native(native[:_reactInternalInstance][:_currentElement])
|
|
42
|
+
React.render_to_static_markup(elem)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require "native"
|
|
2
|
+
require 'active_support'
|
|
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
|
+
container = `container.$$class ? container[0] : container`
|
|
59
|
+
if !(`typeof ReactDOM === 'undefined'`)
|
|
60
|
+
component = Native(`ReactDOM.render(#{element.to_n}, container, function(){#{yield if block_given?}})`) # v0.15+
|
|
61
|
+
elsif !(`typeof React.renderToString === 'undefined'`)
|
|
62
|
+
component = Native(`React.render(#{element.to_n}, container, function(){#{yield if block_given?}})`)
|
|
63
|
+
else
|
|
64
|
+
raise "render is not defined. In React >= v15 you must import it with ReactDOM"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
component.class.include(React::Component::API)
|
|
68
|
+
component
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.is_valid_element(element)
|
|
72
|
+
element.kind_of?(React::Element) && `React.isValidElement(#{element.to_n})`
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.render_to_string(element)
|
|
76
|
+
if !(`typeof ReactDOMServer === 'undefined'`)
|
|
77
|
+
React::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
|
|
78
|
+
elsif !(`typeof React.renderToString === 'undefined'`)
|
|
79
|
+
React::RenderingContext.build { `React.renderToString(#{element.to_n})` }
|
|
80
|
+
else
|
|
81
|
+
raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.render_to_static_markup(element)
|
|
86
|
+
if !(`typeof ReactDOMServer === 'undefined'`)
|
|
87
|
+
React::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
|
|
88
|
+
elsif !(`typeof React.renderToString === 'undefined'`)
|
|
89
|
+
React::RenderingContext.build { `React.renderToStaticMarkup(#{element.to_n})` }
|
|
90
|
+
else
|
|
91
|
+
raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def self.unmount_component_at_node(node)
|
|
96
|
+
if !(`typeof ReactDOM === 'undefined'`)
|
|
97
|
+
`ReactDOM.unmountComponentAtNode(node.$$class ? node[0] : node)` # v0.15+
|
|
98
|
+
elsif !(`typeof React.renderToString === 'undefined'`)
|
|
99
|
+
`React.unmountComponentAtNode(node.$$class ? node[0] : node)`
|
|
100
|
+
else
|
|
101
|
+
raise "unmountComponentAtNode is not defined. In React >= v15 you must import it with ReactDOM"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
Element.instance_eval do
|
|
108
|
+
def self.find(selector)
|
|
109
|
+
selector = begin
|
|
110
|
+
selector.dom_node
|
|
111
|
+
rescue
|
|
112
|
+
selector
|
|
113
|
+
end if `#{selector}.$dom_node !== undefined`
|
|
114
|
+
`$(#{selector})`
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.[](selector)
|
|
118
|
+
find(selector)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
define_method :render do |container = nil, params = {}, &block|
|
|
122
|
+
if `#{self.to_n}._reactrb_component_class === undefined`
|
|
123
|
+
`#{self.to_n}._reactrb_component_class = #{Class.new(React::Component::Base)}`
|
|
124
|
+
end
|
|
125
|
+
klass = `#{self.to_n}._reactrb_component_class`
|
|
126
|
+
klass.class_eval do
|
|
127
|
+
render(container, params, &block)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
React.render(React.create_element(`#{self.to_n}._reactrb_component_class`), self)
|
|
131
|
+
end
|
|
132
|
+
end if Object.const_defined?('Element')
|
|
@@ -0,0 +1,136 @@
|
|
|
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 allow_undefined_props=(allow)
|
|
31
|
+
@allow_undefined_props = allow
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def undefined_props(props)
|
|
35
|
+
self.allow_undefined_props = true
|
|
36
|
+
props.reject { |name, value| rules[name] }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def validate(props)
|
|
40
|
+
self.errors = []
|
|
41
|
+
validate_undefined(props) unless allow_undefined_props?
|
|
42
|
+
props = coerce_native_hash_values(defined_props(props))
|
|
43
|
+
validate_required(props)
|
|
44
|
+
props.each do |name, value|
|
|
45
|
+
validate_types(name, value)
|
|
46
|
+
validate_allowed(name, value)
|
|
47
|
+
end
|
|
48
|
+
errors
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def default_props
|
|
52
|
+
rules
|
|
53
|
+
.select {|key, value| value.keys.include?("default") }
|
|
54
|
+
.inject({}) {|memo, (k,v)| memo[k] = v[:default]; memo}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def defined_props(props)
|
|
60
|
+
props.select { |name| rules.keys.include?(name) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def allow_undefined_props?
|
|
64
|
+
!!@allow_undefined_props
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def rules
|
|
68
|
+
@rules ||= { children: { required: false } }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def define_rule(name, options = {})
|
|
72
|
+
rules[name] = coerce_native_hash_values(options)
|
|
73
|
+
props_wrapper.define_param(name, options[:type])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def errors
|
|
77
|
+
@errors ||= []
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def validate_types(prop_name, value)
|
|
81
|
+
return unless klass = rules[prop_name][:type]
|
|
82
|
+
if !klass.is_a?(Array)
|
|
83
|
+
allow_nil = !!rules[prop_name][:allow_nil]
|
|
84
|
+
type_check("`#{prop_name}`", value, klass, allow_nil)
|
|
85
|
+
elsif klass.length > 0
|
|
86
|
+
validate_value_array(prop_name, value)
|
|
87
|
+
else
|
|
88
|
+
allow_nil = !!rules[prop_name][:allow_nil]
|
|
89
|
+
type_check("`#{prop_name}`", value, Array, allow_nil)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def type_check(prop_name, value, klass, allow_nil)
|
|
94
|
+
return if allow_nil && value.nil?
|
|
95
|
+
return if value.is_a?(klass)
|
|
96
|
+
return if klass.respond_to?(:_react_param_conversion) &&
|
|
97
|
+
klass._react_param_conversion(value, :validate_only)
|
|
98
|
+
errors << "Provided prop #{prop_name} could not be converted to #{klass}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def validate_allowed(prop_name, value)
|
|
102
|
+
return unless values = rules[prop_name][:values]
|
|
103
|
+
return if values.include?(value)
|
|
104
|
+
errors << "Value `#{value}` for prop `#{prop_name}` is not an allowed value"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def validate_required(props)
|
|
108
|
+
(rules.keys - props.keys).each do |name|
|
|
109
|
+
next unless rules[name][:required]
|
|
110
|
+
errors << "Required prop `#{name}` was not specified"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def validate_undefined(props)
|
|
115
|
+
(props.keys - rules.keys).each do |prop_name|
|
|
116
|
+
errors << "Provided prop `#{prop_name}` not specified in spec"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def validate_value_array(name, value)
|
|
121
|
+
klass = rules[name][:type]
|
|
122
|
+
allow_nil = !!rules[name][:allow_nil]
|
|
123
|
+
value.each_with_index do |item, index|
|
|
124
|
+
type_check("`#{name}`[#{index}]", Native(item), klass[0], allow_nil)
|
|
125
|
+
end
|
|
126
|
+
rescue NoMethodError
|
|
127
|
+
errors << "Provided prop `#{name}` was not an Array"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def coerce_native_hash_values(hash)
|
|
131
|
+
hash.each do |key, value|
|
|
132
|
+
hash[key] = Native(value)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
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 react.rb components found in #{components}.rb" unless loaded?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def loaded?
|
|
26
|
+
!!v8_context.eval('Opal.React')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def components
|
|
32
|
+
# Make this configurable at some point
|
|
33
|
+
'components'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def opal(file)
|
|
37
|
+
if Opal::Processor.respond_to?(:load_asset_code)
|
|
38
|
+
Opal::Processor.load_asset_code(assets, file)
|
|
39
|
+
else
|
|
40
|
+
Opal::Sprockets.load_asset(file, assets)
|
|
41
|
+
end
|
|
42
|
+
rescue # What exception is being caught here?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def assets
|
|
46
|
+
::Rails.application.assets
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|