2010-11-09 6 views
2

Je suis aux prises avec du code qui ressemble à l'exemple ci-dessous (mais qui fait quelque chose d'utile). Le bloc qui est passé à def_my_method est bien sûr créé dans le contexte de la classe, quand je veux l'évaluer dans le contexte de l'instance qui a la méthode de l'instance. Comment puis-je faire cela?Ruby: Passer un bloc à une macro de classe qui définit des méthodes d'instance

module Formatters 
    # Really useful methods, included in several classes 
    def format_with_stars(str) 
    return "*** #{str} ***" 
    end 
end 

class Test 
    include Formatters 
    STRINGS = ["aa", "bb"] 

    def self.def_my_method(method_name, another_parameter, &format_proc) 
    define_method(method_name) do 
     # In reality, some more complex code here, then... 
     return STRINGS.map(&format_proc) 
    end 
    end 

    def_my_method(:star_strings, "another_parameter") { |str| format_with_stars(str) } 
    # Define other methods 
end 

tt = Test.new 
puts tt.star_strings 
# Throws undefined method `format_with_stars' for Test:Class (NoMethodError) 

Répondre

3

Vous pouvez utiliser instance_exec pour exécuter le bloc transmis dans le bon contexte. Au lieu de passer &format_proc directement à l'appel à map, passez un bloc qui l'appelle en utilisant instance exec.

Quelque chose comme ceci:

def self.def_my_method(method_name, another_parameter, &format_proc) 
    define_method(method_name) do 
    # In reality, some more complex code here, then... 
    return STRINGS.map{|str| instance_exec(str, &format_proc)} 
    end 
end 

Ce produit ceci:

$ ruby tt.rb 
*** aa *** 
*** bb *** 

pour moi (où tt.rb est le nom arbitraire que j'ai donné le fichier), qui je pense est ce que vous voulez pour cette Exemple.

+1

Vous n'avez pas besoin du proc enveloppé, vous pouvez simplement faire 'STRINGS.map {| str | instance_exec (str, & format_proc)} '. – sepp2k

+0

@ sepp2k - oh oui, c'est une façon plus claire - merci. J'ai incorporé votre suggestion dans la réponse. (Quelqu'un intéressé - jetez un coup d'œil à l'historique d'édition pour voir mon idée originale d'utiliser un wrapper 'lambda' pour passer à' map'.) – matt

+0

instance_exec semble parfait, mais ce n'est pas le noyau Ruby est-il? Mon Googling suggère qu'il s'agit d'une extension à Object introduite dans Rails 2. Mon exemple simplifié était Ruby pur, bien que le vrai soit Rails - mais Rails 1.2! Donc, je ne vois pas comment utiliser ça. :-( –

1
 

... 

class Test 
- include Formatters 
+ extend Formatters 

... 
 

devrait faire l'affaire.

+0

Cela fonctionne pour mon exemple trivial, mais seulement en changeant mes méthodes d'instance en méthodes de classe, ce qui n'est certainement pas quelque chose que je peux faire dans le vrai code. –

Questions connexes