reactrb 0.7.42

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.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +6 -0
  3. data/.gitignore +33 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE +19 -0
  7. data/README.md +117 -0
  8. data/Rakefile +28 -0
  9. data/config.ru +16 -0
  10. data/example/examples/Gemfile +7 -0
  11. data/example/examples/app/basics.js.rb +42 -0
  12. data/example/examples/app/items.rb +11 -0
  13. data/example/examples/app/jquery.js +5 -0
  14. data/example/examples/app/nodes.rb +61 -0
  15. data/example/examples/app/react-router.js +6 -0
  16. data/example/examples/app/react_api_demo.rb +29 -0
  17. data/example/examples/app/rerendering.rb +72 -0
  18. data/example/examples/app/reuse.rb +59 -0
  19. data/example/examples/app/show.rb +52 -0
  20. data/example/examples/config.ru +38 -0
  21. data/example/rails-tutorial/.gitignore +17 -0
  22. data/example/rails-tutorial/Gemfile +51 -0
  23. data/example/rails-tutorial/README.rdoc +28 -0
  24. data/example/rails-tutorial/Rakefile +6 -0
  25. data/example/rails-tutorial/app/assets/images/.keep +0 -0
  26. data/example/rails-tutorial/app/assets/javascripts/application.rb +15 -0
  27. data/example/rails-tutorial/app/assets/stylesheets/application.css +15 -0
  28. data/example/rails-tutorial/app/controllers/application_controller.rb +6 -0
  29. data/example/rails-tutorial/app/controllers/concerns/.keep +0 -0
  30. data/example/rails-tutorial/app/controllers/home_controller.rb +6 -0
  31. data/example/rails-tutorial/app/helpers/application_helper.rb +2 -0
  32. data/example/rails-tutorial/app/mailers/.keep +0 -0
  33. data/example/rails-tutorial/app/models/.keep +0 -0
  34. data/example/rails-tutorial/app/models/concerns/.keep +0 -0
  35. data/example/rails-tutorial/app/views/components.rb +3 -0
  36. data/example/rails-tutorial/app/views/components/home/show.rb +47 -0
  37. data/example/rails-tutorial/app/views/layouts/application.html.erb +14 -0
  38. data/example/rails-tutorial/bin/bundle +3 -0
  39. data/example/rails-tutorial/bin/rails +8 -0
  40. data/example/rails-tutorial/bin/rake +8 -0
  41. data/example/rails-tutorial/bin/setup +29 -0
  42. data/example/rails-tutorial/bin/spring +15 -0
  43. data/example/rails-tutorial/config.ru +4 -0
  44. data/example/rails-tutorial/config/application.rb +26 -0
  45. data/example/rails-tutorial/config/boot.rb +3 -0
  46. data/example/rails-tutorial/config/database.yml +25 -0
  47. data/example/rails-tutorial/config/environment.rb +5 -0
  48. data/example/rails-tutorial/config/environments/development.rb +41 -0
  49. data/example/rails-tutorial/config/environments/production.rb +79 -0
  50. data/example/rails-tutorial/config/environments/test.rb +42 -0
  51. data/example/rails-tutorial/config/initializers/assets.rb +11 -0
  52. data/example/rails-tutorial/config/initializers/backtrace_silencers.rb +7 -0
  53. data/example/rails-tutorial/config/initializers/cookies_serializer.rb +3 -0
  54. data/example/rails-tutorial/config/initializers/filter_parameter_logging.rb +4 -0
  55. data/example/rails-tutorial/config/initializers/inflections.rb +16 -0
  56. data/example/rails-tutorial/config/initializers/mime_types.rb +4 -0
  57. data/example/rails-tutorial/config/initializers/session_store.rb +3 -0
  58. data/example/rails-tutorial/config/initializers/wrap_parameters.rb +14 -0
  59. data/example/rails-tutorial/config/locales/en.yml +23 -0
  60. data/example/rails-tutorial/config/routes.rb +59 -0
  61. data/example/rails-tutorial/config/secrets.yml +22 -0
  62. data/example/rails-tutorial/db/seeds.rb +7 -0
  63. data/example/rails-tutorial/lib/assets/.keep +0 -0
  64. data/example/rails-tutorial/lib/tasks/.keep +0 -0
  65. data/example/rails-tutorial/log/.keep +0 -0
  66. data/example/rails-tutorial/public/404.html +67 -0
  67. data/example/rails-tutorial/public/422.html +67 -0
  68. data/example/rails-tutorial/public/500.html +66 -0
  69. data/example/rails-tutorial/public/favicon.ico +0 -0
  70. data/example/rails-tutorial/public/robots.txt +5 -0
  71. data/example/rails-tutorial/test/controllers/.keep +0 -0
  72. data/example/rails-tutorial/test/fixtures/.keep +0 -0
  73. data/example/rails-tutorial/test/helpers/.keep +0 -0
  74. data/example/rails-tutorial/test/integration/.keep +0 -0
  75. data/example/rails-tutorial/test/mailers/.keep +0 -0
  76. data/example/rails-tutorial/test/models/.keep +0 -0
  77. data/example/rails-tutorial/test/test_helper.rb +10 -0
  78. data/example/rails-tutorial/vendor/assets/javascripts/.keep +0 -0
  79. data/example/rails-tutorial/vendor/assets/stylesheets/.keep +0 -0
  80. data/example/sinatra-tutorial/.DS_Store +0 -0
  81. data/example/sinatra-tutorial/Gemfile +5 -0
  82. data/example/sinatra-tutorial/README.md +8 -0
  83. data/example/sinatra-tutorial/_comments.json +42 -0
  84. data/example/sinatra-tutorial/app/example.rb +290 -0
  85. data/example/sinatra-tutorial/app/jquery.js +5 -0
  86. data/example/sinatra-tutorial/config.ru +58 -0
  87. data/example/sinatra-tutorial/public/base.css +62 -0
  88. data/example/todos/Gemfile +11 -0
  89. data/example/todos/README.md +37 -0
  90. data/example/todos/Rakefile +8 -0
  91. data/example/todos/app/application.rb +22 -0
  92. data/example/todos/app/components/app.react.rb +61 -0
  93. data/example/todos/app/components/footer.react.rb +31 -0
  94. data/example/todos/app/components/todo_item.react.rb +46 -0
  95. data/example/todos/app/components/todo_list.react.rb +25 -0
  96. data/example/todos/app/models/todo.rb +19 -0
  97. data/example/todos/config.ru +14 -0
  98. data/example/todos/index.html.haml +16 -0
  99. data/example/todos/spec/todo_spec.rb +28 -0
  100. data/example/todos/vendor/base.css +410 -0
  101. data/example/todos/vendor/bg.png +0 -0
  102. data/example/todos/vendor/jquery.js +4 -0
  103. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +4 -0
  104. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
  105. data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
  106. data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
  107. data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
  108. data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
  109. data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
  110. data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +105 -0
  111. data/lib/rails-helpers/top_level_rails_component.rb +54 -0
  112. data/lib/react/api.rb +127 -0
  113. data/lib/react/callbacks.rb +42 -0
  114. data/lib/react/component.rb +269 -0
  115. data/lib/react/component/api.rb +50 -0
  116. data/lib/react/component/base.rb +9 -0
  117. data/lib/react/component/class_methods.rb +190 -0
  118. data/lib/react/component/props_wrapper.rb +82 -0
  119. data/lib/react/element.rb +77 -0
  120. data/lib/react/event.rb +76 -0
  121. data/lib/react/ext/hash.rb +9 -0
  122. data/lib/react/ext/string.rb +8 -0
  123. data/lib/react/native_library.rb +53 -0
  124. data/lib/react/observable.rb +29 -0
  125. data/lib/react/rendering_context.rb +109 -0
  126. data/lib/react/state.rb +140 -0
  127. data/lib/react/top_level.rb +97 -0
  128. data/lib/react/validator.rb +136 -0
  129. data/lib/reactive-ruby/component_loader.rb +45 -0
  130. data/lib/reactive-ruby/isomorphic_helpers.rb +196 -0
  131. data/lib/reactive-ruby/rails.rb +7 -0
  132. data/lib/reactive-ruby/rails/component_mount.rb +44 -0
  133. data/lib/reactive-ruby/rails/controller_helper.rb +13 -0
  134. data/lib/reactive-ruby/rails/railtie.rb +14 -0
  135. data/lib/reactive-ruby/serializers.rb +15 -0
  136. data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +42 -0
  137. data/lib/reactive-ruby/version.rb +3 -0
  138. data/lib/reactrb.rb +50 -0
  139. data/lib/sources/react-latest.js +21167 -0
  140. data/lib/sources/react-v13.js +21642 -0
  141. data/lib/sources/react-v14.js +20818 -0
  142. data/lib/sources/react-v15.js +21167 -0
  143. data/logo1.png +0 -0
  144. data/logo2.png +0 -0
  145. data/logo3.png +0 -0
  146. data/path_release_steps.md +9 -0
  147. data/reactrb.gemspec +43 -0
  148. data/spec/controller_helper_spec.rb +22 -0
  149. data/spec/index.html.erb +12 -0
  150. data/spec/react/callbacks_spec.rb +106 -0
  151. data/spec/react/component/base_spec.rb +36 -0
  152. data/spec/react/component_spec.rb +721 -0
  153. data/spec/react/dsl_spec.rb +161 -0
  154. data/spec/react/element_spec.rb +47 -0
  155. data/spec/react/event_spec.rb +24 -0
  156. data/spec/react/native_library_spec.rb +10 -0
  157. data/spec/react/observable_spec.rb +7 -0
  158. data/spec/react/param_declaration_spec.rb +286 -0
  159. data/spec/react/react_spec.rb +211 -0
  160. data/spec/react/state_spec.rb +26 -0
  161. data/spec/react/top_level_component_spec.rb +68 -0
  162. data/spec/react/tutorial/tutorial_spec.rb +35 -0
  163. data/spec/react/validator_spec.rb +128 -0
  164. data/spec/reactive-ruby/component_loader_spec.rb +68 -0
  165. data/spec/reactive-ruby/isomorphic_helpers_spec.rb +155 -0
  166. data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +9 -0
  167. data/spec/reactive-ruby/rails/component_mount_spec.rb +66 -0
  168. data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +35 -0
  169. data/spec/spec_helper.rb +109 -0
  170. data/spec/support/react/spec_helpers.rb +57 -0
  171. data/spec/vendor/es5-shim.min.js +6 -0
  172. data/spec/vendor/jquery-2.2.4.min.js +4 -0
  173. metadata +441 -0
data/logo1.png ADDED
Binary file
data/logo2.png ADDED
Binary file
data/logo3.png ADDED
Binary file
@@ -0,0 +1,9 @@
1
+
2
+ For example assuming you are releasing fix to 0.8.18
3
+
4
+ 1. Checkout 0-8-stable
5
+ 2. Update tests, fix the bug and commit the changes.
6
+ 3. Build & Release to RubyGems (Remember the version in version.rb should already be 0.8.19)
7
+ 4. Create a tag 'v0.8.19' pointing to that commit. (`tag v0.8.19`)
8
+ 5. Bump the version in 0-8-stable to 0.8.20 so it will be ready for the next patch level release.
9
+ 6. Commit the version bump, and do a `git push --tags` so the new tag goes up
data/reactrb.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib/', __FILE__)
3
+
4
+ require 'reactive-ruby/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'reactrb'
8
+ s.version = React::VERSION
9
+
10
+ s.authors = ['David Chang', 'Adam Jahn', 'Mitch VanDuyn']
11
+ s.email = 'reactrb@catprint.com'
12
+ s.homepage = 'https://reactrb.org'
13
+ s.summary = 'Opal Ruby wrapper of React.js library.'
14
+ s.license = 'MIT'
15
+ s.description = "Write React UI components in pure Ruby."
16
+ s.files = `git ls-files`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.require_paths = ['lib']
20
+
21
+ s.add_dependency 'opal', '>= 0.8.0'
22
+ s.add_dependency 'opal-activesupport', '>= 0.2.0'
23
+ s.add_dependency 'opal-browser', '0.2.0'
24
+ s.add_development_dependency 'rake'
25
+ s.add_development_dependency 'rspec-rails', '3.3.3'
26
+ s.add_development_dependency 'timecop'
27
+ s.add_development_dependency 'opal-rspec', '0.4.3'
28
+ s.add_development_dependency 'sinatra'
29
+ s.add_development_dependency 'opal-jquery'
30
+
31
+ # For Test Rails App
32
+ s.add_development_dependency 'rails', '4.2.4'
33
+ s.add_development_dependency 'react-rails'
34
+ s.add_development_dependency 'opal-rails', '0.8.1'
35
+ if RUBY_PLATFORM == 'java'
36
+ s.add_development_dependency 'jdbc-sqlite3'
37
+ s.add_development_dependency 'activerecord-jdbcsqlite3-adapter'
38
+ s.add_development_dependency 'therubyrhino'
39
+ else
40
+ s.add_development_dependency 'sqlite3', '1.3.10'
41
+ s.add_development_dependency 'therubyracer', '0.12.2'
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ if ruby?
4
+ class TestController < ActionController::Base; end
5
+
6
+ RSpec.describe TestController, type: :controller do
7
+ render_views
8
+
9
+ describe '#render_component' do
10
+ controller do
11
+ def index
12
+ render_component
13
+ end
14
+ end
15
+
16
+ it 'renders the application layout' do
17
+ get :index, no_prerender: true
18
+ expect(response).to render_template(layout: :application)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ </head>
5
+ <body>
6
+ <%= javascript_include_tag 'vendor/es5-shim.min' %>
7
+ <%= javascript_include_tag 'vendor/jquery-2.2.4.min' %>
8
+ <%= javascript_include_tag @server.main %>
9
+ <div id="placeholder" style="display: none"></div>
10
+ <div id="render_here"></div>
11
+ </body>
12
+ </html>
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe React::Callbacks do
5
+ it 'defines callback' do
6
+ stub_const 'Foo', Class.new
7
+ Foo.class_eval do
8
+ include React::Callbacks
9
+ define_callback :before_dinner
10
+ before_dinner :wash_hand
11
+
12
+ def wash_hand
13
+ end
14
+ end
15
+
16
+ expect_any_instance_of(Foo).to receive(:wash_hand)
17
+ Foo.new.run_callback(:before_dinner)
18
+ end
19
+
20
+ it 'defines multiple callbacks' do
21
+ stub_const 'Foo', Class.new
22
+ Foo.class_eval do
23
+ include React::Callbacks
24
+ define_callback :before_dinner
25
+
26
+ before_dinner :wash_hand, :turn_of_laptop
27
+
28
+ def wash_hand;end
29
+
30
+ def turn_of_laptop;end
31
+ end
32
+
33
+ expect_any_instance_of(Foo).to receive(:wash_hand)
34
+ expect_any_instance_of(Foo).to receive(:turn_of_laptop)
35
+ Foo.new.run_callback(:before_dinner)
36
+ end
37
+
38
+ it 'defines block callback' do
39
+ stub_const 'Foo', Class.new
40
+ Foo.class_eval do
41
+ include React::Callbacks
42
+ attr_accessor :a
43
+ attr_accessor :b
44
+
45
+ define_callback :before_dinner
46
+
47
+ before_dinner do
48
+ self.a = 10
49
+ end
50
+ before_dinner do
51
+ self.b = 20
52
+ end
53
+ end
54
+
55
+ foo = Foo.new
56
+ foo.run_callback(:before_dinner)
57
+ expect(foo.a).to eq(10)
58
+ expect(foo.b).to eq(20)
59
+ end
60
+
61
+ it 'defines multiple callback group' do
62
+ stub_const 'Foo', Class.new
63
+ Foo.class_eval do
64
+ include React::Callbacks
65
+ define_callback :before_dinner
66
+ define_callback :after_dinner
67
+ attr_accessor :a
68
+
69
+ before_dinner do
70
+ self.a = 10
71
+ end
72
+ end
73
+
74
+ foo = Foo.new
75
+ foo.run_callback(:before_dinner)
76
+ foo.run_callback(:after_dinner)
77
+
78
+ expect(foo.a).to eq(10)
79
+ end
80
+
81
+ it 'receives args as callback' do
82
+ stub_const 'Foo', Class.new
83
+ Foo.class_eval do
84
+ include React::Callbacks
85
+ define_callback :before_dinner
86
+ define_callback :after_dinner
87
+
88
+ attr_accessor :lorem
89
+
90
+ before_dinner do |a, b|
91
+ self.lorem = "#{a}-#{b}"
92
+ end
93
+
94
+ after_dinner :eat_ice_cream
95
+ def eat_ice_cream(a,b,c); end
96
+ end
97
+
98
+ expect_any_instance_of(Foo).to receive(:eat_ice_cream).with(4,5,6)
99
+
100
+ foo = Foo.new
101
+ foo.run_callback(:before_dinner, 1, 2)
102
+ foo.run_callback(:after_dinner, 4, 5, 6)
103
+ expect(foo.lorem).to eq('1-2')
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe React::Component::Base do
5
+ after(:each) do
6
+ React::API.clear_component_class_cache
7
+ end
8
+
9
+ it 'can be inherited to create a component class' do
10
+ stub_const 'Foo', Class.new(React::Component::Base)
11
+ Foo.class_eval do
12
+ before_mount do
13
+ @instance_data = ["working"]
14
+ end
15
+ def render
16
+ @instance_data.first
17
+ end
18
+ end
19
+ stub_const 'Bar', Class.new(Foo)
20
+ Bar.class_eval do
21
+ before_mount do
22
+ @instance_data << "well"
23
+ end
24
+ def render
25
+ @instance_data.join(" ")
26
+ end
27
+ end
28
+ expect(rendered_component(Foo)).to eq("<span>working</span>")
29
+ expect(rendered_component(Bar)).to eq("<span>working well</span>")
30
+ end
31
+
32
+ def rendered_component(component)
33
+ React.render_to_static_markup(React.create_element(component))
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,721 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe React::Component do
5
+ after(:each) do
6
+ React::API.clear_component_class_cache
7
+ end
8
+
9
+ it 'defines component spec methods' do
10
+ stub_const 'Foo', Class.new
11
+ Foo.class_eval do
12
+ include React::Component
13
+ def render
14
+ React.create_element('div')
15
+ end
16
+ end
17
+
18
+ # Class Methods
19
+ expect(Foo).to respond_to('initial_state')
20
+ expect(Foo).to respond_to('default_props')
21
+ expect(Foo).to respond_to('prop_types')
22
+
23
+ # Instance method
24
+ expect(Foo.new).to respond_to('component_will_mount')
25
+ expect(Foo.new).to respond_to('component_did_mount')
26
+ expect(Foo.new).to respond_to('component_will_receive_props')
27
+ expect(Foo.new).to respond_to('should_component_update?')
28
+ expect(Foo.new).to respond_to('component_will_update')
29
+ expect(Foo.new).to respond_to('component_did_update')
30
+ expect(Foo.new).to respond_to('component_will_unmount')
31
+ end
32
+
33
+ describe 'Life Cycle' do
34
+ before(:each) do
35
+ stub_const 'Foo', Class.new
36
+ Foo.class_eval do
37
+ include React::Component
38
+ def render
39
+ React.create_element('div') { 'lorem' }
40
+ end
41
+ end
42
+ end
43
+
44
+ it 'invokes `before_mount` registered methods when `componentWillMount()`' do
45
+ Foo.class_eval do
46
+ before_mount :bar, :bar2
47
+ def bar; end
48
+ def bar2; end
49
+ end
50
+
51
+ expect_any_instance_of(Foo).to receive(:bar)
52
+ expect_any_instance_of(Foo).to receive(:bar2)
53
+
54
+ renderToDocument(Foo)
55
+ end
56
+
57
+ it 'invokes `after_mount` registered methods when `componentDidMount()`' do
58
+ Foo.class_eval do
59
+ after_mount :bar3, :bar4
60
+ def bar3; end
61
+ def bar4; end
62
+ end
63
+
64
+ expect_any_instance_of(Foo).to receive(:bar3)
65
+ expect_any_instance_of(Foo).to receive(:bar4)
66
+
67
+ renderToDocument(Foo)
68
+ end
69
+
70
+ it 'allows multiple class declared life cycle hooker' do
71
+ stub_const 'FooBar', Class.new
72
+ Foo.class_eval do
73
+ before_mount :bar
74
+ def bar; end
75
+ end
76
+
77
+ FooBar.class_eval do
78
+ include React::Component
79
+ after_mount :bar2
80
+ def bar2; end
81
+ def render
82
+ React.create_element('div') { 'lorem' }
83
+ end
84
+ end
85
+
86
+ expect_any_instance_of(Foo).to receive(:bar)
87
+
88
+ renderToDocument(Foo)
89
+ end
90
+
91
+ it 'allows block for life cycle callback' do
92
+ Foo.class_eval do
93
+ export_state(:foo)
94
+
95
+ before_mount do
96
+ foo! 'bar'
97
+ end
98
+ end
99
+
100
+ element = renderToDocument(Foo)
101
+ expect(Foo.foo).to be('bar')
102
+ end
103
+ end
104
+
105
+ describe 'New style setter & getter' do
106
+ before(:each) do
107
+ stub_const 'Foo', Class.new
108
+ Foo.class_eval do
109
+ include React::Component
110
+ def render
111
+ div { state.foo }
112
+ end
113
+ end
114
+ end
115
+
116
+ it 'implicitly will create a state variable when first written' do
117
+ Foo.class_eval do
118
+ before_mount do
119
+ state.foo! 'bar'
120
+ end
121
+ end
122
+
123
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>bar</div>')
124
+ end
125
+
126
+ it 'allows kernal method names like "format" to be used as state variable names' do
127
+ Foo.class_eval do
128
+ before_mount do
129
+ state.format! 'yes'
130
+ state.foo! state.format
131
+ end
132
+ end
133
+
134
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>yes</div>')
135
+ end
136
+
137
+ it 'returns an observer with the bang method and no arguments' do
138
+ Foo.class_eval do
139
+ before_mount do
140
+ state.foo!(state.baz!.class.name)
141
+ end
142
+ end
143
+
144
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>React::Observable</div>')
145
+ end
146
+
147
+ it 'returns the current value of a state when written' do
148
+ Foo.class_eval do
149
+ before_mount do
150
+ state.baz! 'bar'
151
+ state.foo!(state.baz!('pow'))
152
+ end
153
+ end
154
+
155
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>bar</div>')
156
+ end
157
+
158
+ it 'can access an explicitly defined state`' do
159
+ Foo.class_eval do
160
+ define_state foo: :bar
161
+ end
162
+
163
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>bar</div>')
164
+ end
165
+
166
+ end
167
+
168
+ describe 'State setter & getter' do
169
+ before(:each) do
170
+ stub_const 'Foo', Class.new
171
+ Foo.class_eval do
172
+ include React::Component
173
+ def render
174
+ React.create_element('div') { 'lorem' }
175
+ end
176
+ end
177
+ end
178
+
179
+ it 'defines setter using `define_state`' do
180
+ Foo.class_eval do
181
+ define_state :foo
182
+ before_mount :set_up
183
+ def set_up
184
+ self.foo = 'bar'
185
+ end
186
+ end
187
+
188
+ element = renderToDocument(Foo)
189
+ expect(element.state.foo).to be('bar')
190
+ end
191
+
192
+ it 'defines init state by passing a block to `define_state`' do
193
+ Foo.class_eval do
194
+ define_state(:foo) { 10 }
195
+ end
196
+
197
+ element = renderToDocument(Foo)
198
+ expect(element.state.foo).to be(10)
199
+ end
200
+
201
+ it 'defines getter using `define_state`' do
202
+ Foo.class_eval do
203
+ define_state(:foo) { 10 }
204
+ before_mount :bump
205
+ def bump
206
+ self.foo = self.foo + 20
207
+ end
208
+ end
209
+
210
+ element = renderToDocument(Foo)
211
+ expect(element.state.foo).to be(30)
212
+ end
213
+
214
+ it 'defines multiple state accessors by passing array to `define_state`' do
215
+ Foo.class_eval do
216
+ define_state :foo, :foo2
217
+ before_mount :set_up
218
+ def set_up
219
+ self.foo = 10
220
+ self.foo2 = 20
221
+ end
222
+ end
223
+
224
+ element = renderToDocument(Foo)
225
+ expect(element.state.foo).to be(10)
226
+ expect(element.state.foo2).to be(20)
227
+ end
228
+
229
+ it 'invokes `define_state` multiple times to define states' do
230
+ Foo.class_eval do
231
+ define_state(:foo) { 30 }
232
+ define_state(:foo2) { 40 }
233
+ end
234
+
235
+ element = renderToDocument(Foo)
236
+ expect(element.state.foo).to be(30)
237
+ expect(element.state.foo2).to be(40)
238
+ end
239
+
240
+ it 'can initialize multiple state variables with a block' do
241
+ Foo.class_eval do
242
+ define_state(:foo, :foo2) { 30 }
243
+ end
244
+ element = renderToDocument(Foo)
245
+ expect(element.state.foo).to be(30)
246
+ expect(element.state.foo2).to be(30)
247
+ end
248
+
249
+ it 'can mix multiple state variables with initializers and a block' do
250
+ Foo.class_eval do
251
+ define_state(:x, :y, foo: 1, bar: 2) {3}
252
+ end
253
+ element = renderToDocument(Foo)
254
+ expect(element.state.x).to be(3)
255
+ expect(element.state.y).to be(3)
256
+ expect(element.state.foo).to be(1)
257
+ expect(element.state.bar).to be(2)
258
+ end
259
+
260
+ it 'gets state in render method' do
261
+ Foo.class_eval do
262
+ define_state(:foo) { 10 }
263
+ def render
264
+ React.create_element('div') { self.foo }
265
+ end
266
+ end
267
+
268
+ element = renderToDocument(Foo)
269
+ expect(Element[element].text).to eq('10')
270
+ end
271
+
272
+ it 'supports original `setState` as `set_state` method' do
273
+ Foo.class_eval do
274
+ before_mount do
275
+ self.set_state(foo: 'bar')
276
+ end
277
+ end
278
+
279
+ element = renderToDocument(Foo)
280
+ expect(element.state.foo).to be('bar')
281
+ end
282
+
283
+ it 'supports original `replaceState` as `set_state!` method' do
284
+ Foo.class_eval do
285
+ before_mount do
286
+ self.set_state(foo: 'bar')
287
+ self.set_state!(bar: 'lorem')
288
+ end
289
+ end
290
+
291
+ element = renderToDocument(Foo)
292
+ expect(element.state.foo).to be_nil
293
+ expect(element.state.bar).to eq('lorem')
294
+ end
295
+
296
+ it 'supports original `state` method' do
297
+ Foo.class_eval do
298
+ before_mount do
299
+ self.set_state(foo: 'bar')
300
+ end
301
+
302
+ def render
303
+ div { self.state[:foo] }
304
+ end
305
+ end
306
+
307
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>bar</div>')
308
+ end
309
+
310
+ it 'transforms state getter to Ruby object' do
311
+ Foo.class_eval do
312
+ define_state :foo
313
+
314
+ before_mount do
315
+ self.foo = [{a: "Hello"}]
316
+ end
317
+
318
+ def render
319
+ div { self.foo[0][:a] }
320
+ end
321
+ end
322
+
323
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>Hello</div>')
324
+ end
325
+ end
326
+
327
+ describe 'Props' do
328
+ describe 'this.props could be accessed through `params` method' do
329
+ before do
330
+ stub_const 'Foo', Class.new
331
+ Foo.class_eval do
332
+ include React::Component
333
+ end
334
+ end
335
+
336
+ it 'reads from parent passed properties through `params`' do
337
+ Foo.class_eval do
338
+ def render
339
+ React.create_element('div') { params[:prop] }
340
+ end
341
+ end
342
+
343
+ element = renderToDocument(Foo, prop: 'foobar')
344
+ expect(Element[element].text).to eq('foobar')
345
+ end
346
+
347
+ it 'accesses nested params as orignal Ruby object' do
348
+ Foo.class_eval do
349
+ def render
350
+ React.create_element('div') { params[:prop][0][:foo] }
351
+ end
352
+ end
353
+
354
+ element = renderToDocument(Foo, prop: [{foo: 10}])
355
+ expect(Element[element].text).to eq('10')
356
+ end
357
+ end
358
+
359
+ # deprecated as of React 14
360
+ # describe 'Props Updating' do
361
+ # before do
362
+ # stub_const 'Foo', Class.new
363
+ # Foo.class_eval do
364
+ # include React::Component
365
+ # end
366
+ # end
367
+ #
368
+ # it 'supports original `setProps` as method `set_props`' do
369
+ # Foo.class_eval do
370
+ # def render
371
+ # React.create_element('div') { params[:foo] }
372
+ # end
373
+ # end
374
+ #
375
+ # element = renderToDocument(Foo, {foo: 10})
376
+ # element.set_props(foo: 20)
377
+ # expect(`#{element.dom_node}.innerHTML`).to eq('20')
378
+ # end
379
+ #
380
+ # it 'supports original `replaceProps` as method `set_props!`' do
381
+ # Foo.class_eval do
382
+ # def render
383
+ # React.create_element('div') { params[:foo] ? 'exist' : 'null' }
384
+ # end
385
+ # end
386
+ #
387
+ # element = renderToDocument(Foo, {foo: 10})
388
+ # element.set_props!(bar: 20)
389
+ # expect(element.getDOMNode.innerHTML).to eq('null')
390
+ # end
391
+ # end
392
+
393
+ describe 'Prop validation' do
394
+ before do
395
+ stub_const 'Foo', Class.new
396
+ Foo.class_eval do
397
+ include React::Component
398
+ end
399
+ end
400
+
401
+ it 'specifies validation rules using `params` class method' do
402
+ Foo.class_eval do
403
+ params do
404
+ requires :foo, type: String
405
+ optional :bar
406
+ end
407
+ end
408
+
409
+ expect(Foo.prop_types).to have_key(:_componentValidator)
410
+ end
411
+
412
+ it 'logs error in warning if validation failed' do
413
+ stub_const 'Lorem', Class.new
414
+ Foo.class_eval do
415
+ params do
416
+ requires :foo
417
+ requires :lorem, type: Lorem
418
+ optional :bar, type: String
419
+ end
420
+
421
+ def render; div; end
422
+ end
423
+
424
+ %x{
425
+ var log = [];
426
+ var org_warn_console = window.console.warn;
427
+ var org_error_console = window.console.error;
428
+ window.console.warn = window.console.error = function(str){log.push(str)}
429
+ }
430
+ renderToDocument(Foo, bar: 10, lorem: Lorem.new)
431
+ `window.console.warn = org_warn_console; window.console.error = org_error_console;`
432
+ expect(`log`).to eq(["Warning: Failed propType: In component `Foo`\nRequired prop `foo` was not specified\nProvided prop `bar` could not be converted to String"])
433
+ end
434
+
435
+ it 'should not log anything if validation pass' do
436
+ stub_const 'Lorem', Class.new
437
+ Foo.class_eval do
438
+ params do
439
+ requires :foo
440
+ requires :lorem, type: Lorem
441
+ optional :bar, type: String
442
+ end
443
+
444
+ def render; div; end
445
+ end
446
+
447
+ %x{
448
+ var log = [];
449
+ var org_warn_console = window.console.warn;
450
+ var org_error_console = window.console.error
451
+ window.console.warn = window.console.error = function(str){log.push(str)}
452
+ }
453
+ renderToDocument(Foo, foo: 10, bar: '10', lorem: Lorem.new)
454
+ `window.console.warn = org_warn_console; window.console.error = org_error_console;`
455
+ expect(`log`).to eq([])
456
+ end
457
+ end
458
+
459
+ describe 'Default props' do
460
+ it 'sets default props using validation helper' do
461
+ stub_const 'Foo', Class.new
462
+ Foo.class_eval do
463
+ include React::Component
464
+ params do
465
+ optional :foo, default: 'foo'
466
+ optional :bar, default: 'bar'
467
+ end
468
+
469
+ def render
470
+ div { params[:foo] + '-' + params[:bar]}
471
+ end
472
+ end
473
+
474
+ expect(React.render_to_static_markup(React.create_element(Foo, foo: 'lorem'))).to eq('<div>lorem-bar</div>')
475
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>foo-bar</div>')
476
+ end
477
+ end
478
+ end
479
+
480
+ describe 'Event handling' do
481
+ before do
482
+ stub_const 'Foo', Class.new
483
+ Foo.class_eval do
484
+ include React::Component
485
+ end
486
+ end
487
+
488
+ it 'works in render method' do
489
+ Foo.class_eval do
490
+ define_state(:clicked) { false }
491
+
492
+ def render
493
+ React.create_element('div').on(:click) do
494
+ self.clicked = true
495
+ end
496
+ end
497
+ end
498
+
499
+ element = React.create_element(Foo)
500
+ instance = renderElementToDocument(element)
501
+ simulateEvent(:click, instance)
502
+ expect(instance.state.clicked).to eq(true)
503
+ end
504
+
505
+ it 'invokes handler on `this.props` using emit' do
506
+ Foo.class_eval do
507
+ after_mount :setup
508
+
509
+ def setup
510
+ self.emit(:foo_submit, 'bar')
511
+ end
512
+
513
+ def render
514
+ React.create_element('div')
515
+ end
516
+ end
517
+
518
+ expect { |b|
519
+ element = React.create_element(Foo).on(:foo_submit, &b)
520
+ renderElementToDocument(element)
521
+ }.to yield_with_args('bar')
522
+ end
523
+
524
+ it 'invokes handler with multiple params using emit' do
525
+ Foo.class_eval do
526
+ after_mount :setup
527
+
528
+ def setup
529
+ self.emit(:foo_invoked, [1,2,3], 'bar')
530
+ end
531
+
532
+ def render
533
+ React.create_element('div')
534
+ end
535
+ end
536
+
537
+ expect { |b|
538
+ element = React.create_element(Foo).on(:foo_invoked, &b)
539
+ renderElementToDocument(element)
540
+ }.to yield_with_args([1,2,3], 'bar')
541
+ end
542
+ end
543
+
544
+ describe '#refs' do
545
+ before do
546
+ stub_const 'Foo', Class.new
547
+ Foo.class_eval do
548
+ include React::Component
549
+ end
550
+ end
551
+
552
+ it 'correctly assigns refs' do
553
+ Foo.class_eval do
554
+ def render
555
+ React.create_element('input', type: :text, ref: :field)
556
+ end
557
+ end
558
+
559
+ element = renderToDocument(Foo)
560
+ expect(element.refs.field).not_to be_nil
561
+ end
562
+
563
+ it 'accesses refs through `refs` method' do
564
+ Foo.class_eval do
565
+ def render
566
+ React.create_element('input', type: :text, ref: :field).on(:click) do
567
+ refs[:field].value = 'some_stuff'
568
+ end
569
+ end
570
+ end
571
+
572
+ element = renderToDocument(Foo)
573
+ simulateEvent(:click, element)
574
+
575
+ expect(element.refs.field.value).to eq('some_stuff')
576
+ end
577
+ end
578
+
579
+ describe '#render' do
580
+ it 'supports element building helpers' do
581
+ stub_const 'Foo', Class.new
582
+ Foo.class_eval do
583
+ include React::Component
584
+
585
+ def render
586
+ div do
587
+ span { params[:foo] }
588
+ end
589
+ end
590
+ end
591
+
592
+ stub_const 'Bar', Class.new
593
+ Bar.class_eval do
594
+ include React::Component
595
+ def render
596
+ div do
597
+ present Foo, foo: 'astring'
598
+ end
599
+ end
600
+ end
601
+
602
+ expect(React.render_to_static_markup(React.create_element(Bar))).to eq('<div><div><span>astring</span></div></div>')
603
+ end
604
+
605
+ it 'builds single node in top-level render without providing a block' do
606
+ stub_const 'Foo', Class.new
607
+ Foo.class_eval do
608
+ include React::Component
609
+
610
+ def render
611
+ div
612
+ end
613
+ end
614
+
615
+ element = React.create_element(Foo)
616
+ expect(React.render_to_static_markup(element)).to eq('<div></div>')
617
+ end
618
+
619
+ it 'redefines `p` to make method missing work' do
620
+ stub_const 'Foo', Class.new
621
+ Foo.class_eval do
622
+ include React::Component
623
+
624
+ def render
625
+ p(class_name: 'foo') do
626
+ p
627
+ div { 'lorem ipsum' }
628
+ p(id: '10')
629
+ end
630
+ end
631
+ end
632
+
633
+ element = React.create_element(Foo)
634
+ markup = '<p class="foo"><p></p><div>lorem ipsum</div><p id="10"></p></p>'
635
+ expect(React.render_to_static_markup(element)).to eq(markup)
636
+ end
637
+
638
+ it 'only overrides `p` in render context' do
639
+ stub_const 'Foo', Class.new
640
+ Foo.class_eval do
641
+ include React::Component
642
+
643
+ before_mount do
644
+ p 'first'
645
+ end
646
+
647
+ after_mount do
648
+ p 'second'
649
+ end
650
+
651
+ def render
652
+ div
653
+ end
654
+ end
655
+
656
+ expect(Kernel).to receive(:p).with('first')
657
+ expect(Kernel).to receive(:p).with('second')
658
+ renderToDocument(Foo)
659
+ end
660
+ end
661
+
662
+ describe 'isMounted()' do
663
+ it 'returns true if after mounted' do
664
+ stub_const 'Foo', Class.new
665
+ Foo.class_eval do
666
+ include React::Component
667
+
668
+ def render
669
+ React.create_element('div')
670
+ end
671
+ end
672
+
673
+ component = renderToDocument(Foo)
674
+ expect(component.mounted?).to eq(true)
675
+ end
676
+ end
677
+
678
+ describe '#children' do
679
+ before(:each) do
680
+ stub_const 'Foo', Class.new
681
+ Foo.class_eval do
682
+ include React::Component
683
+ export_state :the_children
684
+ before_mount do
685
+ the_children! children
686
+ end
687
+ def render
688
+ React.create_element('div') { 'lorem' }
689
+ end
690
+ end
691
+ end
692
+
693
+ it 'returns an Enumerable' do
694
+ ele = React.create_element(Foo) {
695
+ [React.create_element('a'), React.create_element('li')]
696
+ }
697
+ renderElementToDocument(ele)
698
+ nodes = Foo.the_children.map { |ele| ele.element_type }
699
+ expect(nodes).to eq(['a', 'li'])
700
+ end
701
+
702
+ it 'returns an Enumerator when not providing a block' do
703
+ ele = React.create_element(Foo) {
704
+ [React.create_element('a'), React.create_element('li')]
705
+ }
706
+ renderElementToDocument(ele)
707
+ nodes = Foo.the_children.each
708
+ expect(nodes).to be_a(Enumerator)
709
+ expect(nodes.size).to eq(2)
710
+ end
711
+
712
+ it 'returns an empty Enumerator if there are no children' do
713
+ ele = React.create_element(Foo)
714
+ renderElementToDocument(ele)
715
+ nodes = Foo.the_children.each
716
+ expect(nodes.size).to eq(nil)
717
+ expect(nodes.count).to eq(0)
718
+ end
719
+ end
720
+ end
721
+ end