Преобразование нежелательного символа в строку хеш-ключа

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

@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 ==)

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


person Zardoz    schedule 03.12.2010    source источник
comment
Вы действительно должны установить свой Gravatar из-за вашего удивительного имени пользователя.   -  person tadman    schedule 03.12.2010


Ответы (6)


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

assert_equal Hash, assigns(:my_hash).class

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

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

@my_hash = { :my_key => :my_value }.freeze
person tadman    schedule 03.12.2010
comment
Это действительно HashWithIn DifferentAccess. Мне немного интересно, почему Rails конвертирует мой стандартный хэш в этот. Я не назначаю его ничему другому, кроме упомянутого выше (и особенно параметрам). Возможно, вы также знаете, почему Rails выполняет это преобразование между контроллером и представлением (как уже упоминалось, в приведенном выше примере не используются параметры). - person Zardoz; 04.12.2010
comment
Да, это кажется немного глупым, почему он преобразуется в HashWithIndifferentAccess - person maletor; 09.08.2011
comment
Rails автоматически преобразует входящие параметры в безразличный хэш доступа, так что вам не нужно беспокоиться о строках или символах при обращении к ним. params[:foo] и params['foo'] в конечном итоге эквивалентны. Вероятно, отчасти это сделано для того, чтобы помочь разработчикам PHP, Perl и Python, которые не привыкли к символьным ключам, и избежать необходимости помнить, как эти ключи хранятся. - person tadman; 09.08.2011
comment
Я не думаю, что это Rails, но Rspec на самом деле вызывает проблему. У меня такая же проблема. Я постараюсь опубликовать ответ ниже, так как этот вопрос возник, когда я искал свою проблему, хотя ей уже 5 лет! ;) Эта статья объясняет больше: ryanoglesby08.github.io/blog/2012/12/26/ - person mltsy; 22.07.2015

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

assigns(:my_hash).should == { :my_key => :my_value }.stringify_keys
person coder_tim    schedule 10.12.2010
comment
Решил мою проблему. Знаете ли вы какой-либо метод работы с вложенными хэшами? - person grilix; 15.10.2011

АГА! Это происходит не из-за 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, но просто для сравнения я оставил его таким же)

Дополнительные пояснения см. в этой статье: hashie.html" rel="nofollow noreferrer">http://ryanogles.by/rails/hashie/rspec/testing/2012/12/26/rails-controller-specs-dont-always-play-nice-with-hashie .html

person mltsy    schedule 21.07.2015
comment
Ссылка 404s сейчас. :( - person Thomas Schreiter; 28.01.2016
comment
Если кто-то еще ищет ссылку, размещенную в ответе, ryanogles.by/rails/hashie/rspec/testing/2012/12/26/ - person Subash; 07.02.2018

Я знаю, что это устарело, но если вы обновляетесь с 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

person vikasnautiyal    schedule 08.05.2019

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

person maletor    schedule 08.08.2011

Вы можете использовать HashWithIndifferentAccess.new в качестве инициализации хэша:

Thor::CoreExt::HashWithIndifferentAccess.new( to: '[email protected]', from: '[email protected]')
person merqlove    schedule 19.07.2014