Skip to content

Commit

Permalink
Merge pull request #362 from shakacode/add-rails-helper-to-generator
Browse files Browse the repository at this point in the history
Update generator to add ReactOnRails spec helper
  • Loading branch information
justin808 committed Apr 3, 2016
2 parents 5d86a68 + fa16c9a commit cf29f7e
Show file tree
Hide file tree
Showing 19 changed files with 286 additions and 5,200 deletions.
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Metrics/PerceivedComplexity:
Max: 10

Metrics/ClassLength:
Max: 114
Max: 140

Metrics/ParameterLists:
Max: 5
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ before_install:
- sudo apt-get install -y xvfb

install:
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install 5.5.0
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install 5.10.0
- npm install -g npm
- npm install -g poltergeist
- gem install bundler
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file. Items under
Contributors: please follow the recommendations outlined at [keepachangelog.com](http://keepachangelog.com/). Please use the existing headings and styling as a guide, and add a link for the version diff at the bottom of the file. Also, please update the `Unreleased` link to compare to the latest release version.
## [Unreleased]

##### Added
- Generator enhancements
- Generator adds line to spec/rails_helper.rb so that running specs will ensure assets are compiled.
- Other small changes to the generator including adding necessary npm scripts to allow React on Rails to build assets.
- Npm modules updated for generator.
- Added babel-runtime in to the client/package.json created.
- Server rendering
- Added more diagnostics for server rendering.
- Calls to setTimeout and setInterval are not logged for server rendering unless env TRACE_REACT_ON_RAILS is set to YES.
- Updated all project npm dependencies to latest.
- Update to node 5.10.0 for CI.
- Added babel-runtime as a peer dependency for the npm module.

## [5.0.0] - 2016-04-01

##### Added
Expand Down
40 changes: 39 additions & 1 deletion docs/additional-reading/server-rendering-tips.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Server Rendering Tips

## General Tips
- Your code can't reference `document`. Server side JS execution does not have access to `document`, so jQuery and some
other libs won't work in this environment. You can debug this by putting in `console.log`
statements in your code.
Expand All @@ -8,4 +9,41 @@
do this is to use a generator function.
- If you're serious about server rendering, it's worth the effort to have different entry points for client and server rendering. It's worth the extra complexity.

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.
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 side.

## Troubleshooting Server Rendering

1. First be sure your code works with server rendering disabled (`prerender: false`)
2. `export TRACE_REACT_ON_RAILS=YES` Turn this on to get both the invocation code for you component, as well as the whole file used to setup the JavaScript context.

## setTimeout and setInterval

These methods are polyfilled for server rendering to be no-ops. We don't log calls to these by default as some libraries, namely babel-polyfill, will call setTimout. If you wish to log calls to setTimeout and setInterval, set the ENV value: `export TRACE_REACT_ON_RAILS=YES`.

Here's an example of this which shows the line numbers that end up calling setTimeout:
```
➜ ~/shakacode/react_on_rails/gen-examples/examples/basic-server-rendering (add-rails-helper-to-generator u=) ✗ export TRACE_REACT_ON_RAILS=YES
➜ ~/shakacode/react_on_rails/gen-examples/examples/basic-server-rendering (add-rails-helper-to-generator u=) ✗ rspec
Hello World
Building Webpack client-rendering assets...
Completed building Webpack client-rendering assets.
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
react_renderer.rb: 92
wrote file tmp/server-generated.js
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
react_renderer.rb: 92
wrote file tmp/base_js_code.js
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
[SERVER] setTimeout is not defined for execJS. See https://github.com/sstephenson/execjs#faq. Note babel-polyfill will call this.
[SERVER] at setTimeout (<eval>:31:17)
at defer (<eval>:4422:8)
at setImmediate (<eval>:4387:6)
at notify (<eval>:4481:16)
at module.exports (<eval>:4490:6)
at notify (<eval>:4081:4)
at Promise.$resolve (<eval>:4189:8)
at <eval>:793:18
at Function.resolve (<eval>:4265:6)
the hello world example works
```
52 changes: 43 additions & 9 deletions lib/generators/react_on_rails/base_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,15 @@ def create_react_directories

def copy_base_files
base_path = "base/base/"
%w(app/controllers/hello_world_controller.rb
client/.babelrc
client/index.jade
client/server.js
client/webpack.client.base.config.js
client/webpack.client.rails.config.js
REACT_ON_RAILS.md
client/REACT_ON_RAILS_CLIENT_README.md
package.json).each { |file| copy_file(base_path + file, file) }
base_files = %w(app/controllers/hello_world_controller.rb
client/.babelrc
client/index.jade
client/server.js
client/webpack.client.base.config.js
client/webpack.client.rails.config.js
REACT_ON_RAILS.md
client/REACT_ON_RAILS_CLIENT_README.md)
base_files.each { |file| copy_file(base_path + file, file) }
end

def template_base_files
Expand All @@ -127,6 +127,7 @@ def template_base_files
Procfile.dev
Procfile.dev-hot
app/views/hello_world/index.html.erb
package.json
client/app/bundles/HelloWorld/components/HelloWorldWidget.jsx
client/webpack.client.hot.config.js
client/package.json).each { |file| template(base_path + file + ".tt", file) }
Expand Down Expand Up @@ -184,6 +185,32 @@ def append_to_assets_initializer
end
end

def append_to_spec_rails_helper
rails_helper = File.join(destination_root, "spec/rails_helper.rb")
if File.exist?(rails_helper)
add_configure_rspec_to_compile_assets(rails_helper)
else
spec_helper = File.join(destination_root, "spec/spec_helper.rb")
if File.exist?(spec_helper)
add_configure_rspec_to_compile_assets(spec_helper)
else
GeneratorMessages.add_info(
<<-MSG.strip_heredoc
Did not find spec/rails_helper.rb or spec/spec_helper.rb to add line
config.example_status_persistence_file_path = "spec/examples.txt"
MSG
)
end
end
end

CONFIGURE_RSPEC_TO_COMPILE_ASSETS = <<-STR.strip_heredoc
RSpec.configure do |config|
# Ensure that if we are running js tests, we are using latest webpack assets
# This will use the defaults of :js and :server_rendering meta tags
ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
STR

# rename to application.scss from application.css or application.css.scss
def force_application_scss_naming_if_necessary
base_path = "app/assets/stylesheets/"
Expand Down Expand Up @@ -220,6 +247,13 @@ def print_helpful_message
MSG
GeneratorMessages.add_info(message)
end

private

def add_configure_rspec_to_compile_assets(helper_file)
search_str = "RSpec.configure do |config|"
gsub_file(helper_file, search_str, CONFIGURE_RSPEC_TO_COMPILE_ASSETS)
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default class HelloWorldWidget extends React.Component {
<h3>
Hello, {name}!
</h3>
<hr/>
<hr />
<form className="form-horizontal">
<%- if options.skip_bootstrap? -%>
<label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,82 +25,90 @@
},
"homepage": "https://github.com/shakacode/react-webpack-rails-tutorial",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js",
"build:client": "NODE_ENV=production webpack --config webpack.client.rails.config.js",
<%- if options.server_rendering? -%>
"build:server": "NODE_ENV=production webpack --config webpack.server.rails.config.js",
<%- end -%>
"build:dev:client": "webpack -w --config webpack.client.rails.config.js",
"build:dev:server": "webpack -w --config webpack.server.rails.config.js"<% unless options.skip_js_linters? -%>,
<%- if options.server_rendering? -%>
"build:dev:server": "webpack -w --config webpack.server.rails.config.js",
<%- end -%>
"build:production:client": "NODE_ENV=production webpack --config webpack.client.rails.build.config.js",
<%- if options.server_rendering? -%>
"build:production:server": "NODE_ENV=production webpack --config webpack.server.rails.build.config.js",
<%- end -%>
<%- unless options.skip_js_linters? -%>
"lint": "npm run eslint && npm run jscs",
"eslint": "eslint --ext .js,.jsx .",
"jscs": "jscs --verbose ."
"jscs": "jscs --verbose .",
<%- end -%>
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"autoprefixer": "^6.3.2",
"autoprefixer": "^6.3.5",
"axios": "^0.9.1",
"babel": "^6.5.1",
"babel-cli": "^6.5.1",
"babel-core": "^6.5.1",
"babel-loader": "^6.2.2",
"babel-polyfill": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"babel": "^6.5.2",
"babel-cli": "^6.6.5",
"babel-core": "^6.7.4",
"babel-loader": "^6.2.4",
"babel-runtime": "^6.6.1",
"babel-polyfill": "^6.7.4",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.5.0",
"css-loader": "^0.23.1",
"es5-shim": "^4.5.2",
"es5-shim": "^4.5.7",
"expose-loader": "^0.7.1",
<%- if options.redux? -%>
"immutable": "^3.7.6",
<%- end -%>
"imports-loader": "^0.6.5",
"jquery": "^2.2.0",
"jquery-ujs": "^1.2.0",
"loader-utils": "^0.2.12",
"lodash": "^4.3.0",
"jquery": "^2.2.2",
"jquery-ujs": "^1.2.1",
"loader-utils": "^0.2.13",
"lodash": "^4.7.0",
<%- if options.redux? -%>
"mirror-creator": "1.0.0",
"mirror-creator": "1.1.0",
<%- end -%>
"react": "^0.14.7",
"react": "^0.14.8",
<%- unless options.skip_bootstrap? -%>
"react-bootstrap": "^0.28.3",
"react-bootstrap": "^0.28.5",
<%- end -%>
"react-dom": "^0.14.7",
"react-dom": "^0.14.8",
"react-on-rails": "<%= VersionSyntaxConverter.new.rubygem_to_npm %>",
<%- if options.redux? -%>
"react-redux": "^4.4.0",
"react-redux": "^4.4.1",
"redux": "^3.3.1",
"redux-promise": "^0.5.1",
"redux-thunk": "^1.0.3",
"redux-promise": "^0.5.3",
"redux-thunk": "^2.0.1",
<%- end -%>
"webpack": "^1.12.13"
"webpack": "^1.12.14"
},
"devDependencies": {
<%- unless options.skip_js_linters? -%>
"babel-eslint": "^5.0.0-beta8",
<%- end -%>
"babel-plugin-react-transform": "^2.0.0",
"babel-plugin-react-transform": "^2.0.2",
<%- unless options.skip_bootstrap? -%>
"bootstrap-sass": "^3.3.6",
"bootstrap-sass-loader": "^1.0.10",
<%- end -%>
"css-loader": "^0.23.1",
<%- unless options.skip_js_linters? -%>
"eslint": "^1.10.3",
"eslint-config-airbnb": "^5.0.0",
"eslint-config-shakacode": "^1.0.0",
"eslint-plugin-react": "^3.16.1",
"eslint": "^2.6.0",
<%- unless options.skip_js_linters? -%>
"eslint-config-shakacode": "^4.0.0",
<%- end -%>
"eslint-plugin-react": "^4.2.3",
<%- end -%>
"express": "^4.13.4",
"file-loader": "^0.8.5",
"jade": "^1.11.0",
<%- unless options.skip_js_linters? -%>
"jscs": "^2.9.0",
"jscs": "^2.11.0",
<%- end -%>
"node-sass": "^3.4.2",
"react-transform-hmr": "^1.0.2",
"sass-loader": "^3.1.2",
"style-loader": "^0.13.0",
"react-transform-hmr": "^1.0.4",
"sass-loader": "^3.2.0",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"webpack-dev-server": "^1.14.1"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,21 @@
},
"scripts": {
"postinstall": "cd client && npm install",
"test": "rspec && (cd client && npm run lint)",
"express-server": "echo 'visit http://localhost:4000' && cd client && npm start",
"rails-server": "echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev"
"rails-server": "echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev",
"build:production:client": "(cd client && npm run build:production:client --silent)",
<%- if options.server_rendering? -%>
"build:production:server": "(cd client && npm run build:production:server --silent)",
<%- end -%>
"build:client": "(cd client && npm run build:client --silent",
<%- if options.server_rendering? -%>
"build:server": "(cd client && npm run build:server --silent)",
<%- end -%>
"build:dev:client": "(cd client && npm run build:dev:client --silent)",
<%- if options.server_rendering? -%>
"build:dev:server": "(cd client && npm run build:dev:server --silent)",
<%- end -%>
"test": "rspec && (cd client && npm run lint)"
},
"repository": {
"type": "git",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
# ActiveRecord::Migration.maintain_test_schema!

RSpec.configure do |config|
# Ensure that if we are running js tests, we are using latest webpack assets
# This will use the defaults of :js and :server_rendering meta tags
ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)

# Remove this line if you"re not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,29 @@ function select(state) {
}

// Simple example of a React "smart" component
class HelloWorld extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
const HelloWorld = (props) => {
const { dispatch, $$helloWorldStore } = props;
const actions = bindActionCreators(helloWorldActionCreators, dispatch);
const { updateName } = actions;
const name = $$helloWorldStore.get('name');

// This corresponds to the value used in function select above.
// We prefix all property and variable names pointing to Immutable.js objects with '$$'.
// This allows us to immediately know we don't call $$helloWorldStore['someProperty'], but
// instead use the Immutable.js `get` API for Immutable.Map
$$helloWorldStore: PropTypes.instanceOf(Immutable.Map).isRequired,
};
// This uses the ES2015 spread operator to pass properties as it is more DRY
// This is equivalent to:
// <HelloWorldWidget $$helloWorldStore={$$helloWorldStore} actions={actions} />
return (
<HelloWorldWidget {...{ updateName, name }} />
);
};

constructor(props, context) {
super(props, context);
}
HelloWorld.propTypes = {
dispatch: PropTypes.func.isRequired,

render() {
const { dispatch, $$helloWorldStore } = this.props;
const actions = bindActionCreators(helloWorldActionCreators, dispatch);
const { updateName } = actions;
const name = $$helloWorldStore.get('name');

// This uses the ES2015 spread operator to pass properties as it is more DRY
// This is equivalent to:
// <HelloWorldWidget $$helloWorldStore={$$helloWorldStore} actions={actions} />
return (
<HelloWorldWidget {...{ updateName, name }} />
);
}
}
// This corresponds to the value used in function select above.
// We prefix all property and variable names pointing to Immutable.js objects with '$$'.
// This allows us to immediately know we don't call $$helloWorldStore['someProperty'], but
// instead use the Immutable.js `get` API for Immutable.Map
$$helloWorldStore: PropTypes.instanceOf(Immutable.Map).isRequired,
};

// Don't forget to actually use connect!
// Note that we don't export HelloWorld, but the redux "connected" version of it.
Expand Down
Loading

0 comments on commit cf29f7e

Please sign in to comment.