react_on_rails 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 95c7eae480cfc52816e7f263bb1911e708a93687
4
- data.tar.gz: a53501d57413eb474a1ee4015af8999849267402
3
+ metadata.gz: 47bf68c00a2b017047097d27b50e92d6f417b071
4
+ data.tar.gz: 9eca8eecbee44ad2c857680735b44e61027f365c
5
5
  SHA512:
6
- metadata.gz: fe6a6b8a89721d0a65356538765129ad63049f70fa291a6785008fe20cc798390ddfc4f71bd1422efdcd7d7cfc0598e041cd6fa74cdbf1e0a5a831b1c07a58a4
7
- data.tar.gz: ca064e881bef0e9758f32f165801d91176ee168e1cf887914185aeae36d690ca95554e6eb4331bc6d54d5b2ccf6488405ead090f5fd06b77e5d56617b1de58b5
6
+ metadata.gz: 566649c85b68a57e26d9ac79bcd9f2b23802c288eb15c4032542fdbe96bf79b6fd9121ced75bb73fe28e1d20ba235e0b155e10d00ac5ae63a64d8db6aa2e4781
7
+ data.tar.gz: 4ca24d4f09242dd39d0c7842a3c9383c47f9515720bb78a5dd6e575ec0657d293ab2f343000db7d6a4df4306a159374e77d9e67a7afd67650bae3d7d14b7a3b4
data/README.md CHANGED
@@ -19,14 +19,30 @@ Supports:
19
19
  6. Server side rendering with fragment caching
20
20
  7. react-router for client side rendering (and maybe server side eventually)
21
21
 
22
- # Links
22
+ ## OPEN ISSUES
23
+ 1. We've got many open issues. However, none of these should stop you from using this gem if you're using React + Webpack with Rails, especially if you are client rendering.
24
+ 2. Almost all the open issues are nice to haves like more tests, or some things that would be nice to have for server rendering.
25
+ 3. If you want to work on any of the open issues, please comment on the issue. My team is mentoring anybody that's trying to help with the issues. We've got a private slack room for discussing React + Webpack with Rails.
26
+ 4. Longer term, we hope to put in many conveniences into this gem, in terms of Webpack + Rails integration.
27
+
28
+ ## Links
23
29
  1. https://github.com/justin808/react-webpack-rails-tutorial/ See https://github.com/shakacode/react-webpack-rails-tutorial/pull/84 for how we integrated it!
24
30
  2. http://www.railsonmaui.com/blog/2014/10/03/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/
25
31
  3. http://forum.railsonmaui.com
26
- 5. If this project is interesting to you, email me at justin@shakacode.com. We're looking for great
32
+ 4. Interested in consulting for implementing React with Rails, [email us! contact@shakacode.com](mailto: contact@shakacode.com).
33
+ 5. If this project is interesting to you, [email us! contact@shakacode.com](mailto: contact@shakacode.com). We're looking for great
27
34
  developers that want to work with Rails + React with a distributed, worldwide team, for our own
28
35
  products, client work, and open source.
29
36
 
37
+ ## How is different than the [react-rails gem](https://github.com/reactjs/react-rails)?
38
+ 1. `react_on_rails` depends on [webpack](http://webpack.github.io/). `react-rails` integrates closely with sprockets and
39
+ helps you integrate JSX and the react code into a Rails project.
40
+ 2. Likewise, using Webpack as show in the [react-webpack-rails-tutorial](https://github.com/justin808/react-webpack-rails-tutorial/)
41
+ does involve some extra setup. However, we feel that tight and simple integration with the node ecosystem is more than
42
+ worth any minor setup costs.
43
+ 3. `react-rails` depends on `jquery-ujs` for client side rendering. `react_on_rails` has it's own JS code that does not
44
+ depend on jquery.
45
+
30
46
  ## Application Installation
31
47
 
32
48
  Add these lines to your application's Gemfile, sustituting your preferable JavaScript engine.
@@ -48,12 +64,12 @@ And then execute:
48
64
  The main API is a helper:
49
65
 
50
66
  ```ruby
51
- react_component(component_name, props = {}, options = {})
67
+ <%= react_component(component_name, props = {}, options = {}) %>
52
68
  ```
53
69
 
54
70
  Params are:
55
71
 
56
- * **react_component_name**: can be a React component, created using a ES6 class, or React.createClass,
72
+ * **react_component_name**: [string] can be a React component, created using a ES6 class, or React.createClass,
57
73
  or a `generator function` that returns a React component
58
74
 
59
75
  using ES6
@@ -76,12 +92,12 @@ Params are:
76
92
  ```javascript
77
93
  global.MyReactComponentApp = MyReactComponentApp;
78
94
  ```
79
- See `spec/dummy/client/app/startup/serverGlobals.jsx` and
80
- `spec/dummy/client/app/startup/ClientReduxApp.jsx` for examples of this.
95
+ If you're curious as to what the gem generates for the server and client rendering, see [`spec/dummy/client/app/startup/serverGlobals.jsx`](https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/spec/sample_generated_js/server-generated.js)
96
+ and [`spec/dummy/client/app/startup/ClientReduxApp.jsx`](https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/spec/sample_generated_js/client-generated.js) for examples of this. Note, this is not the code that you are providing. You can see the client code by viewing the page source.
81
97
 
82
- * **props**: Ruby Hash which contains the properties to pass to the react object
98
+ * **props**: [hash] Properties to pass to the react object
83
99
 
84
- * **options:**
100
+ * **options:** [hash]
85
101
  * **generator_function**: <true/false> default is false, set to true if you want to use a generator function rather than a React Component.
86
102
  * **prerender**: <true/false> set to false when debugging!
87
103
  * **trace**: <true/false> set to true to print additional debugging information in the browser default is true for development, off otherwise
@@ -98,11 +114,27 @@ Params are:
98
114
  window.HelloWorld = HelloWorld;
99
115
  ```
100
116
  3. Follow the examples in `spec/dummy/client/app/startup/serverGlobals.jsx` to expose your react components
101
- for client side rendering.
117
+ for server side rendering.
102
118
  ```ruby
103
119
  import HelloWorld from '../components/HelloWorld';
104
120
  global.HelloWorld = HelloWorld;
105
121
  ```
122
+
123
+ ## Server Rendering Tips
124
+
125
+ - Your code can't reference `document`. Server side JS execution does not have access to `document`, so jQuery and some
126
+ other libs won't work in this environment. You can debug this by putting in `console.log`
127
+ statements in your code.
128
+ - You can conditionally avoid running code that references document by passing in a boolean prop to your top level react
129
+ component. Since the passed in props Hash from the view helper applies to client and server side code, the best way to
130
+ do this is to use a generator function.
131
+
132
+ You might do something like this in some file for your top level component:
133
+ ```javascript
134
+ global.App = () => <MyComponent serverSide={true} />;
135
+ ```
136
+
137
+ The point is that you have separate files for top level client or server side, and you pass some extra option indicating that rendering is happening server sie.
106
138
 
107
139
  ## Optional Configuration
108
140
 
@@ -174,9 +206,6 @@ Contributions and pull requests welcome!
174
206
  the `div` element where the inline JavaScript will render the component. You might also notice
175
207
  how the props you pass (a Ruby Hash) becomes inline JavaScript on the HTML page.
176
208
 
177
- TODO: Check if this is true still: If you're only doing client rendering, you still *MUST* create an empty version of this file. This
178
- will soon change so that this is not necessary.
179
-
180
209
  ## JavaScript Runtime Configuration
181
210
  See this [discussion on JavaScript performance](https://github.com/shakacode/react_on_rails/issues/21).
182
211
  The net result is that you want to add this line to your Gemfile to get therubyracer as your default
@@ -60,8 +60,9 @@ module ReactOnRailsHelper
60
60
  data_from_server_script_tag = javascript_tag(page_loaded_js)
61
61
 
62
62
  # Create the HTML rendering part
63
- server_rendered_html =
64
- server_rendered_react_component_html(options, props, react_component_name)
63
+ server_rendered_html, console_script =
64
+ server_rendered_react_component_html(options, props, react_component_name,
65
+ data_variable_name, dom_id)
65
66
 
66
67
  rendered_output = content_tag(:div,
67
68
  server_rendered_html,
@@ -71,6 +72,7 @@ module ReactOnRailsHelper
71
72
  <<-HTML.html_safe
72
73
  #{data_from_server_script_tag}
73
74
  #{rendered_output}
75
+ #{console_script}
74
76
  HTML
75
77
  end
76
78
 
@@ -79,22 +81,23 @@ module ReactOnRailsHelper
79
81
  @react_component_index += 1
80
82
  end
81
83
 
82
- def server_rendered_react_component_html(options, props, react_component_name)
84
+ # Returns Array [0]: html, [1]: script to console log
85
+ def server_rendered_react_component_html(options, props, react_component_name, data_variable, dom_id)
83
86
  if prerender(options)
84
87
  render_js_expression = <<-JS
85
88
  (function(React) {
89
+ #{debug_js(react_component_name, data_variable, dom_id, trace(options))}
86
90
  var reactElement = #{render_js_react_element(react_component_name, props.to_json, generator_function(options))}
87
91
  return React.renderToString(reactElement);
88
92
  })(this.React);
89
93
  JS
90
94
  # create the server generated html of the react component with props
91
95
  options[:react_component_name] = react_component_name
92
- server_rendered_react_component_html =
93
- render_js(render_js_expression, options)
96
+ options[:server_side] = true
97
+ render_js_internal(render_js_expression, options)
94
98
  else
95
- server_rendered_react_component_html = ""
99
+ ["",""]
96
100
  end
97
- server_rendered_react_component_html
98
101
  end
99
102
 
100
103
  # Takes javascript code and returns the output from it. This is called by react_component, which
@@ -102,13 +105,23 @@ module ReactOnRailsHelper
102
105
  # This method could be used by itself to render the output of any javascript that returns a
103
106
  # string of proper HTML.
104
107
  def render_js(js_expression, options = {})
108
+ result = render_js_internal(js_expression, options)
109
+ "#{result[0]}\n#{result[1]}".html_safe
110
+ end
111
+
112
+ private
113
+ # Takes javascript code and returns the output from it. This is called by react_component, which
114
+ # sets up the JS code for rendering a react component.
115
+ # This method could be used by itself to render the output of any javascript that returns a
116
+ # string of proper HTML.
117
+ # Returns Array [0]: html, [1]: script to console log
118
+ def render_js_internal(js_expression, options = {})
105
119
  # TODO: This should be changed so that we don't create a new context every time
106
120
  # Example of doing this here: https://github.com/reactjs/react-rails/tree/master/lib/react/rails
107
121
  ReactOnRails::ReactRenderer.new(options).render_js(js_expression,
108
- options).html_safe
122
+ options)
109
123
  end
110
124
 
111
- private
112
125
 
113
126
  def trace(options)
114
127
  options.fetch(:trace) { ReactOnRails.configuration.trace }
@@ -124,7 +137,7 @@ module ReactOnRailsHelper
124
137
 
125
138
  def debug_js(react_component_name, data_variable, dom_id, trace)
126
139
  if trace
127
- "console.log(\"CLIENT SIDE RENDERED #{react_component_name} with data_variable"\
140
+ "console.log(\"RENDERED #{react_component_name} with data_variable"\
128
141
  " #{data_variable} to dom node with id: #{dom_id}\");"
129
142
  else
130
143
  ""
@@ -8,7 +8,11 @@ module ReactOnRails
8
8
  var console = { history: [] };
9
9
  ['error', 'log', 'info', 'warn'].forEach(function (level) {
10
10
  console[level] = function () {
11
- console.history.push({level: level, arguments: Array.prototype.slice.call(arguments)});
11
+ var argArray = Array.prototype.slice.call(arguments);
12
+ if (argArray.length > 0) {
13
+ argArray[0] = '[SERVER] ' + argArray[0];
14
+ }
15
+ console.history.push({level: level, arguments: argArray});
12
16
  };
13
17
  });
14
18
  JS
@@ -20,11 +24,11 @@ var console = { history: [] };
20
24
  CONSOLE_REPLAY = <<-JS
21
25
  var history = console.history;
22
26
  if (history && history.length > 0) {
23
- result += '\\n<script>';
27
+ consoleReplay += '\\n<script>';
24
28
  history.forEach(function (msg) {
25
- result += '\\nconsole.' + msg.level + '.apply(console, ' + JSON.stringify(msg.arguments) + ');';
29
+ consoleReplay += '\\nconsole.' + msg.level + '.apply(console, ' + JSON.stringify(msg.arguments) + ');';
26
30
  });
27
- result += '\\n</script>';
31
+ consoleReplay += '\\n</script>';
28
32
  }
29
33
  JS
30
34
 
@@ -37,25 +41,28 @@ var console = { history: [] };
37
41
  end
38
42
 
39
43
  # js_code: JavaScript expression that returns a string.
40
- # Returns a string of HTML for direct insertion on the page by evaluating js_code.
44
+ # Returns an Array:
45
+ # [0]: string of HTML for direct insertion on the page by evaluating js_code
46
+ # [1]: console messages
41
47
  # Note, js_code does not have to be based on React.
42
48
  # Calling code will probably call 'html_safe' on return value before rendering to the view.
43
49
  def render_js(js_code, options = {})
44
50
  component_name = options.fetch(:react_component_name, "")
51
+ server_side = options.fetch(:server_side, false)
45
52
 
46
- result_js_code = " result = #{js_code}"
53
+ result_js_code = " htmlResult = #{js_code}"
47
54
 
48
55
  js_code_wrapper = <<-JS
49
56
  (function () {
50
- var result = '';
57
+ var htmlResult = '';
58
+ var consoleReplay = '';
51
59
  #{ReactOnRails::ReactRenderer.wrap_code_with_exception_handler(result_js_code, component_name)}
52
- #{after_render}
53
- return result;
60
+ #{console_replay_js_code}
61
+ return JSON.stringify([htmlResult, consoleReplay]);
54
62
  })()
55
63
  JS
56
64
 
57
- trace_rails_on_maui = ENV["TRACE_REACT_ON_RAILS"].present? # Set to anything to print generated code.
58
- if trace_rails_on_maui
65
+ if ENV["TRACE_REACT_ON_RAILS"].present? # Set to anything to print generated code.
59
66
  puts "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
60
67
  puts "react_renderer.rb: 92"
61
68
  puts "wrote file tmp/server-generated.js"
@@ -64,10 +71,12 @@ var console = { history: [] };
64
71
  end
65
72
 
66
73
  if js_context
67
- js_context.eval(js_code_wrapper)
74
+ json_string = js_context.eval(js_code_wrapper)
68
75
  else
69
- ExecJS.eval(js_code_wrapper)
76
+ json_string = ExecJS.eval(js_code_wrapper)
70
77
  end
78
+ # element 0 is the html, element 1 is the script tag for the server console output
79
+ JSON.parse(json_string)
71
80
  end
72
81
 
73
82
  def self.wrap_code_with_exception_handler(js_code, component_name)
@@ -77,13 +86,13 @@ var console = { history: [] };
77
86
  }
78
87
  catch(e) {
79
88
  var lineOne =
80
- 'ERROR: You specifed the option generator_function (could be in your defaults) to be\\n';
89
+ 'ERROR: You specified the option generator_function (could be in your defaults) to be\\n';
81
90
  var lastLine =
82
91
  'A generator function takes a single arg of props and returns a ReactElement.';
83
92
 
84
93
  var msg = '';
85
94
  var shouldBeGeneratorError = lineOne +
86
- 'false, but the react component \\'#{component_name}\\' seems to be a generator function.\\n' +
95
+ 'false, but the React component \\'#{component_name}\\' seems to be a generator function.\\n' +
87
96
  lastLine;
88
97
  var reMatchShouldBeGeneratorError = /Can't add property context, object is not extensible/;
89
98
  if (reMatchShouldBeGeneratorError.test(e.message)) {
@@ -92,7 +101,7 @@ var console = { history: [] };
92
101
  }
93
102
 
94
103
  var shouldBeGeneratorError = lineOne +
95
- 'true, but the react component \\'#{component_name}\\' is not a generator function.\\n' +
104
+ 'true, but the React component \\'#{component_name}\\' is not a generator function.\\n' +
96
105
  lastLine;
97
106
  var reMatchShouldNotBeGeneratorError = /Cannot call a class as a function/;
98
107
  if (reMatchShouldNotBeGeneratorError.test(e.message)) {
@@ -100,25 +109,31 @@ var console = { history: [] };
100
109
  console.error(shouldBeGeneratorError);
101
110
  }
102
111
 
103
- console.error('SERVER SIDE: Exception in server side rendering!');
112
+ #{render_error_messages}
113
+ }
114
+ JS
115
+ end
116
+
117
+ private
118
+
119
+ def self.render_error_messages
120
+ <<-JS
121
+ console.error('Exception in rendering!');
104
122
  if (e.fileName) {
105
- console.error('SERVER SIDE: location: ' + e.fileName + ':' + e.lineNumber);
123
+ console.error('location: ' + e.fileName + ':' + e.lineNumber);
106
124
  }
107
- console.error('SERVER SIDE: message: ' + e.message);
108
- console.error('SERVER SIDE: stack: ' + e.stack);
109
- msg += 'SERVER SIDE Exception in rendering!\\n' +
125
+ console.error('message: ' + e.message);
126
+ console.error('stack: ' + e.stack);
127
+ msg += 'Exception in rendering!\\n' +
110
128
  (e.fileName ? '\\nlocation: ' + e.fileName + ':' + e.lineNumber : '') +
111
129
  '\\nMessage: ' + e.message + '\\n\\n' + e.stack;
112
130
 
113
131
  var reactElement = React.createElement('pre', null, msg);
114
132
  result = React.renderToString(reactElement);
115
- }
116
133
  JS
117
134
  end
118
135
 
119
- private
120
-
121
- def after_render
136
+ def console_replay_js_code
122
137
  @replay_console ? CONSOLE_REPLAY : ""
123
138
  end
124
139
 
@@ -1,3 +1,3 @@
1
1
  module ReactOnRails
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: react_on_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Gordon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-09-20 00:00:00.000000000 Z
11
+ date: 2015-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails