Blog do TaQ

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);

Tags:


Comentários

comments powered by Disqus

Twitter