O básico de Action Cable em Rails 7
Tags: rails, actioncable
Published: 2022-12-18 18:56:50

Se você quer criar um chat em tempo real, ou então criar um joguinho multiplayer, então para fazer em Rails, será necessário usar o Action Cable.

Então, vamos lá.

## O que é Action Cable?

Action Cable integra perfeitamente com WebSockets com o resto da sua aplicação Rails. Isso permite que recursos em tempo real sejam escritos em Ruby no mesmo estilo da sua aplicação, enquanto ainda é performático e escalável. Isso é uma oferecimento full-stack que providencia o framework Javascript do client-side e framework Ruby do server-side.

Agora, que nós sabe o que é Action Cable, vamos entender algumas termologias.

## Termologias

### Connections ou Conexões

Connections (Conexões) formam a base do relacionamento cliente-servidor. Um único servidor Action Cable pode lidar com várias instâncias de conexão. Um único usuário pode ter vários WebSockets abertos para seu aplicativo se usar várias abas do navegador ou dispositivos diferentes.

### Consumers ou consumidores

O cliente de uma conexão WebSocket é chamado de consumer (consumidor). Em Action Cable, o consumer é criado pelo framework javascript do client-side

### Channels ou Canais

Cada consumer pode, por sua vez, assinar vários channels.
Cada canal encapsula uma unidade lógica de trabalho, similar o que controller faz no típico MVC. Por exemplo, você pode ter um `ChatChannel` e um `PresencaChannel` e um consumer pode assinar um ou ambos canais. No minimo, o consumer deve assinar pelo menos um canal.

### Subscribers ou Assinantes

Quando o consumer está assinado em um channel, ele pode agir como um subscriber (assinante). A conexão entre o subscriber e o channel é, surprise-surprise, chamado de assinatura. Um consumer pode agir como um subscriber de um determinado channel quantas vezes quiser. Por exemplo, um consumer pode assinar várias sala de conversa ao mesmo tempo.

### Pub/Sub

Pub/Sub ou Publish-Subscribe (Publicar-Assinar) refere-se a uma paradigma de fila de messagens por meio do qual os remetentes de informações (publishers), enviam dados apara uma classe abstrata de destinatários (subscribers), sem especificar os destinatários individuais. Action Cable usa essa abordagem para comunicar entre o servidor e vários clientes.

### Broadcastings ou Transmissões

Um broadcasting é um link pub/sub em que qualquer coisa transmitida pelo broadcaster é enviada diretamente para os subscribers do channel que estão transmitindo essa chamada broadcasting. Cada canal pode transmitir zero ou mais broadcastings.

Tendo o conhecimento das termologias, vamos criar uma aplicação.

## Criando uma aplicação

O aplicativo que vou criar é um chat.

```bash
rails new chat
cd chat
rails g controller Chat index
```

Coloque a rota `chat#index` como root.

```ruby
# config/routes.rb
Rails.application.routes.draw do
  root 'chat#index'
end
```

Abra o arquivo `app/views/chat/index.html.erb` e coloque o código abaixo.

```erb
<!-- app/views/chat/index.html.erb -->
<div>
  <textarea id="input"></textarea>
  <button id="button">enviar</button>
</div>
<div id="chat"></div>
```

Agora, vamos colocar o Action Cable.

## Instalando o Action Cable

Como a nossa aplicação é um chat, então vamos criar um channel Chat.

Para criar, basta usar o generator.

```bash
rails g channel Chat
```

Tem um arquivo que fica no `app/channels/application_cable/connection.rb` que fica responsável pela conexão.

Porém, você tem fazer a parte de autenticação e autorização, mas como a minha aplicação não tem autenticação, então eu vou ignorar essa parte.

Mas eu vou deixar um exemplo do código de como seria se tivesse uma autenticação.

```ruby
# 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
        # se você estiver usando cookie para autenticação
        verified_user = User.find_by(id: cookies.encrypted[:user_id])
        # se você estiver usando session para autenticação
        verified_user = User.find_by(id: cookies.encrypted['_session']['user_id'])
        # se por acaso, você estiver usando uma gem para autenticação, terá pesquisar sobre isso.
        if verified_user
          verified_user
        else
          reject_unauthorized_connection
        end
      end
  end
end
```

Você pode observar o `identified_by`, ele cria um identificador de conexão, ou seja, você pode usar `current_user` para saber quem é o usuário que está conectado em uma conexão.

Agora, vamos focar no client-side.

## Client Side

Abra o arquivo `app/javascript/channels/chat_channel.js` e coloque o código abaixo.

```javascript
// app/javascript/channels/chat_channel.js
import consumer from "channels/consumer"

// o consumer vai ser criado aqui e vai tentar assinar no canal ChatChannel
consumer.subscriptions.create("ChatChannel", {
  connected() { // vai executar essa método se a conexão foi sucedida
    this.input = document.getElementById("input")
    this.chat = document.getElementById("chat")

    const button = document.getElementById("button")

    button.addEventListener("click", this.enviar_mensagem.bind(this))
  },

  received(dados) { // Aqui recebe os dados do server side
    this.chat.innerHTML += `<p>${dados.mensagem}</p>`
  },

  enviar_mensagem() { // Aqui vai enviar os dados para server side
    let dados = {mensagem: input.value}

    this.perform("mensagem", dados)
    // isso vai enviar os dados para action mensagem do server side

    input.value = ""
  }
});
```

## Server side

Abra o arquivo `app/channels/chat_channel.rb` e coloque o código abaixo.

```ruby
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  # vai executar esse método se o usuário vai fazer uma conexão
  def subscribed
    # o stream_from vai assina em um broadcasting com nome 'chat', isso é útil para criar grupo. 
    stream_from "chat"
  end

  # aqui é a action que recebe os dados do client side
  def mensagem dados
    # isso vai envia a dados para os subscribers (assinantes). O primeiro argumento é o nome do broadcasting, como você pode ver que é igual ao argumento do `stream_from`.
    ActionCable.server.broadcast("chat", dados)
  end
end
```

Agora, execute o `rails server` e abre duas abas do navegador com a url `localhost:3000`, tente manda uma mensagem e verá que a outra aba vai receber a mensagem.

Com isso, nós terminamos por aqui, porém eu vou fazer, um dia, um post, em que vou aprofundar mais o action cable criando um joguinho simples.

Então é isso, tchau!