Обработка метода с необязательным аргументом, за которым следует список аргументов ключевого слова

Я хочу передать необязательный аргумент (любой тип данных, даже хэш), за которым следует список аргументов ключевого слова (который может быть пустым) в метод.

Вот что у меня получилось:

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

Результат метода ожидаемый в следующих случаях:

  • ничего такого

    my_method()
    # => [nil, {}]
    
  • предмет

    my_method('foo')
    # => ["foo", {}]
    
  • буквальный предмет с вариантами

    my_method('foo', bar: 'bar')
    # => ["foo", {:bar=>"bar"}]
    
  • Hash как субъект с опциями

    my_method({foo: 'foo'}, bar: 'bar')
    # => [{:foo=>"foo"}, {:bar=>"bar"}]
    
  • без темы, только варианты

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

При передаче Hash в качестве субъекта и без параметров желаемый результат:

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

Но я получаю следующее; Я не понимаю правильную тему:

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

my_method(foo: 'foo') эквивалентен my_method({foo: 'foo'})? Есть идеи, как я могу получить желаемый результат?

Я обновил в своем ответе, почему он был назначен второму аргументу.

ray 10.01.2019 15:02
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
5
1
99
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Насколько мне известно, это ограничение того, как Ruby передает аргументы. Он не может отличить my_method({foo: 'foo'}) от my_method(foo: 'foo').

Почему бы не обойти это с этим?

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

Это предполагает, что subject не должен быть хешем.

Спасибо за Ваш ответ. К сожалению, иногда мне нужно передать хеш на subject.

Taschetto 10.01.2019 14:57

@Taschetto Хорошо ... но как бы вы отличили тему nil и только варианты от темы Hash и без параметров?

Max 10.01.2019 14:58
my_method({foo: 'foo'}) должен быть предметом хеширования и без опций; my_method(foo: 'foo') должен быть нулевой темой и просто опциями. Но может я ошибаюсь.
Taschetto 10.01.2019 14:59

@Taschetto Я почти уверен, что это ограничение Ruby. Он не может провести различие между этими двумя случаями. В таком случае я бы просто сделал subject необязательным.

Max 10.01.2019 15:00

Возможно, это не лучший ответ, но вы можете попробовать с аргумент ключевого слова:

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

потом

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

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

Альтернатива:

Ты можешь использовать

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

Спасибо за ответ. Да, это работает. Но в моем случае мне пришлось бы реорганизовать большое количество вызовов методов. Мне нужно поддерживать интерфейс метода.

Taschetto 10.01.2019 14:56

@Taschetto, тогда my_method ({foo: 'foo'}, {}) может быть решением?

Xero 10.01.2019 15:00
Ответ принят как подходящий

Видите ли, у вас есть **options в качестве аргумента, который не имеет значения по умолчанию, а первый аргумент имеет значение по умолчанию. Так что поймите следующее случай с одним аргументом,

Всякий раз, когда передается один аргумент, его пытаются назначить второму аргументу (поскольку первый по умолчанию имеет значение nil), и если он терпит неудачу из-за несоответствия типов, он назначается первому аргументу. Так работает my_method(4).

Теперь предположим, что у вас есть один аргумент, переданный как хэш, который соответствует назначению 2-му аргументу, тогда, конечно, он будет назначен второму аргументу, а для первого по умолчанию установлено значение nil.

Если вы хотите, чтобы это работало, вы можете сделать следующее:

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

Или вы можете указать имя аргумента при передаче,

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

Другие вопросы по теме