Ruby: definir el método vs def


Como un ejercicio de programación, he escrito un fragmento de Ruby que crea una clase, crea instancias de dos objetos de esa clase, monkeypatches un objeto, y se basa en method_missing para monkeypatch el otro.

Este es 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

Te darás cuenta de que tengo un parámetro para method_missing. Hice esto porque esperaba usar define_method para crear dinámicamente métodos faltantes con el nombre apropiado. Sin embargo, no funciona. En realidad, incluso usando define_method con un nombre estático como este:

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...

Author: Andrew Grimm, 2008-10-09

3 answers

define_method es un método (privado) de la clase object . Lo estás llamando desde una instancia . No hay ningún método de instancia llamado define_method, por lo que reproduce a su method_missing, esta vez con :define_method (el nombre del método que falta), y :screech (el único argumento que se pasa 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 sobre el que se invoca, utilizando el "clase propia"):

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
 135
Author: Avdi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-05 05:15:43

Auto.clase.define_method (: screech) no funciona, porque define_method es un método privado puedes hacer eso

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
 4
Author: Bill Lynch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-27 18:44:58
def method_missing(m)
    self.class.class_exec do
       define_method(:screech) {puts "This is the new screech."}
    end 
end

El método Screech estará disponible para todos los objetos Monkey.

 4
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-02 01:29:36