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
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe 'the React DSL' do
5
+
6
+ it "will turn the last string in a block into a element" do
7
+ stub_const 'Foo', Class.new
8
+ Foo.class_eval do
9
+ include React::Component
10
+ def render
11
+ div { "hello" }
12
+ end
13
+ end
14
+
15
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>hello</div>')
16
+ end
17
+
18
+ it "has a .span short hand String method" do
19
+ stub_const 'Foo', Class.new
20
+ Foo.class_eval do
21
+ include React::Component
22
+ def render
23
+ div { "hello".span; "goodby".span }
24
+ end
25
+ end
26
+
27
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div><span>hello</span><span>goodby</span></div>')
28
+ end
29
+
30
+ it "has a .br short hand String method" do
31
+ stub_const 'Foo', Class.new
32
+ Foo.class_eval do
33
+ include React::Component
34
+ def render
35
+ div { "hello".br }
36
+ end
37
+ end
38
+
39
+ expect(React.render_to_static_markup(React.create_element(Foo)).gsub("<br/>", "<br>")).to eq('<div><span>hello<br></span></div>')
40
+ end
41
+
42
+ it "has a .td short hand String method" do
43
+ stub_const 'Foo', Class.new
44
+ Foo.class_eval do
45
+ include React::Component
46
+ def render
47
+ table { tr { "hello".td } }
48
+ end
49
+ end
50
+
51
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<table><tr><td>hello</td></tr></table>')
52
+ end
53
+
54
+ it "has a .para short hand String method" do
55
+ stub_const 'Foo', Class.new
56
+ Foo.class_eval do
57
+ include React::Component
58
+ def render
59
+ div { "hello".para }
60
+ end
61
+ end
62
+
63
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div><p>hello</p></div>')
64
+ end
65
+
66
+ it "will treat the component class name as a first class component name" do
67
+ stub_const 'Mod::Bar', Class.new
68
+ Mod::Bar.class_eval do
69
+ include React::Component
70
+ def render
71
+ "a man walks into a bar"
72
+ end
73
+ end
74
+ stub_const 'Foo', Class.new(React::Component::Base)
75
+ Foo.class_eval do
76
+ def render
77
+ Mod::Bar()
78
+ end
79
+ end
80
+
81
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<span>a man walks into a bar</span>')
82
+ end
83
+
84
+ it "can add class names by the haml .class notation" do
85
+ stub_const 'Mod::Bar', Class.new(React::Component::Base)
86
+ Mod::Bar.class_eval do
87
+ collect_other_params_as :attributes
88
+ def render
89
+ "a man walks into a bar".span(attributes)
90
+ end
91
+ end
92
+ stub_const 'Foo', Class.new(React::Component::Base)
93
+ Foo.class_eval do
94
+ def render
95
+ Mod::Bar().the_class
96
+ end
97
+ end
98
+
99
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<span class="the-class">a man walks into a bar</span>')
100
+ end
101
+
102
+ it "can use the 'class' keyword for classes" do
103
+ stub_const 'Foo', Class.new
104
+ Foo.class_eval do
105
+ include React::Component
106
+ def render
107
+ span(class: "the-class") { "hello" }
108
+ end
109
+ end
110
+
111
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<span class="the-class">hello</span>')
112
+ end
113
+
114
+ it "can generate a unrendered node using the .as_node method" do # div { "hello" }.as_node
115
+ stub_const 'Foo', Class.new #(React::Component::Base)
116
+ Foo.class_eval do
117
+ include React::Component
118
+ def render
119
+ span { "hello".span.as_node.class.name }.as_node.render
120
+ end
121
+ end
122
+
123
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<span>React::Element</span>')
124
+ end
125
+
126
+ it "can use the dangerously_set_inner_HTML param" do
127
+ stub_const 'Foo', Class.new
128
+ Foo.class_eval do
129
+ include React::Component
130
+ def render
131
+ div(dangerously_set_inner_HTML: { __html: "Hello&nbsp;&nbsp;Goodby" })
132
+ end
133
+ end
134
+
135
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>Hello&nbsp;&nbsp;Goodby</div>')
136
+ end
137
+
138
+ it "will remove all elements passed as params from the rendering buffer" do
139
+ stub_const 'X2', Class.new
140
+ X2.class_eval do
141
+ include React::Component
142
+ param :ele
143
+ def render
144
+ div do
145
+ ele.render
146
+ ele.render
147
+ end
148
+ end
149
+ end
150
+ stub_const 'Test', Class.new
151
+ Test.class_eval do
152
+ include React::Component
153
+ def render
154
+ X2(ele: b { "hello" })
155
+ end
156
+ end
157
+
158
+ expect(React.render_to_static_markup(React.create_element(Test))).to eq('<div><b>hello</b><b>hello</b></div>')
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,47 @@
1
+ require "spec_helper"
2
+
3
+ if opal?
4
+ describe React::Element do
5
+ it 'bridges `type` of native React.Element attributes' do
6
+ element = React.create_element('div')
7
+ expect(element.element_type).to eq("div")
8
+ end
9
+
10
+ async 'is renderable' do
11
+ element = React.create_element('span')
12
+ div = `document.createElement("div")`
13
+ React.render(element, div) do
14
+ run_async {
15
+ expect(`div.children[0].tagName`).to eq("SPAN")
16
+ }
17
+ end
18
+ end
19
+
20
+ describe 'Event subscription' do
21
+ it 'is subscribable through `on(:event_name)` method' do
22
+ expect { |b|
23
+ element = React.create_element("div").on(:click, &b)
24
+ instance = renderElementToDocument(element)
25
+ simulateEvent(:click, instance)
26
+ }.to yield_with_args(React::Event)
27
+
28
+ expect { |b|
29
+ element = React.create_element("div").on(:key_down, &b)
30
+ instance = renderElementToDocument(element)
31
+ simulateEvent(:keyDown, instance, {key: "Enter"})
32
+ }.to yield_control
33
+
34
+ expect { |b|
35
+ element = React.create_element("form").on(:submit, &b)
36
+ instance = renderElementToDocument(element)
37
+ simulateEvent(:submit, instance, {})
38
+ }.to yield_control
39
+ end
40
+
41
+ it 'returns self for `on` method' do
42
+ element = React.create_element("div")
43
+ expect(element.on(:click){}).to eq(element)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ if opal?
4
+ describe React::Event do
5
+ it "should bridge attributes of native SyntheticEvent (see http://facebook.github.io/react/docs/events.html#syntheticevent)" do
6
+ element = React.create_element('div').on(:click) do |event|
7
+ expect(event.bubbles).to eq(`#{event.to_n}.bubbles`)
8
+ expect(event.cancelable).to eq(`#{event.to_n}.cancelable`)
9
+ expect(event.current_target).to eq(`#{event.to_n}.currentTarget`)
10
+ expect(event.default_prevented).to eq(`#{event.to_n}.defaultPrevented`)
11
+ expect(event.event_phase).to eq(`#{event.to_n}.eventPhase`)
12
+ expect(event.is_trusted?).to eq(`#{event.to_n}.isTrusted`)
13
+ expect(event.native_event).to eq(`#{event.to_n}.nativeEvent`)
14
+ expect(event.target).to eq(`#{event.to_n}.target`)
15
+ expect(event.timestamp).to eq(`#{event.to_n}.timeStamp`)
16
+ expect(event.event_type).to eq(`#{event.to_n}.type`)
17
+ expect(event).to respond_to(:prevent_default)
18
+ expect(event).to respond_to(:stop_propagation)
19
+ end
20
+ instance = renderElementToDocument(element)
21
+ simulateEvent(:click, instance)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe React::NativeLibrary do
5
+ it "will import a React.js library into the Ruby name space" # class BS < NativeLibrary; imports 'ReactBootstrap'; end
6
+ it "exclude specific components from a library" # exclude "Modal" # can't access BS.Modal
7
+ it "rename specific components from a library" # rename "Modal" => "FooBar" # BS.FooBar connects to ReactBootstrap.Modal
8
+ it "can importan multiple libraries into one class"
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe 'React::Observable' do
5
+ # tbd
6
+ end
7
+ end
@@ -0,0 +1,286 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe 'the param macro' do
5
+ it 'defines collect_other_params_as method on params proxy' do
6
+ stub_const 'Foo', Class.new(React::Component::Base)
7
+ Foo.class_eval do
8
+ collect_other_params_as :foo
9
+
10
+ def render
11
+ div { params.foo[:bar] }
12
+ end
13
+ end
14
+
15
+ html = React.render_to_static_markup(React.create_element(Foo, { bar: 'biz' }))
16
+ expect(html).to eq('<div>biz</div>')
17
+ end
18
+
19
+ it 'still defines deprecated collect_other_params_as method' do
20
+ stub_const 'Foo', Class.new(React::Component::Base)
21
+ Foo.class_eval do
22
+ collect_other_params_as :foo
23
+
24
+ def render
25
+ div { foo[:bar] }
26
+ end
27
+ end
28
+
29
+ html = React.render_to_static_markup(React.create_element(Foo, { bar: 'biz' }))
30
+ expect(html).to eq('<div>biz</div>')
31
+ end
32
+
33
+ it 'still defines deprecated param accessor method' do
34
+ stub_const 'Foo', Class.new(React::Component::Base)
35
+ Foo.class_eval do
36
+ param :foo
37
+
38
+ def render
39
+ div { foo }
40
+ end
41
+ end
42
+
43
+ html = React.render_to_static_markup(React.create_element(Foo, {foo: :bar}))
44
+ expect(html).to eq('<div>bar</div>')
45
+ end
46
+
47
+ it "can create and access a required param" do
48
+ stub_const 'Foo', Class.new(React::Component::Base)
49
+ Foo.class_eval do
50
+ param :foo
51
+
52
+ def render
53
+ div { params.foo }
54
+ end
55
+ end
56
+
57
+ expect(React.render_to_static_markup(React.create_element(Foo, {foo: :bar}))).to eq('<div>bar</div>')
58
+ end
59
+
60
+ it "can create and access an optional params" do
61
+ stub_const 'Foo', Class.new(React::Component::Base)
62
+ Foo.class_eval do
63
+
64
+ param foo1: :no_bar1
65
+ param foo2: :no_bar2
66
+ param :foo3, default: :no_bar3
67
+ param :foo4, default: :no_bar4
68
+
69
+ def render
70
+ div { "#{params.foo1}-#{params.foo2}-#{params.foo3}-#{params.foo4}" }
71
+ end
72
+ end
73
+
74
+ expect(React.render_to_static_markup(React.create_element(Foo, {foo1: :bar1, foo3: :bar3}))).to eq('<div>bar1-no_bar2-bar3-no_bar4</div>')
75
+ end
76
+
77
+ it 'can specify validation rules with the type option' do
78
+ stub_const 'Foo', Class.new(React::Component::Base)
79
+ Foo.class_eval do
80
+ param :foo, type: String
81
+ end
82
+
83
+ expect(Foo.prop_types).to have_key(:_componentValidator)
84
+ end
85
+
86
+ it "can type check params" do
87
+ stub_const 'Foo', Class.new(React::Component::Base)
88
+ Foo.class_eval do
89
+
90
+ param :foo1, type: String
91
+ param :foo2, type: String
92
+
93
+ def render
94
+ div { "#{params.foo1}-#{params.foo2}" }
95
+ end
96
+ end
97
+
98
+ expect(React.render_to_static_markup(React.create_element(Foo, {foo1: 12, foo2: "string"}))).to eq('<div>12-string</div>')
99
+ end
100
+
101
+ it 'logs error in warning if validation failed' do
102
+ stub_const 'Lorem', Class.new
103
+ stub_const 'Foo2', Class.new(React::Component::Base)
104
+ Foo2.class_eval do
105
+ param :foo
106
+ param :lorem, type: Lorem
107
+ param :bar, default: nil, type: String
108
+ def render; div; end
109
+ end
110
+
111
+ %x{
112
+ var log = [];
113
+ var org_warn_console = window.console.warn;
114
+ var org_error_console = window.console.error;
115
+ window.console.warn = window.console.error = function(str){log.push(str)}
116
+ }
117
+ renderToDocument(Foo2, bar: 10, lorem: Lorem.new)
118
+ `window.console.warn = org_warn_console; window.console.error = org_error_console;`
119
+
120
+ expect(`log`).to eq(["Warning: Failed propType: In component `Foo2`\nRequired prop `foo` was not specified\nProvided prop `bar` could not be converted to String"])
121
+ end
122
+
123
+ it 'should not log anything if validation passes' do
124
+ stub_const 'Lorem', Class.new
125
+ stub_const 'Foo', Class.new(React::Component::Base)
126
+ Foo.class_eval do
127
+ param :foo
128
+ param :lorem, type: Lorem
129
+ param :bar, default: nil, type: String
130
+
131
+ def render; div; end
132
+ end
133
+
134
+ %x{
135
+ var log = [];
136
+ var org_warn_console = window.console.warn;
137
+ var org_error_console = window.console.error;
138
+ window.console.warn = window.console.error = function(str){log.push(str)}
139
+ }
140
+ renderToDocument(Foo, foo: 10, bar: '10', lorem: Lorem.new)
141
+ `window.console.warn = org_warn_console; window.console.error = org_error_console;`
142
+ expect(`log`).to eq([])
143
+ end
144
+
145
+ describe 'advanced type handling' do
146
+ before(:each) do
147
+ %x{
148
+ window.dummy_log = [];
149
+ window.org_warn_console = window.console.warn;
150
+ window.org_error_console = window.console.warn
151
+ window.console.warn = window.console.error = function(str){window.dummy_log.push(str)}
152
+ }
153
+ stub_const 'Foo', Class.new(React::Component::Base)
154
+ Foo.class_eval { def render; ""; end }
155
+ end
156
+ after(:each) do
157
+ `window.console.warn = window.org_warn_console; window.console.error = window.org_error_console`
158
+ end
159
+
160
+ it "can use the [] notation for arrays" do
161
+ Foo.class_eval do
162
+ param :foo, type: []
163
+ param :bar, type: []
164
+ end
165
+ renderToDocument(Foo, foo: 10, bar: [10])
166
+ expect(`window.dummy_log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `foo` could not be converted to Array"])
167
+ end
168
+
169
+ it "can use the [xxx] notation for arrays of a specific type" do
170
+ Foo.class_eval do
171
+ param :foo, type: [String]
172
+ param :bar, type: [String]
173
+ end
174
+ renderToDocument(Foo, foo: [10], bar: ["10"])
175
+ expect(`window.dummy_log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `foo`[0] could not be converted to String"])
176
+ end
177
+
178
+ it "can convert a json hash to a type" do
179
+ stub_const "BazWoggle", Class.new
180
+ BazWoggle.class_eval do
181
+ def initialize(kind)
182
+ @kind = kind
183
+ end
184
+ attr_accessor :kind
185
+ def self._react_param_conversion(json, validate_only)
186
+ new(json[:bazwoggle]) if json[:bazwoggle]
187
+ end
188
+ end
189
+ Foo.class_eval do
190
+ param :foo, type: BazWoggle
191
+ param :bar, type: BazWoggle
192
+ param :baz, type: [BazWoggle]
193
+ def render
194
+ "#{params.bar.kind}, #{params.baz[0].kind}"
195
+ end
196
+ end
197
+ expect(React.render_to_static_markup(React.create_element(
198
+ Foo, foo: "", bar: {bazwoggle: 1}, baz: [{bazwoggle: 2}]))).to eq('<span>1, 2</span>')
199
+ expect(`window.dummy_log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `foo` could not be converted to BazWoggle"])
200
+ end
201
+
202
+ describe "converts params only once" do
203
+
204
+ it "not on every access" do
205
+ stub_const "BazWoggle", Class.new
206
+ BazWoggle.class_eval do
207
+ def initialize(kind)
208
+ @kind = kind
209
+ end
210
+ attr_accessor :kind
211
+ def self._react_param_conversion(json, validate_only)
212
+ new(json[:bazwoggle]) if json[:bazwoggle]
213
+ end
214
+ end
215
+ Foo.class_eval do
216
+ param :foo, type: BazWoggle
217
+ def render
218
+ params.foo.kind = params.foo.kind+1
219
+ "#{params.foo.kind}"
220
+ end
221
+ end
222
+ expect(React.render_to_static_markup(React.create_element(Foo, foo: {bazwoggle: 1}))).to eq('<span>2</span>')
223
+ end
224
+
225
+ it "even if contains an embedded native object" do
226
+ stub_const "Bar", Class.new(React::Component::Base)
227
+ stub_const "BazWoggle", Class.new
228
+ BazWoggle.class_eval do
229
+ def initialize(kind)
230
+ @kind = kind
231
+ end
232
+ attr_accessor :kind
233
+ def self._react_param_conversion(json, validate_only)
234
+ new(JSON.from_object(json[0])[:bazwoggle]) if JSON.from_object(json[0])[:bazwoggle]
235
+ end
236
+ end
237
+ Bar.class_eval do
238
+ param :foo, type: BazWoggle
239
+ def render
240
+ params.foo.kind.to_s
241
+ end
242
+ end
243
+ Foo.class_eval do
244
+ export_state :change_me
245
+ before_mount do
246
+ Foo.change_me! "initial"
247
+ end
248
+ def render
249
+ Bar(foo: Native([`{bazwoggle: #{Foo.change_me}}`]))
250
+ end
251
+ end
252
+ div = `document.createElement("div")`
253
+ React.render(React.create_element(Foo, {}), div)
254
+ Foo.change_me! "updated"
255
+ expect(`div.children[0].innerHTML`).to eq("updated")
256
+ end
257
+ end
258
+
259
+ it "will alias a Proc type param" do
260
+ Foo.class_eval do
261
+ param :foo, type: Proc
262
+ def render
263
+ params.foo
264
+ end
265
+ end
266
+ expect(React.render_to_static_markup(React.create_element(Foo, foo: lambda { 'works!' }))).to eq('<span>works!</span>')
267
+ end
268
+
269
+ it "will create a 'bang' (i.e. update) method if the type is React::Observable" do
270
+ Foo.class_eval do
271
+ param :foo, type: React::Observable
272
+ before_mount do
273
+ params.foo! "ha!"
274
+ end
275
+ def render
276
+ params.foo
277
+ end
278
+ end
279
+ current_state = ""
280
+ observer = React::Observable.new(current_state) { |new_state| current_state = new_state }
281
+ expect(React.render_to_static_markup(React.create_element(Foo, foo: observer))).to eq('<span>ha!</span>')
282
+ expect(current_state).to eq("ha!")
283
+ end
284
+ end
285
+ end
286
+ end