Подтвердить что ты не робот

Почему двойной splat работает только с символьными клавишами?

Оператор double splat ** работает только с хешами, ключи которых являются символами. Он не будет работать, например, когда ключ является строкой. Это верно для обоих способов; для строительства:

def foo **; end
foo(:a => 3) #=> nil
foo("a" => 3) #=> ArgumentError: wrong number of arguments (1 for 0)

и уничтожение:

def bar *; end
bar(**{:a => 3}) #=> nil
bar(**{"a" => 3}) #=> TypeError: wrong argument type String (expected Symbol)

Почему это ограничено символьными клавишами?

Это может быть связано с тем, что именованное ключевое слово a: 3 совпадает с синтаксическим сахаром для хэша с символьными клавишами, но, как видно выше, двойной знак работает с обычной хеш-нотой :a => 3, поэтому я не уверен в этом.

4b9b3361

Ответ 1

Короткий ответ: как аргументы ключевого слова, которые должен выполнять оператор double-splat, выражаются в Ruby.

Длинный ответ: оператор double-splat предназначен для захвата ключевых слов, как показано в этом ответе: fooobar.com/questions/42935/...

Так как Ruby 2.0, Ruby поддерживает истинные аргументы ключевого слова. Я предполагаю, что в основной реализации они все еще представлены как хэши, ключи которых являются символами, подобно тому, как они были имитированы до того, как язык их поддерживал.

Частые ошибки, которые вы получаете, зависят от реализации. Например, выполнение последнего фрагмента кода в irb показывает функцию, которая вызывает TypeError:

2.1.2 :001 > def bar *; end
 => :bar 
2.1.2 :002 > bar(**{"a" => 3})
TypeError: wrong argument type String (expected Symbol)
    from (irb):2:in `core#hash_merge_kwd'
    from (irb):2
    from /home/mkis/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
2.1.2 :003 > 

hash_merge_kwd - внутренняя функция, определенная здесь: https://github.com/ruby/ruby/blob/d738e3e15533e0f500789faaedcef9ed9ca362b9/vm.c#L2398

Ответ 2

В последнее время я столкнулся с чем-то вроде этого.

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

Model

class ContactForm
  def initialize(name:, email:)
    @name = name
    @email = email
  end
  # Other stuff
end

контроллер

class ContactController < ApplicationController
  def send_mail
    @contact_form = ContactForm.new(contact_params)
    if @contact_form.submit
      # Do stuff
    end
  end

  def contact_params
    params.require(:contact_form).permit(:name, :email).symbolize_keys
  end
end