5
votes

Manipulation d'une méthode avec un argument optionnel suivi d'une liste d'arguments de mot-clé

Je veux passer un argument optionnel (n'importe quel type de données, même un Hash) suivi d'une liste d'arguments de mot-clé (qui peuvent être vides) à une méthode.

Voici ce que j'ai obtenu:

my_method({foo: 'foo'})
# => [nil, {:foo=>"foo"}]

Le résultat de la méthode est comme prévu dans les cas suivants:

  • rien

    my_method({foo: 'foo'})
    # => [{:foo=>"foo"}, {}]
    
  • un sujet

    my_method(bar: 'bar')
    # => [nil, {:bar=>"bar"}]
    
  • un sujet littéral avec des options

    my_method({foo: 'foo'}, bar: 'bar')
    # => [{:foo=>"foo"}, {:bar=>"bar"}]
    
  • un Hash comme sujet avec des options

    my_method('foo', bar: 'bar')
    # => ["foo", {:bar=>"bar"}]
    
  • pas de sujet, seulement des options

    my_method('foo')
    # => ["foo", {}]
    

Lorsque vous passez un Hash comme sujet et aucune option, le résultat souhaité est:

my_method()
# => [nil, {}]

Mais j'obtiens ce qui suit; Je n'obtiens pas le bon sujet:

def my_method(subject = nil, **options)
  [subject, options]
end

Est my_method (foo: 'foo') équivalent à my_method ({foo: 'foo'}) ? Des idées sur la façon dont je pourrais obtenir le résultat souhaité?


1 commentaires

J'ai mis à jour dans ma réponse pourquoi il a été attribué au deuxième argument.


3 Réponses :


1
votes

Autant que je sache, c'est une limitation de la façon dont Ruby passe les arguments. Il ne peut pas faire la différence entre my_method ({foo: 'foo'}) et my_method (foo: 'foo')

Pourquoi ne pas pirater avec ceci?

if subject.is_a?(Hash) && options.empty?
  subject, options = nil, subject
end

Cela suppose que subject ne devrait pas être un hachage.


4 commentaires

Merci pour votre réponse. Malheureusement, je dois parfois passer un hachage à subject .


@Taschetto Ok ... mais comment distinguerais-tu un sujet nul et juste des options d'un sujet Hash et aucune option?


my_method ({foo: 'foo'}) doit être un objet de hachage et aucune option; my_method (foo: 'foo') devrait être un sujet nul et juste des options. Mais peut-être que je me trompe.


@Taschetto Je suis presque sûr que c'est une limitation de Ruby. Il ne peut pas faire la distinction entre ces deux cas. Je voudrais simplement rendre subject non facultatif dans ce cas.



0
votes

Peut-être pas la meilleure réponse, mais vous pouvez essayer avec argument de mot clé :

my_method({foo: 'foo'}, {})

Puis

my_method(subject: {foo: 'foo'})

=> [{:foo=>"foo"}, {}]

Alternative:

Vous pouvez utiliser

def my_method(subject: nil, **options)
  [ subject, options ]
end


2 commentaires

Merci pour votre réponse. Oui, ça marche. Mais dans mon cas, je devrais refactoriser un grand nombre d'appels de méthode. J'ai besoin de maintenir l'interface de la méthode.


@Taschetto alors my_method ({foo: 'foo'}, {}) peut être une solution?



3
votes

Vous voyez, vous avez des ** options comme argument qui n'ont pas de valeur par défaut et le premier argument a une valeur par défaut. Donc, comprenez bien le cas d'un seul argument ,

Chaque fois qu'un seul argument est passé, il est tenté d'assigner au deuxième argument (car le premier a la valeur par défaut nil) et s'il échoue en raison d'une incompatibilité de type, il attribue au premier argument. C'est ainsi que fonctionne my_method (4) .

Supposons maintenant que vous ayez un seul argument passé comme hachage, qui correspond à attribuer au deuxième argument, puis bien sûr, il est affecté au deuxième argument est défini par défaut sur nil.

Si vous voulez le faire fonctionner, vous pouvez faire ce qui suit,

> my_method(subject: {sd: 4})
 => [{:sd=>4}, {}]

Ou vous pouvez fournir le nom de l'argument en passant,

> my_method({sd: 4}, {})
 => [{:sd=>4}, {}]


0 commentaires