Ruby on Rails 5のAction Cableでリアルタイム更新を実装
Ruby on Rails 5から目玉機能として「Action Cable」が追加されております。
Action Cableは、Websocketがより扱いやすくなり、channelの管理などができるようになりました。
Railsでリアルタイム更新を実装する機会がありましたので、作業をまとめてみます。
Action CableのChannelを作成
まずは、リアルタイム更新を行うためのチャンネルを作成します。
このチャンネルに入っているユーザーの間でリアルタイム更新が行われるようになります。
Railsでチャンネルを作成する方法はコントローラーを作成する方法とさほど変わりはありません。
rails g channel [チャンネル名]
例として、ここでは「room」というチャンネルを作ってみましょう。
その場合、「app/channels/room_channel.rb」が作成されているはずです。
class RoomChannel < ApplicationCable::Channel
def subscribed
# stream_from "some_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
コメントのとおりですが、「subscribed」にはクライアントが接続された時に実行され、「unsubscribed」は解除された時に実行されます。
「subscribed」ではチャンネル名(ストリーム)を宣言し、このチャンネル間でデータを受信・送信が行われます。
CoffeeScriptによるフロント側の処理
チャンネルを作成すると、「app/assets/javascripts/channels/room.coffee」が作成されているはずです。
App.room, = App.cable.subscriptions.create "RoomChannel",
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
# Called when there's incoming data on the websocket for this channel
「connected」が繋がったときに実行され、「disconnected」は、解除された時に実行されます。
「received」が、受信した時に実行されるコールバック関数となっており、リアルタイム更新の重要関数となります。
エンド側のチャンネルを設定してみる
まずは、エンド側のチャンネルの設定を修正してみます。
class RoomChannel < ApplicationCable::Channel
def subscribed
# stream_from "some_channel"
stream_from "room_channel_#{params['roomid']}"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def event(data)
ActionCable.server.broadcast "room_channel_#{data['roomid']}", messege: data['messege']
end
end
チャンネル名は「room_channel_」の次にルームID(roomid)が来るようにしましょう。
そして、「event」関数を作成し、チャンネルへroomidを返す処理を作成します。
フロント側の処理
フロント側でチャンネルのコネクションを書きます。
#jQuery(document).ready ->
roomid= $('input:hidden[name="roomid"]').val()
App.room= App.cable.subscriptions.create { channel: "RoomChannel", roomid: roomid},
#connected: ->
# Called when the subscription is ready for use on the server
#disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
# Called when there's incoming data on the websocket for this channel
console.log(data)
event: ->
@perform 'event', message:$('input:hidden[name="messege"]').val()
先頭行のコメントアウトは、「Turbolinks」の有無に合わせて変えて下さい。
前提として、roomidを格納しているnameが「roomid」のアイテムが存在してる場合を想定して作成しております。
また、送信するアイテムとして「messege」も定義します。
App.roomには、「subscribed」のチャンネルへparams「roomid」を渡す処理となっています。
「event」は、room_channel.rb側のevent関数を呼んでおり、「broadcast_to」により「room_channnel_[roomid]」へ「roomid」を送っています。
「received」では、broadcast_toで送れられた「roomid」を受信しているので、コンソール上で文字の表示を行っております。
ここまで作業すると、ブラウザのコンソールで以下のコマンドを叩くとリアルタイム更新を確認することができます。
App.room.event()
まとめ
今回は単純に「messege」を送受信しているだけですが、ユーザーIDとメッセージに置き換えるだけで簡単にリアルタイム更新によるチャットを作成することができます。
もちろん、チャットルームに入室していないユーザーは更新されないので、必要最低限の処理で実装することができます。
受信したデータは、jQueryなりvue.jsで簡単に表示できるため、「Active Cable」の登場でリアルタイム更新の敷居が低くなったという印象を受けました。