47 votes

Puis-je avoir des paramètres nommés obligatoires en Ruby 2.x ?

Ruby 2.0 ajoute des paramètres nommés, comme ceci :

def say(greeting: 'hi')
  puts greeting
end

say                     # => puts 'hi'
say(greeting: 'howdy')  # => puts 'howdy'

Comment puis-je utiliser des paramètres nommés sans donner de valeur par défaut, afin qu'ils soient obligatoires ?

92voto

Marc-André Lafortune Points 34140

Il n'y a pas de méthode spécifique dans Ruby 2.0.0, mais vous pouvez peut le faire Ruby 2.1.0 avec une syntaxe telle que def foo(a:, b:) ...

En Ruby 2.0.x, vous pouvez l'appliquer en plaçant toute expression qui lève une exception, par exemple :

def say(greeting: raise "greeting is required")
  # ...
end

Si vous prévoyez de faire cela souvent (et que vous ne pouvez pas utiliser Ruby 2.1+), vous pouvez utiliser une méthode d'aide comme :

def required
  method = caller_locations(1,1)[0].label
  raise ArgumentError,
    "A required keyword argument was not specified when calling '#{method}'"
end

def say(greeting: required)
  # ...
end

say # => A required keyword argument was not specified when calling 'say'

0 votes

J'ai également tenté de combiner cette réponse avec celle de @Ben Taitelbaum : gist.github.com/rdp/5390849 :)

19voto

Adam Points 710

À l'heure actuelle (Ruby 2.0.0-preview1), vous pouvez utiliser la signature de méthode suivante :

def say(greeting: greeting_to_say)
  puts greeting
end

El greeting_to_say est juste un espace réservé qui ne sera pas évalué si vous fournissez un argument au paramètre nommé. Si vous ne le passez pas (en appelant simplement say() ), ruby lèvera l'erreur :

NameError: undefined local variable or method `greeting_to_say' for (your scope)

Cependant, cette variable n'est liée à rien et, pour autant que je sache, ne peut être référencée à l'intérieur de votre méthode. Vous pouvez toujours utiliser greeting comme variable locale pour référencer ce qui a été transmis pour le paramètre nommé.

Si tu allais vraiment faire ça je recommande d'utiliser def say(greeting: greeting) afin que le message d'erreur fasse référence au nom que vous donnez à votre paramètre. J'ai seulement choisi des noms différents dans l'exemple ci-dessus pour illustrer ce que ruby utilisera dans le message d'erreur que vous obtiendrez pour ne pas avoir fourni un argument au paramètre nommé requis.

Tangentiellement, si vous appelez say('hi') ruby va lever ArgumentError: wrong number of arguments (1 for 0) ce qui est un peu déroutant, mais ce n'est qu'un aperçu 1.

4 votes

Maintenant que je le vois, c'est évident. Et cela n'a rien à voir avec Ruby 2.0 ou les arguments nommés, cela fonctionne de la même manière avec les arguments optionnels "normaux" et cela a toujours été le cas : def m(a = b) end; m # NameError: undefined local variable or method 'b' for main:Object .

1 votes

@Adam, je trouve votre réponse un peu trompeuse. La raison pour laquelle vous obtenez ces erreurs est que vous pouvez avoir un code Ruby arbitraire comme valeur par défaut d'un argument. Cela s'applique également aux arguments positionnels normaux et n'a rien à voir avec les arguments de mots-clés requis. Essayez de mettre Time.now comme valeur par défaut et voyez ce qui se passe. Réponse de Mark-Andre est plus à propos ici.

0 votes

@Dimitar Je pensais que le mécanisme était clair, et qu'il s'agissait d'un joli hack autour d'une fonctionnalité manquante qui produisait quand même une erreur utile. Mais j'ai remarqué que les notes de publication de la version 2.1.0-preview1 et la réponse de Marc-André seront en effet la voie officielle, donc j'ai changé la réponse acceptée (désolé, @Adam).

12voto

Ben Taitelbaum Points 5119

En combinant les solutions de @awendt et @Adam,

def say(greeting: ->{ raise ArgumentError.new("greeting is required") }.call)
  puts greeting
end

Vous pouvez assécher cela avec quelque chose comme :

def required(arg)
  raise ArgumentError.new("required #{arg}")
end

def say(greeting: required('greeting'))
  puts greeting
end

Et en combinant cela avec la solution de @Marc-André : https://gist.github.com/rdp/5390849

4voto

thedanotto Points 1852

En Ruby 2.3, je peux faire

def say(greeting:)
  puts greeting
end

Alors utilisez-le avec...

say(greeting: "hello there")

0 votes

Ceci devrait être marqué comme la bonne réponse car les nouvelles versions de Ruby le permettent. Vous obtenez ArgumentError gratuitement.

3voto

awendt Points 2569

Pourquoi pas :

def say(greeting: nil)
  greeting or raise ArgumentError
  puts greeting
end

say                     # => raises ArgumentError
say(greeting: 'howdy')  # => puts 'howdy'

En dehors de cela, la tâche s'annonce difficile. Selon le ce site Les arguments des mots-clés "sont des paramètres nommés qui ont des valeurs par défaut".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X