with method dynamically define_method define ruby metaprogramming

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.