Ruby: define_method vs. def

En tant qu’exercice de programmation, j’ai écrit un fragment Ruby qui crée une classe, instancie deux objects de cette classe, associe un object à monkeypath et utilise method_missing pour monkeypatch.

Voici l’accord. 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 dynamicment 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 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éconcertant, c’est que je n’ai qu’un seul argument pour method_missing

define_method est une méthode (privée) de l’object Classe . Vous l’appelez depuis une instance . Il n’y a pas de méthode d’instance appelée define_method , elle est donc récursive dans 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 objects 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’object auquel il est appelé, en utilisant la “classe propre” de l’object):

 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 que vous pouvez 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 objects Monkey.