首页 > 解决方案 > Ruby 2.6.3 - 以符号为键的“警告”哈希会产生 ArgumentError

问题描述

使用 Ruby 2.6.3,我遇到了一个错误,当我尝试“警告”一个至少有一个键作为符号的哈希时,会引发 ArgumentError。如果我尝试“放置”相同的哈希,则不会出现错误,并且哈希会正常打印。

 >> h = {:one => 1}
  => {:one=>1}

 >> puts h
 {:one=>1}
  => nil

 >> warn h
  Traceback (most recent call last):
    5: from /Users/rishi/.rvm/rubies/ruby-2.6.3/bin/irb:23:in `<main>'
    4: from /Users/rishi/.rvm/rubies/ruby-2.6.3/bin/irb:23:in `load'
    3: from /Users/rishi/.rvm/rubies/ruby-2.6.3/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
    2: from (irb):48
    1: from /Users/rishi/.rvm/rubies/ruby-2.6.3/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_warn.rb:15:in `block in <module:Kernel>' 
  ArgumentError (unknown keyword: one) 

它似乎在我的 kernel_warn.rb 的第 15 行抛出错误:

module_function define_method(:warn) {|*messages, uplevel: nil|
   ...

谁能建议如何解决?

标签: rubyhashsymbols

解决方案


因为就像永远一样,Ruby 允许在方法调用中去掉 hash-as-last-argument 周围的花括号,以允许类似关键字的参数。这允许 Rails 做例如

link_to "Delete", @item, method: "delete"

这被解释为

link_to("Delete", @item, { method: "delete" })

然后 Ruby 获得了对关键字参数的支持,但它们必须被破解,以一种仍然可以使所有旧代码运行的方式。所以关键字参数被视为这个参数最终哈希。你的问题是:如果签名中有关键字参数,那么关键字参数会严格检查签名;你不能有额外的关键字参数,就像你不能有额外的位置参数一样。请记住,这些都是等价的:

warn h
warn({ one: 1 })
warn(one: 1)

Ruby 在签名中看到一个位置参数,并查看传递的最后一个参数是否是散列。是的,因此它假定它是需要与签名进行比较的关键字参数散列 - 并且one:不是有效的关键字。

该怎么办?确保哈希不是最后一个参数。在微不足道的层面上,您可以这样做:

warn h, "was my hash"
# {:one=>1}
# was my hash

您可以做的另一件事是记住需要warn一个字符串;如果它没有得到一个,它将对参数进行字符串化。所以你可以先发制人:

warn h.to_s
# {:one=>1}

tl;dr:Ruby 关键字参数很奇怪。


推荐阅读