Ceci est pour une API publique déjà existante que je ne peux pas casser, mais que je souhaite étendre. p>
Actuellement, la méthode prend une chaîne ou un symbole ou quelque chose d'autre qui a du sens lorsqu'il est passé comme le premier paramètre à J'aimerais ajouter la possibilité d'envoyer une liste de chaînes, symboles, et cetera. Je pourrais simplement utiliser Je vais appeler envoyer p> p>
is_a? Array code>, mais il existe d'autres façons d'envoyer des listes, et ce n'est pas très rubis-ish. P>
mapper code> sur la liste, la première inclination est d'utiliser réponse_to? : Carte code>. Mais une chaîne répond également à : mappe code>, afin que cela ne fonctionne pas. P>
9 Réponses :
Pouvez-vous simplement changer de comportement basé sur le paramètre.class.name? C'est moche, mais si je comprends correctement, vous avez une seule méthode que vous passerez plusieurs types à - vous devrez différencier d'une manière ou d'une autre. P>
Alternativement, ajoutez simplement une méthode qui gère un paramètre de type de tableau. C'est un comportement légèrement différent, une méthode supplémentaire pourrait donc avoir un sens. P>
Votre deuxième commentaire est une meilleure solution, mais j'espère plus que des gourus Stackoverflow. Le premier a le même problème que is_a? Array Code>: Un utilisateur peut envoyer une chaîne qui n'est pas une chaîne code> ou une liste qui n'est pas un tableau code>.
Je ne sais pas à quel point les cordes code> strings code> sont cependant. NON Array CODE> Les listes sont assez courantes - de nombreux objets sont énumérables code>. Malheureusement, les cordes ...
Utilisez maréchal pour sérialiser vos objets avant de les envoyer. < / p>
Je suis confus. Envoyer code> est la méthode qui appelle une fonction par nom dans Ruby. Comment aidera le maréchal?
Depuis Ce que je ferais, c'est du type de canard pour être énumérable ( ou même nettoyant: p> Array code> et String code> sont tous les deux énumérables code>, il n'y a pas un moyen élégant de dire "une chose qui est une chaîne enuméroable, mais pas une chaîne , "du moins pas dans la voie en train d'être discuté. répond_to ?: [] code>) puis utilisez un cas code> code> Déclaration, comme: p>
Merci pour l'explication étendue, mais dans la question initiale, j'ai demandé quelque chose de nettoyeur que is_a? Array code>. En utilisant is_a? String code> (ou une variante comme si vous avez montré) enfreint l'API d'origine pour personne qui utilise quelque chose qui utilise quelque chose comme une chaîne pour envoyer code>. En fait, la plupart des utilisateurs utilisent probablement symbole code> à la place. Je pourrais utiliser x.is_a? (String) || x.is_a? (Symbole) Code>, mais ce n'est pas très ruby-ish, et peut casser l'API pour certains de mes utilisateurs.
Si vous ne voulez pas que vous ne voulez pas soinyyypatch, massez simplement la liste à une chaîne appropriée avant l'envoi. Si cela ne vous dérange pas de soinyyypatching ou héritant, mais que vous voulez garder la même méthode Signature:
class ToBePatched
alias_method :__old_takes_a_string, :takes_a_string
#since the old method wanted only a string, check for a string and call the old method
# otherwise do your business with the map on things that respond to a map.
def takes_a_string( string_or_mappable )
return __old_takes_a_string( string_or_mappable ) if String === string_or_mappable
raise ArgumentError unless string_or_mappable.responds_to?( :map )
# do whatever you wish to do
end
end
Peut-être que la question n'était peut-être pas assez claire, mais une nuit de sommeil m'a montré deux façons propres pour répondre à cette question.
1: 2: Envoyer des lancers to_sym code> est disponible sur String code> et symbole code> et doit être disponible sur tout ce que les charcelets comme une chaîne. p> TypeError CODE> Un tableau. P> begin
obj.send(arg, ...)
rescue TypeError
# do array stuff
end
La première solution a du sens, puisque l'envoi attend effectivement un symbole. Mais j'espère que vous n'êtes pas sérieux à propos de la deuxième solution. Les exceptions ne sont pas un flux de contrôle.
@Sarah: aléatoire, mais je pense qu'environ 40% de tous vos commentaires et réponses à propos de la phrase "Les exceptions ne sont pas du flux de contrôle".
@Telemachus - au moins récemment. :)
Disons que votre fonction est nommée Func
Je ferais un tableau des paramètres avec P>
def func(param) a = Array.new a << param a.flatten! func_array(a) end
Pourquoi ne pas les traiter tous comme des tableaux code> code>? Le comportement que vous souhaitez pour Le plus tard fort> p> Ceci est essentiellement juste une version syntaxiquement sucrée ou Réponse d'Arnaud , bien qu'il existe des différences subtiles si vous passez un tableau code> contenant d'autres Il y a une différence supplémentaire à voir avec qui retournera toujours string code> s est identique à celui d'un tableau code> contenant uniquement cette chaîne code>: [* arg] code> Trick fonctionne car l'opérateur Splat ( * code>) active un seul élément en lui-même ou un tableau code> dans une liste intégrée de Ses éléments. p> Array code> s. P> FOO code> " S Valeur de retour. Si vous appelez foo (bar ,: baz) code>, vous serez peut-être surpris d'obtenir [baz] code> retour. Pour résoudre ce problème, vous pouvez ajouter un Kestrel A >: p> arg code> comme passé. Ou vous pouvez faire retourner (obj) code> afin que vous puissiez chaîner des appels vers foo code>. C'est à vous de décider quel type de comportement de retour de valeur que vous voulez. P> p>
Beaucoup plus beau que mon post original. Je ne peux jamais m'habituer à l'opérateur *.
Un détail critique qui a été négligé dans toutes les réponses: Strings Do pas em> répondez à : mappe code>, la réponse la plus simple est donc dans la question initiale: utilisez simplement < code> répondre_to? : Carte code>. p>
entre ces 3 types, je ferais ce que
is_array = var.respond_to?(:to_h) is_string = var.respond_to?(:each_char) is_symbol = var.respond_to?(:to_proc)
Rien à voir avec votre question, mais j'espère que vous faites des chèques avant de passer la saisie de l'utilisateur à la méthode d'envoi! Yikes.
bon point. Dans notre cas, les docs indiquent clairement que le paramètre est un nom de méthode. C'est une API assez profonde que cela devrait être suffisant ....
Ce n'est pas une question de documentation - c'est un trou de sécurité. S'ils passent dans une backtitude, l'envoi du deuxième paramètre est exécuté dans une coquille. Est-ce vraiment ce que vous voulez? Vous ferez beaucoup mieux à examiner l'entrée dans une déclaration de cas, puis envoyez des symboles de votre propre construction à envoyer.
Ce n'est qu'un trou de sécurité s'il est accessible à l'extérieur. C'est l'API d'une bibliothèque et non d'une application. S'ils veulent une coquille, c'est leur problème.