2017-07-10 9 views
3

Dans Ruby 2.4.1, j'ai une méthode comme ceci:Ruby à double paramètre floc est trop gourmand avec l'argument de Hash

def example(*args, **kwargs) 
    p args 
    p kwargs 
end 

je peux passer des arguments de position qui sont pas Hash très bien:

irb(main):001:0> example("Greetings") 
["Greetings"] 
{} 

Et si je veux utiliser des paramètres nommés, c'est très bien aussi:

irb(main):002:0> example(something: 42) 
[] 
{:something=>42} 

Mais en essayant de passer un Hash comme argument de position, cela se produit:

irb(main):002:0> example({something: 42}) 
[] 
{:something=>42} 

Je veux *args prendre {something: 42}, pas **kwargs.

Les arguments de position doivent être en option, mais même si *args étaient arg=nil, le double-floc est encore trop gourmand:

irb(main):001:0> def example(arg=nil, **kwargs) 
irb(main):002:1> p arg 
irb(main):003:1> p kwargs 
irb(main):004:1> end 
=> :example 

irb(main):005:0> example({"key":"value"}) 
nil 
{:key=>"value"} 

irb(main):006:0> example({"key":"value"}, this_is: "in kwargs") 
{:key=>"value"} 
{:this_is=>"in kwargs"} 

Comment puis-je passer un Hash comme argument de position lorsque la méthode prend également **?

+1

Cela créerait une API très confuse. Quelle est la motivation derrière le fait de faire passer un Hash dans un argument positionnel et d'avoir des options de type hash aussi? Pourquoi ne pouvez-vous pas le déclarer avec des arguments spécifiques, comme 'example (arg = nil, option: false, other: true)'? – tadman

+0

@tadman: J'espérais que le premier argument positionnel pourrait être un Hash de valeurs de chaîne à convertir en JSON en tant que charge utile afin que le code client n'ait pas à appeler '.to_json'. Les arguments nommés sont des options qui vont dans les options ['RestClient :: Resource'] (http://www.rubydoc.info/gems/rest-client/RestClient/Resource), qui peuvent prendre beaucoup d'options différentes J'ai donc pensé qu'il serait plus simple de saisir toutes les possibilités plutôt que d'en choisir plusieurs pour les transmettre. – Deltik

+0

Si vous passez une clé de chaîne à la place de la clé de symbole Elle apparaîtra dans * args. Kwargs fonctionne uniquement avec les touches de symbole. –

Répondre

2

C'est toujours difficile à gérer parce que le **kwargs va prendre en charge agressivement ce hachage. Cela présente aussi une certaine confusion de la part de l'appelant, car ils vont devoir faire attention lors de l'appel, peu importe ce que vous faites:

# Is this options or the body? It's not clear. 
example({ payload: true }) 

je recommande de passer à quelque chose de plus explicite, comme:

def example(**kwargs) 
    body = kwargs.delete(:body) 
end 

Puis l'appeler comme ceci:

example(body: { ... }, user: "username", ...) 

Si vous pouvez préciser les arguments autorisés, encore mieux:

def example(body: nil, user: nil, password: nil) 
    # ... 
end