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