Métodos singleton no Ruby 1.9
Publicado em Developer
Durante o percurso do curso (credo!) da segunda turma de Ruby eu percebi que algumas das "magias negras" da linguagem podem confundir (e talvez até assustar) quem está chegando agora. Apesar de eu tentar passar esse tipo de coisa de um jeito mais claro, realmente tem horas que fica meio complicado "externar" alguns dos conceitos que ocorrem nessas coisas que são diferenciais de Ruby em relação á algumas outras linguagens. Toda vez que eu vejo que não consegui passar o conceito claro de uma tacada só fico pensando em como revisar a didática e os códigos da aula para atingir isso.
Por exemplo, a "facilidade" de inserir um método em um objeto (não na classe), que pode ser feito assim:
1 str1 = "oi" 2 str2 = "hello" 3 4 (class << str1; self; end;).class_eval do 5 define_method(:foo) do 6 puts "bar!" 7 end 8 end 9 10 str1.foo 11 str2.foo
Rodando isso vamos ter:
[taq@~]ruby singleton.rb bar! singleton.rb:11: undefined method `foo' for "hello":String (NoMethodError)
Até aí tudo bem, somente str1 que tem o método, mas o lance do (class << str1; self; end;) realmente assusta um pouco. Aí fui dar uma verificada no Ruby 1.9, que tem um método que facilita muito esse tipo de coisa e ... ué, cadê o mardito?
Fiquei até as tantas procurando o dito cujo no código do SVN, tanto no código em Ruby como no código em C, escovando bits e nada de achar. Aí chutei o balde e pedi ajuda, mandei uma mensagem para a lista de discussão perguntando onde foi que o método se enfiou. A primeira resposta foi que o método não estava definido, mas puxa, isso eu já sabia, pelo menos no código atual. Aí mencionei algumas URLs onde ele já havia sido comentado, inclusive na documentação do 1.9 e no Changelog, comentado pelo próprio Matz. Como alguma outra resposta estava demorando um pouquinho e eu estava bem curioso sobre o assunto, entrei em contato com o Mauricio Fernandez e perguntei se ele sabia de alguma coisa.
Ele me respondeu que realmente o código do método estava lá, mas foi perdido (acidentalmente ou não) durante o merge do YARV (que inclusive eu mencionei aqui). E hoje o Nobuyoshi Nakada me confirmou isso e indicou que já foi corrigido. Vejam a thread aqui.
E agora, senhoras e senhores, com vocês, o novo jeito de fazer o código acima, já no Ruby 1.9, com define_singleton_method:
1 str1 = "oi" 2 str2 = "hello" 3 4 str1.define_singleton_method(:foo) { puts "bar!" } 5 6 str1.foo 7 str2.foo
Rodando o bicho:
[taq@]ruby1.9 singleton2.rb bar! singleton2.rb:7:in `method_missing': undefined method `foo' for "hello":String (NoMethodError)
Melhor de bom hein? E tem o fato que a performance do Ruby 1.9 está muito boa, tem casos aqui que eu vi que chega a ficar de 2 a 4 vezes mais rápida! Vou montar alguns benchmarks e publico o resultado aqui depois.
Atualizado: Dei uma olhada no Changelog da versão que peguei do SVN e aqui está o dito cujo, inclusive com um link para a minha mensagem na lista Ruby-talk:
Thu Aug 30 14:06:50 2007 Nobuyoshi Nakada* proc.c (rb_obj_define_method): reverted. [ruby-talk:266637]
Aqui está o código que voltou:
1 /* Kernel */ 2 rb_define_method(rb_mKernel, "define_singleton_method", rb_obj_define_method, -1);
Comentários
Comentários fechados.
Artigos anteriores
- Pull requests em modo raiz - sex, 22 de dezembro de 2023, 09:57:09 -0300
- Qual a idade do seu repositório? - ter, 27 de dezembro de 2022, 12:50:35 -0300
- Utilizando ctags em projetos Rails mais recentes - qui, 24 de junho de 2021, 08:23:43 -0300
- Fazendo o seu projeto brotar - seg, 15 de julho de 2019, 08:57:05 -0300
- Learn Functional Programming with Elixir - sex, 02 de março de 2018, 18:47:13 -0300
- Ambiente mínimo - Driver Driven Development - qua, 23 de agosto de 2017, 15:15:03 -0300
- Ambiente mínimo - repositórios de código - dom, 16 de abril de 2017, 13:02:14 -0300
- Ambiente mínimo - terminal e navegador - dom, 02 de abril de 2017, 21:43:29 -0300
- Utilizando muitas gems no seu projeto? - sáb, 29 de outubro de 2016, 11:57:55 -0200
- Desenvolvedores e inteligência artificial - seg, 11 de julho de 2016, 09:09:38 -0300
Tem esse post bem antigo do Why que explica bem legal essa coisa de metaclasses e como declarar métodos singleton. Inclusive, de cara ele já mostra alguns pequenos métodos utilitários para trabalhar com isso (que uso sempre que preciso).
http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
Vale a pena dar uma checada
Caio, obrigado! Realmente fica bem mais fácil assim, e por isso que eu corri atrás quando não vi o método no código ehehe. :-)
Lucious, com Ruby você pode mexer em muitas coisas e talvez "bagunçar o coreto" em algumas, como por exemplo, alterar algum método de alguma classe "base" para um comportamento não esperado, e pode dar um tiro no pé fácil, fácil fazendo isso. No caso acima, não há limitações no método inserido diferentes dos outros similares que já estavam definidos no objeto.
Eu particularmente vejo esse tipo de coisa como uma coisa boa e não como uma coisa ruim, pois se você souber o que está fazendo a linguagem te dá muitos recursos e muita flexibilidade. Do contrário ... bom, aí é melhor nem mexer. :-)
Taq, eu me amarro em Ruby, ouvi falar há algum tempo e comecei a estudar, apesar de ter parado um pouco. Mas esse lance de definir métodos em objetos não diminui a segurança? Afinal posso definir um método que mexe em coisa que eu normalmente não poderia mexer, ou não é assim que funciona? Existe alguma restrição à o que pode ser feito com método "pós definido"?
Olá, Taq, parabéns pelo post!
Eu também nunca fui fã desse (class << str1; self; end;), mas como eu nunca usei ele pra coisas muito úteis não me irritava tanto.
Essa nova maneira ficou realmente melhor (sempre que vejo um "eval", em qualquer linguagem, logo penso que foi uma gambiarra dos criadores, mesmo em Ruby).