はじめに

Railsアプリを生き生きとさせたいと思ったことがあるなら、Action Cableをチェックしてみてください。これはRailsに組み込まれたWebSocketsの機能で、サーバーとブラウザがリアルタイムで接続し、会話できるようになります。ページのリフレッシュも不要で、煩わしいポーリングもなし — 更新がすぐに表示されます。

このガイドでは、楽しいデモを作成します: ライブトラフィックカウンターです。誰かがあなたのサイトにアクセスすると、その訪問をデータベースに記録し、メモリ内のカウンターを増やし、オンラインの全員に更新された数をブロードキャストします。タブを閉じると、オンラインユーザー数がすぐに減ります。シンプルですが、Action CableがRails 7にもたらすリアルタイムの魔法を示しています。

ステップ 1: 訪問モデル

まずは、訪問を記録するモデルを作成しましょう。

bin/rails g model Visit session_id:string ip:string user_agent:string
bin/rails db:migrate
# app/models/visit.rb
class Visit < ApplicationRecord
end

ステップ 2: カスタム接続の作成

Railsはすでにapp/channels/application_cable/に2つの基本クラスを提供しています:
- channel.rb → すべてのチャンネルの基本クラス。
- connection.rb → すべてのWebSocket接続の基本クラス。

通常はこれらを編集しません。その代わりに、app/channels/内に独自の接続クラスを作成して拡張します。これにより、デフォルトをクリーンに保ち、ロジックを明確な場所に置くことができます。

# app/channels/traffic_connection.rb
module ApplicationCable
  class TrafficConnection < Connection
    identified_by :session_id, :ip, :user_agent

    def connect
      self.session_id = request.session.id
      self.ip         = request.ip
      self.user_agent = request.user_agent
    end
  end
end

ステップ 3: メモリ内のカウンター

高速化のために、アトミックカウンターを使用します。起動時にデータベースから総訪問数を取得し、オンラインユーザーをゼロから開始します。

# config/initializers/traffic_counter.rb
$online_users = Concurrent::AtomicFixnum.new(0)
$total_visits = Concurrent::AtomicFixnum.new(Visit.count)

ステップ 4: チャンネル

ここで楽しいことが起こります — 訪問を記録し、カウンターを更新し、ブロードキャストします。

# app/channels/traffic_channel.rb
class TrafficChannel < ApplicationCable::Channel
  def subscribed
    # 訪問を記録
    Visit.create!(session_id: connection.session_id,
                  ip: connection.ip,
                  user_agent: connection.user_agent)

    # カウンターを更新
    $online_users.increment
    $total_visits.increment

    stream_from "traffic_channel"
    broadcast_counts
  end

  def unsubscribed
    $online_users.decrement
    broadcast_counts
  end

  private

  def broadcast_counts
    ActionCable.server.broadcast("traffic_channel", {
      online: $online_users.value,
      total:  $total_visits.value
    })
  end
end

ステップ 5: JavaScript側

コマンドbin/rails generate channel Trafficを実行すると、Railsは2つのファイルを作成します: RubyのチャンネルとJavaScriptのサブスクリプションファイルです。デフォルトでは、JSファイルはconsumer.jsをインポートします。これはアプリが生成されたときにRailsが設定したものです。

// app/javascript/channels/traffic_channel.js
import consumer from "./consumer"

consumer.subscriptions.create("TrafficChannel", {
  received(data) {
    document.querySelector("#online-count").textContent = data.online
    document.querySelector("#total-count").textContent = data.total
  }
})

consumer.jsファイルは、アプリを新しく作成したときにRailsによって生成されます。これは、グローバルなWebSocket接続を/cableに設定し、以下のようになります:

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

export default createConsumer()

ステップ 6: ビュー

<h1>🚦 ライブトラフィック</h1>
<p>現在オンライン: <span id="online-count">0</span></p>
<p>総訪問数: <span id="total-count"><%= Visit.count %></span></p>

ステップ 7: ルーティング

# config/routes.rb
Rails.application.routes.draw do
  root "home#index"
  mount ActionCable.server => "/cable"
end

ステップ 8: 試してみよう

アプリをbin/devで実行します。いくつかのタブでサイトを開いてみてください。オンラインユーザー数はそれぞれのタブで増加し、閉じると減ります。総訪問数はどんどん増えていきます。見るのがとても満足感があります! 

本番環境向けの注意事項

- 複数のPumaワーカーを使用していますか?カウンターにはRedisを使用してください — メモリはプロセス間で同期されません。
- セッションではなく実際のユーザーを追跡したいですか?接続にcurrent_userを追加してください。
- 訪問をデータベースに保存することで、後で日次トラフィックやブラウザの内訳などの分析を実行できます。

これで完了です — Rails 7でAction Cableを使ったリアルタイムのライブトラフィックカウンターができました。100行未満のコードで、即座に更新され、WebSocketsができることの一端を体験できます。この設定は通知、ダッシュボード、さまざまなリアルタイム機能にも利用できます。一度試してみれば、どこにでも取り入れたくなるでしょう。