react_on_rails 0.1.3 → 0.1.4
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.
- checksums.yaml +4 -4
- data/README.md +41 -12
- data/app/helpers/react_on_rails_helper.rb +23 -10
- data/lib/react_on_rails/react_renderer.rb +40 -25
- data/lib/react_on_rails/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47bf68c00a2b017047097d27b50e92d6f417b071
|
4
|
+
data.tar.gz: 9eca8eecbee44ad2c857680735b44e61027f365c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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
|
-
|
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**:
|
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
|
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
|
-
|
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
|
-
|
93
|
-
|
96
|
+
options[:server_side] = true
|
97
|
+
render_js_internal(render_js_expression, options)
|
94
98
|
else
|
95
|
-
|
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)
|
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(\"
|
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
|
-
|
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
|
-
|
27
|
+
consoleReplay += '\\n<script>';
|
24
28
|
history.forEach(function (msg) {
|
25
|
-
|
29
|
+
consoleReplay += '\\nconsole.' + msg.level + '.apply(console, ' + JSON.stringify(msg.arguments) + ');';
|
26
30
|
});
|
27
|
-
|
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
|
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 = "
|
53
|
+
result_js_code = " htmlResult = #{js_code}"
|
47
54
|
|
48
55
|
js_code_wrapper = <<-JS
|
49
56
|
(function () {
|
50
|
-
var
|
57
|
+
var htmlResult = '';
|
58
|
+
var consoleReplay = '';
|
51
59
|
#{ReactOnRails::ReactRenderer.wrap_code_with_exception_handler(result_js_code, component_name)}
|
52
|
-
#{
|
53
|
-
return
|
60
|
+
#{console_replay_js_code}
|
61
|
+
return JSON.stringify([htmlResult, consoleReplay]);
|
54
62
|
})()
|
55
63
|
JS
|
56
64
|
|
57
|
-
|
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
|
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
|
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
|
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
|
-
|
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('
|
123
|
+
console.error('location: ' + e.fileName + ':' + e.lineNumber);
|
106
124
|
}
|
107
|
-
console.error('
|
108
|
-
console.error('
|
109
|
-
msg += '
|
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
|
-
|
120
|
-
|
121
|
-
def after_render
|
136
|
+
def console_replay_js_code
|
122
137
|
@replay_console ? CONSOLE_REPLAY : ""
|
123
138
|
end
|
124
139
|
|
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.
|
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-
|
11
|
+
date: 2015-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|