En tant qu'exercice de programmation, j'ai écrit un extrait Ruby qui crée une classe, instancie deux objets de cette classe, monkeypatches 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. J'ai fait cela parce que j'espérais utiliser define_method pour créer dynamiquement des méthodes manquantes avec le nom approprié. Mais ça ne marche pas. En fait, même en utilisant define_method avec un nom statique comme celui-ci:
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 plus déroutant, c'est que je n'ai qu'un seul argument pour method_missing
...
define_method
est une méthode (privée) de l'objet Class. Vous l'appelez depuis un instance. Il n'y a 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 transmis à define_method
).
Essayez ceci à la place (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 auquel il est appelé, en utilisant la "classe propre" 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
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
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.