Ruby: define_method vs. def




metaprogramming (2)

Als Programmierübung habe ich ein Ruby-Snippet geschrieben, das eine Klasse erstellt, zwei Objekte aus dieser Klasse instanziiert, ein Objekt abgleicht und auf method_missing angewiesen ist, um das andere Objekt zu fixieren.

Das ist der Deal. Dies funktioniert wie vorgesehen:

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

Sie werden feststellen, dass ich einen Parameter für method_missing habe. Ich tat dies, weil ich hoffte, define_method zu verwenden, um fehlende Methoden mit dem passenden Namen dynamisch zu verursachen. Es funktioniert jedoch nicht. In der Tat, sogar mit define_method mit einem statischen Namen wie folgt:

def method_missing(m)
  puts "No #{m}, so I'll make one..."
  define_method(:screech) do
    puts "This is the new screech."
  end
end

Endet mit folgendem Ergebnis:

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.

Was die Fehlermeldung verwirrender macht ist, dass ich nur ein Argument für method_missing ...


self.class.define_method (: screech) funktioniert nicht, da define_method eine private Methode ist, können Sie das tun

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 ist eine (private) Methode des Objekts Class . Sie rufen es von einer Instanz aus an. Es gibt keine Instanzmethode namens define_method , also rekursiert sie zu Ihrer method_missing , diesmal mit :define_method (der Name der fehlenden Methode) und :screech (das einzige Argument, das Sie an define_method ).

Versuchen Sie es stattdessen (um die neue Methode für alle Monkey-Objekte zu definieren):

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

Oder dies (um es nur auf dem Objekt zu definieren, an dem es aufgerufen wird, unter Verwendung der "Eigenklasse" des Objekts):

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




metaprogramming