+ );
+}
+
+const mapStateToProps = (state) => ({
+ editing: state.editMode,
+ user: state.user,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ onEditClick: () => { dispatch({ type: 'TOGGLE_EDIT_MODE' }) },
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(UserInfo)
diff --git a/client/app/user/userReducer.jsx b/client/app/user/userReducer.jsx
new file mode 100644
index 000000000..6581e95fd
--- /dev/null
+++ b/client/app/user/userReducer.jsx
@@ -0,0 +1,39 @@
+import React, { PropTypes } from 'react';
+
+const initialState = {
+ editMode: false,
+ user: null,
+}
+
+export default function userReducer(state = initialState, action) {
+ switch (action.type) {
+ case 'SET_USER':
+ return {
+ ...state,
+ user: action.user,
+ };
+ case 'TOGGLE_EDIT_MODE':
+ return {
+ ...state,
+ editMode: !state.editMode,
+ };
+ case 'UPDATE_USER':
+ const user = state.user;
+ const changes = action.changes
+ if (state.editMode) {
+ $.ajax({
+ type: 'PUT',
+ url: `/users/${user.id}`,
+ data: { user: changes },
+ dataType: 'json',
+ });
+ }
+ return {
+ ...state,
+ editMode: !state.editMode,
+ user: { ...user, ...changes }
+ };
+ default:
+ return state;
+ }
+}
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 000000000..75693d887
--- /dev/null
+++ b/client/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "react-webpack-rails-tutorial",
+ "version": "0.0.1",
+ "engines": {
+ "node": "5.10.0",
+ "npm": "3.5.0"
+ },
+ "scripts": {
+ "build:test": "webpack --config webpack.config.js",
+ "build:production": "NODE_ENV=production webpack --config webpack.config.js",
+ "build:development": "webpack -w --config webpack.config.js"
+ },
+ "cacheDirectories": ["node_modules", "client/node_modules"],
+ "dependencies": {
+ "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",
+ "es5-shim": "^4.5.7",
+ "expose-loader": "^0.7.1",
+ "immutable": "^3.7.6",
+ "imports-loader": "^0.6.5",
+ "mirror-creator": "1.1.0",
+ "react": "^0.14.8 || ^15.0.0",
+ "react-dom": "^0.14.8 || ^15.0.0",
+ "react-on-rails": "6.0.1",
+ "react-redux": "^4.4.1",
+ "redux": "^3.3.1",
+ "redux-promise": "^0.5.3",
+ "redux-thunk": "^2.0.1",
+ "webpack": "^1.12.14"
+ },
+ "devDependencies": {
+ }
+}
diff --git a/client/webpack.config.js b/client/webpack.config.js
new file mode 100644
index 000000000..57ec939c3
--- /dev/null
+++ b/client/webpack.config.js
@@ -0,0 +1,58 @@
+const webpack = require('webpack');
+const path = require('path');
+
+const devBuild = process.env.NODE_ENV !== 'production';
+const nodeEnv = devBuild ? 'development' : 'production';
+
+config = {
+ entry: [
+ 'es5-shim/es5-shim',
+ 'es5-shim/es5-sham',
+ 'babel-polyfill',
+ './app/registration',
+ ],
+
+ output: {
+ filename: 'webpack-bundle.js',
+ path: '../app/assets/webpack',
+ },
+
+ resolve: {
+ extensions: ['', '.js', '.jsx'],
+ alias: {
+ react: path.resolve('./node_modules/react'),
+ 'react-dom': path.resolve('./node_modules/react-dom'),
+ },
+ },
+ plugins: [
+ new webpack.DefinePlugin({
+ 'process.env': {
+ NODE_ENV: JSON.stringify(nodeEnv),
+ },
+ }),
+ ],
+ module: {
+ loaders: [
+ {
+ test: require.resolve('react'),
+ loader: 'imports?shim=es5-shim/es5-shim&sham=es5-shim/es5-sham',
+ },
+ {
+ test: /\.jsx?$/, loader: 'babel-loader',
+ exclude: /node_modules/,
+ },
+ ],
+ },
+};
+
+module.exports = config;
+
+if (devBuild) {
+ console.log('Webpack dev build for Rails'); // eslint-disable-line no-console
+ module.exports.devtool = 'eval-source-map';
+} else {
+ config.plugins.push(
+ new webpack.optimize.DedupePlugin()
+ );
+ console.log('Webpack production build for Rails'); // eslint-disable-line no-console
+}
diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb
index 68e54f5d5..4d2da8831 100644
--- a/config/initializers/assets.rb
+++ b/config/initializers/assets.rb
@@ -10,3 +10,14 @@
# application.js, application.css, and all non-JS/CSS in app/assets folder are
# already added.
Rails.application.config.assets.precompile += %w( print.css )
+# Add client/assets/ folders to asset pipeline's search path.
+# If you do not want to move existing images and fonts from your Rails app
+# you could also consider creating symlinks there that point to the original
+# rails directories. In that case, you would not add these paths here.
+# If you have a different server bundle file than your client bundle, you'll
+# need to add it here, like this:
+# Rails.application.config.assets.precompile += %w( server-bundle.js )
+
+# Add folder with webpack generated assets to assets.paths
+Rails.application.config.assets.paths << Rails.root.join('app', 'assets',
+ 'webpack')
diff --git a/config/initializers/react_on_rails.rb b/config/initializers/react_on_rails.rb
new file mode 100644
index 000000000..1619a278a
--- /dev/null
+++ b/config/initializers/react_on_rails.rb
@@ -0,0 +1,80 @@
+# Shown below are the defaults for configuration
+ReactOnRails.configure do |config|
+ # Client bundles are configured in application.js
+
+ # Directory where your generated assets go. All generated assets must go to the same directory.
+ # Configure this in your webpack config files. This relative to your Rails root directory.
+ config.generated_assets_dir = File.join(%w(app assets webpack))
+
+ # Define the files we need to check for webpack compilation when running tests.
+ config.webpack_generated_files = %w( webpack-bundle.js )
+
+ # This is the file used for server rendering of React when using `(prerender: true)`
+ # If you are never using server rendering, you may set this to "".
+ # If you are using the same file for client and server rendering, having this set probably does
+ # not affect performance.
+ config.server_bundle_js_file = "webpack-bundle.js"
+
+ # If you are using the ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
+ # with rspec then this controls what npm command is run
+ # to automatically refresh your webpack assets on every test run.
+ config.npm_build_test_command = "npm run build:test"
+
+ # This configures the script to run to build the production assets by webpack. Set this to nil
+ # if you don't want react_on_rails building this file for you.
+ config.npm_build_production_command = "npm run build:production"
+
+ ################################################################################
+ # CLIENT RENDERING OPTIONS
+ # Below options can be overriden by passing options to the react_on_rails
+ # `render_component` view helper method.
+ ################################################################################
+ # default is false
+ config.prerender = false
+
+ # default is true for development, off otherwise
+ config.trace = Rails.env.development?
+
+ ################################################################################
+ # SERVER RENDERING OPTIONS
+ ################################################################################
+ # If set to true, this forces Rails to reload the server bundle if it is modified
+ config.development_mode = Rails.env.development?
+
+ # For server rendering. This can be set to false so that server side messages are discarded.
+ # Default is true. Be cautious about turning this off.
+ config.replay_console = true
+
+ # Default is true. Logs server rendering messages to Rails.logger.info
+ config.logging_on_server = true
+
+ config.raise_on_prerender_error = false # change to true to raise exception on server if the JS code throws
+
+ # Server rendering only (not for render_component helper)
+ # You can configure your pool of JS virtual machines and specify where it should load code:
+ # On MRI, use `therubyracer` for the best performance
+ # (see [discussion](https://github.com/reactjs/react-rails/pull/290))
+ # On MRI, you'll get a deadlock with `pool_size` > 1
+ # If you're using JRuby, you can increase `pool_size` to have real multi-threaded rendering.
+ config.server_renderer_pool_size = 1 # increase if you're on JRuby
+ config.server_renderer_timeout = 20 # seconds
+
+ ################################################################################
+ # MISCELLANEOUS OPTIONS
+ ################################################################################
+
+ # Default is false, enable if your content security policy doesn't include `style-src: 'unsafe-inline'`
+ config.skip_display_none = false
+
+ # The server render method - either ExecJS or NodeJS
+ config.server_render_method = "ExecJS"
+
+ # Client js uses assets not digested by rails.
+ # For any asset matching this regex, non-digested symlink will be created
+ # To disable symlinks set this parameter to nil.
+ config.symlink_non_digested_assets_regex = /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg)/
+
+ ActiveSupport.on_load(:action_view) do
+ include ReactOnRailsHelper
+ end
+end
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..a07191333
--- /dev/null
+++ b/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "react-webpack-rails-tutorial",
+ "version": "0.0.1",
+ "engines": {
+ "node": "5.10.0",
+ "npm": "3.5.0"
+ },
+ "scripts": {
+ "postinstall": "cd client && npm install",
+ "rails-server": "foreman start -f Procfile.dev",
+ "test": "rspec"
+ }
+}
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 4b7930bbf..e43a6330e 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -20,6 +20,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)
+
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: