💎 Cap 16: Ruby — Elegância, Metaprogramação e a Linguagem Lua
Este guia é um material de apoio para quem está começando na linguagem Ruby, especialmente para estudantes que já possuem uma base em lógica de programação.
Criada em 1995 pelo cientista da computação japonês Yukihiro Matsumoto (conhecido como “Matz”), Ruby foi desenhada com uma premissa fundamental: otimizar a linguagem para a felicidade humana e a produtividade do desenvolvedor, e não para a máquina.
Ruby combina o poder prático e de manipulação de texto do Perl com a elegância orientada a objetos do Smalltalk e a sintaxe amigável de linguagens como Python e Eiffel. É a linguagem por trás do consagrado framework web Ruby on Rails e de ferramentas populares como o Jekyll (o próprio gerador de sites estáticos que alimenta este portal!).
🛠️ Instalação e Configuração do Ambiente
1. Instalação do Interpretador Ruby
- Windows: A forma oficial e mais simples é baixar o instalador no RubyInstaller for Windows. Escolha a versão recomendada com Devkit (que permite compilar extensões nativas em C).
- Linux (Ubuntu/Debian):
sudo apt update && sudo apt install ruby-full(ou via gerenciadores comorbenv/asdf). - macOS: Já possui uma versão básica no sistema, mas é recomendado instalar a versão mais recente via Homebrew:
brew install ruby.
Verifique a instalação no terminal:
ruby -v
2. O Console Interativo (IRB)
Uma das melhores ferramentas para aprender Ruby é o IRB (Interactive Ruby), um terminal interativo (REPL) onde você pode testar expressões em tempo real:
irb
No prompt do IRB, você pode digitar comandos como:
irb(main):001> 2 + 3
=> 5
irb(main):002> "ruby".upcase
=> "RUBY"
irb(main):003> exit
3. Editor de Código (VS Code)
- Instale o Visual Studio Code.
- Na aba de Extensões (
Ctrl + Shift + X), instale a extensão oficial Ruby LSP (da Shopify) ou Ruby para suporte a realce de sintaxe, autocompletar e formatação.
🚀 Seu Primeiro Programa em Ruby
Crie um arquivo chamado ola.rb:
puts "Ola, Universo Ruby!"
Execute no terminal:
ruby ola.rb
O resultado será:
Ola, Universo Ruby!
puts, print e p: Qual a diferença?
puts(put string): Exibe o conteúdo e adiciona uma quebra de linha (\n) no final.print: Exibe o conteúdo sem quebra de linha.p: Usado para depuração (inspeção de objetos). Exibe o valor em formato cru (mostra aspas em strings, elementos de arrays etc.).
print "Linha 1 "
print "continua aqui.\n"
puts "Linha 2 com quebra automática."
p "Texto com aspas visíveis no debug" # => "\"Texto com aspas visíveis no debug\""
💎 O Conceito Fundamental: Tudo é um Objeto
Em muitas linguagens (como C, Java ou Go), existem “tipos primitivos” e “objetos”. Em Ruby, absolutamente tudo é um objeto — instâncias de classes com métodos que você pode chamar:
-10.abs # => 10 (chama o método .abs no número -10)
5.times { puts "Oi" } # Executa o bloco 5 vezes
"texto".length # => 5
nil.nil? # => true
📊 Tipos de Dados e Variáveis
Ruby possui tipagem dinâmica (você não precisa declarar tipos) e tipagem forte (não converte tipos incompatíveis de forma silenciosa).
| Tipo | Descrição | Exemplo |
|---|---|---|
Integer |
Números inteiros | 42, -10 |
Float |
Números de ponto flutuante | 3.1415, 99.90 |
String |
Textos e caracteres | "Sofia", 'Olá' |
TrueClass / FalseClass |
Valores booleanos | true, false |
NilClass |
Ausência de valor | nil (equivalente ao null/None) |
Symbol |
Identificadores imutáveis | :id, :ativo, :pendente |
Convenções de Nomenclatura
- Variáveis locais:
snake_case(ex:idade_usuario,total_vendas) - Constantes:
MAIUSCULASouPascalCase(ex:PI = 3.14,TaxaMaxima = 0.2) - Variáveis de instância: prefixadas com
@(ex:@nome) - Variáveis globais: prefixadas com
$(ex:$debug_mode)
Interpolação de Strings
Strings com aspas duplas ("...") suportam interpolação direta de código com #{expressao}:
nome = "Sofia Oliveira"
idade = 28
salario = 6450.50
genero = 'F'
# Interpolação limpa e legível
puts "NOME = #{nome}"
puts "IDADE = #{idade}"
puts "GENERO = #{genero}"
# Formatação com sprintf / printf
puts sprintf("SALARIO = %.2f", salario)
⚠️ Atenção: Aspas simples (
'...') são literais e não processam interpolação nem caracteres de escape como\n.
🔢 Operadores
Aritméticos
| Operador | Significado | Exemplo |
|---|---|---|
+ |
Adição | 10 + 5 (15) |
- |
Subtração | 10 - 5 (5) |
* |
Multiplicação | 10 * 5 (50) |
/ |
Divisão (inteira se ambos forem inteiros) | 10 / 4 (2) ou 10.0 / 4 (2.5) |
% |
Resto da divisão (módulo) | 10 % 3 (1) |
** |
Exponenciação | 2 ** 3 (8) |
Comparativos
| Operador | Significado |
|---|---|
== |
Igual a |
!= |
Diferente de |
> |
Maior que |
< |
Menor que |
>= |
Maior ou igual a |
<= |
Menor ou igual a |
<=> |
Operador espaçonave (retorna -1 se menor, 0 se igual, 1 se maior) |
Lógicos
&&eand(E lógico)||eor(OU lógico)!enot(NÃO lógico)
💡 Regra de ouro dos booleanos em Ruby: Em Ruby, apenas
falseenilsão avaliados como falsos. O número0, a string vazia""e um array vazio[]são todos considerados verdadeiros (truthy)!
📥 Entrada e Saída de Dados
A leitura do teclado é feita com gets. O método .chomp remove a quebra de linha residual do Enter.
Conversões explícitas:
.to_i: Converte para inteiro (Integer).to_f: Converte para ponto flutuante (Float).to_s: Converte para texto (String)
print "Digite seu nome: "
nome = gets.chomp
print "Digite sua idade: "
idade = gets.chomp.to_i
print "Digite seu salário: "
salario = gets.chomp.to_f
puts "\n--- DADOS CADASTRADOS ---"
puts "Nome: #{nome}"
puts "Idade em meses: #{idade * 12}"
puts sprintf("Salário anual: R$ %.2f", salario * 12)
🔀 Estruturas Condicionais
1. if / elsif / else
Repare na grafia elsif (sem o “e” no final do else):
print "Digite uma hora do dia (0 a 23): "
hora = gets.chomp.to_i
if hora < 12
puts "Bom dia!"
elsif hora < 18
puts "Boa tarde!"
else
puts "Boa noite!"
end
2. A estrutura unless
unless executa o bloco se a condição for falsa (equivalente a if not):
chovendo = false
unless chovendo
puts "Podemos ir ao parque caminhar!"
end
3. Modificadores Inline (Pós-fixados)
Para comandos curtos de uma linha, Ruby permite colocar o if ou unless no final da instrução:
nota = 8.5
puts "Parabéns, você foi aprovado!" if nota >= 7.0
puts "Atenção: nota insuficiente!" unless nota >= 7.0
4. case / when (Seleção Múltipla)
O case em Ruby é flexível e aceita comparações com intervalos numéricos (Range):
idade = 15
case idade
when 0..12
puts "Criança"
when 13..17
puts "Adolescente"
when 18..59
puts "Adulto"
else
puts "Idoso"
end
🔁 Estruturas de Repetição e Iteradores
Embora Ruby possua laços tradicionais como while e until, o código idiomático em Ruby quase sempre utiliza iteradores baseados em blocos.
1. Laço while e until
contador = 1
while contador <= 3
puts "Contagem while: #{contador}"
contador += 1
end
# until repete ATÉ que a condição se torne verdadeira
fim = 3
until fim == 0
puts "Regressivo until: #{fim}"
fim -= 1
end
2. O Jeito Idiomático Ruby: Iteradores
# Repetição simples com .times
3.times do |i|
puts "Execução número #{i + 1}"
end
# Contagem em intervalo com .upto e .downto
1.upto(3) { |n| puts "Subindo: #{n}" }
3.downto(1) { |n| puts "Descendo: #{n}" }
📏 Vetores (Arrays) e Coleções
Em Ruby, Array é uma coleção ordenada dinâmica que pode crescer conforme a necessidade.
# Criando um array
frutas = ["Maçã", "Banana", "Laranja"]
# Adicionando elementos (operador shovel << ou push)
frutas << "Uva"
frutas.push("Manga")
puts "Total de frutas: #{frutas.length}"
puts "Primeira fruta: #{frutas.first}"
puts "Última fruta: #{frutas.last}"
# Iterando sobre o array com .each
puts "\nLista de frutas:"
frutas.each do |fruta|
puts "- #{fruta}"
end
Transformações Poderosas em Coleções
Ruby brilha na manipulação de listas com métodos de alta ordem:
numeros = [1, 2, 3, 4, 5, 6]
# .map: Transforma cada elemento
dobros = numeros.map { |n| n * 2 }
# => [2, 4, 6, 8, 10, 12]
# .select: Filtra elementos (retorna os que satisfazem a condição)
pares = numeros.select { |n| n.even? }
# => [2, 4, 6]
# .sum: Soma todos os valores
total = numeros.sum
# => 21
🗂️ Dicionários / Mapas (Hash)
O Hash é uma estrutura de dados baseada em pares chave-valor:
aluno = {
nome: "Lucas Pereira",
idade: 22,
curso: "Ciência da Computação",
matriculado: true
}
puts "Aluno: #{aluno[:nome]}"
puts "Curso: #{aluno[:curso]}"
# Percorrendo chave e valor
aluno.each do |chave, valor|
puts "#{chave.to_s.capitalize}: #{valor}"
end
🧩 Funções e Métodos
Métodos são definidos com a palavra-chave def e finalizados com end.
Retorno Implícito
Em Ruby, a palavra-chave return é opcional: a última linha avaliada dentro do método é retornada automaticamente.
# Método com parâmetros e valor padrão
def calcular_area_retangulo(base, altura = 1.0)
base * altura # Retorno implícito
end
area1 = calcular_area_retangulo(5.0, 3.0)
area2 = calcular_area_retangulo(4.0) # Usa altura padrão = 1.0
puts "Área 1 = #{area1}" # => 15.0
puts "Área 2 = #{area2}" # => 4.0
Métodos Predicados (?)
Por convenção, métodos que retornam true ou false terminam com uma interrogação ?:
def numero_positivo?(n)
n > 0
end
puts numero_positivo?(10) # => true
puts numero_positivo?(-5) # => false
🧱 Blocos e Closures
Um bloco em Ruby é um trecho de código anônimo que pode ser passado como argumento para qualquer método. Podem ser escritos de duas formas:
- Em uma linha:
{ |parametro| ... } - Em múltiplas linhas:
do |parametro| ... end
Podemos criar nossos próprios métodos que recebem blocos usando a instrução yield:
def executar_duas_vezes
yield
yield
end
executar_duas_vezes { puts "Olá do bloco!" }
🪄 Metaprogramação: Código que Escreve Código
Metaprogramação é escrever código que cria ou modifica outro código em tempo de execução — definir métodos dinamicamente, interceptar chamadas a métodos que não existem, ou inspecionar/alterar objetos e classes já carregados. Ruby é uma das linguagens mais amigáveis para isso, porque (lembrando a seção anterior) tudo é um objeto, inclusive classes.
define_method: Criando Métodos em Tempo de Execução
class Pessoa
# define_method cria um metodo a partir de uma lista, sem escrever "def" 2x
["nome", "idade"].each do |atributo|
define_method("#{atributo}=") do |valor|
instance_variable_set("@#{atributo}", valor)
end
define_method(atributo) do
instance_variable_get("@#{atributo}")
end
end
end
p1 = Pessoa.new
p1.nome = "Carlos"
p1.idade = 30
puts "Nome: #{p1.nome}, Idade: #{p1.idade}"
Saída: Nome: Carlos, Idade: 30
💡 Isso é exatamente o que
attr_accessor :nome, :idadefaz por baixo dos panos —attr_accessoré, ele mesmo, um pedaço de metaprogramação da biblioteca padrão do Ruby.
method_missing: Interceptando Chamadas Inexistentes
Quando você chama um método que a classe não define, Ruby não falha imediatamente — primeiro chama method_missing, que você pode sobrescrever para responder dinamicamente:
class Pessoa
def method_missing(nome, *args)
if nome.to_s.start_with?("bio_")
"Metodo dinamico chamado: #{nome}"
else
super # Deixa o erro padrão acontecer para outros casos
end
end
def respond_to_missing?(nome, include_private = false)
nome.to_s.start_with?("bio_") || super
end
end
p1 = Pessoa.new
puts p1.bio_resumo # Nunca foi definido -- mas method_missing responde
puts p1.send(:bio_resumo) # .send chama um metodo pelo nome (String/Symbol)
Saída: Metodo dinamico chamado: bio_resumo (duas vezes)
⚠️ Sempre implemente
respond_to_missing?junto commethod_missing. Sem isso, métodos como.respond_to?e.methodsmentem sobre o que o objeto realmente aceita — um bug sutil e clássico de metaprogramação mal feita. É esse tipo de “mágica” (poderosa, mas fácil de abusar) que frameworks como Ruby on Rails usam extensivamente para APIs fluentes, e que exige disciplina para não virar código difícil de depurar.
⚠️ Erros Comuns de Iniciantes
- Esquecer o
chompnogets:nome = gets # Contém "Carlos\n" if nome == "Carlos" # FALSO, porque "Carlos\n" != "Carlos" # Correção: nome = gets.chomp - Escrever
elseifouelif:- Em Ruby, a palavra-chave correta é exclusivamente
elsif.
- Em Ruby, a palavra-chave correta é exclusivamente
- Supor que
0ou""são falsos:if 0 puts "Em Ruby, 0 é VERDADEIRO (truthy)!" end - Confundir aspas simples com aspas duplas na interpolação:
nome = "Ana" puts 'Olá, #{nome}' # Saída literal: Olá, #{nome} puts "Olá, #{nome}" # Saída correta: Olá, Ana
🧠 Ruby x Outras Linguagens
| Característica | Ruby 💎 | Python 🐍 | JavaScript 🌐 |
|---|---|---|---|
| Filosofia | Felicidade do dev e expressividade | Legibilidade e código explícito | Versatilidade e onipresença |
| Delimitação de Blocos | def/if/do ... end |
Indentação obrigatória | Chaves { ... } |
| Tudo é Objeto? | Sim, 100% puro (5.times) |
Quase tudo (funções soltas e tipos primitivos) | Não (primitivos vs objetos) |
| Valores Falsy | Apenas false e nil |
False, None, 0, "", [] |
false, null, undefined, 0, "", NaN |
| Retorno de Métodos | Implícito (última linha) | Explícito (return) |
Explícito (return) |
🌙 E a Linguagem Lua?
Lua (1993, PUC-Rio) compartilha com Ruby a tipagem dinâmica e uma sintaxe enxuta — mas nasceu com um objetivo bem diferente: ser pequena e embarcável, para ser incorporada dentro de outros programas como linguagem de script (motores de jogos como Roblox e LÖVE2D, o editor Neovim, e até equipamentos industriais). Onde Ruby otimiza para a felicidade do desenvolvedor de aplicações completas, Lua otimiza para rodar rápido e ocupar pouco espaço dentro de um hospedeiro maior escrito em C.
print("Ola, Universo Lua!")
Saída: Ola, Universo Lua!
A Única Estrutura de Dados: table
Diferente de Ruby (Array e Hash separados), Lua tem uma única estrutura composta, a table, que serve tanto como array quanto como dicionário:
local aluno = {
nome = "Sofia",
idade = 22,
notas = {8.5, 9.0, 7.5}
}
print("Nome: " .. aluno.nome)
-- ATENCAO: arrays em Lua comecam no indice 1, nao 0!
print("Primeira nota: " .. aluno.notas[1])
local soma = 0
for i, nota in ipairs(aluno.notas) do
soma = soma + nota
end
print(string.format("Media: %.2f", soma / #aluno.notas))
Saída:
Nome: Sofia
Primeira nota: 8.5
Media: 8.33
⚠️ O índice 1, não 0, é a pegadinha nº1 de quem vem de C/Python/Ruby para Lua.
aluno.notas[1]é o primeiro elemento —aluno.notas[0]simplesmente retornanil, sem erro, o que pode mascarar bugs silenciosamente.
Funções de Primeira Classe e Closures
Assim como em Ruby (blocos) e Python, funções em Lua podem ser guardadas em variáveis e passadas como argumento — a base do seu uso como “linguagem de cola” para scriptar comportamento dentro de um jogo ou aplicação hospedeira:
local function saudar(nome)
return "Ola, " .. nome .. "!"
end
local function executarCallback(funcao, valor)
return funcao(valor)
end
print(executarCallback(saudar, "Mundo"))
-- Closure: uma funcao que "lembra" variaveis do escopo onde foi criada
local function criarContador()
local total = 0
return function()
total = total + 1
return total
end
end
contador = criarContador()
print(contador())
print(contador())
print(contador())
Saída:
Ola, Mundo!
1
2
3
🎓 Conclusão
Ruby se destaca pela facilidade de leitura e pela rica expressividade de sua sintaxe. Dominar Ruby abre portas tanto para entender ferramentas de automação e DevOps quanto para o desenvolvimento web robusto com Ruby on Rails e sites estáticos com Jekyll. Lua mostra o outro extremo do espectro de linguagens dinâmicas: onde Ruby maximiza expressividade para quem escreve aplicações, Lua maximiza simplicidade e footprint mínimo para quem embute um interpretador dentro de outro programa — a mesma dinamicidade de tipos, aplicada a objetivos de design opostos.