JavaScript はWebアプリケーションの特にフロントエンドで大切な存在です。
Rails もWebアプリケーションの開発で便利な機能がたくさんあります。
この2つのメカニズムを使うのに、どちらかによせて混ぜ合わせるのではなく
それぞれに分離して、2つのメカニズムをクロスして使うほうが幸せを感じます。
Ruby on Rails に慣れていない、JavaScript のエンジニア向けに View に
React と Redux を利用した JavaScript と Rails をクロスする手順を記します。
このサンプルはGitHubで公開しています。
Environment
- Ruby: v2.2.3
- Rails: v4.2.5
- Node.js: v4.2.1
- React: v0.14.0
- Redux: v3.0.4
- webpack: v1.12.9
- yo: v1.5.0
- generator-redux: v0.3.1
Rails new
-
rails newのコマンドでWebアプリケーションが簡単に構築できます。
$ rails new rails-react-example
$ cd ./rails-react-example
Yeoman
- Yeoman の Generator を利用して Redux を構築します。
$ npm install --global yo
$ npm install --global generator-redux
-
Phoenix の様に JavaScript を
/private/staticに設置します。 -
yo reduxのコマンドで対話式にWebアプリケーションが構築できます。 -
Rails が
3000ポートを標準で利用するので、ここでは4000を設定します。
$ mkdir -p ./private/static
$ cd ./private/static
$ yo redux
? What's the name of your application? static
? Describe your application in one sentence: ...
? Which port would you like to run on? 4000
? Install dependencies? No
npm
-
package.jsonファイルをルートディレクトリにコピーします。 -
npm installコマンドで JavaScript のライブラリがインストールされます。
$ cd ../../
$ cp ./private/static/package.json .
$ echo /node_modules >> .gitignore
$ npm install
-
package.jsonのscriptsのコマンドにcd ./private/static &&を付加します。
"scripts": {
"start": "cd ./private/static && DEBUG=true node server.js",
"build": "cd ./private/static && webpack -p --config webpack.production.js",
"build-dev": "cd ./private/static && webpack -p"
},
-
npm startのコマンドを実行すると Node.js のwebpack-dev-server(内部はexpress) でWebアプリケーションが起動します。 -
http://localhost:4000/にアクセスすると Welcome Home! のサンプルが表示されます。
view-source:
<html>
<head>
<meta charset="utf-8">
<title>Redux/React App</title>
<link href="./static/app.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="main"></div>
<script src="./static/bundle.js"></script>
</body>
</html>
これは Redux Generator が出力したサンプルで、Update Title をクリックして入力すると Welcome の隣の文字が変更されます。
redux-devtools や react-hot-loader など開発に便利なパッケージがバンドルされています。
まだ Node.js しか使っていないので、次は Rails も使います。
Rails controller
-
rails generate controllerコマンドで React の Virtual DOM を設置する場所を作ります。
$ rails generate controller main index
-
routes.rbに Rails のWebアプリケーションrootを設定します。
Rails.application.routes.draw do
root 'main#index'
end
-
main_helper.rbに Development なら先ほどの Node.js のWebアプリケーションを参照するメソッドを準備します。
module MainHelper
def pack_path(path)
url = ""
url = "http://localhost:4000/static/" if Rails.env.development?
url + path
end
end
-
index.html.erbに Virtual DOM が設置されるid="main"を準備します。 -
app.cssとbundle.jsを MainHelper のpack_pathに設定します。
<div id="main"></div>
<%= stylesheet_link_tag pack_path('app.css') %>
<%= javascript_include_tag pack_path('bundle.js') %>
-
webpack.config.jsのpublicPathに MainHelper で指定した URL を追加します。
output: {
path: __dirname + '/static/',
publicPath: 'http://localhost:4000/static/',
filename: 'bundle.js',
hot: true
},
Development
- Rails と Node.js のWebアプリケーションを実行するのに foreman を利用します。
-
Gemfileに foreman を追加します。
Gemfile:
$ echo "gem 'foreman', require: false" >> Gemfile
-
Procfileに Rails と Node.js のWebアプリケーションの起動コマンドを追加します。
Procfile:
$ echo "web: rails server" >> Procfile
$ echo "pack: npm start" >> Procfile
-
bundleコマンドを Ruby のライブラリをインストールします。 -
foreman startコマンドでWebアプリケーションを起動します。
$ bundle
$ foreman start
http://localhost:3000/ にアクセスすると Redux のサンプルが表示されます。
StyleSheet と JavaScript が http://localhost:4000/ を参照しています。
view-source:
<div id="main"></div>
<link rel="stylesheet" media="screen" href="http://localhost:4000/static/app.css" />
<script src="http://localhost:4000/static/bundle.js"></script>
Rails が出力したわずかな View に Virtual DOM で View を足しているので
見た目や、性能は 4000 ポートの Node.js のWebアプリケーションと同じです。
もちろん redux-devtools や react-hot-loader が使えるので開発が捗ります。
Production
Production では redux-devtools や react-hot-loader は不要なので、webpack でまとめます。
-
Asset Precompile に
app.cssとbundle.jsを追加します。
Rails.application.config.assets.precompile += %w(app.css bundle.js)
-
webpack.production.jsをvendor/assets/に出力されるように編集します。
output: {
path: __dirname + '../../../vendor/assets/',
publicPath: '/static/',
filename: 'javascripts/bundle.js',
},
plugins: [
new webpack.NoErrorsPlugin(),
devFlagPlugin,
new ExtractTextPlugin('stylesheets/app.css')
],
-
npm run buildコマンドでapp.cssとbundle.jsが生成されます。 -
rake assets:precompileコマンドをapp.cssとbundle.jsを Precompile します。 -
rails s -e productionコマンドでWebアプリケーションを起動します。
$ echo /public/assets >> .gitignore
$ echo /vendor/assets >> .gitignore
$ npm run build
$ RAILS_ENV=production rake assets:precompile
$ RAILS_SERVE_STATIC_FILES=o SECRET_KEY_BASE=o rails s -e production
http://localhost:3000/ にアクセスすると Redux のサンプルが表示されます。
StyleSheet と JavaScript が /assets/ を参照しています。
view-source:
<div id="main"></div>
<link rel="stylesheet" media="screen" href="/assets/app-5edef2394b419bead200965dfdae792e54801bdd97f057758a8bc0a72f25685d.css" />
<script src="/assets/bundle-66f3247c874d05e4bf42eb08d4fc66d88bd796bdbca894259cd898078a39bf97.js"></script>
これで JavaScript on Node.js X Ruby on Rails の基本の準備ができました。
View は React に任せて Rails は View にあまり関心を持たないほうがいいですね。
Tips
見た目が寂しなら Material-UIで簡単に装飾できます。
$ npm install material-ui --save
- RaisedButton でボタンをいい感じに仕上げます。
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as HomeActions from '../actions/HomeActions';
import styles from '../../css/app.css';
import RaisedButton from 'material-ui/lib/raised-button';
class Home extends Component {
render() {
const {title, dispatch} = this.props;
const actions = bindActionCreators(HomeActions, dispatch);
return (
<main>
<h1 className={styles.text}>Welcome {title}!</h1>
<RaisedButton label='Update Title' onClick={e => actions.changeTitle(prompt())} />
</main>
);
}
}
export default connect(state => state.Sample)(Home)
JavaScript のライブラリの npm を Ruby のライブラリの Gem に作り替えて使う方法もよいですが
関心事を分離して開発することが幸せと感じます。
Enjoy JavaScript on Node.js X Ruby on Rails



