Phoenix Framework (Elixir)で React.jsを動かしてみる、を実践してみた。

さて**Phoenix Framework**ですが…
Elixir, Phoenix あたりの記事を探しているとTwitterでこんなの見つけました。
[**Phoenix Framework (Elixir)で React.jsを動かしてみる** - Misoca開発ブログ http://tech.misoca.jp/entry/2015/05/21/130456 …](https://twitter.com/eitoball/status/601532390511546369)
*eitoballさん、どうもありがとうございます*

Reactもちょっと前の仕事でいじりかけていたのでこれ幸いと試してみました…しかし、実際に手を動かしてみるといくつか*ハマリポイント*があったので補足しつつ自分用にメモします。

------

## 予め入れておくもの

#### **Elixir** (当たり前)

[**Elixirで試しに何か書いてみる(その1)**](http://qiita.com/HirofumiTamori/items/602d9b326cb4816c2fe6])
及びそこから先のリンク先である
[**Installing Elixir**](http://elixir-lang.org/install.html)
を参考にしてインストールしてください。同時にElixirの本体(?)であるErlang/OTPとビルドツールmixもインストールされます。
ここはまずハマリポイントはないと思います(あるとしたら古い**Erlang/OTP**が入ってしまっているとかでしょうかね)。

#### **Phoenix Framework**

これもないと話になりません。[**Phoenixの本家サイト**](http://www.phoenixframework.org/)から[**Get started with Phoenix**](http://www.phoenixframework.org/v0.13.1/docs/up-and-running)をクリックしてドキュメントを読みつつ**Phoenix Framework**をインストールしてください。
ただし、ドキュメントのバージョンによって内容が変わりそうなのでトップページから**Get started with Phoenix**をクリックするほうがいいと思います。
今回は2015年5月26日時点の最新、v0.13.1に沿って簡単に解説します。

1.　パッケージマネージャ**Hex**をインストールする

```bash
$ mix local.hex
```

2.　Phoenix installerをダウンロードしてmixでインストールする

```bash
$ mix archive.install https://github.com/phoenixframework/phoenix/releases/download/v0.13.1/phoenix_new-0.13.1.ez
```
(やはりバージョンがしょっちゅう変わると思われるので要注意。実は0.12.0では今回のプログラムは**phoenix_html**がインストールされなくて(手動で修正は可能らしい)[失敗します](https://github.com/phoenixframework/phoenix/issues/886)。)

#### **node.js**

**Phoenix**はいくつかのnodeベースのコマンドを利用しているのでnode.js(及びnpm)をインストールしておきます。
node.jsについてはインストール、設定ともにあちこちに解説記事があるので代表的なところだけリンクを書きます。
[**node.js本家**](https://nodejs.org/)
[**node.js日本ユーザグループ**](http://nodejs.jp/)
個人的にはバージョン切り替えに[**nodebrew**](https://github.com/hokaccha/nodebrew)を使っているので…
[ここ](http://bisque.hatenablog.jp/entry/2015/05/01/node.js%E3%81%AE%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89%E3%83%A1%E3%83%A2)とか[ここ](http://www.nilab.info/z3/20150502_02_nodebrew_and_nodejs_in_debian.html)を参考にしてインストールしてください。手元ではv0.10.38,v0.12.4で動作確認できました。**io.js**でも行けそうですが試してません。

#### **bower**

[**bower**](http://bower.io/)はフロントエンド(JavaScript)用のパッケージマネージャです。Phoenixはbowerとビルドツール[**brunch**](http://brunch.io/)に依存していますが、brunchはインストールしてくれるのにbowerはインストールしてくれないので自分でインストールします[^1]。

```bash
$ npm install -g bower
```
でインストール完了。
[^1]: それにしてもJavaScript用のパッケージマネージャやビルドツールって一体いくつあるんでしょうね。


#### **inotify-tools**

ファイル変更検知ツールです。これを入れておかないと[**inotifywait**](http://blog.tokumaru.org/2013/06/web.html)がなくて失敗します[^2]。
[^2]: Phoenix Frameworkも最近のWeb Frameworkの流行のように、ファイルを置き換えると自動的にリビルド、リロードされるようになっています。inotifywaitはその検知に使っているようです。

Ubuntu, Debianは

```bash
$ sudo apt-get install inotify-tools
```

でインストールできます。

## **mix**でプロジェクトを作成する

1.　プロジェクトのディレクトリを構成する[^3]。

```bash
$ mix phoenix.new react_phoenix
```

[^3]: 元記事、ここのディレクトリ名が間違っていました。コピペで実行するとあとで失敗します。
あとは質問に**Y**を押していって

```text
We are all set! Run your Phoenix application:

    $ cd react_phoenix
    $ mix phoenix.server

You can also run it inside IEx (Interactive Elixir) as:

    $ iex -S mix phoenix.server
```

が表示されればOKです。```*running npm install```のところで少し時間がかかります。

2.　**jquery**と**react**のbowerパッケージのインストール

**ここは元記事と手順を変えました。**
まず1.で作られたディレクトリに移動します。

```bash
$ cd react_phoenix
```

続いてbowerを使う準備をします。

```bash
$ bower init
```

初めてbowerを起動した時だけ

```text 
? May bower anonymously report usage statistics to improve the tool over time? (Y/n)
```

と聞いてきますので、情報収集手伝ってあげるよ！という人は**Y**(または**enter**)を押してください。
あとは今回は全部**enter**でよいです(実はぼくもbowerに全然慣れてないので細かいところがわからなかったのです)。

最後

```text

{
  name: 'react_phoenix',
  version: '0.0.0',
  license: 'MIT',
  ignore: [
    '**/.*',
    'node_modules',
    'bower_components',
    'test',
    'tests'
  ]
}

? Looks good? (Y/n)
```

まで表示されたら**Y**(または**Enter**)を押して完了。

次に今、生成された**bower.json**を修正します。
**生成されたbower.json**

```text
{
  "name": "react_phoenix",
  "version": "0.0.0",
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ]
}
```

に依存ライブラリ、**jqueryとreactの記述**を加えます。

```text
{
  "name": "react_phoenix",
  "version": "0.0.0",
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "jquery": "~2.1.4",
    "react": "~0.13.3"
   }
}
```

そしてjqueryとreactをbowerからインストールします。

```bash
$ bower install
```

```text 
bower not-cached    git://github.com/facebook/react-bower.git#~0.13.3
bower resolve       git://github.com/facebook/react-bower.git#~0.13.3
bower not-cached    git://github.com/jquery/jquery.git#~2.1.4
bower resolve       git://github.com/jquery/jquery.git#~2.1.4
bower download      https://github.com/facebook/react-bower/archive/v0.13.3.tar.gz
bower download      https://github.com/jquery/jquery/archive/2.1.4.tar.gz
bower extract       react#~0.13.3 archive.tar.gz
bower extract       jquery#~2.1.4 archive.tar.gz
bower resolved      git://github.com/facebook/react-bower.git#0.13.3
bower resolved      git://github.com/jquery/jquery.git#2.1.4
bower install       react#0.13.3
bower install       jquery#2.1.4
```

インストール完了。

### ディレクトリの状態

さて、ここでディレクトリがどうなっているか確認しておきましょう。

```text
your_host:~/react_phoenix$ ls
bower.json        _build  deps  mix.exs   node_modules  priv       test
brunch-config.js  config  lib   mix.lock  package.json  README.md  web
```
ここの**web**以下にファイルを書き足していきます。
**web**以下はこんな感じになっています。

```text 
web
├── channels
├── controllers
│   └── page_controller.ex
├── models
├── router.ex
├── static
│   ├── css
│   │   └── app.scss
│   ├── js
│   │   └── app.js
│   └── vendor
│       └── phoenix.js
├── templates
│   ├── layout
│   │   └── application.html.eex
│   └── page
│       └── index.html.eex
├── views
│   ├── error_view.ex
│   ├── layout_view.ex
│   └── page_view.ex
└── web.ex
```
### 表示用のコントローラとビューを追加する(reactは後から追加する)

また[**misocaブログ**](http://tech.misoca.jp/entry/2015/05/21/130456)に戻りましょう。

1.　コントローラを記述
``` web/controllers/hello_world_controller.ex```を以下のように作成します。

```ex
defmodule ReactPhoenix.HelloWorldController do
  use ReactPhoenix.Web, :controller

  plug :action

  def index(conn, _params) do
    render conn, "index.html"
  end
end
```

`:controller`　はElixirのatomです。またReactPhoenix.**W**ebでWは大文字です。最初typoして間違い探しに手間取りました。

2.　ビューを記述
同じように ```web/views/hello_world_view.ex``` を以下のように作成します。

```ex
defmodule ReactPhoenix.HelloWorldView do
  use ReactPhoenix.Web, :view
end
```

3.　テンプレートを作成

まずディレクトリをひとつ作成。

```bash
$ mkdir web/templates/hello_world
```

そして以下の内容でファイル ``` web/templates/hello_world/index.html.eex```(拡張子がhtml.**eex**)を作成します。

```html 
<h2>HelloWorld#index</h2>
```

4.　ルーター(router)の設定

Webのディレクトリと参照先を管理しているrouterを設定します。このへんはrailsとかsinatraとかと同じですね。web/router.exに1行追加して

```ex
defmodule ReactPhoenix.Router do
  use ReactPhoenix.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", ReactPhoenix do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
    # 次の1行を追加する
    get "/hello_world", HelloWorldController, :index
  end

  # Other scopes may use custom stacks.
  # scope "/api", ReactPhoenix do
  #   pipe_through :api
  # end
end
```

5.　サーバーを実行する

```bash
$ iex -S mix phoenix.server
```

すると初回だけいろいろビルドします…(その後はもし依存関係が変わっていたり、内容が更新されていた場合に必要な物だけビルドされます)

```text 
Erlang/OTP 17 [erts-6.4] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]

==> poolboy (compile)
Compiled src/poolboy_worker.erl
Compiled src/poolboy_sup.erl
Compiled src/poolboy.erl
==> decimal
Compiled lib/decimal.ex
Generated decimal app
==> poison
Compiled lib/poison.ex
Compiled lib/poison/decoder.ex
Compiled lib/poison/parser.ex
Compiled lib/poison/encoder.ex
Generated poison app
==> ranch (compile)
Compiled src/ranch_transport.erl
Compiled src/ranch_acceptors_sup.erl
Compiled src/ranch.erl
Compiled src/ranch_app.erl
Compiled src/ranch_protocol.erl
...
(中略)
...
Generated react_phoenix app
[info] Running ReactPhoenix.Endpoint with Cowboy on port 4000 (http)
Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> [BABEL] Note: The code generator has deoptimised the styling of "bower_components/jquery/dist/jquery.js" as it exceeds the max of "100KB".
[info] GET /
[info] Processing by ReactPhoenix.PageController.index/2
  Parameters: %{"format" => "html"}
  Pipelines: [:browser]
...
```
まで来たらサーバー起動完了です。
``http://localhost:4000`` などで実際にアクセスしてみましょう。

![react_phoenix1.png](https://qiita-image-store.s3.amazonaws.com/0/46294/d88352db-f71e-8b27-7bea-372be0b6b331.png)

↑このように表示が出たらとりあえず成功です。

### Reactを使ってみる

1.　テンプレートファイルを書き換える。

テンプレートファイル ```web/templates/hello_world/index.html.eex```を次のように書き換えます。

```html
<div id="hello_world"></div>
````

2.　JavaScriptファイルをReactで書き換える

`web/static/js/app.js`は元々は

```js
import {Socket} from "phoenix"

// let socket = new Socket("/ws")
// socket.connect()
// let chan = socket.chan("topic:subtopic", {})
// chan.join().receive("ok", chan => {
//   console.log("Success!")
// })

let App = {
}

export default App
```
のようなスケルトンですが、これを

```js
import {Socket} from "phoenix"

// let socket = new Socket("/ws")
// socket.connect()
// let chan = socket.chan("topic:subtopic", {})
// chan.join().receive("ok", chan => {
//   console.log("Success!")
// })

$(function() {
  React.render(
    <h1 className="jumbotron">Hello from React!!!</h1>,
    document.getElementById('hello_world')
  );
});

let App = {
}

export default App
```

のように書き換えます。

サーバーを再起動するか、サーバーを立ち上げたままでも編集によってファイルの内容が変わるとそれを検知して自動的にリビルドが開始されますが、**初回だけものすごく時間がかかります**(非力なマシンだと**1分以上**かかる)。最初これに気づかずに何度もリロードしたりサーバーを止めたりしてジタバタしてしまいました。

最終的に

![react_phoenix2.png](https://qiita-image-store.s3.amazonaws.com/0/46294/2859fcb3-ec05-428d-c383-1d1ce8abc0d9.png)

↑この画面が出たら成功です。
あとはReact関連のところをいろいろ書き換えて試してみることができます。