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
@@ -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