Skip to content

Instantly share code, notes, and snippets.

@kelvinst
Last active August 7, 2023 20:49
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kelvinst/fccfe340b9a9eea2befe to your computer and use it in GitHub Desktop.
Save kelvinst/fccfe340b9a9eea2befe to your computer and use it in GitHub Desktop.
Metaprogramanção em ruby - o básico

Metaprogramanção em ruby - o básico

Bem, se você é um programador há algum tempo já deve ter ouvido falar sobre metaprogramação. Eu sei, dá três tipos de arrepio só de ouvir o termo. E sim, é uma coisa bem cabeluda, é um recurso muito poderoso e muitas vezes difícil de entender. Mas agora seus problemas acabaram 👍 👍! Vou explicar pra vocês o que é metaprogramação e como fazer isso em ruby!

O que é?

Certo, sobre o termo e o conceito então: metaprogramação é um termo usado para definir um programa, ou uma rotina, que manipula o próprio programa (ou outro, tanto faz) em tempo de execução. Ou seja, um programa que programa. É basicamente um código que permite alterar e criar mais código ao ser executado.

Se você pensar bem sobre esta teoria é bem fácil elencar um metaprograma certo? Consegue adivinhar um tipo de metaprograma que você usa no seu dia a dia? Isso mesmo! Compiladores são metaprogramas! Você deve estar pensando: "Ah, mas um compilar não altera o próprio código, ele só gera novo código". Pois então, gerar novo código a partir de um código é metaprogramar. Sem metaprogramação estaríamos escrevendo rotinas em Assembly ainda.

Mas tá, um compilador é algo muito difícil de implementar, então metaprogramar deve ser bem difícil! Agora que vem a melhor parte: esse conceito é aplicado de forma muito simples no ruby. Como o ruby é uma linguagem interpretada, ou seja, o código vai sendo "traduzido" conforme executa, ele tem recursos de metaprogramação desde a camada mais básica de sua infraestrutura!

Como faz?

Então, agora botando a mão na massa! Meu objetivo aqui é mostrar para vocês alguns dos recursos mais usados para metaprogramar em ruby. Logo, será uma visão geral sobre como metaprogramar, minha intenção não é demonstrar como funcionam por baixo ou algo assim. Isso fica para um próximo post.

Runtime evaluation

Muitas linguagens já suportam a metaprogramação, e cada uma de uma forma bem específica. Uma forma que boa parte das linguagens oferece é o bom ou mau e velho runtime evaluation, que te permite executar código a partir de um texto qualquer.

Em ruby é assim:

eval "puts 'oi'" # oi

Javascript tem isso também:

eval('console.log("oi")'); // oi

Mas não use eval por favor, eval == evil! Se você não se cuidar, isso pode permitir injeção de código no seu programa! E injeção de código pode deixar o seu dia um inferno mesmo.

Open classes

Mas então usar o que? Existem muitos outros recursos que você pode usar para metaprogramar! E não é difícil! Um exemplo é o que chamamos de open classes, que é a habilidade de reabrir uma classe que já existe e implementar coisa nova nela. Isso é muito poderoso!

Você deve lembrar que em ruby TUDO é objeto, inclusive números por exemplo. Então olha o que dá pra fazer:

# se você consultar qual a classe de um número assim:
1.class
# => Fixnum

# então agora que sabemos que a Fixnum é a classe dos números, vamos ver o que podemos fazer
class Fixnum
  def square
    self * self # self ** 2 também funciona
  end
end

# agora é só chamar o método que acabamos de criar na classe Fixnum
2.square
# => 4

Viu? Legal né? Mas denovo, use isso com cuidade, você não vai quere sobreescrever um comportamento padrão dos números por exemplo:

class Fixnum
  def *(value)
    self + value
  end
end

3 * 10
# => 13

Mas é tudo questão de bom senso também, certo? Pior que as vezes você não sabe que está sobreescrevendo um método de outra lib por exemplo. Mas não vamos entrar em muitos detalhes!

Ghost methods

Vamos falar sobre mais um recurso do ruby que permite metaprogramação! Vou falar sobre algo que é muito usado em frameworks ruby: method_missing. O recurso de method_missing te permite programar uma ação que deve ser feita quando alguém chamar um método do objeto que não tenha sido declarado. O nome usado em geral para este recurso é ghost methods. Olha como implementar isso em ruby:

class Hello
  def say_hello
    puts 'hello'
  end
end

hello = Hello.new
hello.say_hello
# hello
    
class HelloDecorator
  def initialize(object)
    @object = object
  end
  
  def say_hi
    puts 'hi'
  end
  
  def method_missing(method_name, *args)
    @object.send(method_name, *args)
  end
end

hi = HelloDecorator.new(hello)
hi.say_hi
# hi

hi.say_hello
# hello

Você deve ter percebido que usei o método send na variável @object. Este método send é outro recurso do ruby, que permite chamar um método a partir do nome dele, que no caso eu recebi esse nome como parâmetro no method_missing. Outro jeito de implementar uma chamada de método a partir do nome é com algo que comentei antes: com eval. Mas como eu disse, eval == evil, então: use send sempre que puder. 😁

Conclusão

Acredito que já seja bastante coisa para você processar por hoje, mas não pare por aí, tem muito mais sobre metaprogramação em ruby que é muito legal! Talvez em um próximo post eu compartilhe com vocês mais algumas coisas!

@SparK-Cruz
Copy link

Legal a ideia do method_missing chamar um método em um objeto interno, isso me lembra a implementação de prototype do javascript. Dependendo do objeto que foi dado new/create, a instância tem um prototype diferente e caso o método não exista no objeto atual ele olha no prototype.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment