Introducción
Si alguna vez has querido que tu aplicación Rails se sienta viva, deberías probar Action Cable. Es la forma integrada de Rails de manejar WebSockets, lo que básicamente significa que tu servidor y el navegador pueden mantenerse conectados y comunicarse en tiempo real. No más recargas de página, no más sondeos torpes: las actualizaciones aparecen instantáneamente.
En esta guía, construiremos una pequeña demostración divertida: un contador de tráfico en vivo. Cuando alguien llega a tu sitio, registraremos esa visita en la base de datos, incrementaremos algunos contadores en memoria y transmitiremos los números actualizados a todos los que estén en línea. ¿Cierras la pestaña? El conteo de usuarios en línea baja de inmediato. Es simple, pero muestra la magia en tiempo real que Action Cable trae a Rails 7.
Paso 1: El Modelo de Visita
Primero, hagamos un modelo para registrar visitas.
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
Paso 2: Creando una Conexión Personalizada
Rails ya te proporciona dos clases base en app/channels/application_cable/:
- channel.rb → la clase base para todos tus canales.
- connection.rb → la clase base para todas tus conexiones WebSocket.
No solemos editar esas. En su lugar, las extendemos creando nuestra propia clase de conexión dentro de app/channels/. Esto mantiene los valores predeterminados limpios y coloca nuestra lógica en un lugar claro.
# 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
Paso 3: Contadores en Memoria
Usaremos contadores atómicos para hacer las cosas rápidas. Al iniciar, obtenemos el total de visitas de la base de datos y comenzamos con los usuarios en línea en cero.
# config/initializers/traffic_counter.rb
$online_users = Concurrent::AtomicFixnum.new(0)
$total_visits = Concurrent::AtomicFixnum.new(Visit.count)
Paso 4: El Canal
Aquí es donde sucede la diversión: registramos la visita, actualizamos los contadores y transmitimos.
# app/channels/traffic_channel.rb
class TrafficChannel < ApplicationCable::Channel
def subscribed
# registrar visita
Visit.create!(session_id: connection.session_id,
ip: connection.ip,
user_agent: connection.user_agent)
# actualizar contadores
$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
Paso 5: El Lado de JavaScript
Cuando ejecutas bin/rails generate channel Traffic, Rails crea dos archivos: el canal de Ruby y el archivo de suscripción de JavaScript. Por defecto, el archivo JS importa consumer.js, que Rails también configuró por ti cuando se generó la aplicación.
// 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
}
})
El archivo consumer.js es generado por Rails cuando creas una nueva aplicación. Configura la conexión global de WebSocket a /cable y se ve así:
// app/javascript/channels/consumer.js
import { createConsumer } from "@rails/actioncable"
export default createConsumer()
Paso 6: La Vista
<h1>🚦 Tráfico en Vivo</h1>
<p>Actualmente en línea: <span id="online-count">0</span></p>
<p>Total de visitas: <span id="total-count"><%= Visit.count %></span></p>
Paso 7: Rutas
# config/routes.rb
Rails.application.routes.draw do
root "home#index"
mount ActionCable.server => "/cable"
end
Paso 8: Pruébalo
Ejecuta tu aplicación con bin/dev. Abre tu sitio en varias pestañas. El contador de usuarios en línea aumenta con cada una, y baja cuando las cierras. El total de visitas sigue subiendo. ¡Es muy satisfactorio de ver!
Notas para Producción
- ¿Múltiples trabajadores de Puma? Usa Redis para los contadores: la memoria no se sincroniza entre procesos.
- ¿Quieres rastrear usuarios reales en lugar de sesiones? Agrega current_user en la conexión.
- Almacenar visitas en la base de datos significa que puedes realizar análisis más tarde, como tráfico diario o desgloses por navegador.
Y eso es todo: un contador de tráfico en vivo en tiempo real en Rails 7 con Action Cable. En menos de 100 líneas de código, tienes actualizaciones instantáneas y un vistazo a lo que los WebSockets pueden hacer. Esta misma configuración funciona para notificaciones, paneles y todo tipo de características en tiempo real. Una vez que lo pruebes, querrás esparcirlo por todas partes.