最近プロジェクトで、Action Cableを使う機会があり、いろいろ調べたので、備忘のため書いておこうと思います。
(そして、Qiita初投稿イェイ!!)
Ruby:2.4.1
Rails:5.1.2
はじめに
Action Cableって何?
-
WebSocketとRailsのその他の部分をシームレスに統合したもの
-
一度、HTTPのコネクションが確立すると、websocket通信で、サーバー・クライアントの両方からリクエストが遅れる状態を簡単に実装できちゃいます。
てな感じです。
サーバー側のコンポーネント
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
protected
def find_verified_user
if current_user = User.find_by(id: cookies.signed[:user_id])
current_user
else
reject_unauthorized_connection
end
end
end
end
クライアントとサーバー側の関係を確立させる基礎が上で確立されます。
同時に、current_userが初期状態では使えませんでしたが、これで使えるようになります。
deviseを使用されている方のため、current_#{model名}の依存はないので、ご安心を・・・
ここで、ユーザーが特定できた時点で、コネクションが確立します。
クライアント側のコンポーネント
メッセージ送信はこちらを参考にしてください。
connected: ->
# FIXME: While we wait for cable subscriptions to always be finalized before sending messages
setTimeout =>
@followCurrentMessage()
, 1000
followCurrentMessage: ->
# channel購読の条件
connectedで、購読するchannelを定義します。
デフォルトでは、何も書かれていない状態になるので、connection.rbで接続が確立した時点で、
channelのコンシューマーになります。
特定のページで、channel購読をする(例えば、メッセージ画面でのみしたい)であれば、
templateに任意のパラメーターを設定して、jsで取得し、条件を追記してみてもいいかもです。
# 省略
followCurrentMessage: ->
if messageId = $("#message-list").data('message-id')
@perform 'follow', message_id: messageId
else
@perform 'unfollow'
ここでは、templateでdata-message-id="XX"を取得して、代入できれば、
channel購読ができるようになっています。
サーバー・クライアント間のやりとり
クライアント側
# 省略
followCurrentMessage: ->
if messageId = $("#message-list").data('message-id')
@perform 'follow', message_id: messageId
else
@perform 'unfollow'
サーバー側
channels/XXXX.coffeeで定義したメソッドをここで指定します
class CommentsChannel < ApplicationCable::Channel
def follow(data)
stop_all_streams
stream_from "messages:#{data['message_id'].to_i}:comments"
end
def unfollow
stop_all_streams
end
end
__stream_from__に注目です。ここで、任意のchannelが設定できます。
followメソッドもクライアント側で設定した引数をつかって、サーバーサイドで利用できます。
class CommentRelayJob < ApplicationJob
def perform(comment)
ActionCable.server.broadcast "messages:#{comment.message_id}:comments",
comment: CommentsController.render(partial: 'comments/comment', locals: { comment: comment })
end
end
設定されたコメントを指定のチャンネルを購読しているユーザーにブロードキャストをすれば、完成です。
modelに以下のコードを追加すれば、createされたコメントがブロードキャストされます。
after_commit { CommentRelayJob.perform_later(self) }
インフラ的な要素であったり、railsの仕様がうまく理解できてませんが、上記で問題ないと思います。
以上です。
参考サイト:
Rails Guide
actioncable-example