66 votes

Ruby : define_method vs. def

En guise d'exercice de programmation, j'ai écrit un extrait Ruby qui crée une classe, instancie deux objets à partir de cette classe, monkeypatch un objet, et s'appuie sur method_missing pour monkeypatch l'autre.

Voici l'affaire. Cela fonctionne comme prévu :

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

Vous remarquerez que j'ai un paramètre pour method_missing. Je l'ai fait parce que j'espérais utiliser define_method pour créer dynamiquement des méthodes manquantes avec le nom approprié. Cependant, cela ne fonctionne pas. En fait, même en utilisant define_method avec un nom statique comme ceci :

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

Se termine par le résultat suivant :

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.

Ce qui rend le message d'erreur encore plus déconcertant, c'est que je n'ai qu'un seul argument pour l'option method_missing ...

138voto

Avdi Points 13086

define_method est une méthode (privée) de l'objet Classe . Vous l'appelez depuis un instance . Il n'existe pas de méthode d'instance appelée define_method donc il revient à votre method_missing , cette fois avec :define_method (le nom de la méthode manquante), et :screech (le seul argument que vous avez passé à define_method ).

Essayez plutôt ceci (pour définir la nouvelle méthode sur tous les objets 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

Ou ceci (pour le définir uniquement sur l'objet sur lequel il est appelé, en utilisant la "eigenclass" de l'objet) :

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

4voto

Self.class.define_method(:screech) ne fonctionne pas, car define_method est une méthode privée. Vous pouvez le faire

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

4voto

Andrew Points 21
def method_missing(m)
    self.class.class_exec do
       define_method(:screech) {puts "This is the new screech."}
    end 
end

La méthode screech sera disponible pour tous les objets Monkey.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X