Explorando as funcionalidades do Ruby 3

O Ruby 3, lançado em dezembro de 2020, representa um marco na evolução da linguagem. Com o ambicioso projeto Ruby 3x3, que prometia tornar o Ruby três vezes mais rápido que o Ruby 2, a versão trouxe não apenas ganhos de performance, mas também novas paradigmas de concorrência, tipagem opcional e expressividade sintática. Este artigo explora as principais funcionalidades do Ruby 3 sob a perspectiva de uma lista final de 1200 temas, oferecendo exemplos práticos para cada conceito.

1. Ruby 3: A nova era da performance e concorrência

1.1. O projeto Ruby 3x3: três vezes mais rápido que o Ruby 2

O principal objetivo do Ruby 3 era alcançar performance três vezes superior ao Ruby 2. Embora esse número seja uma meta aspiracional, os ganhos reais são significativos, especialmente em aplicações com carga computacional intensa.

# Exemplo de benchmark simples comparando Ruby 2 vs Ruby 3
require 'benchmark'

def calculo_pesado(n)
  (1..n).reduce(0) { |acc, i| acc + Math.sqrt(i) * Math.sin(i) }
end

Benchmark.bm do |x|
  x.report("Ruby 3") { 100.times { calculo_pesado(10000) } }
end

1.2. MJIT e YJIT: compiladores Just-In-Time para aceleração real

O Ruby 3 introduziu dois compiladores JIT: MJIT (Method-based JIT) e posteriormente YJIT (Yet Another JIT), desenvolvido pela Shopify. O YJIT, disponível a partir do Ruby 3.1, oferece ganhos ainda mais expressivos.

# Ativando YJIT na linha de comando
# ruby --yjit meu_script.rb

# Exemplo de código que se beneficia do YJIT
def fibonacci(n)
  return n if n <= 1
  fibonacci(n - 1) + fibonacci(n - 2)
end

puts fibonacci(35)  # Execução otimizada com YJIT

1.3. Ractor: concorrência segura e paralelismo sem race conditions

Ractor (Ruby Actor) permite paralelismo real sem compartilhamento de estado, eliminando race conditions.

# Exemplo de Ractor para processamento paralelo
ractor1 = Ractor.new do
  sleep 1
  "Resultado do Ractor 1"
end

ractor2 = Ractor.new do
  sleep 2
  "Resultado do Ractor 2"
end

puts ractor1.take  # Aguarda 1 segundo
puts ractor2.take  # Aguarda 2 segundos (execução paralela)

2. Tipagem estática opcional com RBS e TypeProf

2.1. RBS: arquivos de assinatura de tipos para código Ruby

RBS (Ruby Signature) permite declarar tipos em arquivos separados, sem modificar o código original.

# Arquivo: pessoa.rbs
class Pessoa
  attr_reader nome: String
  attr_reader idade: Integer
  def initialize: (String nome, Integer idade) -> void
  def apresentar: () -> String
end

2.2. TypeProf: inferência automática de tipos para projetos legados

TypeProf analisa seu código e gera arquivos RBS automaticamente.

# Comando para gerar assinaturas automaticamente
# typeprof meu_arquivo.rb

# Exemplo de código analisado pelo TypeProf
def soma(a, b)
  a + b
end

# TypeProf inferiria: def soma: (Integer, Integer) -> Integer

2.3. Integração com ferramentas de análise estática e IDEs

Ferramentas como Steep e Solargraph usam RBS para fornecer autocomplete e detecção de erros em tempo real.

# Configuração básica do Steep
# Arquivo: Steepfile
target :app do
  check "lib"
  signature "sig"
end

3. Pattern Matching: expressividade e legibilidade

3.1. Pattern Matching básico: case/in para desestruturação de dados

def avaliar_resposta(resposta)
  case resposta
  in { status: 200, body: }
    "Sucesso: #{body}"
  in { status: 404 }
    "Não encontrado"
  in { status: 500..599 }
    "Erro do servidor"
  else
    "Resposta desconhecida"
  end
end

puts avaliar_resposta({ status: 200, body: "OK" })

3.2. Padrões avançados: arrays, hashes, objetos e combinadores

def processar_dados(dado)
  case dado
  in [Integer => x, String => y] if x > 0
    "Array com inteiro positivo e string: #{x}, #{y}"
  in { nome: /^A/, idade: 18..30 }
    "Jovem com nome começando com A"
  in Array => arr if arr.all? { |e| e.is_a?(Numeric) }
    "Array numérico: #{arr.sum}"
  end
end

puts processar_dados([42, "Ruby"])
puts processar_dados({ nome: "Ana", idade: 25 })

3.3. One-line pattern matching com o operador => e in

# Pattern matching em uma linha
{ nome: "João", idade: 30 } => { nome:, idade: }
puts "#{nome} tem #{idade} anos"

# Verificação condicional com in
if [1, 2, 3] in [Integer, Integer, Integer]
  puts "Array de três inteiros"
end

4. Novidades na sintaxe e no núcleo da linguagem

4.1. Endless methods: definição de métodos em uma única linha

def quadrado(x) = x * x
def saudacao(nome) = "Olá, #{nome}!"

puts quadrado(5)     # 25
puts saudacao("Ana") # Olá, Ana!

4.2. Find pattern: busca em arrays com [*first, target, *last]

def encontrar_elemento(array, alvo)
  case array
  in [*before, ^alvo, *after]
    { antes: before, depois: after }
  else
    nil
  end
end

dados = [10, 20, 30, 40, 50]
resultado = encontrar_elemento(dados, 30)
puts resultado  # {:antes=>[10, 20], :depois=>[40, 50]}

4.3. Numbered parameters: _1, _2 em blocos sem nomear argumentos

numeros = [1, 2, 3, 4, 5]

# Sem numbered parameters
dobrados = numeros.map { |n| n * 2 }

# Com numbered parameters
dobrados = numeros.map { _1 * 2 }

# Múltiplos parâmetros
pares = [[1, 2], [3, 4]]
somas = pares.map { _1 + _2 }
puts somas  # [3, 7]

5. Fiber Scheduler e concorrência assíncrona

5.1. Fiber Scheduler: interface para I/O não bloqueante

require 'async'

def tarefa_demorada(nome, tempo)
  Async do
    sleep tempo
    puts "#{nome} concluída após #{tempo} segundos"
  end
end

Async do
  tarefa_demorada("Tarefa 1", 2)
  tarefa_demorada("Tarefa 2", 1)
end

5.2. Fiber.schedule e execução concorrente leve

require 'fiber'

scheduler = Scheduler.new
Fiber.set_scheduler(scheduler)

Fiber.schedule do
  sleep 1
  puts "Fiber 1"
end

Fiber.schedule do
  sleep 2
  puts "Fiber 2"
end

5.3. Compatibilidade com gems assíncronas

# Usando a gem async-http
require 'async'
require 'async/http/internet'

Async do
  internet = Async::HTTP::Internet.new
  response = internet.get("https://api.github.com")
  puts response.read
end

6. Melhorias na ferramenta de pacotes e segurança

6.1. Bundler integrado: gemas gerenciadas diretamente pelo Ruby

# Ruby 3+ inclui bundler como gem padrão
# Arquivo Gemfile
source 'https://rubygems.org'

gem 'rails', '~> 7.0'
gem 'puma', '~> 6.0'
gem 'sqlite3', '~> 1.6'

6.2. Gemas de segurança: set, net-* e dependências atualizadas

# Verificando vulnerabilidades em gems
# bundle audit check --update

# Exemplo de uso seguro de net/http
require 'net/http'
require 'uri'

uri = URI('https://api.exemplo.com')
response = Net::HTTP.get_response(uri)
puts response.body if response.is_a?(Net::HTTPSuccess)

6.3. Type checking em gems: RBS como padrão da comunidade

# Adicionando types a uma gem
# Estrutura de diretórios:
# minha_gem/
#   sig/
#     minha_gem.rbs
#   lib/
#     minha_gem.rb

# Arquivo sig/minha_gem.rbs
module MinhaGem
  VERSION: String
  def self.saudacao: (String nome) -> String
end

7. Depuração e análise de código avançadas

7.1. Debug gem: novo depurador com REPL e breakpoints remotos

# debug.rb
require 'debug'

def calculo_complexo(x, y)
  resultado = x + y
  debugger  # Ponto de interrupção
  resultado * 2
end

puts calculo_complexo(10, 20)

7.2. TracePoint e profiling com --rjit-stats e --mjit-stats

# Ativando estatísticas do YJIT
# ruby --yjit --yjit-stats meu_script.rb

# Usando TracePoint para profiling
TracePoint.new(:call) do |tp|
  puts "Chamada: #{tp.method_id} em #{tp.defined_class}"
end.enable

def exemplo
  sleep 0.1
end

exemplo

7.3. Ferramentas de cobertura e análise de tipos em tempo real

# Usando coverage do Ruby padrão
require 'coverage'
Coverage.start

require_relative 'meu_codigo'

result = Coverage.result
puts result.inspect

8. Casos de uso e migração para Ruby 3

8.1. Aplicações web com Rails 7 e Ruby 3: sinergia e ganhos reais

# Gemfile para Rails 7 com Ruby 3
source 'https://rubygems.org'
ruby '~> 3.2'

gem 'rails', '~> 7.1'
gem 'puma', '~> 6.0'
gem 'importmap-rails'
gem 'turbo-rails'
gem 'stimulus-rails'

8.2. Scripts de processamento de dados com Pattern Matching

def processar_logs(logs)
  logs.map do |log|
    case log
    in { level: "ERROR", message:, timestamp: }
      "[#{timestamp}] ERRO: #{message}"
    in { level: "WARN", message:, timestamp: }
      "[#{timestamp}] AVISO: #{message}"
    in { level: "INFO", message:, timestamp: }
      "[#{timestamp}] INFO: #{message}"
    else
      "Log desconhecido"
    end
  end
end

logs = [
  { level: "ERROR", message: "Falha na conexão", timestamp: "2024-01-01" },
  { level: "INFO", message: "Servidor iniciado", timestamp: "2024-01-01" }
]

puts processar_logs(logs)

8.3. Roteiro de migração: compatibilidade, gemas e testes com Ruby 3

# Checklist de migração para Ruby 3
# 1. Verificar compatibilidade de gems
# bundle update
# bundle exec ruby -c app.rb

# 2. Executar testes
# bundle exec rspec spec/

# 3. Ativar YJIT gradualmente
# RUBY_YJIT_ENABLE=1 bundle exec rails server

# 4. Verificar warnings de depreciação
# ruby -W:deprecated meu_script.rb

Referências