A Arte dos Métodos em Ruby: Dominando a Base da Linguagem

Quando estamos aprendendo Ruby, rapidamente percebemos como os métodos são fundamentais para a estrutura da linguagem. Diferente de outras linguagens de programação, Ruby trata métodos com uma elegância singular, oferecendo diversas formas de defini-los e chamá-los. Vamos explorar em detalhes como funcionam os métodos em Ruby e algumas técnicas avançadas que podem elevar seu código a um novo patamar.

Definindo Métodos: A Base de Tudo

Em Ruby, definimos métodos usando a palavra-chave def. O corpo do método contém expressões Ruby normais, e o valor de retorno é o resultado da última expressão executada (ou o argumento de um return explícito).

def saudacao(nome)
  "Olá, #{nome}!"
end

puts saudacao("Maria") # Imprime: Olá, Maria!

Uma característica interessante é que podemos redefinir um método sem erros - Ruby apenas emite um aviso e usa a segunda definição. Isso, combinado com a capacidade de reabrir classes, permite modificar comportamentos em tempo de execução.

A partir do Ruby 3.0, também podemos criar métodos de uma linha com uma sintaxe mais concisa, às vezes chamada de "método sem fim":

def quadrado(n) = n * n

Esta sintaxe é especialmente útil para métodos simples que realizam apenas uma operação.

Nomes de Métodos: Convenções Importantes

Os nomes de métodos em Ruby devem começar com letra minúscula ou underscore, seguido por uma combinação de letras, dígitos e underscores. Por convenção, os métodos que retornam valores booleanos (predicados) geralmente terminam com ?:

1.even? # => false
2.even? # => true

Métodos "perigosos" ou que modificam o objeto receptor costumam terminar com exclamação !. Estes são frequentemente emparelhados com uma versão "segura" sem a exclamação:

texto = "meu código"
texto.chop   # => "meu códig" (retorna uma cópia modificada)
texto        # => "meu código" (original permanece inalterado)
texto.chop!  # => "meu códig" (modifica o objeto original)
texto        # => "meu códig" (o objeto foi modificado)

Métodos que podem aparecer no lado esquerdo de uma atribuição terminam com sinal de igual =:

class Pessoa
  def nome=(novo_nome)
    @nome = novo_nome
  end
end

p = Pessoa.new
p.nome = "Ana Vitória" # Invoca o método nome=

O Receptor do Método: Self e Além

Em Ruby, cada método é chamado em um objeto receptor. Dentro do método, a palavra-chave self refere-se a esse receptor.

Podemos definir métodos para classes (métodos de classe) prefixando o nome do método com self.:

class Computador
  def self.funcao
    "Receio não poder fazer isso"
  end
end

puts Computador.funcao # Chama o método na própria classe

Também podemos anexar métodos a objetos específicos:

mac = Computador.new
def mac.introducao
  "Eu sou um Mac"
end

puts mac.introducao # => "Eu sou um Mac"

Parâmetros de Método: Flexibilidade em Ação

Ruby oferece várias formas de definir parâmetros, tornando os métodos extremamente flexíveis:

Parâmetros com Valores Padrão

def musico_favorito(arg1="Chico", arg2="Elis", arg3="Caetano")
  "#{arg1}, #{arg2}, #{arg3}."
end

musico_favorito                    # => "Chico, Elis, Caetano."
musico_favorito("Milton")          # => "Milton, Elis, Caetano."
musico_favorito("Milton", "Maria") # => "Milton, Maria, Caetano."

Parâmetros de Tamanho Variável (Splat)

Usando um asterisco antes do nome do parâmetro, podemos capturar múltiplos argumentos em um único array:

def argumentos_variaveis(arg1, *resto)
  "arg1=#{arg1} -- resto=#{resto.inspect}"
end

argumentos_variaveis("um")                 # => arg1=um -- resto=[]
argumentos_variaveis("um", "dois", "três") # => arg1=um -- resto=["dois", "três"]

Parâmetros de Palavra-chave (Keyword Parameters)

def metodo_com_keywords(cidade:, estado:, cep:)
  "Moro em #{cidade}, #{estado} #{cep}"
end

metodo_com_keywords(cidade: "São Paulo", estado: "SP", cep: "01311-000")
# => "Moro em São Paulo, SP 01311-000"

Podemos também coletar palavras-chave arbitrárias usando o operador de duplo splat **:

def varargs(arg1, **resto)
  "arg1=#{arg1}. resto=#{resto.inspect}"
end

varargs("um", cor: "vermelho", tamanho: "G")
# => arg1=um. resto={:cor=>"vermelho", :tamanho=>"G"}

Chamando Métodos: A Execução em Prática

Para chamar um método, você especifica (opcionalmente) um receptor, o nome do método e os argumentos:

arquivo.download_mp3("samba", velocidade: :normal) { |p| mostrar_progresso(p) }

Se omitirmos o receptor, Ruby usa self como receptor padrão. Isso é como os métodos privados funcionam - eles só podem ser chamados implicitamente no objeto atual.

Uma observação importante: ao chamar um método no lado esquerdo de uma atribuição (com nome terminando em =), você deve especificar explicitamente o receptor:

class Pessoa
  def nome=(novo_nome)
    @nome = novo_nome
  end
  
  def alterar_coisas(novo_nome)
    # Isso cria uma variável local chamada 'nome'
    nome = novo_nome  
    
    # Para chamar o método, precisamos usar 'self'
    self.nome = novo_nome  
  end
end

Valores de Retorno: O Resultado Final

Todo método retorna um valor - o valor da última expressão executada (ou o argumento de return):

def positivo_ou_negativo(arg)
  case
  when arg > 0 then "positivo"
  when arg < 0 then "negativo"
  else "zero"
  end
end

positivo_ou_negativo(42) # => "positivo"

Você pode usar return para sair do método prematuramente:

def encontrar_quadrado_grande
  100.times do |num|
    quadrado = num * num
    return num, quadrado if quadrado > 1000
  end
end

numero, quadrado = encontrar_quadrado_grande
# numero => 32, quadrado => 1024

Expansão em Chamadas de Método (Splat)

Assim como podemos coletar múltiplos argumentos em um array, podemos fazer o contrário - converter uma coleção em argumentos individuais usando o operador de splat:

def cinco(a, b, c, d, e)
  "Recebi #{a} #{b} #{c} #{d} #{e}"
end

cinco(*[1, 2, 3, 4, 5]) # => "Recebi 1 2 3 4 5"
cinco(1, 2, *[3, 4, 5]) # => "Recebi 1 2 3 4 5"
cinco(*(1..5))          # => "Recebi 1 2 3 4 5"

Da mesma forma, podemos expandir hashes em argumentos de palavra-chave usando o duplo splat:

def metodo_com_keywords(cidade:, estado:, cep:)
  "Moro em #{cidade}, #{estado} #{cep}"
end

dados = {cidade: "Recife", estado: "PE", cep: "50030-150"}
metodo_com_keywords(**dados) # => "Moro em Recife, PE 50030-150"

Passando Blocos como Argumentos

Uma técnica poderosa em Ruby é a capacidade de converter um objeto Proc em um bloco para passá-lo a um método:

["a", "b", "c"].map { |s| s.upcase } # => ["A", "B", "C"]

Existe um atalho muito usado com o operador & quando o bloco apenas chama um método:

["a", "b", "c"].map(&:upcase) # => ["A", "B", "C"]

Isso funciona porque a classe Symbol implementa o método to_proc, retornando um objeto Proc que chama o método correspondente ao símbolo.

Outra aplicação interessante é criar blocos dinamicamente e passá-los aos métodos:

print "Operação ((m)ultiplicação ou (s)oma): "
operador = gets
print "número: "
numero = Integer(gets)

metodo = numero.method(operador.start_with?("m") ? :* : :+)
puts((1..10).map(&metodo).join(", "))

Conclusão: O Poder dos Métodos em Ruby

Os métodos em Ruby são muito mais do que simples funções. Eles formam a base da expressividade e flexibilidade que torna a linguagem tão querida por desenvolvedores em todo o mundo. Com recursos como parâmetros nomeados, expansão de coleções e integração com blocos, os métodos Ruby podem ser adaptados para uma ampla variedade de cenários de programação.

Dominar a arte de definir e chamar métodos em Ruby é essencial para escrever código elegante, expressivo e de fácil manutenção.


Comentários (0)

Nenhum comentário:

Postar um comentário