Нежелательное преобразование символа в строку хеш-ключа - PullRequest
13 голосов
/ 03 декабря 2010

Когда я назначаю в свой контроллер

@my_hash = { :my_key => :my_value }

и проверяю этот контроллер, выполняя

get 'index'
assigns(:my_hash).should == { :my_key => :my_value }

, я получаю следующее сообщение об ошибке:

expected: {:my_key=>:my_value},
got: {"my_key"=>:my_value} (using ==)

Почему происходит автоматическое преобразование символа в строку?Почему это влияет на ключ хеша?

Ответы [ 6 ]

8 голосов
/ 10 декабря 2010

Вы можете попробовать вызвать "stringify_keys":

assigns(:my_hash).should == { :my_key => :my_value }.stringify_keys
6 голосов
/ 03 декабря 2010

Это может закончиться как HashWithIndifferentAccess, если Rails каким-то образом овладеет им, и это использует строковые ключи внутри. Возможно, вы захотите убедиться, что класс такой же:

assert_equal Hash, assigns(:my_hash).class

Параметры всегда обрабатываются как хэши типа индифферентного доступа, поэтому вы можете получить их, используя либо строку, либо символ. Если вы присваиваете это хешу params при вызове get или post, или вы можете быть преобразованы.

Еще одна вещь, которую вы можете сделать, это заморозить ее и посмотреть, попытается ли кто-нибудь ее изменить, потому что это должно вызвать исключение:

@my_hash = { :my_key => :my_value }.freeze
1 голос
/ 08 мая 2019

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

В Rails-4 исправлена ​​эта проблема: https://github.com/rails/rails/pull/5082.Я предлагаю обновить ваши тесты, чтобы иметь ожидания по сравнению с реальными ключами.

В Rails-3 метод assigns преобразует ваш @my_hash в HashWithIndifferentAccess, который преобразует все ключи в соответствие -

def assigns(key = nil)
  assigns = @controller.view_assigns.with_indifferent_access
  key.nil? ? assigns : assigns[key]
end

https://github.com/rails/rails/blob/3-2-stable/actionpack/lib/action_dispatch/testing/test_process.rb#L7-L10

Rails-4 обновил его, чтобы вернуть оригинальные ключи -

def assigns(key = nil)
  assigns = {}.with_indifferent_access
  @controller.view_assigns.each { |k, v| assigns.regular_writer(k, v) }
  key.nil? ? assigns : assigns[key]
end

https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_dispatch/testing/test_process.rb#L7-L11

1 голос
/ 22 июля 2015

AHA! Это происходит не из-за Rails, а из-за Rspec.

У меня была такая же проблема при тестировании значения Hashie::Mash в спецификации контроллера (но это относится ко всему, что крякает как Hash)

В частности, в спецификации контроллера, когда вы вызываете assigns для доступа к переменным экземпляра, установленным в действии контроллера, он не возвращает точно установленную вами переменную экземпляра, а скорее копию переменной, которую Rspec хранит как член HashWithIndifferentAccess (содержащий все назначенные переменные экземпляра). К сожалению, когда вы вставляете Hash (или что-либо, что наследуется от Hash) в HashWithIndifferentAccess, он автоматически преобразуется в экземпляр того же, о, очень удобного, но не совсем точного класса :)

Самый простой обходной путь - избежать преобразования путем непосредственного доступа к переменной до ее преобразования «для вашего удобства», используя: controller.view_assigns['variable_name'] (примечание: ключ здесь должен быть строкой, а не символом)

Таким образом, тест в исходном посте должен пройти, если он был изменен на:

get 'index'
controller.view_assigns['my_hash'].should == { :my_key => :my_value }

(конечно, .should больше не поддерживается в новых версиях RSpec, но просто для сравнения я сохранил то же самое)

См. Эту статью для дальнейшего объяснения: http://ryanogles.by/rails/hashie/rspec/testing/2012/12/26/rails-controller-specs-dont-always-play-nice-with-hashie.html

0 голосов
/ 19 июля 2014

Вы можете использовать HashWithIndifferentAccess.new как Hash init:

Thor::CoreExt::HashWithIndifferentAccess.new( to: 'mail@somehost.com', from: 'from@host.com')
0 голосов
/ 09 августа 2011

Вы также можете передать объект Hash инициализатору HashWithIndifferentAccess.

...