reactrb 0.8.3 → 0.8.4
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 +4 -4
- data/.rubocop.yml +8 -0
- data/component-name-lookup.md +145 -0
- data/config.ru +0 -1
- data/lib/react/api.rb +31 -2
- data/lib/react/component.rb +15 -126
- data/lib/react/component/class_methods.rb +45 -22
- data/lib/react/component/dsl_instance_methods.rb +57 -0
- data/lib/react/component/props_wrapper.rb +6 -0
- data/lib/react/component/tags.rb +96 -0
- data/lib/react/native_library.rb +76 -39
- data/lib/react/rendering_context.rb +19 -21
- data/lib/react/top_level.rb +19 -29
- data/lib/reactive-ruby/rails/component_mount.rb +3 -1
- data/lib/reactive-ruby/rails/controller_helper.rb +10 -10
- data/lib/reactive-ruby/version.rb +1 -1
- data/lib/reactrb.rb +16 -15
- data/lib/reactrb/auto-import.rb +32 -0
- data/lib/sources/react-v14.js +84 -0
- data/spec/react/component_spec.rb +0 -41
- data/spec/react/dsl_spec.rb +114 -4
- data/spec/react/native_library_spec.rb +293 -5
- data/spec/react/opal_jquery_extensions_spec.rb +4 -2
- data/spec/spec_helper.rb +3 -1
- metadata +7 -2
@@ -0,0 +1,57 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
module DslInstanceMethods
|
4
|
+
def children
|
5
|
+
if `#{@native}.props.children==undefined`
|
6
|
+
nodes = []
|
7
|
+
else
|
8
|
+
nodes = [`#{@native}.props.children`].flatten
|
9
|
+
end
|
10
|
+
class << nodes
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
def to_n
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def each(&block)
|
18
|
+
if block_given?
|
19
|
+
%x{
|
20
|
+
React.Children.forEach(#{self.to_n}, function(context){
|
21
|
+
#{yield React::Element.new(`context`)}
|
22
|
+
})
|
23
|
+
}
|
24
|
+
nil
|
25
|
+
else
|
26
|
+
Enumerator.new(`React.Children.count(#{self.to_n})`) do |y|
|
27
|
+
%x{
|
28
|
+
React.Children.forEach(#{self.to_n}, function(context){
|
29
|
+
#{y << Element.new(`context`)}
|
30
|
+
})
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
nodes
|
38
|
+
end
|
39
|
+
|
40
|
+
def params
|
41
|
+
@props_wrapper
|
42
|
+
end
|
43
|
+
|
44
|
+
def props
|
45
|
+
Hash.new(`#{@native}.props`)
|
46
|
+
end
|
47
|
+
|
48
|
+
def refs
|
49
|
+
Hash.new(`#{@native}.refs`)
|
50
|
+
end
|
51
|
+
|
52
|
+
def state
|
53
|
+
@state_wrapper ||= StateWrapper.new(@native, self)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,5 +1,11 @@
|
|
1
1
|
module React
|
2
2
|
module Component
|
3
|
+
|
4
|
+
def deprecated_params_method(name, *args, &block)
|
5
|
+
React::Component.deprecation_warning"Direct access to param `#{name}`. Use `params.#{name}` instead."
|
6
|
+
params.send(name, *args, &block)
|
7
|
+
end
|
8
|
+
|
3
9
|
class PropsWrapper
|
4
10
|
attr_reader :props
|
5
11
|
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
# 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
|
+
|
15
|
+
# note: any tag can end in _as_node but this is deprecated
|
16
|
+
|
17
|
+
# the present method is retained as a legacy behavior
|
18
|
+
|
19
|
+
def present(component, *params, &children)
|
20
|
+
React::RenderingContext.render(component, *params, &children)
|
21
|
+
end
|
22
|
+
|
23
|
+
def present_as_node(component, *params, &children)
|
24
|
+
React::RenderingContext.build_only(component, *params, &children)
|
25
|
+
end
|
26
|
+
|
27
|
+
# define each predefined tag as an instance method
|
28
|
+
|
29
|
+
HTML_TAGS.each do |tag|
|
30
|
+
define_method(tag) do |*params, &children|
|
31
|
+
React::RenderingContext.render(tag, *params, &children)
|
32
|
+
end
|
33
|
+
alias_method tag.upcase, tag
|
34
|
+
const_set tag.upcase, tag
|
35
|
+
# handle deprecated _as_node style
|
36
|
+
define_method("#{tag}_as_node") do |*params, &children|
|
37
|
+
React::RenderingContext.build_only(tag, *params, &children)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# use method_missing to look up component names in the form of "Foo(..)"
|
42
|
+
# where there is no preceeding scope.
|
43
|
+
|
44
|
+
def method_missing(name, *params, &children)
|
45
|
+
if name =~ /_as_node$/
|
46
|
+
# handle deprecated _as_node style
|
47
|
+
component = find_component(name.gsub(/_as_node$/, ''))
|
48
|
+
return React::RenderingContext.build_only(component, *params, &children) if component
|
49
|
+
else
|
50
|
+
component = find_component(name)
|
51
|
+
return React::RenderingContext.render(component, *params, &children) if component
|
52
|
+
end
|
53
|
+
Object.method_missing(name, *params, &children)
|
54
|
+
end
|
55
|
+
|
56
|
+
# install methods with the same name as the component in the parent class/module
|
57
|
+
# thus component names in the form Foo::Bar(...) will work
|
58
|
+
|
59
|
+
class << self
|
60
|
+
def included(component)
|
61
|
+
_name, parent = find_name_and_parent(component)
|
62
|
+
class << parent
|
63
|
+
define_method _name do |*params, &children|
|
64
|
+
React::RenderingContext.render(component, *params, &children)
|
65
|
+
end
|
66
|
+
# handle deprecated _as_node style
|
67
|
+
define_method "#{_name}_as_node" do |*params, &children|
|
68
|
+
React::RenderingContext.build_only(component, *params, &children)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def find_name_and_parent(component)
|
76
|
+
split_name = component.name && component.name.split('::')
|
77
|
+
if split_name && split_name.length > 1
|
78
|
+
[split_name.last, split_name.inject([Module]) { |a, e| a + [a.last.const_get(e)] }[-2]]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def find_component(name)
|
86
|
+
scopes = self.class.name.split('::').inject([Module]) do |nesting, next_const|
|
87
|
+
nesting + [nesting.last.const_get(next_const)]
|
88
|
+
end.reverse
|
89
|
+
scope = scopes.detect { |s| s.const_defined?(name) } || return
|
90
|
+
component = scope.const_get(name)
|
91
|
+
return component if component.method_defined?(:render)
|
92
|
+
raise "#{name} does not appear to be a react component."
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/react/native_library.rb
CHANGED
@@ -1,53 +1,90 @@
|
|
1
1
|
module React
|
2
|
-
|
3
|
-
|
4
|
-
@renames_and_exclusions ||= {}
|
5
|
-
end
|
2
|
+
# NativeLibrary handles importing JS libraries. Importing native components is handled
|
3
|
+
# by the React::Base. It also provides several methods used by auto-import.rb
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# A NativeLibrary is simply a wrapper that holds the name of the native js library.
|
6
|
+
# It responds to const_missing and method_missing by looking up objects within the js library.
|
7
|
+
# If the object is a react component it is wrapped by a reactrb component class, otherwise
|
8
|
+
# a nested NativeLibrary is returned.
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
# Two macros are provided: imports (for naming the native library) and renames which allows
|
11
|
+
# the members of a library to be given different names within the ruby name space.
|
12
|
+
|
13
|
+
# Public methods used by auto-import.rb are import_const_from_native and find_and_render_component
|
14
|
+
class NativeLibrary
|
15
|
+
class << self
|
16
|
+
def imports(native_name)
|
17
|
+
@native_prefix = "#{native_name}."
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def rename(rename_list)
|
22
|
+
# rename_list is a hash in the form: native_name => ruby_name, native_name => ruby_name
|
23
|
+
rename_list.each do |js_name, ruby_name|
|
24
|
+
native_name = lookup_native_name(js_name)
|
25
|
+
if lookup_native_name(js_name)
|
26
|
+
create_component_wrapper(self, native_name, ruby_name) ||
|
27
|
+
create_library_wrapper(self, native_name, ruby_name)
|
28
|
+
else
|
29
|
+
raise "class #{name} < React::NativeLibrary could not import #{js_name}. "\
|
30
|
+
"Native value #{scope_native_name(js_name)} is undefined."
|
31
|
+
end
|
23
32
|
end
|
24
|
-
name
|
25
33
|
end
|
26
|
-
end
|
27
34
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
35
|
+
def import_const_from_native(klass, const_name, create_library)
|
36
|
+
native_name = lookup_native_name(const_name) ||
|
37
|
+
lookup_native_name(const_name[0].downcase + const_name[1..-1])
|
38
|
+
native_name && (
|
39
|
+
create_component_wrapper(klass, native_name, const_name) || (
|
40
|
+
create_library &&
|
41
|
+
create_library_wrapper(klass, native_name, const_name)))
|
33
42
|
end
|
34
|
-
|
35
|
-
|
43
|
+
|
44
|
+
def const_missing(const_name)
|
45
|
+
import_const_from_native(self, const_name, true) || super
|
36
46
|
end
|
37
|
-
React::RenderingContext.build_or_render(node_only, name, *args, &block)
|
38
|
-
rescue
|
39
|
-
end
|
40
47
|
|
41
|
-
|
42
|
-
|
43
|
-
|
48
|
+
def method_missing(method_name, *args, &block)
|
49
|
+
method = method_name.gsub(/_as_node$/, '') # remove once _as_node is deprecated.
|
50
|
+
component_class = get_const(method) if const_defined?(method)
|
51
|
+
component_class ||= import_const_from_native(self, method, false)
|
52
|
+
raise 'could not import a react component named: '\
|
53
|
+
"#{scope_native_name method}" unless component_class
|
54
|
+
if method == method_name
|
55
|
+
React::RenderingContext.render(component_class, *args, &block)
|
56
|
+
else # remove once _as_node is deprecated.
|
57
|
+
React::RenderingContext.build_only(component_class, *args, &block)
|
58
|
+
end
|
59
|
+
end
|
44
60
|
|
45
|
-
|
46
|
-
renames_and_exclusions.merge!(rename_list.invert)
|
47
|
-
end
|
61
|
+
private
|
48
62
|
|
49
|
-
|
50
|
-
|
63
|
+
def lookup_native_name(js_name)
|
64
|
+
native_name = scope_native_name(js_name)
|
65
|
+
`eval(#{native_name}) !== undefined && native_name`
|
66
|
+
rescue
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def scope_native_name(js_name)
|
71
|
+
"#{@native_prefix}#{js_name}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_component_wrapper(klass, native_name, ruby_name)
|
75
|
+
if React::API.native_react_component?(native_name)
|
76
|
+
new_klass = klass.const_set ruby_name, Class.new
|
77
|
+
new_klass.class_eval do
|
78
|
+
include React::Component
|
79
|
+
imports native_name
|
80
|
+
end
|
81
|
+
new_klass
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_library_wrapper(klass, native_name, ruby_name)
|
86
|
+
klass.const_set ruby_name, Class.new(React::NativeLibrary).imports(native_name)
|
87
|
+
end
|
51
88
|
end
|
52
89
|
end
|
53
90
|
end
|
@@ -4,12 +4,11 @@ module React
|
|
4
4
|
attr_accessor :waiting_on_resources
|
5
5
|
end
|
6
6
|
|
7
|
-
def self.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
7
|
+
def self.build_only(name, *args, &block)
|
8
|
+
React::Component.deprecation_warning(
|
9
|
+
'..._as_node is deprecated. Render component and then use the .node method instead'
|
10
|
+
)
|
11
|
+
React::RenderingContext.build { React::RenderingContext.render(name, *args, &block) }.to_n
|
13
12
|
end
|
14
13
|
|
15
14
|
def self.render(name, *args, &block)
|
@@ -55,15 +54,12 @@ module React
|
|
55
54
|
element
|
56
55
|
end
|
57
56
|
|
58
|
-
def self.build
|
57
|
+
def self.build
|
59
58
|
current = @buffer
|
60
59
|
@buffer = []
|
61
60
|
return_val = yield @buffer
|
62
61
|
@buffer = current
|
63
62
|
return_val
|
64
|
-
#ensure
|
65
|
-
# @buffer = current
|
66
|
-
# return_val
|
67
63
|
end
|
68
64
|
|
69
65
|
def self.as_node(element)
|
@@ -71,7 +67,7 @@ module React
|
|
71
67
|
element
|
72
68
|
end
|
73
69
|
|
74
|
-
class << self;
|
70
|
+
class << self; alias delete as_node; end
|
75
71
|
|
76
72
|
def self.replace(e1, e2)
|
77
73
|
@buffer[@buffer.index(e1)] = e2
|
@@ -85,24 +81,26 @@ module React
|
|
85
81
|
end
|
86
82
|
|
87
83
|
class ::Object
|
88
|
-
|
89
|
-
|
90
|
-
define_method(tag) do | *args, &block |
|
84
|
+
[:span, :td, :th, :while_loading].each do |tag|
|
85
|
+
define_method(tag) do |*args, &block|
|
91
86
|
args.unshift(tag)
|
92
|
-
return
|
93
|
-
React::RenderingContext.render(*args) {
|
87
|
+
return send(*args, &block) if is_a? React::Component
|
88
|
+
React::RenderingContext.render(*args) { to_s }
|
94
89
|
end
|
95
90
|
end
|
96
91
|
|
97
92
|
def para(*args, &block)
|
98
|
-
args.unshift(
|
99
|
-
return
|
100
|
-
React::RenderingContext.render(*args) {
|
93
|
+
args.unshift(:p)
|
94
|
+
return send(*args, &block) if is_a? React::Component
|
95
|
+
React::RenderingContext.render(*args) { to_s }
|
101
96
|
end
|
102
97
|
|
103
98
|
def br
|
104
|
-
return
|
105
|
-
React::RenderingContext.render(
|
99
|
+
return send(:br) if is_a? React::Component
|
100
|
+
React::RenderingContext.render(:span) do
|
101
|
+
React::RenderingContext.render(to_s)
|
102
|
+
React::RenderingContext.render(:br)
|
103
|
+
end
|
106
104
|
end
|
107
105
|
end
|
108
106
|
end
|
data/lib/react/top_level.rb
CHANGED
@@ -3,25 +3,20 @@ require 'active_support'
|
|
3
3
|
require 'react/component/base'
|
4
4
|
|
5
5
|
module React
|
6
|
-
|
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)
|
6
|
+
|
14
7
|
ATTRIBUTES = %w(accept acceptCharset accessKey action allowFullScreen allowTransparency alt
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
8
|
+
async autoComplete autoPlay cellPadding cellSpacing charSet checked classID
|
9
|
+
className cols colSpan content contentEditable contextMenu controls coords
|
10
|
+
crossOrigin data dateTime defer dir disabled download draggable encType form
|
11
|
+
formAction formEncType formMethod formNoValidate formTarget frameBorder height
|
12
|
+
hidden href hrefLang htmlFor httpEquiv icon id label lang list loop manifest
|
13
|
+
marginHeight marginWidth max maxLength media mediaGroup method min multiple
|
14
|
+
muted name noValidate open pattern placeholder poster preload radioGroup
|
15
|
+
readOnly rel required role rows rowSpan sandbox scope scrolling seamless
|
16
|
+
selected shape size sizes span spellCheck src srcDoc srcSet start step style
|
17
|
+
tabIndex target title type useMap value width wmode dangerouslySetInnerHTML)
|
18
|
+
|
19
|
+
HASH_ATTRIBUTES = %w(data aria)
|
25
20
|
|
26
21
|
def self.create_element(type, properties = {}, &block)
|
27
22
|
React::API.create_element(type, properties, &block)
|
@@ -78,11 +73,6 @@ module React
|
|
78
73
|
end
|
79
74
|
|
80
75
|
Element.instance_eval do
|
81
|
-
class Element
|
82
|
-
class DummyContext < React::Component::Base
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
76
|
def self.find(selector)
|
87
77
|
selector = begin
|
88
78
|
selector.dom_node
|
@@ -96,11 +86,11 @@ Element.instance_eval do
|
|
96
86
|
find(selector)
|
97
87
|
end
|
98
88
|
|
99
|
-
define_method :render do
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
)
|
89
|
+
define_method :render do |container = nil, params = {}, &block|
|
90
|
+
klass = Class.new(React::Component::Base)
|
91
|
+
klass.class_eval do
|
92
|
+
render(container, params, &block)
|
93
|
+
end
|
94
|
+
React.render(React.create_element(klass), self)
|
105
95
|
end
|
106
96
|
end if Object.const_defined?('Element')
|
@@ -10,7 +10,9 @@ module ReactiveRuby
|
|
10
10
|
def react_component(name, props = {}, options = {}, &block)
|
11
11
|
options = context_initializer_options(options, name) if options[:prerender]
|
12
12
|
props = serialized_props(props, name, controller)
|
13
|
-
super(top_level_name, props, options, &block)
|
13
|
+
super(top_level_name, props, options, &block).gsub("\n","")
|
14
|
+
.gsub(/(<script>.*<\/script>)<\/div>$/,'</div>\1').html_safe +
|
15
|
+
footers
|
14
16
|
end
|
15
17
|
|
16
18
|
private
|