Metaprogramação em Ruby

Metaprogramação em RubyNeste artigo, vamos ver aspectos diferentes da metaprogramação em Ruby, mas, antes de começar, você precisa saber o que é metaprogramação.

Metaprogramação é a programação de programas que escrevem ou manipulam outros programas (ou a si próprios) assim como seus dados, ou que fazem parte do trabalho em tempo de compilação. Isso permite que os programadores sejam mais produtivos ao evitar que parte do código seja escrita manualmente.

Ruby é uma linguagem de programação interpretada multiparadigma, de tipagem dinâmica e forte, com gerenciamento de memória automático, originalmente planejada e desenvolvida no Japão em 1995, por Yukihiro “Matz” Matsumoto, para ser usada como linguagem de script.

O livecoder brasileiro LucasMRThomaz desenvolveu no LiveEdu um blog com Ruby on Rails. Assista abaixo ao seu último vídeo:

Metaprogramação em Ruby é, na realidade, bem simples e isso se dá pelo fato de que todo código Ruby é executado, não há separação entre fases de compilação e runtime, cada linha de código é executado contra um self particular.

Vamos analisar especificamente como podemos ler e analisar o nosso código em Ruby, como podemos chamar métodos (ou enviar mensagens) dinamicamente e como podemos gerar novos métodos durante o tempo de execução do nosso programa.

ENJOYING THIS POST?

Get More Benefits With LiveEdu Pro

Fazendo perguntas ao nosso código

Um aspecto da metaprogramação em Ruby que se destaca é ser capaz de perguntar ao nosso código questões sobre si mesmo durante o tempo de execução. Isso também é conhecido como introspecção. Assim como podemos nos fazer perguntas como “Por que estou aqui?”, nosso código pode fazer o mesmo, embora as perguntas não possam ser tão existenciais.

Sou capaz de responder a esta chamada de método?

Podemos perguntar a qualquer objeto se ele tem a capacidade de fornecer uma resposta a uma chamada de método específico antes de fazê-lo usando o método respond_to?.

Qual é a aparência da minha cadeia de ancestralidade?

Se você verificar um modelo ActiveRecord no Rails 5, verá que ele tem 71 antepassados. Isso inclui os pais diretos através da hierarquia de classes e também os módulos que estão incluídos em qualquer árvore de classe. Isso é um pouco louco e vai mostrar o quão grande é o Rails.

Quais variáveis de instância e métodos foram definidos?

Podemos usar o método methods para nos fornecer uma lista de todos os métodos disponíveis para um objeto específico e o método instance_variables para nos fornecer uma lista das variáveis de instância definidas/usadas por este objeto.

Enviando mensagens

Ruby é uma linguagem dinâmica. Consiste de uma série de objetos que podem passar mensagens de um lado para outro entre si. Esta passagem de mensagem é geralmente o que nos referimos quando dizemos “chamar um método”. Vamos dar uma olhada no método downcase de objetos String.

Quando invocamos ou chamamos esse método usando a notação de ponto, o que estamos realmente dizendo é que estamos passando uma mensagem para a String, e ela decide como responder a essa mensagem. Neste caso, ele responde com uma versão minúscula de si mesma.

Vamos entender melhor. Há três partes com as quais estamos trabalhando: a primeira, “Roberto Alomar“, é o objeto, aquele que receberá esta mensagem. O . (ponto) diz ao objeto receptor que estaremos enviando algum comando ou mensagem. O que se segue após o ponto, downcase, é a mensagem que estamos enviando. Podemos dizer que estamos enviando a mensagem downcase para “Roberto Alomar“. Ele descobre o que fazer ou envia de volta uma vez que recebe essa mensagem.

Em Ruby, isso pode ser feito de outra maneira, usando o método send:

Geralmente, você não usaria esse formulário na programação normal, mas como o Ruby nos permite enviar mensagens (ou invocar métodos) neste formulário, ele dá a opção de enviar uma mensagem dinâmica ou chamar métodos dinamicamente.

Isso pode não parecer muita coisa, mas esta é uma das construções que nos permite escrever código muito dinâmico em Ruby. Na próxima seção, veremos como podemos gerar novo código dinamicamente em Ruby usando o método define_method.

Gerando novos métodos

Outro aspecto da metaprogramação que Ruby nos dá é a capacidade de gerar novo código durante o tempo de execução. Faremos isso usando um método da classe Module chamado define_method. Este método funciona passando um símbolo que se torna o nome do nosso novo método e, ao fornecer um bloco, damos ao nosso novo método seu corpo. Aqui está um exemplo simples:

Você já deve ter visto o método delegate antes, que vem no ActiveSupport com Rails e estende Módulo. Isso nos permite dizer que quando você chama um determinado método, você chama esse método em um objeto diferente ao invés do atual (self). Nós vamos criar uma versão muito mais simples deles como uma maneira de mostrar a metaprogramação. Você pode ver o código fonte para a versão Rails aqui.

Primeiro, vamos adicionar um novo método à classe Module (que todas as classes têm em sua cadeia de ancestrais) chamado delegar.

Quando esse método é chamado, ele irá definir um novo método cujo trabalho é delegar o trabalho para outro objeto, como um proxy.

Você pode ver que nós chamamos o método do telefone na Company, mas é o Receptionist que responde realmente a chamada.

Dólares e centavos

Você provavelmente já ouviu falar que é ruim armazenar e usar o dinheiro como um float por causa de questões de aritmética de ponto flutuante. Uma das maneiras de lidar com isso é armazenar o dinheiro em centavos. $10.25 seria armazenado no banco de dados como 1025 centavos.

Entretanto, os usuários não vão querer armazenar valores em centavos, então precisamos de algum código para nos ajudar a converter doláres e centavos. Vamos usar a metaprogramação para nos ajudar a tornar as coisas mais fáceis.

Vejamos uma classe chamada Purchase que tem um campo no banco de dados chamado price_cents. Esta é a aparência da classe:

Se este fosse um objeto ActiveRecord no Rails, não teríamos que incluir a linha attr_accessor: price_cents porque já faria isso por nós, mas para este exemplo, estamos apenas usando um objeto Ruby antigo. Este código agora nos dá a capacidade de interagir com o campo da seguinte forma:

Mas de onde vieram os métodos price e price= ? Nosso método money_fieldsmethod acaba criando estes dois novos métodos que interagem com os métodos price_cents e price_cents= que vêm da linha attr_accessor ou existem para nós a partir do ActiveRecord.

O método money_fields passa através de um ou mais campos que foram passados ao método criando métodos de leitor e escritor para a forma de dólar do campo. Para mostrar que ele funciona como esperado, aqui está um conjunto de testes que testa as diferentes conversões:

Conclusão

Metaprogramação é fantástico, mas apenas quando é usado com moderação. A metaprogramação pode ajudá-lo a escrever código repetitivo mais facilmente (como o exemplo de campos de dinheiro), pode ajudá-lo a depurar e analisar o que seu código está fazendo, mas também pode adicionar indireção e torná-lo muito mais difícil de descobrir o que está realmente acontecendo no código. Use somente a metaprogramação se ela fornecer uma clara vantagem.

A maioria dos métodos que vimos hoje vem da classe Object ou da classe Module. Explore mais por sua conta!

No ano passado, a CBSI divulgou uma ótima apostila gratuita de 170 páginas sobre Ruby para você conhecer mais desta linguagem que a cada dia ganha mais usuários.

Praticamente, toda a comunidade brasileira já deve ter ouvido falar de Fábio Akita, co-fundador e CTO da Codeminer 42 e co-organizador da Rubyconf Brasil. Se você quiser conhecer melhor este evangelizador do Ruby, confira a entrevista que o Bugginho Developer fez com ele.

 

LiveEdu Pro
Check Out the LiveEdu Pro Subscription

Read previous post:
como-enviar-um-formulario-web-em-python
Como enviar um formulário web em Python

Hoje, vamos ver três maneiras diferentes de enviar um formulário web em Python. Para isso, faremos uma pesquisa na web...

Close