Rails
Node.js
reactjs
redux

Node.js X Rails Engine

More than 3 years have passed since last update.

8日目 に続けての投稿です。

今回は React and ReduxRails Engine でクロスさせる開発を紹介します。

Rails Engine は Rails に Webアプリケーションの部品として機能を追加できる仕組みです。

React and Redux もコンポーネントを組合せて機能を作ります。


  • Rails Engine: Web Application Component Framework

  • React and Redux: Web Client Component Framework

React and Redux の コンポーネント を Rails Engine でさらに コンポーネント にする仕組みだと

関心事の分離して設計できて コンポーネント指向 が捗ります。

サンプルはこちらです。


Environment


  • Ruby: v2.2.3

  • Rails: v4.2.5

  • React: v0.14.0

  • Redux: v3.0.4


Rails

まずは、Rails に Virtual DOM をレンダリングするためのサーバーサイドを構築します。


Setup



  • rails plugin new のコマンドで Rails Engine を作成します。


  • --mountable のオプションで Rails の部品として開発できます。


  • foreman は後で Node.js と Rails を起動するのに利用します。

rails plugin new rails_engine_react_example --mountable \

--skip-javascript \
--skip-bundle
$ cd rails_engine_react_example/
$ echo "gem 'foreman', require: false" >> Gemfile
$ bundle


Making



  • engine.rbgenerate コマンドのデフォルトを記述します。


lib/rails_engine_react_example/engine.rb

module RailsEngineReactExample

class Engine < ::Rails::Engine
isolate_namespace RailsEngineReactExample

config.generators do |g|
g.javascripts false
g.stylesheets false
g.helper false
end
end
end




  • rails generate controller コマンドで controllerview を作ります。

$ rails generate controller main index



  • routes.rbmain_controllerroot に設定します。


  • *path は Tips の Redux Router で利用しています。


config/routes.rb

RailsEngineReactExample::Engine.routes.draw do

root 'main#index'
get '*path', to: 'main#index'
end



  • index.html.erb に Virtual DOM がレンダリングされる id="main" を記述します。


app/views/rails_engine_react_example/main/index.html.erb

<div id="main"></div>




  • application_helper.rbstylesheetjavascript の参照先を切替えるメソッドを追加します。


app/helpers/rails_engine_react_example/application_helper.rb

module RailsEngineReactExample

module ApplicationHelper
def switch_stylesheet_link_tag(path)
path = "http://localhost:4000/assets/stylesheets/#{path}.css" if ENV["DEBUG"]
stylesheet_link_tag(path)
end

def switch_javascript_include_tag(path)
path = "http://localhost:4000/assets/javascripts/#{path}.js" if ENV["DEBUG"]
javascript_include_tag(path)
end
end
end




  • application.html.erb に先ほどのヘルパーのメソッドを記述します。


app/views/layouts/rails_engine_react_example/application.html.erb

<!DOCTYPE html>

<html>
<head>
<title>RailsEngineReactExample</title>
<%= switch_stylesheet_link_tag "rails_engine_react_example/application" %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
<%= switch_javascript_include_tag "rails_engine_react_example/application" %>
</body>
</html>


Run



  • rails server コマンドで起動して http://localhost:3000/rails_engine_react_example にアクセスします。

engine

真っ白ですね... 安心してください、 Virtual DOM はこれからです。


Node.js

次は、Node.js で Virtual DOM をレンダリングするためのフロントエンドを構築します。


Setup


  • Node.js は /private/static に設置します。

  • yo redux のコマンドで React and Redux が構築できます。


    • Node.js は 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



  • jsapp にリネームします。(csscomponents で記述する流れですね。)


  • package.json ファイルはルートディレクトリにコピーします。

$ mv js app

$ cd ../../
$ cp ./private/static/package.json .
$ echo /node_modules >> .gitignore
$ npm install



  • package.jsonscripts のコマンドに cd ./private/static && を付加します。


package.json

"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"
},



  • webpack.config.js を Rails のヘルパーのメソッドで参照できるように調整します。


private/static/webpack.config.js

entry: [

'webpack-dev-server/client?http://localhost:4000',
'webpack/hot/only-dev-server',
'./app/index.js'
],
output: {
path: __dirname + '/assets/',
publicPath: 'http://localhost:4000/assets/',
filename: 'javascripts/rails_engine_react_example/application.js',
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
devFlagPlugin,
new ExtractTextPlugin('stylesheets/rails_engine_react_example/application.css')
],



  • webpack.production.js を Rails の app/assets/ に出力するように調整します。


private/static/webpack.production.js

entry: [

'./app/index.js'
],
output: {
path: __dirname + '../../../app/assets/',
publicPath: '/static/',
filename: 'javascripts/rails_engine_react_example/application.js',
},
plugins: [
new webpack.NoErrorsPlugin(),
devFlagPlugin,
new ExtractTextPlugin('stylesheets/rails_engine_react_example/application.css')
],


Run


  • Rails Engine は Rails にマウントしている状態の dummy が提供されています。


  • foreman の Procfile に Rails と Node.js の起動コマンドを追加します。


  • DEBUG=true foreman start コマンドで起動します。

$ cd test/dummy/

$ echo "web: rails server" >> Procfile
$ echo "pack: npm start" >> Procfile
$ DEBUG=true foreman start

http://localhost:3000/rails_engine_react_example にアクセスすると Redux のサンプルが表示されます。

development



  • npm run build コマンドで application.jsapplication.css が生成されます。


  • rails server コマンドで起動します。

$ npm run build

$ rails server

http://localhost:3000/rails_engine_react_example にアクセスすると Redux のサンプルが表示されます。

production

これで Node.js X Rails Engine の基本の準備ができました。

ViewReact に任せて RailsView にあまり関心を持たないほうがいいですね。


Tips


Redux Router

RailsRouter もあまり関心を持たないほうが...


Setup



  • react-routerredux-simple-router を利用します。

$ npm install react-router --save

$ npm install redux-simple-router --save


Making

コードは一部ですが Router や Link を記述します。



  • App.js に Router を記述します。


private/static/app/containers/App.js

<Router history={history}>

<Route path="/" component={Home}>
<Route path="parent" component={Parent}>
<Route path="child" component={Child} />
<Route path="child/:id" component={Child} />
</Route>
</Route>
</Router>



  • Home.js に Link を記述します。


private/static/app/components/Home.js

<dev>

<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/parent">parent</Link></li>
<li><Link to="/parent/child">child</Link></li>
<li><Link to="/parent/child/1">child 1</Link></li>
<li><Link to="/parent/child/2">child 2</Link></li>
<li><Link to="/parent/child/3">child 3</Link></li>
</ul>
</dev>


Run

Link のエリアをクリックとサーバーサイドにはリクエストが送信されませんが、レンダリングされます。

URLを入力するとサーバーサイドにはリクエストが送信されますが、Rails に *path を設定しているので Virtual DOM がレンダリングされます。

redux-router

Redux は状態を管理するメカニズムがあります。ページ遷移も状態として管理されます。

RailsMVC2 ですが、 View に状態が管理されるので MVC ですね。

Enjoy Node.js X Rails Engine