Rails(ActionCable)とNuxt.jsでチャットアプリを作ってみる

[シェアフル AdventCalendar2018](https://adventar.org/calendars/3523) 16 日目の記事です。

# はじめに
つい最近RubyとVue.jsについて勉強し始めた身なのですが、勉強がてらチャットアプリを作ってみました。
Rails5にはActionCableというWebsocket通信を標準でサポートしてくれるものがあるようなので、今回はこちらとNuxt.jsを用いて双方向通信を行いました。

完成品としては、以下のようなものになります。
![作成するチャットアプリイメージ](https://s19.aconvert.com/convert/p3r68-cdx67/906pu-m7jni.gif)

# ActionCableとは
ActionCableは、RailsにおいてWebsocketによる双方向通信をシームレスに行うことを可能にする技術であり、Rails5から組み込まれました。
Websocketというのは、HTTP上で双方向通信を可能にするプロトコルのことで、これを用いることでチャットアプリ等のリアルタイムなアプリケーション開発を行うことができます。

# 構成

- サーバサイド

Ruby  2.6.0
Rails 5.2.2

- フロントエンド

Nuxt.js 2.0.0
Node 8.11.0
npm 5.6.0

## サーバサイドの実装
今回、サーバサイドはまずは、RailsをAPIモードで作成します。

```bash
$ rails new websocket-test --api
$ bundle install --path vendor/bundle
```

DBはこの記事のチャットでは扱わないため、DB設定は済んでいる前提で進めていきます。

```bash
$ rails g channel post
```

これで、追加された下記ファイルを変更していきます。

```ruby:app/channels/post_channel.rb
class PostChannel < ApplicationCable::Channel
  def subscribed
    # stream_from "some_channel"
    stream_from 'post:message'
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def post(data)
    PostChannel.broadcast_to('message', data['message'])
  end
end
```

上記を作成した段階で、一度websocketとの疎通が通るかの確認を行ってみます。

フロント側を作成するサーバ上で `wscat` というものを用いて疎通確認を行います。
wscatは、Node.js上でwebsocketを扱う際に使う `ws` というモジュールに同梱されており、コマンドライン上でwebsocketサーバへの接続を行ったり、websocketサーバを立てたりすることができます。

```bash
$ npm i -g ws
```

インストールが終われば、rails上で `rails s` を行い、 railsのサーバ上に対して接続してみます。

```bash
$ wscat -c ws://<railsサーバのIP>:<railsを起動しているポート>/cable
```

接続がうまくいけば、以下のようになり、pingが数秒おきに返ってきます。

```shell
connected (press CTRL+C to quit)
< {"type":"welcome"}
< {"type":"ping","message":154653xxxx}
< {"type":"ping","message":154653xxxx}
```

この際、Vagrantなどでrailsサーバを立てていると、 `Request origin not allowed` といったエラーが返ってきますので、下記を追記します。

```ruby:config/initializers/action_cable.rb
ActionCable.server.config.disable_request_forgery_protection = true
```

## フロントエンドの実装
こちらはNuxt.jsで作成したのですが、最初に `create-nuxt-app` を使った以外はnuxtに関係あるものは使っていないので、だいたいVueです。
cssフレームワークとしては、Buefyを利用しています。

画面に関しては最後にソースをあげていますのでそちらをご確認ください。
記事内では、websocketに関連する部分のみをpickして説明していきます。

まずは、フロントからActionCableへの接続を行うために必要なライブラリをインストールします。

```bash
$ npm i actioncable --save
```

画面側は省き、script部分のみ説明していきます。
まずは、全体です。

```vuejs
<script>
import ActionCable from 'actioncable';

export default {
  data() {
    return {
      messageText: ""
    };
  },
  created() {
    const cable = ActionCable.createConsumer('ws://<railsサーバIP>:<起動port>/cable');

    this.messageChannel = cable.subscriptions.create( "PostChannel",{
      received: (data) => {
        this.$store.commit("addMessage", data);
      },
    })
  },
  methods: {
    handleClick: function() {
      //ActionCable PostChannelにおけるpostメソッドを実行する
      this.messageChannel.perform('post', { 
        message: this.messageText, 
      });
      // console.log(this.$store.state.messages);
      //メッセージ追加後にテキストボックスを空にする
      this.messageText = ""
    }
  }
};
</script>
```

まずは、created()内でwebsocketのサーバを指定し、先ほどRailsで作成したPostChannelに対してのsubscriptionを作成します。
コールバックとして、 `connected` , `disconnected` , `received` を作成すると、それぞれチャンネルへの接続時、接続の解除時、データ受信時に実行されます。

今回はreceivedのみを作成し、届いたメッセージをstoreに追加しています。
メッセージの画面出力としては、このようにしてstoreに追加された文字列の配列をfor文で出しているだけです。

```javascript
  created() {
    const cable = ActionCable.createConsumer('ws://<railsサーバIP>:<起動port>/cable');

    this.messageChannel = cable.subscriptions.create( "PostChannel",{
      received: (data) => {
        this.$store.commit("addMessage", data);
      },
    })
  },
```

次に、テキストボックスからメッセージを入力し、送信ボタンを押した際のメソッドを見てみます。
createdで作成したthis.messageChannelを用いて、PostChannelにおけるpostメソッドの実行を行なっています。

```javascript
  methods: {
    handleClick: function() {
      //ActionCable PostChannelにおけるpostメソッドを実行する
      this.messageChannel.perform('post', { 
        message: this.messageText, 
      });
      //メッセージ追加後にテキストボックスを空にする
      this.messageText = ""
    }
  }
```

このようにすることで、RailsのActionCableで作成したChannelに対して接続することができ、Nuxt.js上から双方向通信ができるようになります。

# 終わりに
Vue.jsもRailsもほぼほぼ素人だったのですが、ActionCableのおかげでチャットアプリを簡単に作成することができました。
今回はNuxt自体の便利さはほとんど活かせていないので、これを発展させて何かアプリを作れたらなと思います。

今回、nuxt.jsとRailsは別サーバ上で作成したため、 `initializers` の設定が必要になりました。参考サイトにも記載があったのですが、実際には同じサーバ上におき、nginxなどで制御する方が良さそうです。
今回のソースは以下に置いているので、よかったら参考にしてください。

[今回のソース](https://github.com/daitasu/actioncable-nuxt-sample)

## 参考
[Rails5のAction Cableの試食](https://qiita.com/sasurai_usagi3/items/75a30cc0d2d0faa9dfe8)
[Vagrantでサーバー立ててActionCableを試してる時にRequest origin not allowedがでて辛い](https://qiita.com/togana/items/bc75aa9d0bd1f7de357b)

今回、上記の情報を参考にさせていただきました。誠にありがとうございました。

### 追記(2019/2/17)
コメントいただきrails側のソースも追記しました。記事の作業しかしてないので、ご参考程度でお願い致します。誤り等ありましたらお伝えください。
[ソース](https://github.com/daitasu/actioncable-nuxt-sample_back)