シェアフル AdventCalendar2018 16 日目の記事です。
はじめに
つい最近RubyとVue.jsについて勉強し始めた身なのですが、勉強がてらチャットアプリを作ってみました。
Rails5にはActionCableというWebsocket通信を標準でサポートしてくれるものがあるようなので、今回はこちらとNuxt.jsを用いて双方向通信を行いました。
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モードで作成します。
$ rails new websocket-test --api
$ bundle install --path vendor/bundle
DBはこの記事のチャットでは扱わないため、DB設定は済んでいる前提で進めていきます。
$ rails g channel post
これで、追加された下記ファイルを変更していきます。
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サーバを立てたりすることができます。
$ npm i -g ws
インストールが終われば、rails上で rails s
を行い、 railsのサーバ上に対して接続してみます。
$ wscat -c ws://<railsサーバのIP>:<railsを起動しているポート>/cable
接続がうまくいけば、以下のようになり、pingが数秒おきに返ってきます。
connected (press CTRL+C to quit)
< {"type":"welcome"}
< {"type":"ping","message":154653xxxx}
< {"type":"ping","message":154653xxxx}
この際、Vagrantなどでrailsサーバを立てていると、 Request origin not allowed
といったエラーが返ってきますので、下記を追記します。
ActionCable.server.config.disable_request_forgery_protection = true
フロントエンドの実装
こちらはNuxt.jsで作成したのですが、最初に create-nuxt-app
を使った以外はnuxtに関係あるものは使っていないので、だいたいVueです。
cssフレームワークとしては、Buefyを利用しています。
画面に関しては最後にソースをあげていますのでそちらをご確認ください。
記事内では、websocketに関連する部分のみをpickして説明していきます。
まずは、フロントからActionCableへの接続を行うために必要なライブラリをインストールします。
$ npm i actioncable --save
画面側は省き、script部分のみ説明していきます。
まずは、全体です。
<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文で出しているだけです。
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メソッドの実行を行なっています。
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などで制御する方が良さそうです。
今回のソースは以下に置いているので、よかったら参考にしてください。
参考
Rails5のAction Cableの試食
Vagrantでサーバー立ててActionCableを試してる時にRequest origin not allowedがでて辛い
今回、上記の情報を参考にさせていただきました。誠にありがとうございました。
追記(2019/2/17)
コメントいただきrails側のソースも追記しました。記事の作業しかしてないので、ご参考程度でお願い致します。誤り等ありましたらお伝えください。
ソース