react_on_rails 10.0.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc +4 -0
  3. data/.rubocop.yml +2 -2
  4. data/.travis.yml +3 -0
  5. data/CHANGELOG.md +81 -88
  6. data/CONTRIBUTING.md +0 -7
  7. data/Dockerfile_tests +12 -0
  8. data/Gemfile +8 -13
  9. data/NEWS.md +2 -0
  10. data/PROJECTS.md +10 -2
  11. data/README.md +137 -121
  12. data/app/helpers/react_on_rails_helper.rb +1 -533
  13. data/docker-compose.yml +11 -0
  14. data/docs/additional-reading/caching-and-performance.md +4 -0
  15. data/docs/additional-reading/capistrano-deployment.md +18 -0
  16. data/docs/additional-reading/elastic-beanstalk.md +53 -33
  17. data/docs/additional-reading/server-rendering-tips.md +4 -4
  18. data/docs/additional-reading/troubleshooting-when-using-webpacker.md +90 -0
  19. data/docs/api/ruby-api.md +7 -2
  20. data/docs/basics/configuration.md +23 -9
  21. data/docs/basics/i18n.md +4 -0
  22. data/docs/basics/upgrading-react-on-rails.md +181 -0
  23. data/docs/tutorial.md +7 -11
  24. data/lib/generators/react_on_rails/base_generator.rb +6 -3
  25. data/lib/generators/react_on_rails/dev_tests_generator.rb +1 -1
  26. data/lib/generators/react_on_rails/install_generator.rb +1 -1
  27. data/lib/generators/react_on_rails/react_no_redux_generator.rb +1 -1
  28. data/lib/generators/react_on_rails/react_with_redux_generator.rb +2 -2
  29. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +1 -1
  30. data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb +3 -2
  31. data/lib/generators/react_on_rails/templates/dev_tests/spec/simplecov_helper.rb +2 -2
  32. data/lib/react_on_rails/assets_precompile.rb +8 -4
  33. data/lib/react_on_rails/configuration.rb +34 -20
  34. data/lib/react_on_rails/error.rb +4 -0
  35. data/lib/react_on_rails/locales_to_js.rb +2 -2
  36. data/lib/react_on_rails/prerender_error.rb +2 -2
  37. data/lib/react_on_rails/react_on_rails_helper.rb +547 -0
  38. data/lib/react_on_rails/server_rendering_pool/{exec.rb → ruby_embedded_java_script.rb} +37 -40
  39. data/lib/react_on_rails/server_rendering_pool.rb +21 -10
  40. data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +2 -2
  41. data/lib/react_on_rails/test_helper.rb +1 -1
  42. data/lib/react_on_rails/utils.rb +14 -52
  43. data/lib/react_on_rails/version.rb +1 -1
  44. data/lib/react_on_rails/version_checker.rb +33 -12
  45. data/lib/react_on_rails/webpacker_utils.rb +42 -0
  46. data/lib/react_on_rails.rb +3 -2
  47. data/package.json +2 -5
  48. data/rakelib/dummy_apps.rake +2 -1
  49. data/rakelib/example_type.rb +1 -1
  50. data/rakelib/examples.rake +3 -2
  51. data/rakelib/lint.rake +3 -2
  52. data/rakelib/node_package.rake +2 -1
  53. data/rakelib/release.rake +4 -5
  54. data/rakelib/run_rspec.rake +3 -4
  55. data/rakelib/task_helpers.rb +1 -1
  56. data/react_on_rails.gemspec +23 -13
  57. data/yarn.lock +3 -39
  58. metadata +75 -49
  59. data/lib/react_on_rails/server_rendering_pool/node.rb +0 -83
  60. data/lib/react_on_rails/test_helper/node_process_launcher.rb +0 -14
@@ -1,537 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Metrics/ModuleLength
4
- # NOTE:
5
- # For any heredoc JS:
6
- # 1. The white spacing in this file matters!
7
- # 2. Keep all #{some_var} fully to the left so that all indentation is done evenly in that var
8
- require "react_on_rails/prerender_error"
9
- require "addressable/uri"
10
- require "react_on_rails/utils"
11
- require "react_on_rails/json_output"
12
-
13
3
  module ReactOnRailsHelper
14
- include ReactOnRails::Utils::Required
15
-
16
- COMPONENT_HTML_KEY = "componentHtml"
17
-
18
- # The env_javascript_include_tag and env_stylesheet_link_tag support the usage of a webpack
19
- # dev server for providing the JS and CSS assets during development mode. See
20
- # https://github.com/shakacode/react-webpack-rails-tutorial/ for a working example.
21
- #
22
- # The key options are `static` and `hot` which specify what you want for static vs. hot. Both of
23
- # these params are optional, and support either a single value, or an array.
24
- #
25
- # static vs. hot is picked based on whether
26
- # ENV["REACT_ON_RAILS_ENV"] == "HOT"
27
- #
28
- # <%= env_stylesheet_link_tag(static: 'application_static',
29
- # hot: 'application_non_webpack',
30
- # media: 'all',
31
- # 'data-turbolinks-track' => "reload") %>
32
- #
33
- # <!-- These do not use turbolinks, so no data-turbolinks-track -->
34
- # <!-- This is to load the hot assets. -->
35
- # <%= env_javascript_include_tag(hot: ['http://localhost:3500/vendor-bundle.js',
36
- # 'http://localhost:3500/app-bundle.js']) %>
37
- #
38
- # <!-- These do use turbolinks -->
39
- # <%= env_javascript_include_tag(static: 'application_static',
40
- # hot: 'application_non_webpack',
41
- # 'data-turbolinks-track' => "reload") %>
42
- #
43
- # NOTE: for Turbolinks 2.x, use 'data-turbolinks-track' => true
44
- # See application.html.erb for usage example
45
- # https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/app%2Fviews%2Flayouts%2Fapplication.html.erb
46
- def env_javascript_include_tag(args = {})
47
- send_tag_method(:javascript_include_tag, args)
48
- end
49
-
50
- # Helper to set CSS assets depending on if we want static or "hot", which means from the
51
- # Webpack dev server.
52
- #
53
- # In this example, application_non_webpack is simply a CSS asset pipeline file which includes
54
- # styles not placed in the webpack build.
55
- #
56
- # We don't need styles from the webpack build, as those will come via the JavaScript include
57
- # tags.
58
- #
59
- # The key options are `static` and `hot` which specify what you want for static vs. hot. Both of
60
- # these params are optional, and support either a single value, or an array.
61
- #
62
- # <%= env_stylesheet_link_tag(static: 'application_static',
63
- # hot: 'application_non_webpack',
64
- # media: 'all',
65
- # 'data-turbolinks-track' => true) %>
66
- #
67
- def env_stylesheet_link_tag(args = {})
68
- send_tag_method(:stylesheet_link_tag, args)
69
- end
70
-
71
- # react_component_name: can be a React component, created using a ES6 class, or
72
- # React.createClass, or a
73
- # `generator function` that returns a React component
74
- # using ES6
75
- # let MyReactComponentApp = (props, railsContext) => <MyReactComponent {...props}/>;
76
- # or using ES5
77
- # var MyReactComponentApp = function(props, railsContext) { return <YourReactComponent {...props}/>; }
78
- # Exposing the react_component_name is necessary to both a plain ReactComponent as well as
79
- # a generator:
80
- # See README.md for how to "register" your react components.
81
- # See spec/dummy/client/app/startup/serverRegistration.jsx and
82
- # spec/dummy/client/app/startup/ClientRegistration.jsx for examples of this
83
- #
84
- # options:
85
- # props: Ruby Hash or JSON string which contains the properties to pass to the react object. Do
86
- # not pass any props if you are separately initializing the store by the `redux_store` helper.
87
- # prerender: <true/false> set to false when debugging!
88
- # id: You can optionally set the id, or else a unique one is automatically generated.
89
- # html_options: You can set other html attributes that will go on this component
90
- # trace: <true/false> set to true to print additional debugging information in the browser
91
- # default is true for development, off otherwise
92
- # replay_console: <true/false> Default is true. False will disable echoing server rendering
93
- # logs to browser. While this can make troubleshooting server rendering difficult,
94
- # so long as you have the default configuration of logging_on_server set to
95
- # true, you'll still see the errors on the server.
96
- # raise_on_prerender_error: <true/false> Default to false. True will raise exception on server
97
- # if the JS code throws
98
- # Any other options are passed to the content tag, including the id.
99
- def react_component(component_name, raw_options = {})
100
- internal_result = internal_react_component(component_name, raw_options)
101
- server_rendered_html = internal_result["result"]["html"]
102
- console_script = internal_result["result"]["consoleReplayScript"]
103
-
104
- if server_rendered_html.is_a?(String)
105
- build_react_component_result_for_server_rendered_string(
106
- server_rendered_html: server_rendered_html,
107
- component_specification_tag: internal_result["tag"],
108
- console_script: console_script,
109
- options: internal_result["options"]
110
- )
111
- elsif server_rendered_html.is_a?(Hash)
112
- puts "[DEPRECATION] ReactOnRails: Use react_component_hash to return a Hash to your ruby view code"
113
- build_react_component_result_for_server_rendered_hash(
114
- server_rendered_html: server_rendered_html,
115
- component_specification_tag: internal_result["tag"],
116
- console_script: console_script,
117
- options: internal_result["options"]
118
- )
119
- else
120
- raise "server_rendered_html is expected to be a String. If you're trying to use a generator function to
121
- return a Hash to your ruby view code, then use react_component_hash instead of react_component and
122
- see https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/client/app/startup/ReactHelmetServerApp.jsx
123
- for an example of the necessary javascript configuration."
124
- end
125
- end
126
-
127
- def react_component_hash(component_name, raw_options = {})
128
- internal_result = internal_react_component(component_name, raw_options)
129
- server_rendered_html = internal_result["result"]["html"]
130
- console_script = internal_result["result"]["consoleReplayScript"]
131
-
132
- if server_rendered_html.is_a?(Hash)
133
- build_react_component_result_for_server_rendered_hash(
134
- server_rendered_html: server_rendered_html,
135
- component_specification_tag: internal_result["tag"],
136
- console_script: console_script,
137
- options: internal_result["options"]
138
- )
139
- else
140
- raise "Generator function is expected to return an Object. See
141
- https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/client/app/startup/ReactHelmetServerApp.jsx
142
- for an example of the necessary javascript configuration."
143
- end
144
- end
145
-
146
- # Separate initialization of store from react_component allows multiple react_component calls to
147
- # use the same Redux store.
148
- #
149
- # store_name: name of the store, corresponding to your call to ReactOnRails.registerStores in your
150
- # JavaScript code.
151
- # props: Ruby Hash or JSON string which contains the properties to pass to the redux store.
152
- # Options
153
- # defer: false -- pass as true if you wish to render this below your component.
154
- def redux_store(store_name, props: {}, defer: false)
155
- redux_store_data = { store_name: store_name,
156
- props: props }
157
- if defer
158
- @registered_stores_defer_render ||= []
159
- @registered_stores_defer_render << redux_store_data
160
- "YOU SHOULD NOT SEE THIS ON YOUR VIEW -- Uses as a code block, like <% redux_store %> "\
161
- "and not <%= redux store %>"
162
- else
163
- @registered_stores ||= []
164
- @registered_stores << redux_store_data
165
- result = render_redux_store_data(redux_store_data)
166
- prepend_render_rails_context(result)
167
- end
168
- end
169
-
170
- # Place this view helper (no parameters) at the end of your shared layout. This tell
171
- # ReactOnRails where to client render the redux store hydration data. Since we're going
172
- # to be setting up the stores in the controllers, we need to know where on the view to put the
173
- # client side rendering of this hydration data, which is a hidden div with a matching class
174
- # that contains a data props.
175
- def redux_store_hydration_data
176
- return if @registered_stores_defer_render.blank?
177
- # rubocop:disable Performance/UnfreezeString
178
- @registered_stores_defer_render.reduce("".dup) do |accum, redux_store_data|
179
- # rubocop:enable Performance/UnfreezeString
180
- accum << render_redux_store_data(redux_store_data)
181
- end.html_safe
182
- end
183
-
184
- def sanitized_props_string(props)
185
- ReactOnRails::JsonOutput.escape(props.is_a?(String) ? props : props.to_json)
186
- end
187
-
188
- # Helper method to take javascript expression and returns the output from evaluating it.
189
- # If you have more than one line that needs to be executed, wrap it in an IIFE.
190
- # JS exceptions are caught and console messages are handled properly.
191
- def server_render_js(js_expression, options = {})
192
- wrapper_js = <<-JS.strip_heredoc
193
- (function() {
194
- var htmlResult = '';
195
- var consoleReplayScript = '';
196
- var hasErrors = false;
197
-
198
- try {
199
- htmlResult =
200
- (function() {
201
- return #{js_expression};
202
- })();
203
- } catch(e) {
204
- htmlResult = ReactOnRails.handleError({e: e, name: null,
205
- jsCode: '#{escape_javascript(js_expression)}', serverSide: true});
206
- hasErrors = true;
207
- }
208
-
209
- consoleReplayScript = ReactOnRails.buildConsoleReplay();
210
-
211
- return JSON.stringify({
212
- html: htmlResult,
213
- consoleReplayScript: consoleReplayScript,
214
- hasErrors: hasErrors
215
- });
216
-
217
- })()
218
- JS
219
-
220
- result = ReactOnRails::ServerRenderingPool.server_render_js_with_console_logging(wrapper_js)
221
-
222
- # IMPORTANT: To ensure that Rails doesn't auto-escape HTML tags, use the 'raw' method.
223
- html = result["html"]
224
- console_log_script = result["consoleLogScript"]
225
- raw("#{html}#{replay_console_option(options[:replay_console_option]) ? console_log_script : ''}")
226
- rescue ExecJS::ProgramError => err
227
- raise ReactOnRails::PrerenderError, component_name: "N/A (server_render_js called)",
228
- err: err,
229
- js_code: wrapper_js
230
- # rubocop:enable Style/RaiseArgs
231
- end
232
-
233
- def json_safe_and_pretty(hash_or_string)
234
- return "{}" if hash_or_string.nil?
235
- unless hash_or_string.class.in?([Hash, String])
236
- raise "#{__method__} only accepts String or Hash as argument "\
237
- "(#{hash_or_string.class} given)."
238
- end
239
-
240
- json_value = hash_or_string.is_a?(String) ? hash_or_string : hash_or_string.to_json
241
-
242
- ReactOnRails::JsonOutput.escape(json_value)
243
- end
244
-
245
- private
246
-
247
- def build_react_component_result_for_server_rendered_string(
248
- server_rendered_html: required("server_rendered_html"),
249
- component_specification_tag: required("component_specification_tag"),
250
- console_script: required("console_script"),
251
- options: required("options")
252
- )
253
- content_tag_options = options.html_options
254
- content_tag_options[:id] = options.dom_id
255
-
256
- rendered_output = content_tag(:div,
257
- server_rendered_html.html_safe,
258
- content_tag_options)
259
-
260
- result_console_script = options.replay_console ? console_script : ""
261
- result = compose_react_component_html_with_spec_and_console(
262
- component_specification_tag, rendered_output, result_console_script
263
- )
264
-
265
- prepend_render_rails_context(result)
266
- end
267
-
268
- def build_react_component_result_for_server_rendered_hash(
269
- server_rendered_html: required("server_rendered_html"),
270
- component_specification_tag: required("component_specification_tag"),
271
- console_script: required("console_script"),
272
- options: required("options")
273
- )
274
- content_tag_options = options.html_options
275
- content_tag_options[:id] = options.dom_id
276
-
277
- unless server_rendered_html[COMPONENT_HTML_KEY]
278
- raise "server_rendered_html hash expected to contain \"#{COMPONENT_HTML_KEY}\" key."
279
- end
280
-
281
- rendered_output = content_tag(:div,
282
- server_rendered_html[COMPONENT_HTML_KEY].html_safe,
283
- content_tag_options)
284
-
285
- result_console_script = options.replay_console ? console_script : ""
286
- result = compose_react_component_html_with_spec_and_console(
287
- component_specification_tag, rendered_output, result_console_script
288
- )
289
-
290
- # Other HTML strings need to be marked as html_safe too:
291
- server_rendered_hash_except_component = server_rendered_html.except(COMPONENT_HTML_KEY)
292
- server_rendered_hash_except_component.each do |key, html_string|
293
- server_rendered_hash_except_component[key] = html_string.html_safe
294
- end
295
-
296
- result_with_rails_context = prepend_render_rails_context(result)
297
- { COMPONENT_HTML_KEY => result_with_rails_context }.merge(
298
- server_rendered_hash_except_component
299
- )
300
- end
301
-
302
- def compose_react_component_html_with_spec_and_console(component_specification_tag, rendered_output, console_script)
303
- # IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
304
- # rubocop:disable Layout/IndentHeredoc
305
- <<-HTML.html_safe
306
- #{component_specification_tag}
307
- #{rendered_output}
308
- #{console_script}
309
- HTML
310
- # rubocop:enable Layout/IndentHeredoc
311
- end
312
-
313
- # prepend the rails_context if not yet applied
314
- def prepend_render_rails_context(render_value)
315
- return render_value if @rendered_rails_context
316
-
317
- data = rails_context(server_side: false)
318
-
319
- @rendered_rails_context = true
320
-
321
- rails_context_content = content_tag(:script,
322
- json_safe_and_pretty(data).html_safe,
323
- type: "application/json",
324
- id: "js-react-on-rails-context")
325
-
326
- "#{rails_context_content}\n#{render_value}".html_safe
327
- end
328
-
329
- def internal_react_component(component_name, raw_options = {})
330
- # Create the JavaScript and HTML to allow either client or server rendering of the
331
- # react_component.
332
- #
333
- # Create the JavaScript setup of the global to initialize the client rendering
334
- # (re-hydrate the data). This enables react rendered on the client to see that the
335
- # server has already rendered the HTML.
336
-
337
- options = ReactOnRails::ReactComponent::Options.new(name: component_name, options: raw_options)
338
-
339
- # Setup the page_loaded_js, which is the same regardless of prerendering or not!
340
- # The reason is that React is smart about not doing extra work if the server rendering did its job.
341
- component_specification_tag = content_tag(:script,
342
- json_safe_and_pretty(options.props).html_safe,
343
- type: "application/json",
344
- class: "js-react-on-rails-component",
345
- "data-component-name" => options.name,
346
- "data-trace" => (options.trace ? true : nil),
347
- "data-dom-id" => options.dom_id)
348
-
349
- # Create the HTML rendering part
350
- result = server_rendered_react_component_html(options.props,
351
- options.name,
352
- options.dom_id,
353
- prerender: options.prerender,
354
- trace: options.trace,
355
- raise_on_prerender_error: options.raise_on_prerender_error)
356
-
357
- { "options" => options, "tag" => component_specification_tag, "result" => result }
358
- end
359
-
360
- def render_redux_store_data(redux_store_data)
361
- result = content_tag(:script,
362
- json_safe_and_pretty(redux_store_data[:props]).html_safe,
363
- type: "application/json",
364
- "data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe)
365
-
366
- prepend_render_rails_context(result)
367
- end
368
-
369
- def props_string(props)
370
- props.is_a?(String) ? props : props.to_json
371
- end
372
-
373
- # Returns Array [0]: html, [1]: script to console log
374
- # NOTE, these are NOT html_safe!
375
- def server_rendered_react_component_html(
376
- props, react_component_name, dom_id,
377
- prerender: required("prerender"),
378
- trace: required("trace"),
379
- raise_on_prerender_error: required("raise_on_prerender_error")
380
- )
381
- return { "html" => "", "consoleReplayScript" => "" } unless prerender
382
-
383
- # On server `location` option is added (`location = request.fullpath`)
384
- # React Router needs this to match the current route
385
-
386
- # Make sure that we use up-to-date bundle file used for server rendering, which is defined
387
- # by config file value for config.server_bundle_js_file
388
- ReactOnRails::ServerRenderingPool.reset_pool_if_server_bundle_was_modified
389
-
390
- # Since this code is not inserted on a web page, we don't need to escape props
391
- #
392
- # However, as JSON (returned from `props_string(props)`) isn't JavaScript,
393
- # but we want treat it as such, we need to compensate for the difference.
394
- #
395
- # \u2028 and \u2029 are valid characters in strings in JSON, but are treated
396
- # as newline separators in JavaScript. As no newlines are allowed in
397
- # strings in JavaScript, this causes an exception.
398
- #
399
- # We fix this by replacing these unicode characters with their escaped versions.
400
- # This should be safe, as the only place they can appear is in strings anyway.
401
- #
402
- # Read more here: http://timelessrepo.com/json-isnt-a-javascript-subset
403
-
404
- # rubocop:disable Layout/IndentHeredoc
405
- wrapper_js = <<-JS
406
- (function() {
407
- var railsContext = #{rails_context(server_side: true).to_json};
408
- #{initialize_redux_stores}
409
- var props = #{props_string(props).gsub("\u2028", '\u2028').gsub("\u2029", '\u2029')};
410
- return ReactOnRails.serverRenderReactComponent({
411
- name: '#{react_component_name}',
412
- domNodeId: '#{dom_id}',
413
- props: props,
414
- trace: #{trace},
415
- railsContext: railsContext
416
- });
417
- })()
418
- JS
419
- # rubocop:enable Layout/IndentHeredoc
420
-
421
- result = ReactOnRails::ServerRenderingPool.server_render_js_with_console_logging(wrapper_js)
422
-
423
- if result["hasErrors"] && raise_on_prerender_error
424
- # We caught this exception on our backtrace handler
425
- raise ReactOnRails::PrerenderError, component_name: react_component_name,
426
- # Sanitize as this might be browser logged
427
- props: sanitized_props_string(props),
428
- err: nil,
429
- js_code: wrapper_js,
430
- console_messages: result["consoleReplayScript"]
431
- # rubocop:enable Style/RaiseArgs
432
- end
433
- result
434
- rescue ExecJS::ProgramError => err
435
- # This error came from execJs
436
- raise ReactOnRails::PrerenderError, component_name: react_component_name,
437
- # Sanitize as this might be browser logged
438
- props: sanitized_props_string(props),
439
- err: err,
440
- js_code: wrapper_js
441
- # rubocop:enable Style/RaiseArgs
442
- end
443
-
444
- def initialize_redux_stores
445
- return "" unless @registered_stores.present? || @registered_stores_defer_render.present?
446
- declarations = "var reduxProps, store, storeGenerator;\n".dup # rubocop:disable Performance/UnfreezeString
447
-
448
- all_stores = (@registered_stores || []) + (@registered_stores_defer_render || [])
449
-
450
- # rubocop:disable Performance/UnfreezeString
451
- result = <<-JS.dup
452
- ReactOnRails.clearHydratedStores();
453
- JS
454
- # rubocop:enable Performance/UnfreezeString
455
-
456
- result << all_stores.each_with_object(declarations) do |redux_store_data, memo|
457
- store_name = redux_store_data[:store_name]
458
- props = props_string(redux_store_data[:props])
459
- memo << <<-JS.strip_heredoc
460
- reduxProps = #{props};
461
- storeGenerator = ReactOnRails.getStoreGenerator('#{store_name}');
462
- store = storeGenerator(reduxProps, railsContext);
463
- ReactOnRails.setStore('#{store_name}', store);
464
- JS
465
- end
466
- result
467
- end
468
-
469
- # This is the definitive list of the default values used for the rails_context, which is the
470
- # second parameter passed to both component and store generator functions.
471
- # rubocop:disable Metrics/AbcSize
472
- def rails_context(server_side: required("server_side"))
473
- @rails_context ||= begin
474
- result = {
475
- inMailer: in_mailer?,
476
- # Locale settings
477
- i18nLocale: I18n.locale,
478
- i18nDefaultLocale: I18n.default_locale
479
- }
480
- if defined?(request) && request.present?
481
- # Check for encoding of the request's original_url and try to force-encoding the
482
- # URLs as UTF-8. This situation can occur in browsers that do not encode the
483
- # entire URL as UTF-8 already, mostly on the Windows platform (IE11 and lower).
484
- original_url_normalized = request.original_url
485
- if original_url_normalized.encoding.to_s == "ASCII-8BIT"
486
- original_url_normalized = original_url_normalized.force_encoding("ISO-8859-1").encode("UTF-8")
487
- end
488
-
489
- # Using Addressable instead of standard URI to better deal with
490
- # non-ASCII characters (see https://github.com/shakacode/react_on_rails/pull/405)
491
- uri = Addressable::URI.parse(original_url_normalized)
492
- # uri = Addressable::URI.parse("http://foo.com:3000/posts?id=30&limit=5#time=1305298413")
493
-
494
- result.merge!(
495
- # URL settings
496
- href: uri.to_s,
497
- location: "#{uri.path}#{uri.query.present? ? "?#{uri.query}" : ''}",
498
- scheme: uri.scheme, # http
499
- host: uri.host, # foo.com
500
- port: uri.port,
501
- pathname: uri.path, # /posts
502
- search: uri.query, # id=30&limit=5
503
- httpAcceptLanguage: request.env["HTTP_ACCEPT_LANGUAGE"]
504
- )
505
- end
506
- if ReactOnRails.configuration.rendering_extension
507
- custom_context = ReactOnRails.configuration.rendering_extension.custom_context(self)
508
- result.merge!(custom_context) if custom_context
509
- end
510
- result
511
- end
512
-
513
- @rails_context.merge(serverSide: server_side)
514
- end
515
-
516
- def replay_console_option(val)
517
- val.nil? ? ReactOnRails.configuration.replay_console : val
518
- end
519
-
520
- def use_hot_reloading?
521
- ENV["REACT_ON_RAILS_ENV"] == "HOT"
522
- end
523
-
524
- def send_tag_method(tag_method_name, args)
525
- asset_type = use_hot_reloading? ? :hot : :static
526
- assets = Array(args[asset_type])
527
- options = args.delete_if { |key, _value| %i[hot static].include?(key) }
528
- send(tag_method_name, *assets, options) unless assets.empty?
529
- end
530
-
531
- def in_mailer?
532
- return false unless defined?(controller)
533
- return false unless defined?(ActionMailer::Base)
534
-
535
- controller.is_a?(ActionMailer::Base)
536
- end
4
+ include ReactOnRails::Helper
537
5
  end
@@ -0,0 +1,11 @@
1
+ lint:
2
+ image: dylangrafmyre/docker-lint:latest
3
+ working_dir: /app/
4
+ volumes:
5
+ - '.:/app/'
6
+ tests:
7
+ build: .
8
+ dockerfile: Dockerfile_tests
9
+ working_dir: /app/
10
+ volumes:
11
+ - '.:/app/'
@@ -0,0 +1,4 @@
1
+ # Caching and Performance
2
+
3
+ Caching and performance optimizations are now part of React on Rails Pro. For more information,
4
+ [contact Justin](mailto:justin@shakacode.com).
@@ -0,0 +1,18 @@
1
+ # Capistrano Deployment
2
+ Make sure ReactOnRails is working in development environment.
3
+
4
+ Add the following to development your Gemfile and bundle install.
5
+ ``` ruby
6
+ group :development do
7
+ gem 'capistrano-yarn'
8
+ end
9
+ ```
10
+ Then run Bundler to ensure Capistrano is downloaded and installed.
11
+ ``` sh
12
+ $ bundle install
13
+ ```
14
+ Add the following in your Capfile.
15
+ ``` ruby
16
+ require 'capistrano/yarn'
17
+ ```
18
+ If the deployment is taking too long or getting stuck at assets:precompile stage, it probably is because of memory. Webpack consumes a lot of memory so if possible, try increasing the RAM of your server.
@@ -3,41 +3,61 @@
3
3
  In order to deploy a React on Rails app to elastic beanstalk, you must install yarn on each instance.
4
4
  If yarn is not installed, asset compilation will fail on the elastic beanstalk instance.
5
5
 
6
- You can install yarn by adding a `yarn.config` file to your `.ebextensions` folder which contains these commands.
6
+ You can install `yarn` by adding a `0x_install_yarn.config` file to your `.ebextensions` folder which contains these commands.
7
7
 
8
8
  ```
9
- commands:
10
-
11
- 01_node_get:
12
- cwd: /tmp
13
- command: 'sudo curl --silent --location https://rpm.nodesource.com/setup_6.x | sudo bash -'
14
-
15
- 02_node_install:
16
- cwd: /tmp
17
- command: 'sudo yum -y install nodejs'
18
-
19
- 03_yarn_get:
20
- cwd: /tmp
21
- # don't run the command if yarn is already installed (file /usr/bin/yarn exists)
22
- test: '[ ! -f /usr/bin/yarn ] && echo "yarn not installed"'
23
- command: 'sudo wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo'
24
-
25
- 04_yarn_install:
26
- cwd: /tmp
27
- test: '[ ! -f /usr/bin/yarn ] && echo "yarn not installed"'
28
- command: 'sudo yum -y install yarn'
29
-
30
- 05_mkdir_webapp_dir:
31
- command: mkdir /home/webapp
32
- ignoreErrors: true
33
-
34
- 06_chown_webapp_dir:
35
- command: chown webapp:webapp /home/webapp
36
- ignoreErrors: true
37
-
38
- 07_chmod_webapp_dir:
39
- command: chmod 700 /home/webapp
40
- ignoreErrors: true
9
+ files:
10
+ "/opt/elasticbeanstalk/hooks/appdeploy/pre/09_yarn.sh" :
11
+ mode: "000755"
12
+ owner: root
13
+ group: root
14
+ content: |
15
+ #!/usr/bin/env bash
16
+ set -xe
17
+
18
+ EB_SCRIPT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k script_dir)
19
+ EB_APP_STAGING_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k app_staging_dir)
20
+ EB_APP_USER=$(/opt/elasticbeanstalk/bin/get-config container -k app_user)
21
+ EB_SUPPORT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k support_dir)
22
+
23
+ . $EB_SUPPORT_DIR/envvars
24
+ . $EB_SCRIPT_DIR/use-app-ruby.sh
25
+
26
+ # Install nodejs
27
+ echo "install nodejs"
28
+ curl --silent --location https://rpm.nodesource.com/setup_6.x | sudo bash -
29
+ yum -y install nodejs
30
+ echo "install yarn"
31
+ # install yarn
32
+ wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo;
33
+ yum -y install yarn;
34
+
35
+ # yarn install
36
+ cd $EB_APP_STAGING_DIR
37
+ yarn
38
+
39
+ # mkdir /home/webapp
40
+ mkdir -p /home/webapp
41
+ chown webapp:webapp /home/webapp
42
+ chmod 700 /home/webapp
43
+ ```
44
+
45
+ This script installs `yarn` and all `node.js` dependencies before the rails do `assets:precompile`. Also, it creates `/home/webapp` directory allowing the precompile task to create temp files.
46
+
47
+ Your app can be deployed to elastic beanstalk successfully. However, the react app javascript files are under `public/packs`. If you are using nginx, you need to let it know the location of `https://yourhost/packs`.
41
48
 
49
+ In your `proxy.conf` setting, please add the following code.
50
+
51
+ ```
52
+ ...
53
+ server{
54
+ ...
55
+ location /packs {
56
+ root /var/app/current/public;
57
+ }
58
+ ...
59
+ }
60
+ ...
42
61
  ```
43
62
 
63
+ You can find an example code here: https://github.com/imgarylai/react_on_rails_with_aws_ebs.