react_on_rails 0.1.1 → 0.1.2
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/.coveralls.yml +1 -0
- data/.eslintignore +1 -0
- data/.eslintrc +16 -0
- data/.jscsrc +5 -0
- data/.rubocop.yml +26 -0
- data/.scss-lint.yml +205 -0
- data/.travis.yml +10 -2
- data/Dockerfile +14 -0
- data/README.md +187 -50
- data/Rakefile +89 -4
- data/app/helpers/react_on_rails_helper.rb +148 -82
- data/docker-compose.yml +5 -0
- data/lib/react_on_rails/configuration.rb +12 -5
- data/lib/react_on_rails/react_renderer.rb +115 -22
- data/lib/react_on_rails/version.rb +1 -1
- data/react_on_rails.gemspec +1 -0
- data/ruby-lint.yml +22 -0
- metadata +25 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6ff4186feb1c1939c00dd049ef22d868aea660f6
|
|
4
|
+
data.tar.gz: 0b5808c38aeb2a3092ec374e6fa8ff2871849f57
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c3262b0bb0634626438299e5330e4bd4268a743ddb83f795adfbb2a8e29ed3dc9daf4c987dfca29113c68d66d1daf9e2d5b5277bf59b02cb391dfa28ab6f59e
|
|
7
|
+
data.tar.gz: 0885111f10b06d283bbc39058492a1f5d6e9ff958cc61ba1a4b67ece33a7c78af6006232a1ef380be349d6d7ea8fa2021a022e749574ad9f649fea0107902536
|
data/.coveralls.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
service_name: travis-ci
|
data/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
node_modules
|
data/.eslintrc
ADDED
data/.jscsrc
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# This is the configuration used to check the rubocop source code.
|
|
2
|
+
# Check out: https://github.com/bbatsov/rubocop
|
|
3
|
+
|
|
4
|
+
AllCops:
|
|
5
|
+
Include:
|
|
6
|
+
- '**/Rakefile'
|
|
7
|
+
- '**/config.ru'
|
|
8
|
+
Exclude:
|
|
9
|
+
- 'vendor/**/*'
|
|
10
|
+
- 'spec/fixtures/**/*'
|
|
11
|
+
- 'node_modules/**/*'
|
|
12
|
+
- 'db/**/*'
|
|
13
|
+
- 'db/schema.rb'
|
|
14
|
+
- 'db/seeds.rb'
|
|
15
|
+
- 'client/node_modules/**/*'
|
|
16
|
+
- 'bin/**/*'
|
|
17
|
+
- !ruby/regexp /old_and_unused\.rb$/
|
|
18
|
+
|
|
19
|
+
Metrics/LineLength:
|
|
20
|
+
Max: 120
|
|
21
|
+
|
|
22
|
+
Style/StringLiterals:
|
|
23
|
+
EnforcedStyle: double_quotes
|
|
24
|
+
|
|
25
|
+
Style/Documentation:
|
|
26
|
+
Enabled: false
|
data/.scss-lint.yml
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# See http://sass-guidelin.es/#zeros
|
|
2
|
+
|
|
3
|
+
scss_files:
|
|
4
|
+
- 'app/assets/stylesheets/**/*.scss'
|
|
5
|
+
- 'client/assets/stylesheets/**/*.scss'
|
|
6
|
+
|
|
7
|
+
linters:
|
|
8
|
+
# BangFormat:
|
|
9
|
+
# enabled: true
|
|
10
|
+
# space_before_bang: true
|
|
11
|
+
# space_after_bang: false
|
|
12
|
+
#
|
|
13
|
+
# BorderZero:
|
|
14
|
+
# enabled: true
|
|
15
|
+
# convention: zero # or `none`
|
|
16
|
+
#
|
|
17
|
+
ColorKeyword:
|
|
18
|
+
enabled: false
|
|
19
|
+
ColorVariable:
|
|
20
|
+
enabled: false
|
|
21
|
+
#
|
|
22
|
+
# Comment:
|
|
23
|
+
# enabled: true
|
|
24
|
+
#
|
|
25
|
+
# DebugStatement:
|
|
26
|
+
# enabled: true
|
|
27
|
+
#
|
|
28
|
+
# DeclarationOrder:
|
|
29
|
+
# enabled: true
|
|
30
|
+
#
|
|
31
|
+
# DuplicateProperty:
|
|
32
|
+
# enabled: true
|
|
33
|
+
#
|
|
34
|
+
# ElsePlacement:
|
|
35
|
+
# enabled: true
|
|
36
|
+
# style: same_line # or 'new_line'
|
|
37
|
+
#
|
|
38
|
+
# EmptyLineBetweenBlocks:
|
|
39
|
+
# enabled: true
|
|
40
|
+
# ignore_single_line_blocks: true
|
|
41
|
+
#
|
|
42
|
+
# EmptyRule:
|
|
43
|
+
# enabled: true
|
|
44
|
+
#
|
|
45
|
+
# FinalNewline:
|
|
46
|
+
# enabled: true
|
|
47
|
+
# present: true
|
|
48
|
+
#
|
|
49
|
+
HexLength:
|
|
50
|
+
enabled: true
|
|
51
|
+
style: long
|
|
52
|
+
|
|
53
|
+
HexNotation:
|
|
54
|
+
enabled: true
|
|
55
|
+
style: uppercase
|
|
56
|
+
#
|
|
57
|
+
# HexValidation:
|
|
58
|
+
# enabled: true
|
|
59
|
+
#
|
|
60
|
+
IdSelector:
|
|
61
|
+
enabled: true
|
|
62
|
+
#
|
|
63
|
+
# ImportantRule:
|
|
64
|
+
# enabled: true
|
|
65
|
+
#
|
|
66
|
+
# ImportPath:
|
|
67
|
+
# enabled: true
|
|
68
|
+
# leading_underscore: false
|
|
69
|
+
# filename_extension: false
|
|
70
|
+
#
|
|
71
|
+
# Indentation:
|
|
72
|
+
# enabled: true
|
|
73
|
+
# allow_non_nested_indentation: false
|
|
74
|
+
# character: space # or 'tab'
|
|
75
|
+
# width: 2
|
|
76
|
+
#
|
|
77
|
+
LeadingZero:
|
|
78
|
+
enabled: true
|
|
79
|
+
style: include_zero
|
|
80
|
+
#
|
|
81
|
+
# MergeableSelector:
|
|
82
|
+
# enabled: true
|
|
83
|
+
# force_nesting: true
|
|
84
|
+
#
|
|
85
|
+
# NameFormat:
|
|
86
|
+
# enabled: true
|
|
87
|
+
# allow_leading_underscore: true
|
|
88
|
+
# convention: hyphenated_lowercase # or 'camel_case', or 'snake_case', or a regex pattern
|
|
89
|
+
#
|
|
90
|
+
# NestingDepth:
|
|
91
|
+
# enabled: true
|
|
92
|
+
# max_depth: 3
|
|
93
|
+
#
|
|
94
|
+
# PlaceholderInExtend:
|
|
95
|
+
# enabled: true
|
|
96
|
+
#
|
|
97
|
+
# PropertyCount:
|
|
98
|
+
# enabled: false
|
|
99
|
+
# include_nested: false
|
|
100
|
+
# max_properties: 10
|
|
101
|
+
#
|
|
102
|
+
# PropertyUnits:
|
|
103
|
+
# enabled: true
|
|
104
|
+
# global: [
|
|
105
|
+
# 'ch', 'em', 'ex', 'rem', # Font-relative lengths
|
|
106
|
+
# 'cm', 'in', 'mm', 'pc', 'pt', 'px', 'q', # Absolute lengths
|
|
107
|
+
# 'vh', 'vw', 'vmin', 'vmax', # Viewport-percentage lengths
|
|
108
|
+
# 'deg', 'grad', 'rad', 'turn', # Angle
|
|
109
|
+
# 'ms', 's', # Duration
|
|
110
|
+
# 'Hz', 'kHz', # Frequency
|
|
111
|
+
# 'dpi', 'dpcm', 'dppx', # Resolution
|
|
112
|
+
# '%', # Other
|
|
113
|
+
# ]
|
|
114
|
+
# properties: {}
|
|
115
|
+
#
|
|
116
|
+
# PropertySortOrder:
|
|
117
|
+
# enabled: true
|
|
118
|
+
# ignore_unspecified: false
|
|
119
|
+
# separate_groups: false
|
|
120
|
+
#
|
|
121
|
+
# PropertySpelling:
|
|
122
|
+
# enabled: true
|
|
123
|
+
# extra_properties: []
|
|
124
|
+
#
|
|
125
|
+
# QualifyingElement:
|
|
126
|
+
# enabled: true
|
|
127
|
+
# allow_element_with_attribute: false
|
|
128
|
+
# allow_element_with_class: false
|
|
129
|
+
# allow_element_with_id: false
|
|
130
|
+
#
|
|
131
|
+
# SelectorDepth:
|
|
132
|
+
# enabled: true
|
|
133
|
+
# max_depth: 3
|
|
134
|
+
#
|
|
135
|
+
# SelectorFormat:
|
|
136
|
+
# enabled: true
|
|
137
|
+
# convention: hyphenated_lowercase # or 'strict_BEM', or 'hyphenated_BEM', or 'snake_case', or 'camel_case', or a regex pattern
|
|
138
|
+
#
|
|
139
|
+
# Shorthand:
|
|
140
|
+
# enabled: true
|
|
141
|
+
# allowed_shorthands: [1, 2, 3]
|
|
142
|
+
#
|
|
143
|
+
# SingleLinePerProperty:
|
|
144
|
+
# enabled: true
|
|
145
|
+
# allow_single_line_rule_sets: true
|
|
146
|
+
#
|
|
147
|
+
# SingleLinePerSelector:
|
|
148
|
+
# enabled: true
|
|
149
|
+
#
|
|
150
|
+
# SpaceAfterComma:
|
|
151
|
+
# enabled: true
|
|
152
|
+
#
|
|
153
|
+
# SpaceAfterPropertyColon:
|
|
154
|
+
# enabled: true
|
|
155
|
+
# style: one_space # or 'no_space', or 'at_least_one_space', or 'aligned'
|
|
156
|
+
#
|
|
157
|
+
# SpaceAfterPropertyName:
|
|
158
|
+
# enabled: true
|
|
159
|
+
#
|
|
160
|
+
# SpaceBeforeBrace:
|
|
161
|
+
# enabled: true
|
|
162
|
+
# style: space # or 'new_line'
|
|
163
|
+
# allow_single_line_padding: false
|
|
164
|
+
#
|
|
165
|
+
# SpaceBetweenParens:
|
|
166
|
+
# enabled: true
|
|
167
|
+
# spaces: 0
|
|
168
|
+
#
|
|
169
|
+
# StringQuotes:
|
|
170
|
+
# enabled: true
|
|
171
|
+
# style: single_quotes # or double_quotes
|
|
172
|
+
#
|
|
173
|
+
# TrailingSemicolon:
|
|
174
|
+
# enabled: true
|
|
175
|
+
#
|
|
176
|
+
# TrailingZero:
|
|
177
|
+
# enabled: false
|
|
178
|
+
#
|
|
179
|
+
# UnnecessaryMantissa:
|
|
180
|
+
# enabled: true
|
|
181
|
+
#
|
|
182
|
+
# UnnecessaryParentReference:
|
|
183
|
+
# enabled: true
|
|
184
|
+
#
|
|
185
|
+
# UrlFormat:
|
|
186
|
+
# enabled: true
|
|
187
|
+
#
|
|
188
|
+
# UrlQuotes:
|
|
189
|
+
# enabled: true
|
|
190
|
+
#
|
|
191
|
+
# VariableForProperty:
|
|
192
|
+
# enabled: false
|
|
193
|
+
# properties: []
|
|
194
|
+
#
|
|
195
|
+
# VendorPrefix:
|
|
196
|
+
# enabled: true
|
|
197
|
+
# identifier_list: base
|
|
198
|
+
# additional_identifiers: []
|
|
199
|
+
# excluded_identifiers: []
|
|
200
|
+
#
|
|
201
|
+
# ZeroUnit:
|
|
202
|
+
# enabled: true
|
|
203
|
+
#
|
|
204
|
+
# Compass::*:
|
|
205
|
+
# enabled: false
|
data/.travis.yml
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
language: ruby
|
|
2
2
|
rvm:
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
- 2.2.2
|
|
4
|
+
gemfile:
|
|
5
|
+
- spec/dummy/Gemfile
|
|
6
|
+
install: bundle install
|
|
7
|
+
script:
|
|
8
|
+
- rake run_rspec:gem
|
|
9
|
+
- rake run_rspec:dummy
|
|
10
|
+
notifications:
|
|
11
|
+
slack:
|
|
12
|
+
secure: LfcUk4AJ4vAxWwRIyw4tFh8QNbYefMwfG/oLfsN3CdRMWMOtCOHR1GGsRhAOlfVVJ/FvHqVqWj5gK7z7CaO5Uvl7rD3/zJ8QzExKx/iH9yWj55iIPuKLzwFNnBwRpFW/cqyU2lFPPRxGD50BUn3c+qybkuSqtKZ6qtTowwqlxLa5iyM3N95aZp7MEIKCP7cPcnHfLbJyP8wBpotp/rtw62eXM2HIRJJwgjcp+n+My7VFR9DnBXNFf6R91aZHM4U4cHHDbu15HFtH8honVrzK1JQdyqMNHga+j04dFuaS7z9Q369/hsELMOBp/227+Pz7ZRfWZFK4UASguOvyeX7RmGTRpTuWLm1XJeUzfsPZVROecaSVQBve+U7F12yKqilt97QlvRXn2EGyBILqvxtFNNR4S9kgAf72/6EFgiM1TKq7i9zy6lVOnagU2+7amq7UeopX1uoFsUfNKMR7YbgV1WjF0IK95UP0b0/7ZOJlPYgi5zzkQi129qAFWSMmxGk+ZpsttHh/tjJtvAh0A3mHq/zb5w4ub/MbSyZqeDUNgGj72QArOWUFSAStQT1ybsVLeDoKPgOvVq7OV1D64rpcHjBXcqOCit8tDZ+TqkFhcYJo2cITSaqE4zJXn+4F5s7So5O8CyfKYQq+kFJCooYGmfgTUckJpGl7eIvKmL4TN9Q=
|
data/Dockerfile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM ruby:2.2.3
|
|
2
|
+
MAINTAINER Dylan Grafmyre <dylan@shakacode.com>
|
|
3
|
+
|
|
4
|
+
RUN mkdir -p /app
|
|
5
|
+
WORKDIR /app/
|
|
6
|
+
# Setup container for linting with ruby and npm
|
|
7
|
+
RUN gem install rubocop ruby-lint scss_lint
|
|
8
|
+
RUN curl --silent --location https://deb.nodesource.com/setup_0.12 | bash -
|
|
9
|
+
RUN apt-get install -y nodejs
|
|
10
|
+
RUN npm install -g eslint \
|
|
11
|
+
eslint-config-airbnb \
|
|
12
|
+
eslint-plugin-react \
|
|
13
|
+
babel-eslint \
|
|
14
|
+
jscs
|
data/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
[](https://travis-ci.org/shakacode/react_on_rails)
|
|
2
|
+
[](https://coveralls.io/github/shakacode/react_on_rails?branch=master)
|
|
3
|
+
[](https://gemnasium.com/shakacode/react_on_rails)
|
|
1
4
|
# React On Rails
|
|
2
5
|
|
|
3
|
-
Published: https://rubygems.org/gems/react_on_rails
|
|
6
|
+
Gem Published: https://rubygems.org/gems/react_on_rails
|
|
4
7
|
|
|
5
8
|
See [Action Plan for v1.0](https://github.com/shakacode/react_on_rails/issues/1)
|
|
6
9
|
|
|
@@ -16,108 +19,242 @@ Supports:
|
|
|
16
19
|
6. Server side rendering with fragment caching
|
|
17
20
|
7. react-router for client side rendering (and maybe server side eventually)
|
|
18
21
|
|
|
19
|
-
#
|
|
20
|
-
|
|
22
|
+
# Links
|
|
23
|
+
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
|
+
2. http://www.railsonmaui.com/blog/2014/10/03/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/
|
|
25
|
+
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
|
|
27
|
+
developers that want to work with Rails + React with a distributed, worldwide team, for our own
|
|
28
|
+
products, client work, and open source.
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
2. [Samnang Chhun](https://github.com/samnang)
|
|
24
|
-
3. [Alex Fedoseev](https://github.com/alexfedoseev)
|
|
30
|
+
## Application Installation
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
Add these lines to your application's Gemfile, sustituting your preferable JavaScript engine.
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
```ruby
|
|
35
|
+
gem "react_on_rails"
|
|
36
|
+
gem "therubyracer"
|
|
37
|
+
```
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
2. http://www.railsonmaui.com/blog/2014/10/03/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/
|
|
33
|
-
3. http://forum.railsonmaui.com
|
|
34
|
-
4. Lots of work to do in terms of docs, tests
|
|
35
|
-
5. If this project is interesting to you, email me at justin@shakacode.com. We're looking for great
|
|
36
|
-
developers that want to work with Rails + React with a distributed, worldwide team.
|
|
39
|
+
And then execute:
|
|
37
40
|
|
|
41
|
+
$ bundle
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
*See section below titled "Try it out"*
|
|
46
|
+
|
|
47
|
+
### Helper Method
|
|
48
|
+
The main API is a helper:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
react_component(component_name, props = {}, options = {})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Params are:
|
|
55
|
+
|
|
56
|
+
* **react_component_name**: can be a React component, created using a ES6 class, or React.createClass,
|
|
57
|
+
or a `generator function` that returns a React component
|
|
58
|
+
|
|
59
|
+
using ES6
|
|
60
|
+
```javascript
|
|
61
|
+
let MyReactComponentApp = (props) => <MyReactComponent {...props}/>;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
or using ES5
|
|
65
|
+
```javascript
|
|
66
|
+
var MyReactComponentApp = function(props) { return <YourReactComponent {...props}/>; }
|
|
67
|
+
```
|
|
68
|
+
Exposing the react_component_name is necessary to both a plain ReactComponent as well as
|
|
69
|
+
a generator:
|
|
70
|
+
For client rendering, expose the react_component_name on window:
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
window.MyReactComponentApp = MyReactComponentApp;
|
|
74
|
+
```
|
|
75
|
+
For server rendering, export the react_component_name on global:
|
|
76
|
+
```javascript
|
|
77
|
+
global.MyReactComponentApp = MyReactComponentApp;
|
|
78
|
+
```
|
|
79
|
+
See `spec/dummy/client/app/startup/serverGlobals.jsx` and
|
|
80
|
+
`spec/dummy/client/app/startup/ClientApp.jsx` for examples of this.
|
|
81
|
+
|
|
82
|
+
* **props**: Ruby Hash which contains the properties to pass to the react object
|
|
83
|
+
|
|
84
|
+
* **options:**
|
|
85
|
+
* **generator_function**: <true/false> default is false, set to true if you want to use a generator function rather than a React Component.
|
|
86
|
+
* **prerender**: <true/false> set to false when debugging!
|
|
87
|
+
* **trace**: <true/false> set to true to print additional debugging information in the browser default is true for development, off otherwise
|
|
88
|
+
* **replay_console**: <true/false> Default is true. False will disable echoing server rendering logs, which can make troubleshooting server rendering difficult.
|
|
89
|
+
|
|
90
|
+
## JavaScript
|
|
91
|
+
|
|
92
|
+
1. Configure your webpack configuration to create the file used for server rendering if you plan to
|
|
93
|
+
do server rendering.
|
|
94
|
+
2. Follow the examples in `spec/dummy/client/app/startup/clientGlobals.jsx` to expose your react components
|
|
95
|
+
for client side rendering.
|
|
96
|
+
```ruby
|
|
97
|
+
import HelloWorld from '../components/HelloWorld';
|
|
98
|
+
window.HelloWorld = HelloWorld;
|
|
99
|
+
```
|
|
100
|
+
3. Follow the examples in `spec/dummy/client/app/startup/serverGlobals.jsx` to expose your react components
|
|
101
|
+
for client side rendering.
|
|
102
|
+
```ruby
|
|
103
|
+
import HelloWorld from '../components/HelloWorld';
|
|
104
|
+
global.HelloWorld = HelloWorld;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Optional Configuration
|
|
108
|
+
|
|
109
|
+
Create a file `config/react_on_rails.rb` to override any defaults. If you don't specify this file,
|
|
110
|
+
the default options are below.
|
|
111
|
+
|
|
112
|
+
The `server_bundle_js_file` must correspond to the bundle you want to use for server rendering.
|
|
38
113
|
|
|
39
|
-
|
|
114
|
+
```ruby
|
|
115
|
+
# Shown below are the defaults for configuration
|
|
116
|
+
ReactOnRails.configure do |config|
|
|
117
|
+
# Client bundles are configured in application.js
|
|
118
|
+
# Server bundle is a single file for all server rendering of components.
|
|
119
|
+
config.server_bundle_js_file = "app/assets/javascripts/generated/server.js" # This is the default
|
|
120
|
+
|
|
121
|
+
# Below options can be overriden by passing to the helper method.
|
|
122
|
+
config.prerender = false # default is false
|
|
123
|
+
config.generator_function = false # default is false, meaning that you expose ReactComponents directly
|
|
124
|
+
config.trace = Rails.env.development? # default is true for development, off otherwise
|
|
125
|
+
|
|
126
|
+
# For server rendering. This can be set to false so that server side messages are discarded.
|
|
127
|
+
config.replay_console = true # Default is true. Be cautious about turning this off.
|
|
128
|
+
end
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
# Try it out in the simple sample app
|
|
40
132
|
Contributions and pull requests welcome!
|
|
41
133
|
|
|
42
|
-
1. Setup and run the test app
|
|
134
|
+
1. Setup and run the test app in `spec/dummy`. Note, there's no database.
|
|
43
135
|
```bash
|
|
44
136
|
cd spec/dummy
|
|
45
137
|
bundle
|
|
46
138
|
npm i
|
|
47
|
-
foreman start
|
|
48
|
-
```
|
|
139
|
+
foreman start
|
|
140
|
+
```
|
|
49
141
|
2. Caching is turned for development mode. Open the console and run `Rails.cache.clear` to clear
|
|
50
142
|
the cache. Note, even if you stop the server, you'll still have the cache entries around.
|
|
51
143
|
3. Visit http://localhost:3000
|
|
52
144
|
4. Notice that the first time you hit the page, you'll see a message that server is rendering.
|
|
53
145
|
See `spec/dummy/app/views/pages/index.html.erb:17` for the generation of that message.
|
|
146
|
+
5. Look at the layouts in `spec/dummy/app/views/pages` for samples of usage.
|
|
54
147
|
5. Open up the browser console and see some tracing.
|
|
55
148
|
6. Open up the source for the page and see the server rendered code.
|
|
56
|
-
7. If you want to turn off server caching, run the server like:
|
|
149
|
+
7. If you want to turn off server caching, run the server like:
|
|
57
150
|
`export RAILS_USE_CACHE=N && foreman start`
|
|
58
151
|
8. If you click back and forth between the about and react page links, you can see the rails console
|
|
59
152
|
log as well as the browser console to see what's going on with regards to server rendering and
|
|
60
153
|
caching.
|
|
61
154
|
|
|
62
155
|
# Key Tips
|
|
63
|
-
1. See sample app in `spec/dummy` for how to set this up.
|
|
156
|
+
1. See sample app in `spec/dummy` for how to set this up. See note below on ensuring you
|
|
157
|
+
**DO NOT RUN `rails s` and instead run `foreman start`.
|
|
158
|
+
2. Test out the different options and study the JSX samples in `spec/dummy/client/app/startup`.
|
|
159
|
+
3. Experiment with changing the settings on the `render_component` helper calls in the ERB files.
|
|
64
160
|
2. The file used for server rendering is hard coded as `generated/server.js`
|
|
65
161
|
(assets/javascripts/generated/server.js).
|
|
66
|
-
3.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
162
|
+
3. The default for rendering right now is `prerender: false`. **NOTE:** Server side rendering does
|
|
163
|
+
not work for some components, namely react-router, that use an async setup for server rendering.
|
|
164
|
+
You can configure the default for prerender in your config.
|
|
165
|
+
4. You can expose either a React component or a function that returns a React component. If you
|
|
166
|
+
wish to create a React component via a function, rather than simply props, then you need to set
|
|
167
|
+
the property "generator" on that function to true. When that is done, the function is invoked
|
|
168
|
+
with a single parameter of "props", and that function should return a React element.
|
|
169
|
+
5. Be sure you can first render your react component client only before you try to debug server
|
|
170
|
+
rendering!
|
|
171
|
+
4. Open up the HTML source and take a look at the generated HTML and the JavaScript to see what's
|
|
172
|
+
going on under the covers. Not that when server rendering is turned on, then you'll see the
|
|
173
|
+
server rendered react components. When server rendering is turned off, then you'll only see
|
|
174
|
+
the `div` element where the inline JavaScript will render the component. You might also notice
|
|
175
|
+
how the props you pass (a Ruby Hash) becomes inline JavaScript on the HTML page.
|
|
176
|
+
|
|
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
|
+
## JavaScript Runtime Configuration
|
|
181
|
+
See this [discussion on JavaScript performance](https://github.com/shakacode/react_on_rails/issues/21).
|
|
182
|
+
The net result is that you want to add this line to your Gemfile to get therubyracer as your default
|
|
183
|
+
JavaScript engine.
|
|
184
|
+
|
|
75
185
|
```ruby
|
|
76
|
-
|
|
77
|
-
config.bundle_js_file = "app/assets/javascripts/generated/server.js" # This is the default
|
|
78
|
-
config.prerender = true # default is false
|
|
79
|
-
end
|
|
186
|
+
gem "therubyracer"
|
|
80
187
|
```
|
|
81
188
|
|
|
82
|
-
|
|
83
189
|
## References
|
|
84
190
|
* [Making the helper for server side rendering work with JS created by Webpack] (https://github.com/reactjs/react-rails/issues/301#issuecomment-133098974)
|
|
85
191
|
* [Add Demonstration of Server Side Rendering](https://github.com/justin808/react-webpack-rails-tutorial/issues/2)
|
|
86
192
|
* [Charlie Marsh's article "Rendering React Components on the Server"](http://www.crmarsh.com/react-ssr/)
|
|
87
193
|
* [Node globals](https://nodejs.org/api/globals.html#globals_global)
|
|
88
194
|
|
|
89
|
-
## Installation
|
|
90
195
|
|
|
91
|
-
|
|
196
|
+
## Development Setup for Gem Contributors
|
|
92
197
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
198
|
+
### Initial Setup
|
|
199
|
+
After checking out the repo, making sure you have rvm and nvm setup (setup ruby and node),
|
|
200
|
+
cd to `spec/dummy` and run `bin/setup` to install dependencies.
|
|
201
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
96
202
|
|
|
97
|
-
|
|
203
|
+
### Starting the Dummy App
|
|
204
|
+
To run the test app, it's **CRITICAL** to not just run `rails s`. You have to run `foreman start`.
|
|
205
|
+
If you don't do this, then `webpack` will not generate a new bundle,
|
|
206
|
+
and you will be seriously confused when you change JavaScript and the app does not change.
|
|
98
207
|
|
|
99
|
-
|
|
208
|
+
### Install and Release
|
|
209
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version,
|
|
210
|
+
update the version number in `version.rb`, and then run `bundle exec rake release`,
|
|
211
|
+
which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
100
212
|
|
|
101
|
-
|
|
213
|
+
### RSpec Testing
|
|
214
|
+
Run `rake` for testing the gem and `spec/dummy`. Otherwise, the `rspec` command only works for testing within `spec/dummy`.
|
|
102
215
|
|
|
103
|
-
|
|
216
|
+
If you run `rspec` at the top level, you'll see this message: `require': cannot load such file -- rails_helper (LoadError)`
|
|
104
217
|
|
|
105
|
-
|
|
218
|
+
### Debugging
|
|
219
|
+
Start the sample app like this for some debug printing:
|
|
220
|
+
```bash
|
|
221
|
+
TRACE_REACT_ON_RAILS=true && foreman start
|
|
222
|
+
```
|
|
106
223
|
|
|
107
|
-
|
|
224
|
+
### Linting
|
|
225
|
+
All linting is performed from the docker container. You will need docker and docker-compose installed
|
|
226
|
+
locally to lint code changes via the lint container.
|
|
108
227
|
|
|
109
|
-
|
|
228
|
+
* [Install Docker Toolbox for Mac](https://www.docker.com/toolbox)
|
|
229
|
+
* [Install Docker Compose for Linux](https://docs.docker.com/compose/install/)
|
|
110
230
|
|
|
111
|
-
|
|
231
|
+
Once you have docker and docker-compose running locally, run `docker-compose build lint`. This will build
|
|
232
|
+
the `reactonrails_lint` docker image and docker-compose `lint` container. The inital build is slow,
|
|
233
|
+
but after the install, startup is very quick.
|
|
112
234
|
|
|
113
|
-
|
|
235
|
+
### Linting Commands
|
|
236
|
+
Run `rake -D docker` to see all docker linting commands for rake. `rake docker` will run all linters.
|
|
237
|
+
For individual rake linting commands please refer to `rake -D docker` for the list.
|
|
238
|
+
You can run specfic linting for directories or files by using `docker-compose run lint rubocop (file path or directory)`, etc.
|
|
239
|
+
`docker-compose run lint /bin/bash` sets you up to run from the container command line.
|
|
114
240
|
|
|
115
241
|
## Contributing
|
|
116
242
|
|
|
117
243
|
Bug reports and pull requests are welcome on GitHub at https://github.com/shakacode/react_on_rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
|
118
244
|
|
|
119
|
-
|
|
120
245
|
## License
|
|
121
246
|
|
|
122
247
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
123
248
|
|
|
249
|
+
## Updating New Versions of the Gem
|
|
250
|
+
|
|
251
|
+
See https://github.com/svenfuchs/gem-release
|
|
252
|
+
|
|
253
|
+
# Authors
|
|
254
|
+
The Shaka Code team!
|
|
255
|
+
|
|
256
|
+
1. [Justin Gordon](https://github.com/justin808/)
|
|
257
|
+
2. [Samnang Chhun](https://github.com/samnang)
|
|
258
|
+
3. [Alex Fedoseev](https://github.com/alexfedoseev)
|
|
259
|
+
|
|
260
|
+
And based on the work of the [react-rails gem](https://github.com/reactjs/react-rails)
|
data/Rakefile
CHANGED
|
@@ -1,6 +1,91 @@
|
|
|
1
|
-
require "
|
|
2
|
-
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
namespace :run_rspec do
|
|
4
|
+
desc "Run RSpec for top level only"
|
|
5
|
+
task :gem do
|
|
6
|
+
sh %{ rspec --exclude-pattern "spec/dummy/**/*_spec.rb" spec }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
desc "Run RSpec for spec/dummy only"
|
|
10
|
+
task :dummy do
|
|
11
|
+
sh %{ cd spec/dummy && rspec }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
task run_rspec: [:gem, :dummy] do
|
|
15
|
+
puts "Completed all RSpec tests"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
desc "Runs all tests. Run `rake -D run_rspec` to see all available test options"
|
|
19
|
+
task run_rspec: ["run_rspec:run_rspec"]
|
|
20
|
+
|
|
21
|
+
task default: :run_rspec
|
|
22
|
+
|
|
23
|
+
namespace :lint do
|
|
24
|
+
|
|
25
|
+
desc "Run Rubocop as shell"
|
|
26
|
+
task :rubocop do
|
|
27
|
+
sh "rubocop ."
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
desc "Run ruby-lint as shell"
|
|
31
|
+
task :ruby do
|
|
32
|
+
sh "ruby-lint app spec lib"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc "Run scss-lint as shell"
|
|
36
|
+
task :scss do
|
|
37
|
+
sh "scss-lint ."
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
desc "Run eslint as shell"
|
|
41
|
+
task :eslint do
|
|
42
|
+
sh "eslint . --ext .jsx and .js"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc "Run jscs from shell"
|
|
46
|
+
task :jscs do
|
|
47
|
+
sh "jscs ."
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
task lint: [:eslint, :rubocop, :ruby, :jscs, :scss] do
|
|
51
|
+
puts "Completed all linting"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
desc "Runs all linters. Run `rake -D lint` to see all available lint options"
|
|
55
|
+
task lint: ["lint:lint"]
|
|
56
|
+
|
|
57
|
+
namespace :docker do
|
|
58
|
+
desc "Run Rubocop linter from docker"
|
|
59
|
+
task :rubocop do
|
|
60
|
+
sh "docker-compose run lint rake lint:rubocop"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
desc "Run ruby-lint linter from docker"
|
|
64
|
+
task :ruby do
|
|
65
|
+
sh "docker-compose run lint rake lint:ruby"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
desc "Run scss-lint linter from docker"
|
|
69
|
+
task :scss do
|
|
70
|
+
sh"docker-compose run lint rake lint:scss"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
desc "Run eslint linter from docker"
|
|
74
|
+
task :eslint do
|
|
75
|
+
sh "docker-compose run lint rake lint:eslint"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
desc "Run jscs linter from docker"
|
|
79
|
+
task :jscs do
|
|
80
|
+
sh "docker-compose run lint rake lint:jscs"
|
|
81
|
+
end
|
|
82
|
+
desc "Run all linting from docker"
|
|
83
|
+
task :lint do
|
|
84
|
+
sh "docker-compose run lint rake lint"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
desc "Runs all linters from docker. Run `rake -D docker` to see all available lint options"
|
|
89
|
+
task docker: ["docker:lint"]
|
|
3
90
|
|
|
4
|
-
RSpec::Core::RakeTask.new(:spec)
|
|
5
91
|
|
|
6
|
-
task :default => :spec
|
|
@@ -2,132 +2,198 @@ require 'react_on_rails/react_renderer'
|
|
|
2
2
|
|
|
3
3
|
module ReactOnRailsHelper
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# react_component_name: can be a React component, created using a ES6 class, or
|
|
6
|
+
# React.createClass, or a
|
|
7
|
+
# `generator function` that returns a React component
|
|
8
|
+
# using ES6
|
|
9
|
+
# let MyReactComponentApp = (props) => <MyReactComponent {...props}/>;
|
|
10
|
+
# or using ES5
|
|
11
|
+
# var MyReactComponentApp = function(props) { return <YourReactComponent {...props}/>; }
|
|
12
|
+
# Exposing the react_component_name is necessary to both a plain ReactComponent as well as
|
|
13
|
+
# a generator:
|
|
14
|
+
# For client rendering, expose the react_component_name on window:
|
|
15
|
+
# window.MyReactComponentApp = MyReactComponentApp;
|
|
16
|
+
# For server rendering, export the react_component_name on global:
|
|
17
|
+
# global.MyReactComponentApp = MyReactComponentApp;
|
|
18
|
+
# See spec/dummy/client/app/startup/serverGlobals.jsx and
|
|
19
|
+
# spec/dummy/client/app/startup/ClientApp.jsx for examples of this
|
|
6
20
|
# props: Ruby Hash which contains the properties to pass to the react object
|
|
7
21
|
#
|
|
8
|
-
# Naming Conventions:
|
|
9
|
-
# Suppose your component is named "App" (can be anything)
|
|
10
|
-
# 1. Inside app/startup/ServerApp.jsx, setup the component for server rendering. This is used by
|
|
11
|
-
# the global exports in step 2
|
|
12
|
-
# 2. Component_name is changed to CamelizedUpper for the exposed component,
|
|
13
|
-
# so we have App as the exposed global (see app/startup/serverGlobals.jsx)
|
|
14
|
-
# 3. Inside app/startup/ClientApp.jsx, you want to export a function that generates
|
|
15
|
-
# your component, and when you generate the component, you must use this naming convention
|
|
16
|
-
# to get the data.
|
|
17
|
-
# App(__appData__)
|
|
18
|
-
# That way it gets the data you pass from the Rails helper.
|
|
19
|
-
#
|
|
20
22
|
# options:
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
+
# generator_function: <true/false> default is false, set to true if you want to use a
|
|
24
|
+
# generator function rather than a React Component.
|
|
25
|
+
# prerender: <true/false> set to false when debugging!
|
|
26
|
+
# trace: <true/false> set to true to print additional debugging information in the browser
|
|
27
|
+
# default is true for development, off otherwise
|
|
28
|
+
# replay_console: <true/false> Default is true. False will disable echoing server rendering
|
|
29
|
+
# logs, which can make troubleshooting server rendering difficult.
|
|
23
30
|
def react_component(component_name, props = {}, options = {})
|
|
31
|
+
# Create the JavaScript and HTML to allow either client or server rendering of the
|
|
32
|
+
# react_component.
|
|
33
|
+
#
|
|
24
34
|
# Create the JavaScript setup of the global to initialize the client rendering
|
|
25
35
|
# (re-hydrate the data). This enables react rendered on the client to see that the
|
|
26
36
|
# server has already rendered the HTML.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
# We use this react_component_index in case we have the same component multiple times on the page.
|
|
38
|
+
react_component_index = next_react_component_index
|
|
39
|
+
react_component_name = component_name.camelize # Not sure if we should be doing this (JG)
|
|
40
|
+
dom_id = "#{component_name}-react-component-#{react_component_index}"
|
|
41
|
+
|
|
42
|
+
# Setup the page_loaded_js, which is the same regardless of prerendering or not!
|
|
43
|
+
# The reason is that React is smart about not doing extra work if the server rendering did its job.
|
|
44
|
+
data_variable_name = "__#{component_name.camelize(:lower)}Data#{react_component_index}__"
|
|
36
45
|
turbolinks_loaded = Object.const_defined?(:Turbolinks)
|
|
37
|
-
install_render_events = turbolinks_loaded ? turbolinks_bootstrap(
|
|
38
|
-
|
|
46
|
+
install_render_events = turbolinks_loaded ? turbolinks_bootstrap(dom_id) : non_turbolinks_bootstrap
|
|
39
47
|
page_loaded_js = <<-JS
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
(function() {
|
|
49
|
+
window.#{data_variable_name} = #{props.to_json};
|
|
50
|
+
#{define_render_if_dom_node_present(react_component_name, data_variable_name, dom_id, trace(options), generator_function(options))}
|
|
51
|
+
#{install_render_events}
|
|
52
|
+
})();
|
|
45
53
|
JS
|
|
46
54
|
|
|
47
55
|
data_from_server_script_tag = javascript_tag(page_loaded_js)
|
|
48
56
|
|
|
49
57
|
# Create the HTML rendering part
|
|
50
|
-
|
|
58
|
+
server_rendered_html =
|
|
59
|
+
server_rendered_react_component_html(options, props, react_component_name)
|
|
60
|
+
|
|
61
|
+
rendered_output = content_tag(:div,
|
|
62
|
+
server_rendered_html,
|
|
63
|
+
id: dom_id)
|
|
64
|
+
|
|
65
|
+
# IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
|
|
66
|
+
<<-HTML.html_safe
|
|
67
|
+
#{data_from_server_script_tag}
|
|
68
|
+
#{rendered_output}
|
|
69
|
+
HTML
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def next_react_component_index
|
|
73
|
+
@react_component_index ||= -1
|
|
74
|
+
@react_component_index += 1
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def server_rendered_react_component_html(options, props, react_component_name)
|
|
78
|
+
if prerender(options)
|
|
51
79
|
render_js_expression = <<-JS
|
|
52
|
-
|
|
80
|
+
(function(React) {
|
|
81
|
+
var reactElement = #{render_js_react_element(react_component_name, props.to_json, generator_function(options))};
|
|
82
|
+
return React.renderToString(reactElement);
|
|
83
|
+
})(this.React);
|
|
53
84
|
JS
|
|
54
|
-
|
|
85
|
+
# create the server generated html of the react component with props
|
|
86
|
+
options[:react_component_name] = react_component_name
|
|
87
|
+
server_rendered_react_component_html =
|
|
88
|
+
render_js(render_js_expression, options)
|
|
55
89
|
else
|
|
56
90
|
server_rendered_react_component_html = ""
|
|
57
91
|
end
|
|
58
|
-
|
|
59
|
-
rendered_output = content_tag(:div,
|
|
60
|
-
server_rendered_react_component_html,
|
|
61
|
-
id: domId)
|
|
62
|
-
|
|
63
|
-
<<-HTML.strip_heredoc.html_safe
|
|
64
|
-
#{data_from_server_script_tag}
|
|
65
|
-
#{rendered_output}
|
|
66
|
-
HTML
|
|
92
|
+
server_rendered_react_component_html
|
|
67
93
|
end
|
|
68
94
|
|
|
69
95
|
# Takes javascript code and returns the output from it. This is called by react_component, which
|
|
70
96
|
# sets up the JS code for rendering a react component.
|
|
71
97
|
# This method could be used by itself to render the output of any javascript that returns a
|
|
72
98
|
# string of proper HTML.
|
|
73
|
-
def render_js(js_expression)
|
|
74
|
-
ReactOnRails::ReactRenderer.new.render_js(js_expression
|
|
99
|
+
def render_js(js_expression, options = {})
|
|
100
|
+
ReactOnRails::ReactRenderer.new(options).render_js(js_expression,
|
|
101
|
+
options).html_safe
|
|
75
102
|
end
|
|
76
103
|
|
|
77
104
|
private
|
|
78
105
|
|
|
79
|
-
def
|
|
106
|
+
def trace(options)
|
|
107
|
+
options.fetch(:trace) { ReactOnRails.configuration.trace }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def generator_function(options)
|
|
111
|
+
options.fetch(:generator_function) { ReactOnRails.configuration.generator_function }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def prerender(options)
|
|
115
|
+
options.fetch(:prerender) { ReactOnRails.configuration.prerender }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def debug_js(react_component_name, data_variable, dom_id, trace)
|
|
80
119
|
if trace
|
|
81
|
-
<<-JS
|
|
82
|
-
|
|
120
|
+
<<-JS
|
|
121
|
+
console.log("CLIENT SIDE RENDERED #{react_component_name} with data_variable #{data_variable} to dom node with id: #{dom_id}");
|
|
83
122
|
JS
|
|
84
123
|
else
|
|
85
124
|
""
|
|
86
125
|
end
|
|
87
126
|
end
|
|
88
127
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
128
|
+
# react_component_name: See app/helpers/react_on_rails_helper.rb:5
|
|
129
|
+
# props_string: is either the variable name used to hold the props (client side) or the
|
|
130
|
+
# stringified hash of props from the Ruby server side. In terms of the view helper, one is
|
|
131
|
+
# simply passing in the Ruby Hash of props.
|
|
132
|
+
#
|
|
133
|
+
# Returns the JavaScript code to generate a React element.
|
|
134
|
+
def render_js_react_element(react_component_name, props_string, generator_function)
|
|
135
|
+
# "this" is defined by the calling context which is "global" in the execJs
|
|
136
|
+
# environment or window in the client side context.
|
|
137
|
+
js_create_element = if generator_function
|
|
138
|
+
"#{react_component_name}(props)"
|
|
139
|
+
else
|
|
140
|
+
"React.createElement(#{react_component_name}, props)"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
<<-JS
|
|
144
|
+
(function(React) {
|
|
145
|
+
var props = #{props_string};
|
|
146
|
+
return #{js_create_element};
|
|
147
|
+
})(this.React);
|
|
148
|
+
JS
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def define_render_if_dom_node_present(react_component_name, data_variable, dom_id, trace, generator_function)
|
|
152
|
+
inner_js_code = <<-JS_CODE
|
|
153
|
+
var domNode = document.getElementById('#{dom_id}');
|
|
154
|
+
if (domNode) {
|
|
155
|
+
#{debug_js(react_component_name, data_variable, dom_id, trace)}
|
|
156
|
+
var reactElement = #{render_js_react_element(react_component_name, data_variable, generator_function)};
|
|
157
|
+
React.render(reactElement, domNode);
|
|
158
|
+
}
|
|
159
|
+
JS_CODE
|
|
160
|
+
|
|
161
|
+
<<-JS
|
|
162
|
+
var renderIfDomNodePresent = function() {
|
|
163
|
+
#{ReactOnRails::ReactRenderer.wrap_code_with_exception_handler(inner_js_code, react_component_name)}
|
|
164
|
+
}
|
|
99
165
|
JS
|
|
100
166
|
end
|
|
101
167
|
|
|
102
168
|
def non_turbolinks_bootstrap
|
|
103
|
-
<<-JS
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
169
|
+
<<-JS
|
|
170
|
+
document.addEventListener("DOMContentLoaded", function(event) {
|
|
171
|
+
console.log("DOMContentLoaded event fired");
|
|
172
|
+
renderIfDomNodePresent();
|
|
173
|
+
});
|
|
108
174
|
JS
|
|
109
175
|
end
|
|
110
176
|
|
|
111
177
|
def turbolinks_bootstrap(dom_id)
|
|
112
|
-
<<-JS
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
178
|
+
<<-JS
|
|
179
|
+
var turbolinksInstalled = typeof(Turbolinks) !== 'undefined';
|
|
180
|
+
if (!turbolinksInstalled) {
|
|
181
|
+
console.warn("WARNING: NO TurboLinks detected in JS, but it's in your Gemfile");
|
|
182
|
+
#{non_turbolinks_bootstrap}
|
|
183
|
+
} else {
|
|
184
|
+
function onPageChange(event) {
|
|
185
|
+
var removePageChangeListener = function() {
|
|
186
|
+
document.removeEventListener("page:change", onPageChange);
|
|
187
|
+
document.removeEventListener("page:before-unload", removePageChangeListener);
|
|
188
|
+
var domNode = document.getElementById('#{dom_id}');
|
|
189
|
+
React.unmountComponentAtNode(domNode);
|
|
190
|
+
};
|
|
191
|
+
document.addEventListener("page:before-unload", removePageChangeListener);
|
|
192
|
+
|
|
193
|
+
renderIfDomNodePresent();
|
|
194
|
+
}
|
|
195
|
+
document.addEventListener("page:change", onPageChange);
|
|
196
|
+
}
|
|
131
197
|
JS
|
|
132
198
|
end
|
|
133
199
|
end
|
data/docker-compose.yml
ADDED
|
@@ -5,17 +5,24 @@ module ReactOnRails
|
|
|
5
5
|
|
|
6
6
|
def self.configuration
|
|
7
7
|
@configuration ||= Configuration.new(
|
|
8
|
-
|
|
9
|
-
prerender: false
|
|
8
|
+
server_bundle_js_file: "app/assets/javascripts/generated/server.js",
|
|
9
|
+
prerender: false,
|
|
10
|
+
replay_console: true,
|
|
11
|
+
generator_function: false,
|
|
12
|
+
trace: Rails.env.development?
|
|
10
13
|
)
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
class Configuration
|
|
14
|
-
attr_accessor :
|
|
17
|
+
attr_accessor :server_bundle_js_file, :prerender, :replay_console, :generator_function, :trace
|
|
15
18
|
|
|
16
|
-
def initialize(
|
|
17
|
-
|
|
19
|
+
def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil,
|
|
20
|
+
generator_function: nil, trace: nil)
|
|
21
|
+
self.server_bundle_js_file = server_bundle_js_file
|
|
18
22
|
self.prerender = prerender
|
|
23
|
+
self.replay_console = replay_console
|
|
24
|
+
self.generator_function = generator_function
|
|
25
|
+
self.trace = Rails.env.development?
|
|
19
26
|
end
|
|
20
27
|
end
|
|
21
28
|
end
|
|
@@ -1,41 +1,134 @@
|
|
|
1
|
+
# Kudos to react-rails for how to do the polyfill of the console!
|
|
2
|
+
# https://github.com/reactjs/react-rails/blob/master/lib/react/server_rendering/sprockets_renderer.rb
|
|
3
|
+
|
|
1
4
|
module ReactOnRails
|
|
2
5
|
class ReactRenderer
|
|
6
|
+
# Reimplement console methods for replaying on the client
|
|
7
|
+
CONSOLE_POLYFILL = <<-JS
|
|
8
|
+
var console = { history: [] };
|
|
9
|
+
['error', 'log', 'info', 'warn'].forEach(function (level) {
|
|
10
|
+
console[level] = function () {
|
|
11
|
+
console.history.push({level: level, arguments: Array.prototype.slice.call(arguments)});
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
JS
|
|
3
15
|
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
# Script to write to the browser console.
|
|
17
|
+
# NOTE: result comes from enclosing closure and is the server generated HTML
|
|
18
|
+
# that we intend to write to the browser. Thus, the script tag will get executed right after
|
|
19
|
+
# the HTML is rendered.
|
|
20
|
+
CONSOLE_REPLAY = <<-JS
|
|
21
|
+
var history = console.history;
|
|
22
|
+
if (history && history.length > 0) {
|
|
23
|
+
result += '\\n<script>';
|
|
24
|
+
history.forEach(function (msg) {
|
|
25
|
+
result += '\\nconsole.' + msg.level + '.apply(console, ' + JSON.stringify(msg.arguments) + ');';
|
|
26
|
+
});
|
|
27
|
+
result += '\\n</script>';
|
|
28
|
+
}
|
|
29
|
+
JS
|
|
30
|
+
|
|
31
|
+
DEBUGGER = <<-JS
|
|
32
|
+
if (typeof window !== 'undefined') { debugger; }
|
|
33
|
+
JS
|
|
34
|
+
|
|
35
|
+
def base_js_code
|
|
36
|
+
<<-JS
|
|
37
|
+
#{CONSOLE_POLYFILL}
|
|
38
|
+
#{bundle_js_code};
|
|
13
39
|
JS
|
|
14
40
|
end
|
|
15
41
|
|
|
16
|
-
def initialize
|
|
17
|
-
|
|
18
|
-
@
|
|
42
|
+
def initialize(options)
|
|
43
|
+
@context = ExecJS.compile(base_js_code)
|
|
44
|
+
@replay_console = options.fetch(:replay_console) { ReactOnRails.configuration.replay_console }
|
|
19
45
|
end
|
|
20
46
|
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
47
|
+
# js_code: JavaScript expression that returns a string.
|
|
48
|
+
# Returns a string of HTML for direct insertion on the page by evaluating js_code.
|
|
49
|
+
# Note, js_code does not have to be based on React.
|
|
50
|
+
# Calling code will probably call 'html_safe' on return value before rendering to the view.
|
|
51
|
+
def render_js(js_code, options = {})
|
|
52
|
+
component_name = options.fetch(:react_component_name, "")
|
|
53
|
+
|
|
54
|
+
result_js_code = "result = #{js_code}"
|
|
55
|
+
|
|
56
|
+
js_code_wrapper = <<-JS
|
|
57
|
+
(function () {
|
|
58
|
+
var result = '';
|
|
59
|
+
#{ReactOnRails::ReactRenderer.wrap_code_with_exception_handler(result_js_code, component_name)}
|
|
60
|
+
#{after_render};
|
|
61
|
+
return result;
|
|
62
|
+
})()
|
|
28
63
|
JS
|
|
29
64
|
|
|
30
|
-
|
|
31
|
-
|
|
65
|
+
trace_rails_on_maui = ENV["TRACE_REACT_ON_RAILS"].present? # Set to anything to print generated code.
|
|
66
|
+
if trace_rails_on_maui
|
|
67
|
+
puts "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
|
|
68
|
+
puts "react_renderer.rb: 92"
|
|
69
|
+
puts "js_code_wrapper = #{js_code_wrapper.ai}"
|
|
70
|
+
puts "wrote file tmp/server-generated.js"
|
|
71
|
+
File.write('tmp/server-generated.js', js_code_wrapper)
|
|
72
|
+
puts "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
|
|
73
|
+
end
|
|
74
|
+
|
|
32
75
|
@context.eval(js_code_wrapper)
|
|
33
76
|
end
|
|
34
77
|
|
|
78
|
+
def self.wrap_code_with_exception_handler(js_code, component_name)
|
|
79
|
+
<<-JS
|
|
80
|
+
try {
|
|
81
|
+
#{js_code}
|
|
82
|
+
}
|
|
83
|
+
catch(e) {
|
|
84
|
+
var lineOne =
|
|
85
|
+
'ERROR: You specifed the option generator_function (could be in your defaults) to be\\n';
|
|
86
|
+
var lastLine =
|
|
87
|
+
'A generator function takes a single arg of props and returns a ReactElement.';
|
|
88
|
+
|
|
89
|
+
var msg = '';
|
|
90
|
+
var shouldBeGeneratorError = lineOne +
|
|
91
|
+
'false, but the react component \\'#{component_name}\\' seems to be a generator function.\\n' +
|
|
92
|
+
lastLine;
|
|
93
|
+
var reMatchShouldBeGeneratorError = /Can't add property context, object is not extensible/;
|
|
94
|
+
if (reMatchShouldBeGeneratorError.test(e.message)) {
|
|
95
|
+
msg += shouldBeGeneratorError + '\\n\\n';
|
|
96
|
+
console.error(shouldBeGeneratorError);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var shouldBeGeneratorError = lineOne +
|
|
100
|
+
'true, but the react component \\'#{component_name}\\' is not a generator function.\\n' +
|
|
101
|
+
lastLine;
|
|
102
|
+
var reMatchShouldNotBeGeneratorError = /Cannot call a class as a function/;
|
|
103
|
+
if (reMatchShouldNotBeGeneratorError.test(e.message)) {
|
|
104
|
+
msg += shouldBeGeneratorError + '\\n\\n';
|
|
105
|
+
console.error(shouldBeGeneratorError);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.error('SERVER SIDE: Exception in server side rendering!');
|
|
109
|
+
if (e.fileName) {
|
|
110
|
+
console.error('SERVER SIDE: location: ' + e.fileName + ':' + e.lineNumber);
|
|
111
|
+
}
|
|
112
|
+
console.error('SERVER SIDE: message: ' + e.message);
|
|
113
|
+
console.error('SERVER SIDE: stack: ' + e.stack);
|
|
114
|
+
msg += 'SERVER SIDE Exception in rendering!\\n' +
|
|
115
|
+
(e.fileName ? '\\nlocation: ' + e.fileName + ':' + e.lineNumber : '') +
|
|
116
|
+
'\\nMessage: ' + e.message + '\\n\\n' + e.stack;
|
|
117
|
+
|
|
118
|
+
var reactElement = React.createElement('pre', null, msg);
|
|
119
|
+
result = React.renderToString(reactElement);
|
|
120
|
+
}
|
|
121
|
+
JS
|
|
122
|
+
end
|
|
123
|
+
|
|
35
124
|
private
|
|
36
125
|
|
|
126
|
+
def after_render
|
|
127
|
+
@replay_console ? CONSOLE_REPLAY : ""
|
|
128
|
+
end
|
|
129
|
+
|
|
37
130
|
def bundle_js_code
|
|
38
|
-
js_file = Rails.root.join(ReactOnRails.configuration.
|
|
131
|
+
js_file = Rails.root.join(ReactOnRails.configuration.server_bundle_js_file)
|
|
39
132
|
File.read(js_file)
|
|
40
133
|
end
|
|
41
134
|
end
|
data/react_on_rails.gemspec
CHANGED
data/ruby-lint.yml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Not currently using ruby-lint
|
|
2
|
+
---
|
|
3
|
+
presenter: syntastic
|
|
4
|
+
|
|
5
|
+
directories:
|
|
6
|
+
- app
|
|
7
|
+
- lib
|
|
8
|
+
- spec
|
|
9
|
+
|
|
10
|
+
ignore_paths:
|
|
11
|
+
- client
|
|
12
|
+
- node_modules
|
|
13
|
+
|
|
14
|
+
analysis_classes:
|
|
15
|
+
# - argument_amount
|
|
16
|
+
- pedantics
|
|
17
|
+
- shadowing_variables
|
|
18
|
+
# Too many false positives for these
|
|
19
|
+
# - undefined_methods
|
|
20
|
+
# - undefined_variables
|
|
21
|
+
# - unused_variables
|
|
22
|
+
- useless_equality_checks
|
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.2
|
|
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-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -80,6 +80,20 @@ dependencies:
|
|
|
80
80
|
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: coveralls
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
83
97
|
description: See README.md
|
|
84
98
|
email:
|
|
85
99
|
- justin@shakacode.com
|
|
@@ -87,20 +101,29 @@ executables: []
|
|
|
87
101
|
extensions: []
|
|
88
102
|
extra_rdoc_files: []
|
|
89
103
|
files:
|
|
104
|
+
- ".coveralls.yml"
|
|
105
|
+
- ".eslintignore"
|
|
106
|
+
- ".eslintrc"
|
|
90
107
|
- ".gitignore"
|
|
108
|
+
- ".jscsrc"
|
|
91
109
|
- ".rspec"
|
|
110
|
+
- ".rubocop.yml"
|
|
111
|
+
- ".scss-lint.yml"
|
|
92
112
|
- ".travis.yml"
|
|
93
113
|
- CODE_OF_CONDUCT.md
|
|
114
|
+
- Dockerfile
|
|
94
115
|
- Gemfile
|
|
95
116
|
- LICENSE.txt
|
|
96
117
|
- README.md
|
|
97
118
|
- Rakefile
|
|
98
119
|
- app/helpers/react_on_rails_helper.rb
|
|
120
|
+
- docker-compose.yml
|
|
99
121
|
- lib/react_on_rails.rb
|
|
100
122
|
- lib/react_on_rails/configuration.rb
|
|
101
123
|
- lib/react_on_rails/react_renderer.rb
|
|
102
124
|
- lib/react_on_rails/version.rb
|
|
103
125
|
- react_on_rails.gemspec
|
|
126
|
+
- ruby-lint.yml
|
|
104
127
|
homepage: https://github.com/shakacode/react_on_rails
|
|
105
128
|
licenses:
|
|
106
129
|
- MIT
|