Back to list
sjnims

action-cable-realtime

by sjnims

Claude Code plugin marketplace: Rails 8 expert development team with DHH as coordinator and 7 specialists covering routing, Active Record, Hotwire, Action Cable, testing, deployment, and performance

1🍴 0📅 Jan 19, 2026

SKILL.md


name: action-cable-realtime description: This skill should be used when the user asks about Action Cable, WebSockets, real-time features, channels, broadcasting, subscriptions, chat applications, live notifications, presence indicators, collaborative editing, server push, pub/sub patterns, Solid Cable, or streaming updates. Also use when discussing real-time architecture, WebSocket deployment, or alternatives like polling and Server-Sent Events. Examples:

Action Cable & Real-Time: WebSockets in Rails

Overview

Action Cable integrates WebSockets with Rails, enabling real-time features like chat, notifications, and live updates. It provides both server-side Ruby and client-side JavaScript frameworks that work together seamlessly.

Action Cable enables:

  • Real-time chat and messaging
  • Live notifications
  • Presence indicators (who's online)
  • Collaborative editing
  • Live dashboard updates
  • Real-time feeds

Rails 8 introduces Solid Cable, which replaces Redis with database-backed pub/sub, simplifying deployment.

Core Concepts

WebSockets vs HTTP

HTTP (Request-Response):

Client → Request → Server
Client ← Response ← Server
[Connection closes]

WebSocket (Persistent Connection):

Client ↔ Persistent Connection ↔ Server
[Messages flow both directions]
[Connection stays open]

Benefits:

  • Bi-directional communication
  • Low latency (no connection overhead)
  • Server can push to clients
  • Efficient for real-time features

Action Cable Architecture

Browser (Consumer) → WebSocket → Connection → Channels → Broadcasters

Connection: WebSocket connection (one per browser tab) Channel: Logical grouping (like a controller) Subscription: Consumer subscribed to a channel Broadcasting: Message sent to all channel subscribers

Channels

Channels are like controllers for WebSockets.

Creating a Channel

rails generate channel Chat

Generates:

# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  def subscribed
    # Called when consumer subscribes
    stream_from "chat_#{params[:room_id]}"
  end

  def unsubscribed
    # Called when consumer unsubscribes (cleanup)
  end

  def speak(data)
    # Called when consumer sends message
    Message.create!(
      content: data['message'],
      user: current_user,
      room_id: params[:room_id]
    )
  end
end
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"

consumer.subscriptions.create(
  { channel: "ChatChannel", room_id: 123 },
  {
    connected() {
      console.log("Connected to chat")
    },

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

    received(data) {
      // Handle broadcasted message
      const messagesContainer = document.getElementById("messages")
      messagesContainer.insertAdjacentHTML("beforeend", data.html)
    },

    speak(message) {
      this.perform("speak", { message: message })
    }
  }
)

Streaming

Subscribe to broadcasts:

class ChatChannel < ApplicationCable::Channel
  def subscribed
    # Stream from named channel
    stream_from "chat_room_#{params[:room_id]}"

    # Stream for current user only
    stream_for current_user

    # Stop streaming
    stop_all_streams
  end
end

Channel Callbacks

Channels support lifecycle callbacks and exception handling:

class ChatChannel < ApplicationCable::Channel
  before_subscribe :verify_access
  after_subscribe :log_subscription

  rescue_from UnauthorizedError, with: :handle_unauthorized

  def subscribed
    stream_from "chat_#{params[:room_id]}"
  end

  private

  def verify_access
    reject unless current_user.can_access?(params[:room_id])
  end

  def log_subscription
    Rails.logger.info "User #{current_user.id} subscribed to chat"
  end

  def handle_unauthorized(exception)
    # Handle error, optionally broadcast error message
    transmit(error: "Unauthorized access")
  end
end

Available callbacks: before_subscribe, after_subscribe, before_unsubscribe, after_unsubscribe.

Broadcasting

Send messages to channel subscribers:

From Models

class Message < ApplicationRecord
  belongs_to :room
  belongs_to :user

  after_create_commit :broadcast_message

  private

  def broadcast_message
    ActionCable.server.broadcast(
      "chat_room_#{room_id}",
      {
        html: ApplicationController.render(
          partial: 'messages/message',
          locals: { message: self }
        ),
        user: user.name
      }
    )
  end
end

From Controllers

class MessagesController < ApplicationController
  def create
    @message = Message.new(message_params)

    if @message.save
      # Broadcast happens in model callback
      head :ok
    else
      render json: { errors: @message.errors }, status: :unprocessable_entity
    end
  end
end

From Jobs

class NotificationBroadcastJob < ApplicationJob
  queue_as :default

  def perform(notification)
    ActionCable.server.broadcast(
      "notifications_#{notification.user_id}",
      { html: render_notification(notification) }
    )
  end

  private

  def render_notification(notification)
    ApplicationController.render(
      partial: 'notifications/notification',
      locals: { notification: notification }
    )
  end
end

Authentication

Authenticate WebSocket connections:

# 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
      if verified_user = User.find_by(id: cookies.encrypted[:user_id])
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

Now current_user is available in all channels.

Rails 8: Solid Cable

Solid Cable replaces Redis with database-backed pub/sub.

Configuration

# config/cable.yml
production:
  adapter: solid_cable
  polling_interval: 0.1  # 100ms
  message_retention: 1.day
# No Redis needed!
# Solid Cable stores messages in database
# Polls for new messages every 100ms

Migration

rails solid_cable:install
rails db:migrate

Creates solid_cable_messages table.

Trade-offs

Solid Cable:

  • Simpler deployment (no Redis)
  • One database to manage
  • ~100-150ms latency
  • Sufficient for chat, notifications, updates

Redis:

  • Lower latency (<50ms)
  • Higher throughput
  • Better for millions of connections

For most apps, Solid Cable is simpler and sufficient.

See references/solid-cable.md for details.

Common Patterns

Chat Application

# Channel
class ChatChannel < ApplicationCable::Channel
  def subscribed
    stream_from "chat_#{params[:room_id]}"
  end

  def speak(data)
    Message.create!(
      room_id: params[:room_id],
      user: current_user,
      content: data['message']
    )
  end
end

# Model
class Message < ApplicationRecord
  after_create_commit -> {
    broadcast_append_to "chat_#{room_id}",
      target: "messages",
      partial: "messages/message",
      locals: { message: self }
  }
end

Live Notifications

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

# Broadcast to specific user
NotificationChannel.broadcast_to(user, {
  html: render_notification(notification)
})

Presence (Who's Online)

class AppearanceChannel < ApplicationCable::Channel
  def subscribed
    stream_from "appearances"
    broadcast_appearance("online")
  end

  def unsubscribed
    broadcast_appearance("offline")
  end

  def appear
    broadcast_appearance("online")
  end

  def away
    broadcast_appearance("away")
  end

  private

  def broadcast_appearance(status)
    ActionCable.server.broadcast("appearances", {
      user_id: current_user.id,
      username: current_user.name,
      status: status
    })
  end
end

See references/action-cable-patterns.md for more examples.

Testing

Channel Tests

require "test_helper"

class ChatChannelTest < ActionCable::Channel::TestCase
  test "subscribes to stream" do
    subscribe room_id: 42

    assert subscription.confirmed?
    assert_has_stream "chat_42"
  end

  test "receives broadcasts" do
    subscribe room_id: 42

    perform :speak, message: "Hello!"

    assert_broadcast_on("chat_42", message: "Hello!")
  end
end

Integration Tests

test "broadcasts message to chat" do
  room = rooms(:general)

  assert_broadcasts("chat_#{room.id}", 1) do
    Message.create!(room: room, user: users(:alice), content: "Hello!")
  end
end

Deployment Considerations

Standalone Server

For high-traffic apps, run Action Cable on separate servers:

# config/cable.yml
production:
  adapter: solid_cable
  url: wss://cable.example.com

Scaling

Action Cable scales horizontally:

  • Multiple app servers
  • Shared pub/sub (Solid Cable database or Redis)
  • Load balancer with WebSocket support

Monitoring

Track connection count, message throughput, latency, and errors.

Alternatives to Action Cable

Server-Sent Events (SSE)

One-way server → client:

def stream
  response.headers['Content-Type'] = 'text/event-stream'
  response.headers['Cache-Control'] = 'no-cache'

  sse = SSE.new(response.stream)
  sse.write({ message: "Hello" })
ensure
  sse.close
end

Use when: Only server → client needed (notifications, live feeds)

Polling

Regular HTTP requests:

setInterval(() => {
  fetch('/api/notifications/latest')
    .then(r => r.json())
    .then(data => updateUI(data))
}, 5000)

Use when: Simple updates, low frequency, broad browser support needed

Further Reading

For deeper exploration:

  • references/action-cable-patterns.md: Chat, notifications, presence patterns
  • references/solid-cable.md: Database-backed pub/sub in Rails 8

For code examples (in examples/):

  • chat-channel.rb: Real-time chat with typing indicators
  • notifications-channel.rb: User-specific push notifications
  • presence-channel.rb: Online status tracking
  • dashboard-channel.rb: Admin dashboard with live stats
  • multi-room-chat.rb: Multiple rooms with private messages
  • collaborative-editing.rb: Document editing with cursors
  • live-feed.rb: Real-time feed updates

Summary

Action Cable provides:

  • WebSocket integration with Rails
  • Channels for logical grouping
  • Broadcasting to connected clients
  • Authentication via connection identification
  • Solid Cable for database-backed pub/sub (Rails 8)
  • Turbo Streams integration for HTML updates

Master Action Cable and you'll build real-time features that feel magical.

Score

Total Score

75/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon