From aef815e9de3033cbf74b72694a48a0d4f224c0c9 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sat, 26 Sep 2015 21:56:09 -1000 Subject: [PATCH 1/2] Fixes #31: Options passed through to content tag --- README.md | 1 + app/helpers/react_on_rails_helper.rb | 23 ++++++++--- spec/dummy/Gemfile.lock | 2 +- spec/dummy/app/views/pages/_header.erb | 3 ++ ...ver_side_hello_world_with_options.html.erb | 38 +++++++++++++++++++ spec/dummy/config/routes.rb | 1 + 6 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 spec/dummy/app/views/pages/server_side_hello_world_with_options.html.erb diff --git a/README.md b/README.md index 1ee7b17a04..aa345bde8e 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ Params are: * **prerender**: set to false when debugging! * **trace**: set to true to print additional debugging information in the browser default is true for development, off otherwise * **replay_console**: Default is true. False will disable echoing server rendering logs, which can make troubleshooting server rendering difficult. + * Any other options are passed to the content tag, including the id. ## JavaScript diff --git a/app/helpers/react_on_rails_helper.rb b/app/helpers/react_on_rails_helper.rb index c5353eaaed..6508c48181 100644 --- a/app/helpers/react_on_rails_helper.rb +++ b/app/helpers/react_on_rails_helper.rb @@ -31,6 +31,7 @@ module ReactOnRailsHelper # default is true for development, off otherwise # replay_console: Default is true. False will disable echoing server rendering # logs, which can make troubleshooting server rendering difficult. + # Any other options are passed to the content tag, including the id. def react_component(component_name, props = {}, options = {}) # Create the JavaScript and HTML to allow either client or server rendering of the # react_component. @@ -41,16 +42,21 @@ def react_component(component_name, props = {}, options = {}) # We use this react_component_index in case we have the same component multiple times on the page. react_component_index = next_react_component_index react_component_name = component_name.camelize # Not sure if we should be doing this (JG) - dom_id = "#{component_name}-react-component-#{react_component_index}" + if options[:id].nil? + dom_id = "#{component_name}-react-component-#{react_component_index}" + else + dom_id = options[:id] + end # Setup the page_loaded_js, which is the same regardless of prerendering or not! # The reason is that React is smart about not doing extra work if the server rendering did its job. data_variable_name = "__#{component_name.camelize(:lower)}Data#{react_component_index}__" turbolinks_loaded = Object.const_defined?(:Turbolinks) install_render_events = turbolinks_loaded ? turbolinks_bootstrap(dom_id) : non_turbolinks_bootstrap + props_string = props.is_a?(String) ? props : props.to_json page_loaded_js = <<-JS (function() { - window.#{data_variable_name} = #{props.to_json}; + window.#{data_variable_name} = #{props_string}; #{define_render_if_dom_node_present(react_component_name, data_variable_name, dom_id, trace(options), generator_function(options))} #{install_render_events} @@ -61,12 +67,17 @@ def react_component(component_name, props = {}, options = {}) # Create the HTML rendering part server_rendered_html, console_script = - server_rendered_react_component_html(options, props, react_component_name, + server_rendered_react_component_html(options, props_string, react_component_name, data_variable_name, dom_id) + content_tag_options = options.except(:generator_function, :prerender, :trace, + :replay_console, :id, :react_component_name, + :server_side) + content_tag_options[:id] = dom_id + rendered_output = content_tag(:div, server_rendered_html, - id: dom_id) + content_tag_options) # IMPORTANT: Ensure that we mark string as html_safe to avoid escaping. <<-HTML.html_safe @@ -82,12 +93,12 @@ def next_react_component_index end # Returns Array [0]: html, [1]: script to console log - def server_rendered_react_component_html(options, props, react_component_name, data_variable, dom_id) + def server_rendered_react_component_html(options, props_string, react_component_name, data_variable, dom_id) if prerender(options) render_js_expression = <<-JS (function(React) { #{debug_js(react_component_name, data_variable, dom_id, trace(options))} - var reactElement = #{render_js_react_element(react_component_name, props.to_json, generator_function(options))} + var reactElement = #{render_js_react_element(react_component_name, props_string, generator_function(options))} return React.renderToString(reactElement); })(this.React); JS diff --git a/spec/dummy/Gemfile.lock b/spec/dummy/Gemfile.lock index 0943fadb23..7410600b71 100644 --- a/spec/dummy/Gemfile.lock +++ b/spec/dummy/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../.. specs: - react_on_rails (0.1.3) + react_on_rails (0.1.4) execjs (~> 2.5) rails (~> 4.2) diff --git a/spec/dummy/app/views/pages/_header.erb b/spec/dummy/app/views/pages/_header.erb index b37825861f..65526c22ee 100644 --- a/spec/dummy/app/views/pages/_header.erb +++ b/spec/dummy/app/views/pages/_header.erb @@ -5,6 +5,9 @@
  • <%= link_to "Hello World Component Server Rendered", server_side_hello_world_path %>
  • +
  • + <%= link_to "Hello World Component Server Rendered, with extra options", server_side_hello_world_with_options_path %> +
  • <%= link_to "Hello World Component Server Rendered ES5", server_side_hello_world_es5_path %>
  • diff --git a/spec/dummy/app/views/pages/server_side_hello_world_with_options.html.erb b/spec/dummy/app/views/pages/server_side_hello_world_with_options.html.erb new file mode 100644 index 0000000000..acfc9679d5 --- /dev/null +++ b/spec/dummy/app/views/pages/server_side_hello_world_with_options.html.erb @@ -0,0 +1,38 @@ +<%= render "header" %> + +<%= react_component("HelloWorld", + @app_props_server_render.to_json, + prerender: true, + trace: true, + generator_function: false, + id: "my-hello-world-id", + class: "my-hello-world-class", + data: { x: 1, y: 2} + ) %> +
    + +

    React Rails Server Rendering with options

    +

    + This example demonstrates passing extram options to the example + <%= link_to "Hello World Component Server Rendered", server_side_hello_world_path %> + The differences include: +

    +
      +
    • + Sending the props as already converted from JSON to a string. +
    • +
    • + Passing extra params that get passed to the tag shown in the HTML, including the option to set + the id of the component. +
    • +
    +
    +<%%= react_component("HelloWorld",
    +                    @app_props_server_render.to_json,
    +                    prerender: true,
    +                    trace: true,
    +                    generator_function: false,
    +                    id: "my-hello-world-id",
    +                    class: "my-hello-world-class",
    +                    data: { x: 1, y: 2} ) %>
    +
    diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb index 978d645c54..cbf4fc7711 100644 --- a/spec/dummy/config/routes.rb +++ b/spec/dummy/config/routes.rb @@ -10,5 +10,6 @@ get "server_side_log_throw" => "pages#server_side_log_throw" get "server_side_hello_world_es5" => "pages#server_side_hello_world_es5" get "server_side_redux_app" => "pages#server_side_redux_app" + get "server_side_hello_world_with_options" => "pages#server_side_hello_world_with_options" get "server_side_redux_app_cached" => "pages#server_side_redux_app_cached" end From 78306939ce4aaa0a8ce0ca15191c989563a7c940 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sat, 26 Sep 2015 22:29:00 -1000 Subject: [PATCH 2/2] Fixes #31: Replays messages on server Now you can have replay_console set to false and logging_server_set to true and server messages go only to the server. replay_console only affects the browser console display. --- README.md | 1 + app/helpers/react_on_rails_helper.rb | 10 ++++++++-- lib/react_on_rails/configuration.rb | 9 ++++++--- lib/react_on_rails/react_renderer.rb | 17 +++++++++++++++-- .../dummy/config/initializers/react_on_rails.rb | 3 ++- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index aa345bde8e..a3b7a99471 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,7 @@ ReactOnRails.configure do |config| # For server rendering. This can be set to false so that server side messages are discarded. config.replay_console = true # Default is true. Be cautious about turning this off. + config.logging_on_server = true # Default is true. Logs server rendering messags to Rails.logger.info end ``` diff --git a/app/helpers/react_on_rails_helper.rb b/app/helpers/react_on_rails_helper.rb index 6508c48181..238af844ae 100644 --- a/app/helpers/react_on_rails_helper.rb +++ b/app/helpers/react_on_rails_helper.rb @@ -30,7 +30,9 @@ module ReactOnRailsHelper # trace: set to true to print additional debugging information in the browser # default is true for development, off otherwise # replay_console: Default is true. False will disable echoing server rendering - # logs, which can make troubleshooting server rendering difficult. + # logs to browser. While this can make troubleshooting server rendering difficult, + # so long as you have the default configuration of logging_on_server set to + # true, you'll still see the errors on the server. # Any other options are passed to the content tag, including the id. def react_component(component_name, props = {}, options = {}) # Create the JavaScript and HTML to allow either client or server rendering of the @@ -83,7 +85,7 @@ def react_component(component_name, props = {}, options = {}) <<-HTML.html_safe #{data_from_server_script_tag} #{rendered_output} -#{console_script} +#{replay_console(options) ? console_script : ""} HTML end @@ -146,6 +148,10 @@ def prerender(options) options.fetch(:prerender) { ReactOnRails.configuration.prerender } end + def replay_console(options) + options.fetch(:replay_console) { ReactOnRails.configuration.replay_console } + end + def debug_js(react_component_name, data_variable, dom_id, trace) if trace "console.log(\"RENDERED #{react_component_name} with data_variable"\ diff --git a/lib/react_on_rails/configuration.rb b/lib/react_on_rails/configuration.rb index 625c057ace..901d6f20dc 100644 --- a/lib/react_on_rails/configuration.rb +++ b/lib/react_on_rails/configuration.rb @@ -8,16 +8,18 @@ def self.configuration server_bundle_js_file: "app/assets/javascripts/generated/server.js", prerender: false, replay_console: true, + logging_on_server: true, generator_function: false, - trace: Rails.env.development? + trace: Rails.env.development?, ) end class Configuration - attr_accessor :server_bundle_js_file, :prerender, :replay_console, :generator_function, :trace + attr_accessor :server_bundle_js_file, :prerender, :replay_console, :generator_function, :trace, + :logging_on_server def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil, - generator_function: nil, trace: nil) + generator_function: nil, trace: nil, logging_on_server: nil) if File.exist?(server_bundle_js_file) self.server_bundle_js_file = server_bundle_js_file else @@ -26,6 +28,7 @@ def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil, self.prerender = prerender self.replay_console = replay_console + self.logging_on_server = logging_on_server self.generator_function = generator_function self.trace = trace.nil? ? Rails.env.development? : trace end diff --git a/lib/react_on_rails/react_renderer.rb b/lib/react_on_rails/react_renderer.rb index 76a6d4e6f4..345f9cb76d 100644 --- a/lib/react_on_rails/react_renderer.rb +++ b/lib/react_on_rails/react_renderer.rb @@ -76,7 +76,20 @@ def render_js(js_code, options = {}) json_string = ExecJS.eval(js_code_wrapper) end # element 0 is the html, element 1 is the script tag for the server console output - JSON.parse(json_string) + result = JSON.parse(json_string) + if ReactOnRails.configuration.logging_on_server + console_script = result[1] + console_script_lines = console_script.split("\n") + console_script_lines = console_script_lines[2..-2] + re = /console\.log\.apply\(console, \["\[SERVER\] (?.*)"\]\);/ + console_script_lines.each do |line| + match = re.match(line) + if match + Rails.logger.info { "[react_on_rails] #{match[:msg]}"} + end + end + end + return result end def self.wrap_code_with_exception_handler(js_code, component_name) @@ -134,7 +147,7 @@ def self.render_error_messages end def console_replay_js_code - @replay_console ? CONSOLE_REPLAY : "" + (@replay_console || ReactOnRails.configuration.logging_on_server) ? CONSOLE_REPLAY : "" end def base_js_code(bundle_js_code) diff --git a/spec/dummy/config/initializers/react_on_rails.rb b/spec/dummy/config/initializers/react_on_rails.rb index 816cd059db..cdf12f54d4 100644 --- a/spec/dummy/config/initializers/react_on_rails.rb +++ b/spec/dummy/config/initializers/react_on_rails.rb @@ -12,5 +12,6 @@ config.trace = Rails.env.development? # default is true for development, off otherwise # For server rendering. This can be set to false so that server side messages are discarded. - config.replay_console = true # Default is true. Be cautious about turning this off. + config.replay_console = false # Default is true. Be cautious about turning this off. + config.logging_on_server = true # Default is true. Logs server rendering messags to Rails.logger.info end