AgentSkillsCN

action-cable

在 Rails 应用程序中,利用 WebSockets 设置 ActionCable,实现实时功能。当用户提及 WebSockets、ActionCable、频道、广播机制,或通过电缆使用 Turbo Streams 时,可启用此技能,用于实现实时更新、即时通知、变更广播等功能。

SKILL.md
--- frontmatter
name: action-cable
description: Setup and use ActionCable for real-time features in Rails applications using WebSockets. Use when implementing real-time updates, live notifications, broadcasting changes, or when the user mentions WebSockets, ActionCable, channels, broadcasting, or Turbo Streams over cable.

ActionCable Setup

Overview

ActionCable integrates WebSockets with Rails to enable real-time features. This guide covers the basic setup in this application.

Core Components

1. Connection (app/channels/application_cable/connection.rb)

The connection authenticates and authorizes the WebSocket connection using Devise/Warden.

ruby
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 = env['warden'].user
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

2. Channel (app/channels/application_cable/channel.rb)

Base channel class for all application channels.

ruby
module ApplicationCable
  class Channel < ActionCable::Channel::Base
  end
end

3. JavaScript Setup (app/javascript/initializers/actioncable.js)

Initialize ActionCable consumer on the client side.

javascript
import * as ActionCable from '@rails/actioncable'

ActionCable.logger.enabled = false

Broadcasting Updates

With Turbo Streams (recommended)

Subscribe to a stream in views:

slim
= turbo_stream_from record
= turbo_stream_from [record, "channel_name"]

Broadcast Turbo Stream updates from controllers or background jobs:

ruby
# Append content to a target
Turbo::StreamsChannel.broadcast_append_to(
  [record, "channel_name"],
  target: "dom_id",
  partial: "path/to/partial",
  locals: { record: record }
)

# Replace content
Turbo::StreamsChannel.broadcast_replace_to(
  [record, "channel_name"],
  target: "dom_id",
  partial: "path/to/partial",
  locals: { record: record }
)

# Remove element
Turbo::StreamsChannel.broadcast_remove_to(
  [record, "channel_name"],
  target: "dom_id"
)

Example: Organization-scoped notifications

View:

slim
= turbo_stream_from current_organization, "timers"

#notifications

Controller:

ruby
def start_timer
  # ... create timer logic ...

  Turbo::StreamsChannel.broadcast_append_to(
    [current_organization, "timers"],
    target: "notifications",
    partial: "timers/notification",
    locals: { timer: @timer }
  )
end

Configuration

Cable URL (config/cable.yml)

yaml
development:
  adapter: async

test:
  adapter: test

production:
  adapter: solid_cable
  connects_to:
    database:
      writing: cable
  polling_interval: 0.1.seconds
  message_retention: 1.day

Routes (config/routes.rb)

ActionCable is automatically mounted at /cable:

ruby
Rails.application.routes.draw do
  mount ActionCable.server => '/cable'
end

Debugging

Enable logging in development:

javascript
// app/javascript/initializers/actioncable.js
ActionCable.logger.enabled = true

Check connection status:

javascript
const subscription = consumer.subscriptions.create("ExampleChannel", {
  connected() {
    console.log("Connected to ExampleChannel")
  }
})

console.log(subscription)

Security Considerations

  • Always authenticate connections in ApplicationCable::Connection
  • Validate params in channel subscriptions
  • Use stream_for instead of stream_from when possible for automatic scoping
  • Never trust client-side data without validation
  • Consider rate limiting for channels that accept client messages