Entendendo Struct em Ruby on Rails

O Ruby é conhecido por sua sintaxe elegante e flexibilidade, permitindo que os desenvolvedores criem códigos concisos e legíveis. Um dos recursos menos conhecidos, mas poderosos no Ruby, é o Struct, uma classe embutida que oferece uma maneira simples de agrupar atributos relacionados sem a sobrecarga de definir uma classe completa. Embora não seja tão comumente utilizado quanto os modelos ActiveRecord ou POROs (Plain Old Ruby Objects), o Struct pode ser extremamente útil em aplicações Rails em certos cenários.

O que é Struct?

Struct em Ruby é um atalho para definir classes simples que agrupam alguns atributos, fornecendo métodos getters e setters para esses atributos automaticamente. Ele cria uma nova classe que se comporta como um objeto leve, com os atributos que você define ao inicializar.

Por exemplo, digamos que você queira criar um objeto simples para representar um ponto em um espaço 2D com coordenadas x e y. Você pode fazer isso da seguinte forma:

Point = Struct.new(:x, :y)

point = Point.new(10, 20)
puts point.x  # Saída: 10
puts point.y  # Saída: 20

Aqui, Struct.new gera uma classe Point com os atributos x e y, além de métodos para obter e definir esses valores. Nesse caso, o Struct economiza o trabalho de definir explicitamente uma classe com um inicializador e acessores para esses atributos.

Quando Usar Struct no Rails

Embora você trabalhe principalmente com modelos ActiveRecord e objetos de serviço no Rails, o Struct tem seu lugar para objetos simples e leves que não precisam das capacidades completas de um modelo. Aqui estão alguns casos onde o Struct pode ser útil:

1. Armazenamento Temporário de Dados

Quando você precisa armazenar dados temporariamente, mas não deseja criar um modelo ou classe completa, o Struct é ideal. Isso é especialmente verdadeiro quando os dados não precisam persistir no banco de dados, existindo apenas durante uma requisição ou sessão.

Exemplo: Imagine que você está trabalhando em um sistema de reservas e deseja criar um objeto de resumo de viagem sem salvá-lo no banco de dados.

TripSummary = Struct.new(:destination, :duration, :price)

summary = TripSummary.new("Havaí", "7 dias", 1500)
puts summary.destination  # Saída: Havaí
puts summary.price        # Saída: 1500

2. Respostas de API

Se a sua aplicação Rails interage com APIs externas, você pode receber respostas JSON que se mapeiam para dados estruturados. Você pode facilmente envolver essas respostas em um Struct para tornar o manuseio delas mais organizado e legível.

Exemplo: Suponha que você esteja trabalhando com uma API que retorna dados climáticos:

WeatherData = Struct.new(:temperature, :humidity, :wind_speed)

response = { temperature: 75, humidity: 65, wind_speed: 10 }
weather = WeatherData.new(response[:temperature], response[:humidity], response[:wind_speed])

puts weather.temperature  # Saída: 75
puts weather.humidity     # Saída: 65

Usar Struct aqui ajuda a encapsular a resposta em um objeto claro e específico para o domínio.

3. Objetos de Formulário

No Rails, objetos de formulário são frequentemente usados para lidar com formulários complexos que envolvem múltiplos modelos ou atributos que não são do banco de dados. Em vez de criar uma classe nova para cada formulário, você pode usar Struct para reunir atributos temporariamente.

Exemplo: Suponha que você esteja construindo um processo de inscrição com várias etapas. Em uma etapa, você precisa coletar informações pessoais:

PersonalInfo = Struct.new(:first_name, :last_name, :email)

info = PersonalInfo.new("John", "Doe", "john.doe@example.com")

Com isso, você pode passar o objeto PersonalInfo, tornando o processo do formulário mais gerenciável.

Melhorando o Struct com Métodos Personalizados

Uma das grandes vantagens do Struct é que ele se comporta como uma classe Ruby regular, então você pode adicionar métodos personalizados a ele. Isso permite encapsular lógica relacionada ao seu objeto Struct.

Exemplo: Vamos melhorar o exemplo do TripSummary adicionando um método para calcular o custo total com impostos.

TripSummary = Struct.new(:destination, :duration, :price) do
  def total_cost_with_tax(tax_rate)
    price + (price * tax_rate)
  end
end

summary = TripSummary.new("Havaí", "7 dias", 1500)
puts summary.total_cost_with_tax(0.10)  # Saída: 1650

Isso permite manter a lógica relacionada dentro do objeto, assim como faria com uma classe completa.

Comparando Struct com POROs e Modelos

Embora o Struct seja uma ferramenta útil, é importante saber quando não usá-lo. Aqui está uma comparação rápida entre Struct e outros objetos comuns no Rails:

Aspecto Struct PORO Modelo ActiveRecord
Complexidade Leve, simples Pode ser leve ou complexo ORM completo
Persistência Sem interação com banco Sem interação com banco Interage com banco
Personalização Fácil de definir métodos Totalmente personalizável Totalmente personalizável
Caso de Uso Dados temporários, API, formulários Lógica de negócios, objetos de serviço Dados persistentes, lógica de negócios

O Struct em Ruby on Rails é uma maneira versátil e eficiente de lidar com estruturas de dados simples sem a necessidade de classes completas ou modelos ActiveRecord. É perfeito para tarefas leves, como armazenamento temporário de dados, manipulação de respostas de API ou encapsulamento de atributos de formulário. Usando Struct no contexto certo, você pode simplificar seu código e torná-lo mais legível, evitando complexidade desnecessária.