首页 > 解决方案 > HashWithIndifferentAccess 不适用于 Rails 5 中的 % 格式运算符

问题描述

Ruby 字符串的方法%允许用给定哈希的值替换名称占位符:"Hello %{name}" % {name: "World"}结果为"Hello World".

问题是它不适用于HashWithIndifferentAccess参数。在 Rails 5 中,这段代码会引发异常:

2.3.8 :001 > "%{a}" % {"a" => 1}.with_indifferent_access
KeyError: key{a} not found
    from (irb):1:in `%'
    from (irb):1

同样有趣的是,相同的代码在 Rails 4 中运行良好,但在 Rails 5 中却不行

有没有办法修复它(可能是一些猴子补丁ActiveSupport::HashWithIndifferentAccess)并理解为什么%(以及sprintf)的行为发生了变化?

标签: ruby-on-railsrubyruby-on-rails-5

解决方案


ActiveSupport::HashWithIndifferentAccess这主要是因为只要 Rails 一直在发展和default方法(不要与 Hash#default 混淆) ,通过不同版本引入的更改。

如果您在 Rails 4 版本的 ActiveSupport::HashWithIndifferentAccess 中看到默认方法如下所示:

def default(key = nil)
  if key.is_a?(Symbol) && include?(key = key.to_s)
    self[key]
  else
    super
  end
end

很明显,它在被调用时接收一个键,检查它的类型以及是否包含在哈希(self)中作为它的键之一,返回它的值,否则调用 super。

在较新的版本中(较新,因为您的示例在 Rails 4+ 版本中不起作用)它看起来像:

def default(*args)
  super(*args.map { |arg| convert_key(arg) })
end

def convert_key(key) # :doc:
  key.kind_of?(Symbol) ? key.to_s : key
end

如果它是符号或返回相同的元素,则在调用 String 时尝试从传递给默认值的参数中返回每个元素。

在您的示例中,这将返回 nil for default,这解释了您遇到的 KeyError 错误。

行为的错误或差异不%在于引入的更改。


推荐阅读