dynamically - Ruby: define_method vs. def
ruby define method (3)
Como ejercicio de programación, he escrito un fragmento de Ruby que crea una clase, instancia dos objetos de esa clase, monkeypatches un objeto y confía en method_missing para monkeypatch el otro.
Aquí está el trato. Esto funciona según lo previsto:
class Monkey
def chatter
puts "I am a chattering monkey!"
end
def method_missing(m)
puts "No #{m}, so I''ll make one..."
def screech
puts "This is the new screech."
end
end
end
m1 = Monkey.new
m2 = Monkey.new
m1.chatter
m2.chatter
def m1.screech
puts "Aaaaaargh!"
end
m1.screech
m2.screech
m2.screech
m1.screech
m2.screech
Notarás que tengo un parámetro para method_missing. Hice esto porque esperaba usar define_method para crear dinámicamente los métodos que faltan con el nombre apropiado. Sin embargo, no funciona. De hecho, incluso usando define_method con un nombre estático como ese:
def method_missing(m)
puts "No #{m}, so I''ll make one..."
define_method(:screech) do
puts "This is the new screech."
end
end
Termina con el siguiente resultado:
ArgumentError: wrong number of arguments (2 for 1)
method method_missing in untitled document at line 9
method method_missing in untitled document at line 9
at top level in untitled document at line 26
Program exited.
Lo que hace que el mensaje de error sea más desconcertante es que solo tengo un argumento para method_missing
...
self.class.define_method (: chillido) no funciona, porque define_method es un método privado que puedes hacer
class << self
public :define_method
end
def method_missing(m)
puts "No #{m}, so I''ll make one..."
Monkey.define_method(:screech) do
puts "This is the new screech."
end
define_method
es un método (privado) del objeto Class . Lo estás llamando desde una instancia . No hay un método de instancia llamado define_method
, por lo que recurre a su method_missing
, esta vez con :define_method
(el nombre del método que falta), y :screech
(el único argumento que pasó a define_method
).
Pruebe esto en su lugar (para definir el nuevo método en todos los objetos Monkey):
def method_missing(m)
puts "No #{m}, so I''ll make one..."
self.class.send(:define_method, :screech) do
puts "This is the new screech."
end
end
O esto (para definirlo solo en el objeto al que se llama, utilizando la "clase propia" del objeto):
def method_missing(m)
puts "No #{m}, so I''ll make one..."
class << self
define_method(:screech) do
puts "This is the new screech."
end
end
end
def method_missing(m)
self.class.class_exec do
define_method(:screech) {puts "This is the new screech."}
end
end
el método chillido estará disponible para todos los objetos de Monkey.