Alguns exemplos didáticos de metaprogramação em Ruby

Eu acho que pra aprender programação, não adianta somente entender o que está acontecendo, não é só ler  ou "estudar": tem que praticar. O problema é que programação e seus recursos são bastante específicos na resolução de problemas específicos. Precisa de contexto. Por isso é dificil pra qualquer um dizer "ah, agora vou criar exemplos usando técnica tal para exercitar...". Na minha busca por tentar entender um método do Haml, me deparei com alguns conceitos de metaprogramação. Eu fiz isso, de tentar inventar alguma utilidade para o que eu tinha aprendido, mas não consegui. Depois de pesquisar encontrei alguns exemplos que foram úteis para mim, podem ser pra você também se estiver querendo um desafio que exija metaprogramação em ruby.

Um dos recursos de metraprogramação em Ruby é a inclusão de métodos em classes do Ruby em tempo de e execução. Aqui vai um exemplo bem simples.

Suponha que você queria traduzir para o português o método upcase e downcase da classe String, para conseguir fazer algo como:

1
puts "Toda string é um objeto da classe String".maiusculo

É só definir a classe String com os novos métodos em português! A expresão que se usa para isso é "abrir a classe". É simples assim, é como se fosse definir a classe, mas como ela já existe o Ruby sabe que ele só precisa incluir os novos métodos.

1
2
3
4
5
class String
  def maiusculo
    self.upcase
  end
end

O próximo exemplo é muito bom e foi retirado da apresentação do David Paniz e Leonardo Bessa (vide links no fim do post). Suponha que seja necessário calcular a tarifa telefônica, em função o horário da ligação. Da 01h00 às 06h00 a tarifa é 1 centavo, no restante do tempo, meio centavo. Poderíamos definir um método para isso cuja a chamada seria algo como:

1
2
calculo = Calculo.new
calculo.tarifa(horario)

Onde o objeto calculo seria a instância de alguma classe contendo métodos diversos. Ou Pior, uma classe só para conter esse método de cálculo de tarifa.

Mas exite uma maneira mais simples e eficiente. Poderiamos incluir o método tarifa na classe Time, e chamá-lo assim:

1
Time.now.tarifa

Experimente implementar esse método.

Assim como fizemos com a classe string, podemos incluir o método tarifa na classe Time. É por isso que falam tanto que o Ruby é uma linguagem bastante orientada a objeto. Chamar Time.now.tarifa é admitir que tarifa é uma propriedade do objeto retornado por Time.now.

Esses exemplos exploram somente um dos recursos de metaprogramação do Ruby, que é a abertura das classes para incluir nossos próprios métodos. Que fique claro, existe muito mais do que isso em Ruby.

Na verdade essa história toda de meta prgramação, frameworks, ORMs etc... são esforços feitos pra gente chegar num acordo com a máquina, pra gente se comunicar com a ela de um jeito que ela cumpra bem sua função e agente consiga entender. Enfiar métodos em classes do Ruby a princípio me pareceu meio estranho, meio gambiarra, mas como foi mostrado no exemplo da tarifa, podemos conseguir organizar o código de uma maneira mais "humana", uma maneira que faça mais sentido pra nós, e isso nos ajuda a manter nossa mente concentrada em outros problemas da lógica e regras de negócio.
Isso faz algum sentido pra você?

O assunto é bem vasto e esse post é só a ponta do iceberg, ai estão alguns links que me ajuram muito:

Apresentação voodoo é pra jacu (David Paniz e Leonardo Bessa)

Post do katz

aprensentação do David Thomas