LoginSignup
7

More than 3 years have passed since last update.

posted at

updated at

Action Cableについて調べたことまとめ

最近プロジェクトで、Action Cableを使う機会があり、いろいろ調べたので、備忘のため書いておこうと思います。
(そして、Qiita初投稿イェイ!!)

Ruby:2.4.1
Rails:5.1.2

はじめに

Action Cableって何?

  • WebSocketとRailsのその他の部分をシームレスに統合したもの

  • 一度、HTTPのコネクションが確立すると、websocket通信で、サーバー・クライアントの両方からリクエストが遅れる状態を簡単に実装できちゃいます。

てな感じです。

サーバー側のコンポーネント

app/channels/application_cable/connection.rb
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名}の依存はないので、ご安心を・・・

ここで、ユーザーが特定できた時点で、コネクションが確立します。

クライアント側のコンポーネント

メッセージ送信はこちらを参考にしてください。

app/assets/javascripts/channels/comments.coffee
  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で取得し、条件を追記してみてもいいかもです。

app/assets/javascripts/channels/comments.coffee
    # 省略

    followCurrentMessage: ->
      if messageId = $("#message-list").data('message-id')
        @perform 'follow', message_id: messageId
      else
        @perform 'unfollow'

ここでは、templateでdata-message-id="XX"を取得して、代入できれば、
channel購読ができるようになっています。

サーバー・クライアント間のやりとり

クライアント側

app/assets/javascripts/channels/comments.coffee
    # 省略

    followCurrentMessage: ->
      if messageId = $("#message-list").data('message-id')
        @perform 'follow', message_id: messageId
      else
        @perform 'unfollow'

サーバー側

channels/XXXX.coffeeで定義したメソッドをここで指定します

actioncable-examples/app/channels/comments_channel.rb
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メソッドもクライアント側で設定した引数をつかって、サーバーサイドで利用できます。

app/jobs/comment_relay_job.rb
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されたコメントがブロードキャストされます。

app/models/comments.rb
  after_commit { CommentRelayJob.perform_later(self) }

インフラ的な要素であったり、railsの仕様がうまく理解できてませんが、上記で問題ないと思います。

以上です。
参考サイト:
Rails Guide
actioncable-example

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
7