reactrb 0.7.42

Sign up to get free protection for your applications and to get access to all the features.
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