2016-05-13 2 views
-2

J'ai une liste de noms de symboles de méthodes que je voudrais appeler avec quelques arguments. C'est pour une installation d'essai comparative simple que j'essaye de construire. Voici le code essentiel pour les essais:Ruby - méthodes d'appel nommées par des variables de symbole

data = [ 
    ["value", true], 
    ["any value here", true], 
    ["Value", true], 
] 

def test_value_1(string) 
    string == "value" 
end 

def test_value_2(string) 
    string.gsub(/.*?value.*?/, "\\1") 
end 

def test_value_3(string) 
    string.downcase == "value" 
end 

#tests 
[:test_value_1, :test_value_2, :test_value_3].each do |method| 
    data.each do |test| 
    value = test[0] 
    expected_result = test[1] 
    puts "#{method}: #{method.to_proc.call(value) == expected_result ? 'Pass' : 'Fail'}: '#{value}'" 
    end 
end 

Le bit important est le bloc final, après le commentaire #tests. Les méthodes data et test_value_* sont simplement des exemples d'utilisation du bloc de test et n'ont aucune signification intrinsèque, il n'est donc pas nécessaire de creuser.

Ce que je suis vraiment en train de faire se résume à cet extrait de code:

method.to_proc.call(value) 

Et dans ce cas, value est le nom du symbole d'une méthode dans l'espace de noms global (pas directement une méthode d'un objet).

C'est la sortie d'erreur que je reçois:

>$ ruby symbol_methods.rb 
symbol_methods.rb:33:in `call': private method `test_value_1' called for "value":String (NoMethodError) 
    from symbol_methods.rb:33:in `block (2 levels) in <main>' 
    from symbol_methods.rb:30:in `each' 
    from symbol_methods.rb:30:in `block in <main>' 
    from symbol_methods.rb:29:in `each' 
    from symbol_methods.rb:29:in `<main>' 

Je suis perplexe. J'ai essayé public_send(method, value) et method.to_proc.call(value), mais les deux résultent dans l'erreur private method.

Quelle serait la bonne façon d'appeler une méthode nommée comme un symbole dans ce cas? Je cherche à la fois une explication et une réponse syntaxiquement correcte.

Répondre

0

Après une bonne quantité de recherche, je trouve une réponse alternative que Object#send, qui a un avantage caractéristique non prévu. La solution consiste à utiliser le Object#method pour renvoyer un objet Method pour le nom du symbole.

Un objet Method est comme un objet appelable Proc, donc il implémente l'interface #call, ce qui correspond bien à la facture. Object a de nombreux helpers utiles définis dans son interface.

Dans le contexte de la question initiale, voici comment cela fonctionne:

#tests 
[:test_value_1, :test_value_2, :test_value_3].each do |method| 
    data.each do |test| 
    value = test[0] 
    expected_result = test[1] 
    puts "#{method}: #{self.method(method).call(value) == expected_result ? 'Pass' : 'Fail'}: '#{value}'" 
    end 
end 

Les bits importants sont les suivants:

self.method(method).call(value) 

Ce convertira le nom du symbole à un objet Method, puis invoquer la méthode avec value fournie en tant que paramètre. Cela fonctionne environ de manière équivalente au send method solution, en termes fonctionnels. Cependant, il y a quelques différences à noter.

send va être un peu plus efficace, car il n'y a pas de surcharge dans la conversion en Method. Method#call et send utilisent différents mécanismes d'appel internes, et il semble que send a également moins de temps d'appel.

La caractéristique inattendue d'utiliser Object#method est que l'objet Method est facilement converti en un objet Proc (en utilisant Method#to_proc). En tant que tel, il peut être stocké et transmis comme un objet de première classe. Cela signifie qu'il peut être fourni à la place d'un bloc ou fourni comme rappel, ce qui le rend utile pour la mise en œuvre de solutions de répartition flexibles.

4

utilisez plutôt send.

puts "#{method}: #{send(method, value) == expected_result ? 'Pass' : 'Fail'}: '#{value}'" 
+1

@mudasobwa Le point ici est d'appeler une méthode privée. 'public_send' est précisément ce qui ne fonctionnera pas. – sawa

+0

@sawa oh, en effet, désolé pour cela. – mudasobwa

+0

@delta est équivalent à 'send (méthode, value == expected_result? 'Pass': 'Fail')' - il devrait être 'send (méthode, valeur) == expected_result? 'Pass': 'Fail''. Parens importe. –