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

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

Вот что я получил:

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'})? Любые идеи о том, как я могу получить желаемый результат?

3 ответа

Решение

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

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

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

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

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

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

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

Насколько я знаю, это ограничение того, как Руби передает аргументы. Это не может сказать разницу между my_method({foo: 'foo'}) а также my_method(foo: 'foo')

Почему бы не взломать это с этим?

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

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

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

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

затем

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

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

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

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

my_method({foo: 'foo'}, {})
Другие вопросы по тегам