[Rails] ActionCableを活用してSocket通信する

2024/05/23

Rails
ActionCable

こんにちは。

今回は、RailsでActionCableを使ってSocket通信をする方法について紹介します。

ActionCableとは

ActionCableは、Railsに組み込まれているWebSocketフレームワークです。WebSocketを使うことで、リアルタイムでの双方向通信を実現することができます。

ActionCableを使うことで、チャットアプリやリアルタイムな通知機能などを実装することができます。

今回は、ActionCableを使ってWebhookで特定のユーザーのデータが更新された際に、そのユーザーでログインしている状態の画面があった場合に、リアルタイムでデータを更新する処理を実装してみます。

やっていき

ApplicationCable::Connectionに認証情報を追加する

まず、ApplicationCable::Connectionに認証情報を追加します。

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

    private

    def find_verified_user
      User.find(request.session[:user_id])
    rescue ActiveRecord::RecordNotFound
      reject_unauthorized_connection
    end
  end
end

request.session[:user_id]にユーザーIDが入っていることを前提にしています。

deviseを使っている場合は、request.env['warden'].user.idなどで取得することもできます。

チャンネルを作成する

次に、チャンネルを作成します。

app/channels/user_channel.rb

class UserChannel < ApplicationCable::Channel
  def subscribed
    stream_for current_user
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end
end

stream_for current_userで、ユーザーごとのチャンネルを作成しています。

subscribeする

次に、subscribeする処理を実装します。

app/javascript/application.js
import "channels"
app/javascript/channels/consumer.js
import { createConsumer } from "@rails/actioncable"

export default createConsumer()
app/javascript/channels/user_channel.js
import consumer from "channels/consumer"

consumer.subscriptions.create({ channel: "UserChannel" }, {
  connected() {
    console.log("Connected to the UserChannel")
  },

  disconnected() {
    console.log("Disconnected from the UserChannel")
  },

  received(data) {
    const userTarget = document.querySelector(`[data-user-id="${data.user.id}"]`)
    if (userTarget) {
      userTarget.innerHTML = data.user.name
    }
  }
})
app/views/users/show.html.erb
<div data-user-id="<%= @user.id %>">
  <%= @user.name %>
</div>

ブロードキャストする

最後に、ブロードキャストする処理を実装します。

app/models/user.rb
class User < ApplicationRecord
  after_update_commit { broadcast_update }

  def broadcast_update
    UserChannel.broadcast_to(self, user: self)
  end
end

after_update_commitで、ユーザーが更新された際にbroadcast_updateメソッドを呼び出しています。

UserChannel.broadcast_to(self, user: self)で、ユーギザーごとのチャンネルにデータをブロードキャストしています。

これで、特定のユーザーのデータが更新された際に、そのユーザーでログインしている状態の画面があった場合に、リアルタイムでデータを更新する処理が実装できました。

注意点として、この実装の検証のためにbin/rails cでコンソールを開いて、ユーザーを更新してもリアルタイムでデータが更新されない場合があります。その場合は、config/environments/development.rbに以下の設定を追加してください。

config/environments/development.rb
config.action_cable.disable_request_forgery_protection = true

これで、リアルタイムでデータが更新されるようになります。

今回はこのあたりで。