reactrb 0.8.8 → 0.9.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 +4 -4
- data/.codeclimate.yml +24 -3
- data/.gitignore +3 -0
- data/.rubocop.yml +1154 -3
- data/.travis.yml +20 -0
- data/Appraisals +20 -0
- data/CHANGELOG.md +28 -3
- data/Gemfile +4 -5
- data/README.md +6 -9
- data/Rakefile +6 -1
- data/config.ru +7 -6
- 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/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +1 -1
- data/lib/rails-helpers/top_level_rails_component.rb +1 -1
- data/lib/react-sources/react-server.js +2 -0
- data/lib/react/api.rb +13 -12
- data/lib/react/children.rb +30 -0
- data/lib/react/component.rb +27 -46
- data/lib/react/component/class_methods.rb +28 -32
- data/lib/react/component/dsl_instance_methods.rb +4 -34
- data/lib/react/component/params.rb +6 -0
- data/lib/react/component/props_wrapper.rb +22 -27
- data/lib/react/component/should_component_update.rb +98 -0
- data/lib/react/component/tags.rb +45 -4
- data/lib/react/element.rb +26 -13
- data/lib/react/object.rb +15 -0
- data/lib/react/react-source.rb +9 -0
- data/lib/react/rendering_context.rb +97 -93
- data/lib/react/state.rb +27 -21
- 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 +50 -14
- data/lib/react/validator.rb +5 -5
- data/lib/reactive-ruby/isomorphic_helpers.rb +0 -7
- data/lib/reactive-ruby/version.rb +1 -1
- data/lib/reactrb.rb +14 -14
- data/lib/reactrb/deep-compare.rb +24 -0
- data/lib/sources/react-latest.js +2 -0
- data/lib/sources/react-v13.js +4 -1
- data/lib/sources/react-v14.js +3 -84
- data/lib/sources/react-v15.js +3 -0
- data/logo1.png +0 -0
- data/logo2.png +0 -0
- data/logo3.png +0 -0
- data/path_release_steps.md +1 -1
- data/reactrb.gemspec +2 -3
- data/spec/react/children_spec.rb +76 -0
- data/spec/react/component/base_spec.rb +3 -7
- data/spec/react/component_spec.rb +181 -60
- data/spec/react/dsl_spec.rb +26 -19
- data/spec/react/element_spec.rb +16 -1
- data/spec/react/native_library_spec.rb +20 -0
- data/spec/react/opal_jquery_extensions_spec.rb +27 -0
- data/spec/react/param_declaration_spec.rb +47 -78
- data/spec/react/react_spec.rb +7 -9
- data/spec/react/state_spec.rb +29 -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 +33 -5
- data/spec/react/tutorial/tutorial_spec.rb +5 -5
- data/spec/react/validator_spec.rb +10 -13
- data/spec/reactive-ruby/component_loader_spec.rb +3 -0
- data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +5 -4
- data/spec/spec_helper.rb +6 -3
- data/spec/support/react/spec_helpers.rb +9 -2
- metadata +47 -124
- data/example/examples/Gemfile +0 -7
- data/example/examples/app/basics.js.rb +0 -42
- data/example/examples/app/items.rb +0 -11
- data/example/examples/app/jquery.js +0 -5
- data/example/examples/app/nodes.rb +0 -61
- data/example/examples/app/react-router.js +0 -6
- data/example/examples/app/react_api_demo.rb +0 -29
- data/example/examples/app/rerendering.rb +0 -72
- data/example/examples/app/reuse.rb +0 -59
- data/example/examples/app/show.rb +0 -52
- data/example/examples/config.ru +0 -38
- data/example/rails-tutorial/.gitignore +0 -17
- data/example/rails-tutorial/Gemfile +0 -51
- data/example/rails-tutorial/README.rdoc +0 -28
- data/example/rails-tutorial/Rakefile +0 -6
- data/example/rails-tutorial/app/assets/images/.keep +0 -0
- data/example/rails-tutorial/app/assets/javascripts/application.rb +0 -15
- data/example/rails-tutorial/app/assets/stylesheets/application.css +0 -15
- data/example/rails-tutorial/app/controllers/application_controller.rb +0 -6
- data/example/rails-tutorial/app/controllers/concerns/.keep +0 -0
- data/example/rails-tutorial/app/controllers/home_controller.rb +0 -6
- data/example/rails-tutorial/app/helpers/application_helper.rb +0 -2
- data/example/rails-tutorial/app/mailers/.keep +0 -0
- data/example/rails-tutorial/app/models/.keep +0 -0
- data/example/rails-tutorial/app/models/concerns/.keep +0 -0
- data/example/rails-tutorial/app/views/components.rb +0 -3
- data/example/rails-tutorial/app/views/components/home/show.rb +0 -47
- data/example/rails-tutorial/app/views/layouts/application.html.erb +0 -14
- data/example/rails-tutorial/bin/bundle +0 -3
- data/example/rails-tutorial/bin/rails +0 -8
- data/example/rails-tutorial/bin/rake +0 -8
- data/example/rails-tutorial/bin/setup +0 -29
- data/example/rails-tutorial/bin/spring +0 -15
- data/example/rails-tutorial/config.ru +0 -4
- data/example/rails-tutorial/config/application.rb +0 -26
- data/example/rails-tutorial/config/boot.rb +0 -3
- data/example/rails-tutorial/config/database.yml +0 -25
- data/example/rails-tutorial/config/environment.rb +0 -5
- data/example/rails-tutorial/config/environments/development.rb +0 -41
- data/example/rails-tutorial/config/environments/production.rb +0 -79
- data/example/rails-tutorial/config/environments/test.rb +0 -42
- data/example/rails-tutorial/config/initializers/assets.rb +0 -11
- data/example/rails-tutorial/config/initializers/backtrace_silencers.rb +0 -7
- data/example/rails-tutorial/config/initializers/cookies_serializer.rb +0 -3
- data/example/rails-tutorial/config/initializers/filter_parameter_logging.rb +0 -4
- data/example/rails-tutorial/config/initializers/inflections.rb +0 -16
- data/example/rails-tutorial/config/initializers/mime_types.rb +0 -4
- data/example/rails-tutorial/config/initializers/session_store.rb +0 -3
- data/example/rails-tutorial/config/initializers/wrap_parameters.rb +0 -14
- data/example/rails-tutorial/config/locales/en.yml +0 -23
- data/example/rails-tutorial/config/routes.rb +0 -59
- data/example/rails-tutorial/config/secrets.yml +0 -22
- data/example/rails-tutorial/db/seeds.rb +0 -7
- data/example/rails-tutorial/lib/assets/.keep +0 -0
- data/example/rails-tutorial/lib/tasks/.keep +0 -0
- data/example/rails-tutorial/log/.keep +0 -0
- data/example/rails-tutorial/public/404.html +0 -67
- data/example/rails-tutorial/public/422.html +0 -67
- data/example/rails-tutorial/public/500.html +0 -66
- data/example/rails-tutorial/public/favicon.ico +0 -0
- data/example/rails-tutorial/public/robots.txt +0 -5
- data/example/rails-tutorial/test/controllers/.keep +0 -0
- data/example/rails-tutorial/test/fixtures/.keep +0 -0
- data/example/rails-tutorial/test/helpers/.keep +0 -0
- data/example/rails-tutorial/test/integration/.keep +0 -0
- data/example/rails-tutorial/test/mailers/.keep +0 -0
- data/example/rails-tutorial/test/models/.keep +0 -0
- data/example/rails-tutorial/test/test_helper.rb +0 -10
- data/example/rails-tutorial/vendor/assets/javascripts/.keep +0 -0
- data/example/rails-tutorial/vendor/assets/stylesheets/.keep +0 -0
- data/example/sinatra-tutorial/.DS_Store +0 -0
- data/example/sinatra-tutorial/Gemfile +0 -5
- data/example/sinatra-tutorial/README.md +0 -8
- data/example/sinatra-tutorial/_comments.json +0 -42
- data/example/sinatra-tutorial/app/example.rb +0 -290
- data/example/sinatra-tutorial/app/jquery.js +0 -5
- data/example/sinatra-tutorial/config.ru +0 -58
- data/example/sinatra-tutorial/public/base.css +0 -62
- data/example/todos/Gemfile +0 -11
- data/example/todos/README.md +0 -37
- data/example/todos/Rakefile +0 -8
- data/example/todos/app/application.rb +0 -22
- data/example/todos/app/components/app.react.rb +0 -61
- data/example/todos/app/components/footer.react.rb +0 -31
- data/example/todos/app/components/todo_item.react.rb +0 -46
- data/example/todos/app/components/todo_list.react.rb +0 -25
- data/example/todos/app/models/todo.rb +0 -19
- data/example/todos/config.ru +0 -14
- data/example/todos/index.html.haml +0 -16
- data/example/todos/spec/todo_spec.rb +0 -28
- data/example/todos/vendor/base.css +0 -410
- data/example/todos/vendor/bg.png +0 -0
- data/example/todos/vendor/jquery.js +0 -4
data/lib/react/object.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Lazy load HTML tag constants in the form DIV or A
|
|
2
|
+
# This is needed to allow for a HAML expression like this DIV.my_class
|
|
3
|
+
class Object
|
|
4
|
+
class << self
|
|
5
|
+
alias _reactrb_tag_original_const_missing const_missing
|
|
6
|
+
|
|
7
|
+
def const_missing(const_name)
|
|
8
|
+
# Opal uses const_missing to initially define things,
|
|
9
|
+
# so we always call the original, and respond to the exception
|
|
10
|
+
_reactrb_tag_original_const_missing(const_name)
|
|
11
|
+
rescue StandardError => e
|
|
12
|
+
React::Component::Tags.html_tag_class_for(const_name) || raise(e)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
if RUBY_ENGINE == 'opal'
|
|
2
|
+
require 'react.js'
|
|
3
|
+
require "react-server.js"
|
|
4
|
+
else
|
|
5
|
+
require "react/rails/asset_variant"
|
|
6
|
+
react_directory = React::Rails::AssetVariant.new(addons: true).react_directory
|
|
7
|
+
Opal.append_path react_directory.untaint
|
|
8
|
+
Opal.append_path File.expand_path('../../react-sources/', __FILE__).untaint
|
|
9
|
+
end
|
|
@@ -2,113 +2,117 @@ module React
|
|
|
2
2
|
class RenderingContext
|
|
3
3
|
class << self
|
|
4
4
|
attr_accessor :waiting_on_resources
|
|
5
|
-
end
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
def build_only(name, *args, &block)
|
|
7
|
+
React::Component.deprecation_warning(
|
|
8
|
+
'..._as_node is deprecated. Render component and then use the .node method instead'
|
|
9
|
+
)
|
|
10
|
+
React::RenderingContext.build { React::RenderingContext.render(name, *args, &block) }.to_n
|
|
11
|
+
end
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
def render(name, *args, &block)
|
|
14
|
+
remove_nodes_from_args(args)
|
|
15
|
+
@buffer ||= [] unless @buffer
|
|
16
|
+
if block
|
|
17
|
+
element = build do
|
|
18
|
+
saved_waiting_on_resources = waiting_on_resources
|
|
19
|
+
self.waiting_on_resources = nil
|
|
20
|
+
run_child_block(name.nil?, &block)
|
|
21
|
+
if name
|
|
22
|
+
buffer = @buffer.dup
|
|
23
|
+
React.create_element(name, *args) { buffer }.tap do |element|
|
|
24
|
+
element.waiting_on_resources = saved_waiting_on_resources || !!buffer.detect { |e| e.waiting_on_resources if e.respond_to?(:waiting_on_resources) }
|
|
25
|
+
end
|
|
26
|
+
elsif @buffer.last.is_a? React::Element
|
|
27
|
+
@buffer.last.tap { |element| element.waiting_on_resources ||= saved_waiting_on_resources }
|
|
28
|
+
else
|
|
29
|
+
@buffer.last.to_s.span.tap { |element| element.waiting_on_resources = saved_waiting_on_resources }
|
|
26
30
|
end
|
|
27
|
-
elsif @buffer.last.is_a? React::Element
|
|
28
|
-
@buffer.last.tap { |element| element.waiting_on_resources ||= saved_waiting_on_resources }
|
|
29
|
-
else
|
|
30
|
-
@buffer.last.to_s.span.tap { |element| element.waiting_on_resources = saved_waiting_on_resources }
|
|
31
31
|
end
|
|
32
|
+
elsif name.is_a? React::Element
|
|
33
|
+
element = name
|
|
34
|
+
else
|
|
35
|
+
element = React.create_element(name, *args)
|
|
36
|
+
element.waiting_on_resources = waiting_on_resources
|
|
32
37
|
end
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
element = React.create_element(name, *args)
|
|
37
|
-
element.waiting_on_resources = waiting_on_resources
|
|
38
|
+
@buffer << element
|
|
39
|
+
self.waiting_on_resources = nil
|
|
40
|
+
element
|
|
38
41
|
end
|
|
39
|
-
@buffer << element
|
|
40
|
-
self.waiting_on_resources = nil
|
|
41
|
-
element
|
|
42
|
-
end
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
def build
|
|
44
|
+
current = @buffer
|
|
45
|
+
@buffer = []
|
|
46
|
+
return_val = yield @buffer
|
|
47
|
+
@buffer = current
|
|
48
|
+
return_val
|
|
49
|
+
end
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
def as_node(element)
|
|
52
|
+
@buffer.delete(element)
|
|
53
|
+
element
|
|
54
|
+
end
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
alias delete as_node
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
def rendered?(element)
|
|
59
|
+
@buffer.include? element
|
|
60
|
+
end
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
end if args[0] && args[0].is_a?(Hash)
|
|
67
|
-
end
|
|
62
|
+
def replace(e1, e2)
|
|
63
|
+
@buffer[@buffer.index(e1)] = e2
|
|
64
|
+
end
|
|
68
65
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# 1 an object that responds to :acts_as_string?
|
|
75
|
-
# 2 a string,
|
|
76
|
-
# 3 an element that is NOT yet pushed on the rendering buffer
|
|
77
|
-
# 4 or the last element pushed on the buffer
|
|
78
|
-
#
|
|
79
|
-
# in case 1 we change the object to a string, and then it becomes case 2
|
|
80
|
-
# in case 2 we automatically push the string onto the buffer
|
|
81
|
-
# in case 3 we also push the Element onto the buffer IF the buffer is empty
|
|
82
|
-
# case 4 requires no special processing
|
|
83
|
-
#
|
|
84
|
-
# Once we have taken care of these special cases we do a check IF we are in an
|
|
85
|
-
# outer rendering scope. In this case react only allows us to generate 1 Element
|
|
86
|
-
# so we insure that is the case, and also check to make sure that element in the buffer
|
|
87
|
-
# is the element returned
|
|
88
|
-
|
|
89
|
-
def self.run_child_block(is_outer_scope)
|
|
90
|
-
result = yield
|
|
91
|
-
result = result.to_s if result.try :acts_as_string?
|
|
92
|
-
@buffer << result if result.is_a?(String) || (result.is_a?(Element) && @buffer.empty?)
|
|
93
|
-
raise_render_error(result) if is_outer_scope && @buffer != [result]
|
|
94
|
-
end
|
|
66
|
+
def remove_nodes_from_args(args)
|
|
67
|
+
args[0].each do |key, value|
|
|
68
|
+
value.as_node if value.is_a?(Element) rescue nil
|
|
69
|
+
end if args[0] && args[0].is_a?(Hash)
|
|
70
|
+
end
|
|
95
71
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
72
|
+
# run_child_block gathers the element(s) generated by a child block.
|
|
73
|
+
# for example when rendering this div: div { "hello".span; "goodby".span }
|
|
74
|
+
# two child Elements will be generated.
|
|
75
|
+
#
|
|
76
|
+
# the final value of the block should either be
|
|
77
|
+
# 1 an object that responds to :acts_as_string?
|
|
78
|
+
# 2 a string,
|
|
79
|
+
# 3 an element that is NOT yet pushed on the rendering buffer
|
|
80
|
+
# 4 or the last element pushed on the buffer
|
|
81
|
+
#
|
|
82
|
+
# in case 1 we change the object to a string, and then it becomes case 2
|
|
83
|
+
# in case 2 we automatically push the string onto the buffer
|
|
84
|
+
# in case 3 we also push the Element onto the buffer IF the buffer is empty
|
|
85
|
+
# case 4 requires no special processing
|
|
86
|
+
#
|
|
87
|
+
# Once we have taken care of these special cases we do a check IF we are in an
|
|
88
|
+
# outer rendering scope. In this case react only allows us to generate 1 Element
|
|
89
|
+
# so we insure that is the case, and also check to make sure that element in the buffer
|
|
90
|
+
# is the element returned
|
|
91
|
+
|
|
92
|
+
def run_child_block(is_outer_scope)
|
|
93
|
+
result = yield
|
|
94
|
+
result = result.to_s if result.try :acts_as_string?
|
|
95
|
+
@buffer << result if result.is_a?(String) || (result.is_a?(Element) && @buffer.empty?)
|
|
96
|
+
raise_render_error(result) if is_outer_scope && @buffer != [result]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# heurestically raise a meaningful error based on the situation
|
|
108
100
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
def raise_render_error(result)
|
|
102
|
+
improper_render 'A different element was returned than was generated within the DSL.',
|
|
103
|
+
'Possibly improper use of Element#delete.' if @buffer.count == 1
|
|
104
|
+
improper_render "Instead #{@buffer.count} elements were generated.",
|
|
105
|
+
'Do you want to wrap your elements in a div?' if @buffer.count > 1
|
|
106
|
+
improper_render "Instead the component #{result} was returned.",
|
|
107
|
+
"Did you mean #{result}()?" if result.try :reactrb_component?
|
|
108
|
+
improper_render "Instead the #{result.class} #{result} was returned.",
|
|
109
|
+
'You may need to convert this to a string.'
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def improper_render(message, solution)
|
|
113
|
+
raise "a component's render method must generate and return exactly 1 element or a string.\n"\
|
|
114
|
+
" #{message} #{solution}"
|
|
115
|
+
end
|
|
112
116
|
end
|
|
113
117
|
end
|
|
114
118
|
|
data/lib/react/state.rb
CHANGED
|
@@ -33,6 +33,9 @@ module React
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
class State
|
|
36
|
+
|
|
37
|
+
@rendering_level = 0
|
|
38
|
+
|
|
36
39
|
class << self
|
|
37
40
|
attr_reader :current_observer
|
|
38
41
|
|
|
@@ -41,12 +44,23 @@ module React
|
|
|
41
44
|
end
|
|
42
45
|
|
|
43
46
|
def get_state(object, name, current_observer = @current_observer)
|
|
44
|
-
# get current value of name for object, remember that the current object depends on this state,
|
|
47
|
+
# get current value of name for object, remember that the current object depends on this state,
|
|
48
|
+
# current observer can be overriden with last param
|
|
45
49
|
new_observers[current_observer][object] << name if current_observer && !new_observers[current_observer][object].include?(name)
|
|
46
50
|
states[object][name]
|
|
47
51
|
end
|
|
48
52
|
|
|
49
|
-
def
|
|
53
|
+
def set_state(object, name, value, wait_till_thread_completes = nil)
|
|
54
|
+
states[object][name] = value
|
|
55
|
+
if wait_till_thread_completes
|
|
56
|
+
notify_observers_after_thread_completes(object, name, value)
|
|
57
|
+
elsif @rendering_level == 0
|
|
58
|
+
notify_observers(object, name, value)
|
|
59
|
+
end
|
|
60
|
+
value
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def notify_observers(object, name, value)
|
|
50
64
|
object_needs_notification = object.respond_to? :update_react_js_state
|
|
51
65
|
observers_by_name[object][name].dup.each do |observer|
|
|
52
66
|
observer.update_react_js_state(object, name, value)
|
|
@@ -55,23 +69,14 @@ module React
|
|
|
55
69
|
object.update_react_js_state(nil, name, value) if object_needs_notification
|
|
56
70
|
end
|
|
57
71
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@delayed_updates
|
|
63
|
-
@delayed_updater
|
|
64
|
-
|
|
65
|
-
@delayed_updates = []
|
|
66
|
-
@delayed_updater = nil
|
|
67
|
-
delayed_updates.each do |object, name, value|
|
|
68
|
-
set_state2(object, name, value)
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
else
|
|
72
|
-
set_state2(object, name, value)
|
|
72
|
+
def notify_observers_after_thread_completes(object, name, value)
|
|
73
|
+
(@delayed_updates ||= []) << [object, name, value]
|
|
74
|
+
@delayed_updater ||= after(0) do
|
|
75
|
+
delayed_updates = @delayed_updates
|
|
76
|
+
@delayed_updates = []
|
|
77
|
+
@delayed_updater = nil
|
|
78
|
+
delayed_updates.each { |args| notify_observers(*args) }
|
|
73
79
|
end
|
|
74
|
-
value
|
|
75
80
|
end
|
|
76
81
|
|
|
77
82
|
def will_be_observing?(object, name, current_observer)
|
|
@@ -108,7 +113,7 @@ module React
|
|
|
108
113
|
current_observers.delete(@current_observer)
|
|
109
114
|
end
|
|
110
115
|
|
|
111
|
-
def set_state_context_to(observer) # wrap all execution that may set or get states in a block so we know which observer is executing
|
|
116
|
+
def set_state_context_to(observer, rendering = nil) # wrap all execution that may set or get states in a block so we know which observer is executing
|
|
112
117
|
if `typeof window.reactive_ruby_timing !== 'undefined'`
|
|
113
118
|
@nesting_level = (@nesting_level || 0) + 1
|
|
114
119
|
start_time = Time.now.to_f
|
|
@@ -116,10 +121,12 @@ module React
|
|
|
116
121
|
end
|
|
117
122
|
saved_current_observer = @current_observer
|
|
118
123
|
@current_observer = observer
|
|
124
|
+
@rendering_level += 1 if rendering
|
|
119
125
|
return_value = yield
|
|
120
126
|
return_value
|
|
121
127
|
ensure
|
|
122
128
|
@current_observer = saved_current_observer
|
|
129
|
+
@rendering_level -= 1 if rendering
|
|
123
130
|
@nesting_level = [0, @nesting_level - 1].max if `typeof window.reactive_ruby_timing !== 'undefined'`
|
|
124
131
|
return_value
|
|
125
132
|
end
|
|
@@ -130,11 +137,10 @@ module React
|
|
|
130
137
|
|
|
131
138
|
[:new_observers, :current_observers, :observers_by_name].each do |method_name|
|
|
132
139
|
define_method(method_name) do
|
|
133
|
-
instance_variable_get("@#{method_name}")
|
|
140
|
+
instance_variable_get("@#{method_name}") ||
|
|
134
141
|
instance_variable_set("@#{method_name}", Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = [] } })
|
|
135
142
|
end
|
|
136
143
|
end
|
|
137
|
-
|
|
138
144
|
end
|
|
139
145
|
end
|
|
140
146
|
end
|
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
|