Introduzione

Se hai mai desiderato che la tua app Rails sembrasse viva, dovresti dare un'occhiata a Action Cable. È il modo integrato di Rails per gestire i WebSockets, il che significa che il tuo server e il browser possono rimanere connessi e comunicare in tempo reale. Niente più aggiornamenti di pagina, niente polling ingombrante: gli aggiornamenti appaiono istantaneamente.

In questa guida, costruiremo una divertente demo: un contatore di traffico live. Quando qualcuno visita il tuo sito, registreremo quella visita nel database, incrementeremo alcuni contatori in memoria e trasmetteremo i numeri aggiornati a tutti online. Chiudi la scheda? Il conteggio degli utenti online scende immediatamente. È semplice, ma mostra la magia in tempo reale che Action Cable porta a Rails 7.

Passo 1: Il Modello di Visita

Per prima cosa, creiamo un modello per registrare le visite.

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

Passo 2: Creare una Connessione Personalizzata

Rails ti fornisce già due classi base in app/channels/application_cable/:
- channel.rb → la classe base per tutti i tuoi canali.
- connection.rb → la classe base per tutte le tue connessioni WebSocket.

Di solito non le modifichiamo. Invece, le estendiamo creando la nostra classe di connessione all'interno di app/channels/. Questo mantiene i valori predefiniti puliti e mette la nostra logica in un posto chiaro.

# 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

Passo 3: Contatori in Memoria

Utilizzeremo contatori atomici per rendere le cose veloci. All'avvio, prendiamo il totale delle visite dal DB e iniziamo gli utenti online da zero.

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

Passo 4: Il Canale

Qui è dove avviene il divertimento: registriamo la visita, aggiorniamo i contatori e trasmettiamo.

# app/channels/traffic_channel.rb
class TrafficChannel < ApplicationCable::Channel
  def subscribed
    # registra la visita
    Visit.create!(session_id: connection.session_id,
                  ip: connection.ip,
                  user_agent: connection.user_agent)

    # aggiorna i contatori
    $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

Passo 5: Il Lato JavaScript

Quando esegui bin/rails generate channel Traffic, Rails crea due file: il canale Ruby e il file di sottoscrizione JavaScript. Per impostazione predefinita, il file JS importa consumer.js, che Rails ha anche impostato per te quando è stata generata l'app.

// 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
  }
})

Il file consumer.js viene generato da Rails quando crei una nuova app. Imposta la connessione WebSocket globale a /cable e appare così:

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

export default createConsumer()

Passo 6: La Vista

<h1>🚦 Traffico Live</h1>
<p>Attualmente online: <span id="online-count">0</span></p>
<p>Visite totali: <span id="total-count"><%= Visit.count %></span></p>

Passo 7: Rotte

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

Passo 8: Provalo

Esegui la tua app con bin/dev. Apri il tuo sito in alcune schede. Il conteggio degli utenti online aumenta con ognuna di esse e scende quando le chiudi. Le visite totali continuano a salire. È super soddisfacente da vedere! 

Note per la Produzione

- Hai più worker Puma? Usa Redis per i contatori: la memoria non si sincronizza tra i processi.
- Vuoi tracciare utenti reali invece delle sessioni? Aggiungi current_user nella connessione.
- Memorizzare le visite nel DB significa che puoi eseguire analisi in seguito, come traffico giornaliero o suddivisioni per browser.

E questo è tutto: un contatore di traffico live in tempo reale in Rails 7 con Action Cable. In meno di 100 righe di codice, hai aggiornamenti istantanei e un assaggio di cosa possono fare i WebSockets. Questa stessa configurazione funziona per notifiche, dashboard e tutti i tipi di funzionalità in tempo reale. Una volta che l'hai provato, vorrai spargerlo ovunque.