ActionCable provides seamless Rails integration through its engine, helpers, generators, and configuration system. This enables developers to easily add real-time features to Rails applications using familiar conventions.
Rails engine that automatically configures ActionCable within Rails applications.
class ActionCable::Engine < Rails::Engine
# Engine configuration
config.action_cable = ActiveSupport::OrderedOptions.new
config.action_cable.mount_path = ActionCable::INTERNAL[:default_mount_path]
config.eager_load_namespaces << ActionCable
endThe engine automatically:
/cable)View helpers for integrating ActionCable with Rails applications.
module ActionCable::Helpers::ActionCableHelper
# Generate meta tag with ActionCable URL for JavaScript client
# @return [String] HTML meta tag with action-cable-url
def action_cable_meta_tag
tag "meta", name: "action-cable-url", content: (
ActionCable.server.config.url ||
ActionCable.server.config.mount_path ||
raise("No Action Cable URL configured")
)
end
endUsage Examples:
<!-- In Rails layout file -->
<head>
<%= action_cable_meta_tag %>
<%= javascript_include_tag 'application' %>
</head>
<!-- Generates -->
<meta name="action-cable-url" content="/cable" />
<!-- Or with custom URL -->
<meta name="action-cable-url" content="ws://cable.example.com" />Rails generator for creating ActionCable channels with boilerplate code.
class Rails::Generators::ChannelGenerator < NamedBase
# Generate channel file and related assets
def create_channel_file
end
# Command line arguments
argument :actions, type: :array, default: [], banner: "method method"
# Options
class_option :assets, type: :boolean
endUsage Examples:
# Generate basic channel
rails generate channel Chat
# Generate channel with actions
rails generate channel Chat speak typing
# Generate channel with JavaScript assets
rails generate channel Chat speak --assetsGenerated files:
app/channels/chat_channel.rb - Server-side channel classapp/assets/javascripts/channels/chat.js - Client-side JavaScript (if --assets)app/channels/application_cable/ - Base connection and channel classesThe generator creates channels with this structure:
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
# stream_from "some_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def speak(data)
# Handle the speak action
end
def typing(data)
# Handle the typing action
end
endGenerated base classes for connection and channel:
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
# Connection authentication and identification logic
end
end
# app/channels/application_cable/channel.rb
module ApplicationCable
class Channel < ActionCable::Channel::Base
# Shared channel functionality
end
endConfigure ActionCable server settings in Rails applications.
class ActionCable::Server::Configuration
# Server URL for WebSocket connections
attr_accessor :url
# Mount path for ActionCable server (default: "/cable")
attr_accessor :mount_path
# Allowed request origins for CORS
attr_accessor :allowed_request_origins
# Connection class resolver
attr_accessor :connection_class
# Worker pool size
attr_accessor :worker_pool_size
# Logger instance
attr_accessor :logger
# Subscription adapter configuration
attr_accessor :cable
# Pubsub adapter class
attr_accessor :pubsub_adapter
endConfiguration Examples:
# config/environments/production.rb
Rails.application.configure do
# Configure ActionCable server URL
config.action_cable.url = 'wss://cable.example.com'
# Or use mount path for same-origin connections
config.action_cable.mount_path = '/cable'
# Configure allowed origins
config.action_cable.allowed_request_origins = [
'https://example.com',
/https:\/\/.*\.example\.com/
]
# Set worker pool size
config.action_cable.worker_pool_size = 10
end
# config/environments/development.rb
Rails.application.configure do
# Development allows localhost origins automatically
config.action_cable.mount_path = '/cable'
endConfigure subscription adapters via config/cable.yml:
# config/cable.yml
development:
adapter: async
test:
adapter: test
production:
adapter: redis
url: redis://localhost:6379/1
channel_prefix: myapp_productionAdapter-specific configuration:
# Redis configuration
production:
adapter: redis
url: redis://user:password@redis.example.com:6380/0
channel_prefix: myapp_production
timeout: 1
reconnect_attempts: 3
# PostgreSQL configuration
production:
adapter: postgresql
url: postgresql://user:password@localhost/myapp_production
channel_prefix: myapp_production
# Multiple Redis instances
production:
adapter: redis
url:
- redis://redis1.example.com:6379/0
- redis://redis2.example.com:6379/0
channel_prefix: myapp_productionConfigure custom connection classes:
# config/application.rb
module MyApp
class Application < Rails::Application
# Specify custom connection class
config.action_cable.connection_class = -> { "MyConnection".constantize }
# Or use lambda for lazy loading
config.action_cable.connection_class = -> {
"ApplicationCable::Connection".safe_constantize || ActionCable::Connection::Base
}
end
endActionCable automatically mounts routes when configured:
# Routes are automatically added by the engine
# Equivalent to:
Rails.application.routes.draw do
mount ActionCable.server => '/cable', internal: true
end# config/routes.rb
Rails.application.routes.draw do
# Mount at custom path
mount ActionCable.server => '/websocket'
# Or disable automatic mounting and mount manually
# config.action_cable.mount_path = nil
mount ActionCable.server => '/custom/cable/path'
endConfigure Puma server for ActionCable:
# config/puma.rb
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
# Preload application for ActionCable
preload_app!
# Allow puma to be restarted by `rails restart` command
plugin :tmp_restart
# ActionCable configuration
if ENV['RAILS_ENV'] == 'production'
# Bind to specific interface for ActionCable
bind 'tcp://0.0.0.0:3000'
endConfigure Nginx for ActionCable WebSocket proxying:
# nginx.conf
upstream cable {
server 127.0.0.1:3000;
}
server {
listen 80;
server_name example.com;
location /cable {
proxy_pass http://cable;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Deploy ActionCable with Capistrano:
# config/deploy.rb
set :application, 'myapp'
set :repo_url, 'git@github.com:user/myapp.git'
# Restart ActionCable server after deployment
after 'deploy:restart', 'actioncable:restart'
namespace :actioncable do
task :restart do
on roles(:app) do
execute :touch, release_path.join('tmp/restart.txt')
end
end
endRails provides test helpers for ActionCable:
# Test helper methods available in Rails tests
class ActionCable::TestHelper
# Assert that a broadcast was made
def assert_broadcasts(stream, number)
end
# Assert no broadcasts were made
def assert_no_broadcasts(stream)
end
# Capture broadcasts during block execution
def capture_broadcasts(stream, &block)
end
endTesting Examples:
# test/channels/chat_channel_test.rb
class ChatChannelTest < ActionCable::Channel::TestCase
test "subscribes to stream" do
subscribe room_id: 1
assert subscription.confirmed?
assert_has_stream "chat_1"
end
test "speaks and broadcasts" do
subscribe room_id: 1
assert_broadcasts "chat_1", 1 do
perform :speak, message: "Hello"
end
end
end
# Integration test
class ChatSystemTest < ActionDispatch::SystemTestCase
test "real time messaging" do
visit chat_room_path(1)
assert_broadcasts "chat_1", 1 do
fill_in "message", with: "Hello everyone"
click_button "Send"
end
assert_selector ".message", text: "Hello everyone"
end
endConfigure test adapter for testing:
# config/cable.yml
test:
adapter: test
# Or inline adapter for immediate execution
test:
adapter: inline