Рубиновые методы и дополнительные параметры
Я играю с Ruby on Rails, и я пытаюсь создать метод с дополнительными параметрами. По-видимому, есть много способов сделать это. Я пытаюсь назвать необязательные параметры хэшем и не определяя их. Выход отличается. Посмотрите:
# 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!
Что случилось со вторым подходом для дополнительных параметров?
Второй метод работает только в том случае, если я не передаю какой-либо необязательный параметр или передаю все.
Что мне не хватает?
Ответы
Ответ 1
Способ использования необязательных аргументов в ruby заключается в том, что вы указываете знак равенства, и если аргумент не передается, то то, что вы указали, используется. Итак, если второй аргумент не передан во втором примере, тогда
{age: 27, weight: 160, city: "New York"}
. Если вы используете синтаксис хэша после первого аргумента, то этот точный хеш передается.
Лучшее, что вы можете сделать, это
def my_info2(name, options = {})
options = {age: 27, weight: 160, city: "New York"}.merge(options)
...
Ответ 2
Проблема заключается в том, что значение по умолчанию options
- это весь Hash
во второй опубликованной вами версии. Таким образом, значение по умолчанию, полное Hash
, становится переопределенным. То, почему ничего не работает, потому что это активирует значение по умолчанию, которое является Hash
, и все входящие также работают, потому что оно перезаписывает значение по умолчанию с помощью Hash
идентичных ключей.
Я настоятельно рекомендую использовать Array
для захвата всех дополнительных объектов, которые находятся в конце вызова метода.
def my_info(name, *args)
options = args.extract_options!
age = options[:age] || 27
end
Я понял этот трюк от чтения через источник Rails. Однако обратите внимание, что это работает только в том случае, если вы включили ActiveSupport. Или, если вам не нужны накладные расходы всего драгоценного камня ActiveSupport, просто используйте два метода, добавленные в Hash
и Array
, чтобы сделать это возможным.
rails/activesupport/lib/active_support/core_ext/array/ extract_options.rb
Поэтому, когда вы вызываете свой метод, назовите его так же, как и любой другой вспомогательный метод Rails с дополнительными параметрами.
my_info "Ned Stark", "Winter is coming", :city => "Winterfell"
Ответ 3
Если вы хотите по умолчанию использовать значения в хэшах параметров, вы хотите объединить значения по умолчанию в своей функции. Если вы по умолчанию по умолчанию ставите параметр по умолчанию, он будет переписан:
def my_info(name, options = {})
options.reverse_merge!(age: 27, weight: 160, city: "New York")
...
end
Ответ 4
Во втором подходе, когда вы говорите,
my_info2 "Bill", age: 28
Пройдет {age: 28}, и весь исходный хэш по умолчанию (возраст: 27, вес: 160, город: "Нью-Йорк" } будет переопределен. Вот почему он не отображается должным образом.
Ответ 5
Для значений по умолчанию в вашем хеше вы должны использовать этот
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
Ответ 6
Чтобы ответить на вопрос "почему?": способ, которым вы вызываете свою функцию,
my_info "Bill", age: 99, weight: 300, city: "Sao Paulo"
на самом деле делает
my_info "Bill", {:age => 99, :weight => 300, :city => "Sao Paulo"}
Обратите внимание, что вы передаете два параметра: "Bill"
и хеш-объект, что приведет к полному игнорированию хэш-значения по умолчанию, которое вы указали в my_info2
.
Вы должны использовать подход по умолчанию, который упомянули другие ответчики.
Ответ 7
#fetch
- ваш друг!
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
Ответ 8
Вы также можете определить сигнатуры методов с аргументами ключевого слова (Новый, начиная с Ruby 2.0, так как этот вопрос старый):
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')
Это позволяет:
- Дополнительные параметры (
:weight
и :age
)
- Значения по умолчанию
- Произвольный порядок параметров
- Дополнительные значения, собранные в хэше с использованием двойного символа (
:favorite_food
, собранного в rest_of_options
)
Ответ 9
В моделях ActiveRecord существует метод method_missing, который можно переопределить, чтобы ваш класс динамически отвечал на вызовы напрямую. Вот хороший пост в блоге на нем.
Ответ 10
Я не вижу ничего плохого в использовании оператора или оператора для установки значений по умолчанию. Здесь пример реальной жизни (обратите внимание, использует метод rails image_tag
):
Файл:
def gravatar_for(user, options = {} )
height = options[:height] || 90
width = options[:width] || 90
alt = options[:alt] || user.name + " 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
Приставки:
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\" />"
Похоже на использование метода initialize при вызове класса.
Ответ 11
Почему бы просто не использовать nil?
def method(required_arg, option1 = nill, option2 = nil)
...
end