Ruby on rails 哈希键的不需要的符号到字符串转换

Ruby on rails 哈希键的不需要的符号到字符串转换,ruby-on-rails,unit-testing,testing,rspec,rspec2,Ruby On Rails,Unit Testing,Testing,Rspec,Rspec2,当我在控制器中分配 @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 ==) 为什么会发生这种符号到字符串的自动转换?为什么它

当我在控制器中分配

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

为什么会发生这种符号到字符串的自动转换?为什么它会影响散列的键呢?

如果Rails以某种方式控制了它,并且在内部使用了字符串键,那么它可能会以无差异访问的
散列结束。您可能希望验证该类是否相同:

assert_equal Hash, assigns(:my_hash).class
参数总是作为一种散列进行处理,因此您可以使用字符串或符号进行检索。如果您在
get
post
调用中将其分配给您的params散列,或者您可能正在转换

您可以做的另一件事是冻结它,并查看是否有人试图修改它,因为这会引发异常:

@my_hash = { :my_key => :my_value }.freeze
您可以尝试调用“stringify_keys”:


您还可以将
Hash
对象传递给
hashWithInferenceTaccess
的初始值设定项。您可以使用
hashWithInferenceTaccess。新的
作为Hash init:

Thor::CoreExt::HashWithIndifferentAccess.new( to: 'mail@somehost.com', from: 'from@host.com')

啊哈!这不是因为Rails本身,而是因为Rspec

我在控制器规范中测试
Hashie::Mash
的值时遇到了同样的问题(但它适用于任何像
Hash
这样的庸医)

具体地说,在控制器规范中,当您调用
assigns
以访问控制器操作中设置的实例变量时,它并没有准确返回您设置的实例变量,而是返回Rspec存储为
hashWithInferenceAccess的成员的变量副本(包含所有分配的实例变量)。不幸的是,当您将
散列
(或从
散列
继承的任何内容)粘贴到
散列中时,它会自动转换为同一类的实例,哦,非常方便,但不是很准确:)

最简单的解决方法是,在转换变量之前,使用:
controller.view\u assigns['variable\u name']
(注意:此处的键必须是字符串,而不是符号),直接访问变量以避免转换

因此,如果将原始帖子中的测试更改为:

get 'index'
controller.view_assigns['my_hash'].should == { :my_key => :my_value }
(当然,
.should
在新版本的RSpec中不再受支持,但为了便于比较,我保持不变)

有关更多说明,请参阅本文:

我知道这很旧,但如果您从Rails-3升级到4,您的控制器测试可能仍有使用符号键的
哈希
,但与字符串化版本相比,只是为了防止错误的预期

Rails-4已修复此问题:。 我建议更新您的测试,使其具有与实际密钥相对应的期望值

在Rails-3中,
assigns
方法将您的
@my_hash
转换为
hashWithInferenceAccess
,该方法将所有键字符串化-

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

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

你真的应该设置你的Gravatar,因为你的用户名太棒了。它确实是一个无所谓的hashWith Access。我有点奇怪为什么Rails会将我的标准哈希转换成那个。我不会将其分配给上述以外的任何对象(尤其是不分配给params)。您可能还知道为什么Rails在控制器和视图之间进行转换(如上所述,在上面的示例中没有使用参数),这似乎有点愚蠢,为什么要将其转换为
hashWithInferenceTaccess
Rails会自动将传入参数转换为Inference access散列,这样在引用它时就不必担心字符串或符号了
params[:foo]
params['foo']
最终是等效的。这可能在某种程度上是为了帮助不习惯对键进行符号化的PHP、Perl和Python开发人员,并避免记住键的存储形式。我不认为是Rails,而是Rspec导致了这个问题。我也有同样的问题。我将尝试在下面发布一个答案,因为这个问题是在我搜索我的问题时提出的,即使它已经5年了!;)这篇文章解释了更多:解决了我的问题。。您知道任何处理嵌套哈希的方法吗?链接404现在:(如果其他人仍在寻找答案中发布的链接,
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