Ruby: define method vs. def

Jako ćwiczenie programistyczne napisałem fragment Ruby, który tworzy klasę, tworzy instancje dwóch obiektów z tej klasy, monkeypatches jeden obiekt i opiera się na method_missing aby monkeypatch drugi.

Umowa jest taka. Działa to zgodnie z przeznaczeniem:
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

Zauważysz, że mam parametr dla method_missing. Zrobiłem to, ponieważ miałem nadzieję użyć define_method do dynamicznego tworzenia brakujących metod o odpowiedniej nazwie. Jednak to nie działa. W rzeczywistości, nawet używając define_method o statycznej nazwie jak tak:

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

Kończy się następującym wynikiem:

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.

Co sprawia, że komunikat o błędzie jest bardziej oszałamiający jest to, że mam tylko jeden argument dla method_missing...

Author: Andrew Grimm, 2008-10-09

3 answers

define_method jest (prywatną) metodą obiektu klasy . Wywołujesz go z instancji . Nie ma metody instancji o nazwie define_method, więc rekursuje ona do twojej method_missing, tym razem za pomocą :define_method (Nazwa brakującej metody) i :screech (jedyny argument przekazany do define_method).

Spróbuj zamiast tego (aby zdefiniować nową metodę na wszystkich obiektach małpy):

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

Lub this (aby zdefiniować go tylko na obiekcie, do którego jest wywołany, używając obiektu "eigenclass"): {]}

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/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-05 05:15:43

Self.klasy.define_method (: screech) nie działa, ponieważ define_method jest metodą prywatną możesz to zrobić

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/doraprojects.net/template/agent.layouts/content.php on line 54
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

Metoda Screech będzie dostępna dla wszystkich obiektów małp.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-02 01:29:36