【问题标题】:Unwanted symbol to string conversion of hash key不需要的符号到哈希键的字符串转换
【发布时间】:2011-05-19 21:23:22
【问题描述】:

当我在我的控制器中分配时

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

为什么会发生这种从符号到字符串的自动转换?为什么会影响hash的key?

【问题讨论】:

  • 你真的应该设置你的 Gravatar,因为你的用户名很棒。

标签: ruby-on-rails unit-testing testing rspec rspec2


【解决方案1】:

您可以尝试调用“stringify_keys”:

assigns(:my_hash).should == { :my_key => :my_value }.stringify_keys

【讨论】:

  • 解决了我的问题.. 你知道任何使用嵌套哈希的方法吗?
【解决方案2】:

如果 Rails 以某种方式获得它,它可能会以 HashWithIndifferentAccess 结尾,并且在内部使用字符串键。您可能想验证类是否相同:

assert_equal Hash, assigns(:my_hash).class

参数总是作为哈希的无关访问类型处理,因此您可以使用字符串或符号检索。如果您在 getpost 调用上将其分配给您的 params 哈希,或者您可能会被转换。

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

@my_hash = { :my_key => :my_value }.freeze

【讨论】:

  • 确实是HashWithIndifferentAccess。我想知道为什么 Rails 确实将我的标准哈希转换为那个。除了上面提到的以外,我没有将它分配给其他任何东西(尤其是不分配给参数)。你是否也知道为什么 Rails 会在控制器和视图之间进行这种转换(如上例所述,没有使用任何参数)。
  • 是的,这似乎有点傻,为什么它被转换为HashWithIndifferentAccess
  • Rails 会自动将传入的参数转换为无关紧要的访问哈希,这样您在引用它时就不需要关心字符串或符号。 params[:foo]params['foo'] 最终是等价的。这在一定程度上可能是为了帮助不习惯符号键的 PHP、Perl 和 Python 开发人员,并避免记住键的存储方式。
  • 我认为不是 Rails,而是 Rspec 实际上导致了问题。我有同样的问题。我将尝试在下面发布答案,因为当我搜索我的问题时出现了这个问题,即使它已经 5 岁了! ;) 本文解释更多:ryanoglesby08.github.io/blog/2012/12/26/…
【解决方案3】:

啊哈!这不是因为 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 }

(当然,新版本的 RSpec 不再支持.should,但为了比较,我保持不变)

请参阅这篇文章以获得进一步的解释: http://ryanogles.by/rails/hashie/rspec/testing/2012/12/26/rails-controller-specs-dont-always-play-nice-with-hashie.html

【讨论】:

【解决方案4】:

我知道这是旧的,但如果你从 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

【讨论】:

    【解决方案5】:

    您还可以将 Hash 对象传递给 HashWithIndifferentAccess 的初始化程序。

    【讨论】:

      【解决方案6】:

      您可以使用HashWithIndifferentAccess.new 作为哈希初始化:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-31
        • 1970-01-01
        • 2012-10-31
        • 2015-03-20
        • 2016-07-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多