Рубиновые методы и дополнительные параметры

Я играю с 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&#39;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&#39;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