Esto funciona (probado en IRB):
NOTA: Esto cambia sólo str
- no todos los casos de cadena. Lea a continuación para obtener más información sobre por qué esto funciona
another_str = "please don't change me!"
str = "ha, try to change my to_s! hahaha!"
proc = Proc.new { "take that, Mr. str!" }
singleton_class = class << str; self; end
singleton_class.send(:define_method, :to_s) do
proc.call
end
puts str.to_s #=> "take that, Mr. str!"
puts another_str.to_s #=> "please don't change me!"
# What! We called String#define_method, right?
puts String #=> String
puts singleton_class #=> #<Class:#<String:0x3c788a0>>
# ... nope! singleton_class is *not* String
# Keep reading if you're curious :)
Esto funciona porque se está abriendo de singleton class str y la definición de un método allí. Debido a que esto, así como la llamada a Module#define_method, tiene lo que algunos llaman un "alcance plano", puede acceder a variables que estarían fuera del alcance si usó def to_s; 'whatever'; end
.
Es posible que desee echa un vistazo a algunos de estos otros "hechizos" metaprogramación aquí:
media.pragprog.com/titles/ppmetr/spells.pdf
¿Por qué sólo cambian str
?
Porque Ruby tiene un par de trucos interesantes en sus mangas. En el modelo de objetos de Ruby, una invocación a un método da como resultado que el receptor no solo busque su clase (y son antepasados ), sino también su clase singleton (o como lo llamaría Matz, es clase propia). Esta clase singleton es lo que le permite [re] definir un método para un solo objeto. Estos métodos se llaman "métodos singleton". En el ejemplo anterior, estamos haciendo exactamente eso: definir un nombre de método singleton to_s
. Es functionaly idéntica a ésta:
def str.to_s
...
end
La única diferencia es que se llega a utilizar un cierre al llamar Module#define_method
, mientras que def
es una palabra clave, lo que resulta en un cambio de alcance.
¿Por qué no puede ser más sencillo?
Bueno, la buena noticia es que se está programando en Ruby, por lo que no dude en ir loco:
class Object
def define_method(name, &block)
singleton = class << self; self; end
singleton.send(:define_method, name) { |*args| block.call(*args) }
end
end
str = 'test'
str.define_method(:to_s) { "hello" }
str.define_method(:bark) { "woof!" }
str.define_method(:yell) { "AAAH!" }
puts str.to_s #=> hello
puts str.bark #=> woof!
puts str.yell #=> AAAH!
Y, si usted es curioso ...
Usted conocer los métodos de clase? O, en algunos idiomas, los llamaríamos métodos estáticos? Bueno, esos no realmente existen en Ruby. En Ruby, los métodos de clase son solo métodos definidos en la clase singleton del objeto Class.
Si todo eso suena loco, eche un vistazo a los enlaces que proporcioné arriba. Una gran parte del poder de Ruby solo se puede aprovechar si sabes cómo metaprogramar, en cuyo caso querrás saber realmente sobre las clases/métodos únicos y, más en general, el modelo de objetos de Ruby.
HTH
-Charles
Pero necesito modificarlo solo en un objeto, no en todos (en la clase). – Pablo
Eso es lo que hace. Cada objeto tiene lo que se llama una clase singleton: su propia clase personal para almacenar métodos. Pruébelo: verá que otras instancias de String no se ven afectadas. Actualizaré mi respuesta con una prueba para mostrar que funciona. – Charles
Oh, mi error, lo intentaré. – Pablo