Action Cable + Flux + React でリアルタイムチャットデモを作ってみた


## 成果物

デモ（Heroku） http://fluxchat.takeyu-web.com/


ソースコード https://github.com/takeyuweb/rails-fluxchat-example

## キーワード

- Rails 4.2
- ActionCable
- ES6
- React
- Flux (Alt)

## Action Cable?

まだコード全部は読んでないのですが、WebSocketを使ったリアルタイム通信を手軽に実装するためのサーバ・クライアントフレームワーク実装だと感じました。（違ってたらご指摘下さい！）

Railsらしく規約に従ってサーバ・クライアント側で所定のクラスを作っておけば、実際の接続処理や接続の監視・再接続、受け取ったデータのハンドリングなど細かいことはActionCableが良い感じにしてくれるっぽい。

なおAction CableのサーバはこれまでのWebサーバ（`rails s`なやつとか）とは別に立ち上げる必要があります。（もちろんポートも空けます）

```ruby
# cable/config.ru
require ::File.expand_path('../../config/environment',  __FILE__)
Rails.application.eager_load!

require 'action_cable/process/logging'

run ActionCable.server

```
で28080番ポートでWebSocketサーバを起動（※ポートは決まってないのでなんでもいいです。ここでは公式のサンプルに合わせました）
```bash
$ bundle exec puma -p 28080 cable/config.ru
```
HTTPサーバはもちろん別
```bash
$ bundle exec rails s
```

なおRails5の新機能ではありますがRails4でも使えます。

```ruby
gem 'rails', '4.2.4'
gem 'actioncable', github: 'rails/actioncable'
gem 'puma'
```


## ポイント

### FluxとWebScoket(ActionCable)

Fluxについてまだ勉強不足なので理解違いがあるかもしれませんが、図の「Web API Utils」にあてはめて実装してみました。

<img src="https://raw.githubusercontent.com/facebook/flux/master/docs/img/flux-diagram-white-background.png:image=https://raw.githubusercontent.com/facebook/flux/master/docs/img/flux-diagram-white-background.png" />

https://github.com/facebook/flux


#### 送信

`View (React Component) -> Action Creator -> Channel (ActionCable Subscription)`

Viewで発生したイベントからAction Creatorを経由してActionCableのChannelへメッセージを投げます。


#### 受信

`Channel (ActionCable Subscription) -> Action Creator -> Dispatcher -> Store -> View (React Component)`

Channelへ入ってきたデータをAction Creatorに渡し、後のことは任せます。

### Action Cable

#### 認証

WebSocketではCookieが使えるので、署名済みCookieを使うのが楽です。

#### Action Cableで送信するデータ

[公式のサンプルアプリ](https://github.com/rails/actioncable-examples)ではRails5の新しいRendererでアクション外で部分テンプレートを処理しHTMLを送りつける感じですが、Action Cable自体はなんでも送れるので、クライアント側でそれにあわせた処理をすれば良いです。

今回のサンプルでは単に送り側（Ruby/Rails）でモデルインスタンスを`.to_json`して、受け取り側（JavaScript/ActionCable.Subscription）で`JSON.parse`してみました。


#### コントローラやバックグラウンド処理からクライアントに通知を送る

`ActionCable.server.broadcast`であとはよしなに。時間がかかることもあるので、Active Jobと組み合わせて非同期で実行するのがベター。

```ruby
class MessageRelayJob < ApplicationJob
  def perform(message)
    if message.to.present?
      # 宛先が指定されている場合はそこと自分に送る
      ActionCable.server.broadcast "chat_activity:#{message.uuid}", message.to_json
      ActionCable.server.broadcast "chat_activity:#{message.to}", message.to_json
    else
      # そうでなければ全体へ
      ActionCable.server.broadcast 'chat_activity', message.to_json
    end
  end
end
```

実際には全体へのブロードキャストは宛先が死ぬほど増えそうなので、チャットであればチャットルームIDなどで絞り込む必要がありそう。

### require('react')みたいなことがしたい、あとES6で書きたい

俺の最近のRailsのJS開発環境を教えてやる
http://qiita.com/kozo002/items/a276569e85395fce801e

こちらの記事が参考になりました。

## 楽しい

そんな感じでとりあえずやってみたAction CableとFluxですがなかなか楽しいです。

実運用ではコネクション張りっぱなしによるリソースの消費がどんな具合かとか、負荷テストはどうやるの？とか、サーバは何使う？とか、経路上のたとえばELBがどうなっているかとか、Android標準ブラウザの対応が4.4以降で割と非対応端末多そうな問題とかいろいろ考えることはありますが、ぜひ使っていきたいと思っています。

## 気になるセキュリティ

未調査。

セキュリティ回りで参考になりそうな記事メモ。

[IPA セキュア・プログラミング講座：Webアプリケーション編](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/web09.html)

[Webアプリ開発者のためのHTML5セキュリティ入門](http://www.slideshare.net/muneakinishimura/webhtml5-31749532)

[Herokuで作るWebSocketアプリケーション](http://shunjikonishi.github.io/slides/websocket/index.html#/)

[WebSocket通信のメリットを考える](http://blog.flect.co.jp/labo/2014/04/websocket-8124.html)

> 近年のJavaScriptプログラミングでは処理全体をdocument#readyのクロージャで括るのが一般的ですが、その場合仮にXSSの脆弱性があったとしても外部から接続済みのWebSocketインスタンスにアクセスすることは不可能です。

DHHのサンプルや今回のデモチャットでは`window.App.cable.connection.webSocket`でWebSocketインスタンスがとれてしまうので、そのあたり取り回しを考えた方がよいかも。