Skip to content
Menu
CDhistory
CDhistory

Auditoria do Método Ruby Usando o Módulo#prepend

Posted on Dezembro 12, 2021 by admin

Ontem eu estava entrevistando para um cargo de desenvolvedor Ruby em uma empresa aqui em Denver quando meu entrevistador colocou um bom desafio de metaprogramação: ele me pediu para escrever um auditor de método automatizado.

Ele descreveu um método de classe que pegaria o nome de um método de instância e geraria uma trilha de papel automática e logada a qualquer momento que o método alvo fosse chamado. Algo como isto:

Que produziria uma saída parecida com isto:

Produzi uma solução que funcionava mas envolvia alguns hacks potencialmente perigosos para Object#send. Antes de apresentar minha solução, eu disse “isso provavelmente é uma má idéia, mas…” e eu estava certo. Boas ideias raramente começam assim.

A solução que ele propôs envolvia o método de invólucro frequentemente ignorado Module#prepend e eu achei que era fixe o suficiente para justificar um write-up.

A estratégia era usar o comportamento de Module#prepend para criar um método de invólucro gerado dinamicamente para Messenger#share.

Naquele wrapper, teremos uma mensagem “Performing” disparada antes do corpo do método ser executado, a própria execução do método, e depois uma mensagem final “Exiting” assim que a execução do método tiver terminado com sucesso.

But to understand how to use Module#prepend to do this, we need first a good understanding of how method inheritance works in Ruby.

  • Ancestor Chains
  • O Módulo em si mesmo
  • Arrasar tudo junto
  • Conclusion

Ancestor Chains

Cada objecto Ruby fica no fim do que se chama uma Ancestor Chain. É uma lista dos objetos dos antepassados dos quais o objeto herda. Ruby usa esta Corrente Ancestral para determinar qual versão de um método (se houver alguma) é executada quando o objeto recebe uma mensagem.

Pode realmente ver a árvore dos antepassados para qualquer objeto chamando Module#ancestors. Por exemplo, aqui está a cadeia de ancestrais para a nossa Messenger class:

Quando nós #include ou #prepend um módulo em uma classe, Ruby faz alterações na cadeia de ancestrais dessa classe, ajustando quais métodos são encontrados e em que ordem. A grande diferença entre #include e #prepend é onde essa mudança é feita. Vamos criar um módulo chamado Auditable que irá (eventualmente) conter o código que faz a nossa magia de auditoria:

Se usarmos Module#include para importar os métodos (actualmente inexistentes) de Auditable, o Ruby irá apertar esse módulo como um antepassado da nossa classe Messenger. Aqui, veja por si mesmo:

Quando chamamos um método de Messenger, o Ruby irá olhar para o que está definido na classe Messenger e – se não conseguir encontrar um método que corresponda à chamada – subirá a cadeia de ancestrais para Auditable para procurar novamente. Se ele não encontrar o que está procurando em Auditable ele passa para Object e assim por diante.

Isso é #include. Se em vez disso usarmos Module#prepend para importar o conteúdo do módulo, obtemos um efeito totalmente diferente:

#prepend faz de Messenger um antepassado de Auditable. Espere. O quê?

Isto pode parecer que Messenger é agora uma superclasse de Auditable, mas não é exactamente isso que está a acontecer aqui. Instâncias da classe Messenger ainda são instâncias da classe Messenger mas o Ruby irá agora procurar métodos para usar para Messenger no módulo Auditable antes de os procurar na própria classe Messenger.

E isso, amigos, é o que vamos aproveitar para construir este auditor: se criarmos um método chamado Auditable#share, o Ruby irá descobrir isso antes de encontrar Messenger#share. Podemos então usar um super chamar em Auditable#share para acessar (e executar!) o método original definido em Messenger.

O Módulo em si mesmo

Não vamos realmente criar um método chamado Auditable#share. Por que não? Porque queremos que este seja um utilitário flexível. Se codificarmos o método Auditable#share, poderemos usá-lo apenas em métodos que implementem o método #share. Ou pior, teríamos que reimplementar este mesmo padrão de auditor para cada método que queremos auditar. Não obrigado.

Então, ao invés disso, vamos definir nosso método dinamicamente e o método da classe audit_method para dispará-lo:

Quando audit_method é chamado em uma classe implementadora, um método é criado no módulo Auditable com o mesmo nome do método para auditoria. No nosso caso, ele irá criar um método chamado Auditable#share. Como mencionado, Ruby irá encontrar este método antes de encontrar o método original em Messenger, porque estamos a pré-utilizar o módulo Auditable na classe de implementação.

Isso significa que podemos usar uma super chamada para alcançar a cadeia dos antepassados e executar Messenger#send. Quando fazemos isso, passamos os argumentos que coletamos (*arguments) pela chain também.

Após termos chamado o método original, imprimimos nossa mensagem de saída e a chamamos de um dia. Bom trabalho, gang!

>

Arrasar tudo junto

Agora é só uma questão de prepend levar este módulo para a nossa classe Messenger e devemos estar prontos para ir:

E bom Deus funciona:

As implicações aqui são enormes para a auditoria, mas há mais neste truque. Você pode usar o prepending para mudar o comportamento dos objetos sem alterar os objetos em si. Esta é uma forma adaptável e clara de criar componentes de maior ordem em Ruby. Teste de desempenho, tratamento de erros. Você pode fazer muito aqui.

Conclusion

Módulos Ruby são mais complicados do que a maioria das pessoas pensam. Não é tão simples como “despejar” código de um lugar para o outro, e compreender essas diferenças desbloqueia algumas ferramentas realmente limpas para o seu cinto de utilidades.

>

Você deve ter notado que eu só falei sobre Module#include e Module#prepend hoje, e que eu não toquei em Module#extend. Isso é porque funciona muito diferente dos seus primos. Vou escrever uma explicação detalhada de Module#extend em breve para completar o conjunto.

Por agora, se quiserem saber mais, recomendo a leitura dos módulos Ruby: Incluir vs Pré-Gaste vs Extender por Léonard Hetsch. Foi muito útil para juntar tudo isto.

Deixe uma resposta Cancelar resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *

Artigos recentes

  • Acela está de volta: NYC ou Boston por $99
  • Entrada OMIM – # 608363 – CHROMOSOME 22q11.2 SÍNDROME DE DUPLICAÇÃO
  • Kate Albrecht’s Parents – Learn More About Her Father Chris Albrecht And Mother Annie Albrecht
  • Temple Fork Outfitters
  • Burr (romance)

Arquivo

  • Fevereiro 2022
  • Janeiro 2022
  • Dezembro 2021
  • Novembro 2021
  • Outubro 2021
  • Setembro 2021
  • Agosto 2021
  • Julho 2021
  • Junho 2021
  • Maio 2021
  • Abril 2021
  • DeutschDeutsch
  • NederlandsNederlands
  • SvenskaSvenska
  • DanskDansk
  • EspañolEspañol
  • FrançaisFrançais
  • PortuguêsPortuguês
  • ItalianoItaliano
  • RomânăRomână
  • PolskiPolski
  • ČeštinaČeština
  • MagyarMagyar
  • SuomiSuomi
  • 日本語日本語
©2022 CDhistory | Powered by WordPress & Superb Themes