Je joue avec Ruby on Rails et j'essaie de créer une méthode avec des paramètres facultatifs. Apparemment, il y a plusieurs façons de le faire. J'essaie de nommer les paramètres facultatifs comme des hachages et sans les définir. La sortie est différente. Regarde:
# This functions works fine!
def my_info(name, options = {})
age = options[:age] || 27
weight = options[:weight] || 160
city = options[:city] || "New York"
puts "My name is #{name}, my age is #{age}, my weight is #{weight} and I live in {city}"
end
my_info "Bill"
-> My name is Bill, my age is 27, my weight is 160 and I live in New York
-> OK!
my_info "Bill", age: 28
-> My name is Bill, my age is 28, my weight is 160 and I live in New York
-> OK!
my_info "Bill", weight: 200
-> My name is Bill, my age is 27, my weight is 200 and I live in New York
-> OK!
my_info "Bill", city: "Scottsdale"
-> My name is Bill, my age is 27, my weight is 160 and I live in Scottsdale
-> OK!
my_info "Bill", age: 99, weight: 300, city: "Sao Paulo"
-> My name is Bill, my age is 99, my weight is 300 and I live in Sao Paulo
-> OK!
****************************
# This functions doesn't work when I don't pass all the parameters
def my_info2(name, options = {age: 27, weight: 160, city: "New York"})
age = options[:age]
weight = options[:weight]
city = options[:city]
puts "My name is #{name}, my age is #{age}, my weight is #{weight} and I live in #{city}"
end
my_info2 "Bill"
-> My name is Bill, my age is 27, my weight is 160 and I live in New York
-> OK!
my_info2 "Bill", age: 28
-> My name is Bill, my age is 28, my weight is and I live in
-> NOT OK! Where is my weight and the city??
my_info2 "Bill", weight: 200
-> My name is Bill, my age is , my weight is 200 and I live in
-> NOT OK! Where is my age and the city??
my_info2 "Bill", city: "Scottsdale"
-> My name is Bill, my age is , my weight is and I live in Scottsdale
-> NOT OK! Where is my age and my weight?
my_info2 "Bill", age: 99, weight: 300, city: "Sao Paulo"
-> My name is Bill, my age is 99, my weight is 300 and I live in Sao Paulo
-> OK!
Qu'est-ce qui ne va pas avec la deuxième approche pour les paramètres facultatifs?
Qu'est-ce que je rate?
La manière dont les arguments facultatifs fonctionnent dans Ruby est que vous spécifiez un signe égal et si aucun argument n'est passé, alors ce que vous avez spécifié est utilisé. Donc, si aucun second argument n'est passé dans le second exemple, alors
{age: 27, weight: 160, city: "New York"}
est utilisé. Si vous utilisez la syntaxe de hachage après le premier argument, alors le hash exact est passé.
Le mieux que vous puissiez faire est
def my_info2(name, options = {})
options = {age: 27, weight: 160, city: "New York"}.merge(options)
...
Le problème est que la valeur par défaut de options
est l'intégralité de Hash
dans la deuxième version publiée. Ainsi, la valeur par défaut, l’ensemble Hash
, est remplacée. C'est pourquoi rien ne fonctionne, car cela active la valeur par défaut qui est la Hash
et que toutes les entrer est également valable, car il écrase la valeur par défaut avec une Hash
de clés identiques.
Je suggère fortement d'utiliser une variable Array
pour capturer tous les objets supplémentaires se trouvant à la fin de votre appel de méthode.
def my_info(name, *args)
options = args.extract_options!
age = options[:age] || 27
end
J'ai appris cette astuce en lisant le code source de Rails. Toutefois, notez que cela ne fonctionne que si vous incluez ActiveSupport. Ou, si vous ne souhaitez pas que tout le joyau ActiveSupport soit surchargé, utilisez simplement les deux méthodes ajoutées à Hash
et Array
pour rendre cela possible.
Rails/actifsupport/lib/active_support/core_ext/array / extract_options.rb
Ainsi, lorsque vous appelez votre méthode, appelez-la comme toute autre méthode d'assistance Rails avec des options supplémentaires.
my_info "Ned Stark", "Winter is coming", :city => "Winterfell"
Si vous souhaitez définir les valeurs par défaut dans votre hachage d'options, vous souhaitez fusionner les valeurs par défaut de votre fonction. Si vous mettez les valeurs par défaut dans le paramètre default lui-même, il sera écrasé:
def my_info(name, options = {})
options.reverse_merge!(age: 27, weight: 160, city: "New York")
...
end
En deuxième approche, quand vous dites,
my_info2 "Bill", age: 28
Il passera {age: 28} et le hachage complet par défaut original {age: 27, poids: 160, ville: "New York"} sera remplacé. C'est pourquoi cela ne s'affiche pas correctement.
Vous pouvez également définir des signatures de méthodes avec des arguments de mots clés (Nouveau depuis, Ruby 2.0, cette question étant ancienne):
def my_info2(name, age: 27, weight: 160, city: "New York", **rest_of_options)
p [name, age, weight, city, rest_of_options]
end
my_info2('Joe Lightweight', weight: 120, age: 24, favorite_food: 'crackers')
Cela permet de:
:weight
et :age
):favorite_food
collecté dans rest_of_options
)#fetch
est votre ami!
class Example
attr_reader :age
def my_info(name, options = {})
@age = options.fetch(:age, 27)
self
end
end
person = Example.new.my_info("Fred")
puts person.age #27
Pour les valeurs par défaut dans votre hash, vous devriez utiliser ceci
def some_method(required_1, required_2, options={})
defaults = {
:option_1 => "option 1 default",
:option_2 => "option 2 default",
:option_3 => "option 3 default",
:option_4 => "option 4 default"
}
options = defaults.merge(options)
# Do something awesome!
end
Pour répondre à la question "pourquoi?": Comment vous appelez votre fonction,
my_info "Bill", age: 99, weight: 300, city: "Sao Paulo"
est en train de faire
my_info "Bill", {:age => 99, :weight => 300, :city => "Sao Paulo"}
Notez que vous transmettez deux paramètres, "Bill"
et un objet de hachage, ce qui entraînera l'ignorance complète de la valeur de hachage par défaut fournie dans my_info2
.
Vous devez utiliser l’approche de la valeur par défaut mentionnée par les autres répondants.
Je ne vois rien de mal à utiliser un opérateur ou pour définir les valeurs par défaut. Voici un exemple concret (note, utilise la méthode image_tag
de Rails):
fichier:
def gravatar_for(user, options = {} )
height = options[:height] || 90
width = options[:width] || 90
alt = options[:alt] || user.name + "'s gravatar"
gravatar_address = 'http://1.gravatar.com/avatar/'
clean_email = user.email.strip.downcase
hash = Digest::MD5.hexdigest(clean_email)
image_tag gravatar_address + hash, height: height, width: width, alt: alt
end
console:
2.0.0-p247 :049 > gravatar_for(user)
=> "<img alt=\"jim's gravatar\" height=\"90\" src=\"http://1.gravatar.com/avatar/<hash>\" width=\"90\" />"
2.0.0-p247 :049 > gravatar_for(user, height: 123456, width: 654321)
=> "<img alt=\"jim's gravatar\" height=\"123456\" src=\"http://1.gravatar.com/avatar/<hash>\" width=\"654321\" />"
2.0.0-p247 :049 > gravatar_for(user, height: 123456, width: 654321, alt: %[dogs, cats, mice])
=> "<img alt=\"dogs cats mice\" height=\"123456\" src=\"http://1.gravatar.com/avatar/<hash>\" width=\"654321\" />"
Cela ressemble à l'utilisation de la méthode initialize lors de l'appel d'une classe.
Pourquoi ne pas utiliser nil?
def method(required_arg, option1 = nil, option2 = nil)
...
end
Il existe une méthode method_missing sur les modèles ActiveRecord que vous pouvez remplacer pour que votre classe réponde de manière dynamique aux appels directement. Voici un article de Nice blog on it.