Ruby 2.3.3: 奇怪的 Tempfile.new([name, prefix]) basename converted to hash

Ruby 2.3.3: Weird Tempfile.new([name, prefix]) basename converted to hash

正在为我们的 Rails 3.2.22.2 应用程序测试到 Ruby 2.3.3 的升级,并遇到一个奇怪的情况,我们将数组作为第一个参数传递给 Tempfile.new , 但它最终变成了一个散列。

我已修补 tempfile.rb 以输出传入的 basename 参数。

irb 会话中(非 Rails),一切正常:

> require 'tempfile'
true
> Tempfile.new(['test', '.csv'])
["home", ".csv"] # output of basename argument for Tempfile.new
 => #<Tempfile:/var/blah/test###.csv> 

rails console 会话中:

> Tempfile.new(['test', '.csv'])
{"test"=>nil, ".csv"=>nil}
ArgumentError: unexpected prefix: {"test"=>nil, ".csv"=>nil}
from /path/to/ruby-2.3.3/lib/ruby/2.3.0/tmpdir.rb:113:in `make_tmpname'

一定是 gem 之类的东西,但我终究无法弄清楚为什么会发生这种情况,或者是什么地方或什么改变了行为。

关于如何调试有什么想法或建议吗?

刚刚在我的控制台上试过了,没有错误。尝试一些事情,

  1. 确保您在 rail 应用程序中使用 ruby 2.3 或更高版本,因为我相信之前对方法 make_tmpname 的处理方式不同。
  2. 确保 .csv 周围的引号是引号而不是波浪号 `.
  3. 如果我这样做 ruby 2.3.1,我会得到同样的错误 Tempfile.new(['test', /re/])

我希望这对您有所帮助,归根结底,导致您出错的是此方法 try_convert,它为您传递给 Tempfile.new[= 的第二个参数返回 nil 16=]

我就是这样解决的。

class Tempfile 
  def initialize(basename="", tmpdir=nil, mode: 0, **options)
    warn "Tempfile.new doesn't call the given block." if block_given?

    basename = basename.keys if basename.kind_of?(Hash)

    @unlinked = false
    @mode = mode|File::RDWR|File::CREAT|File::EXCL
    ::Dir::Tmpname.create(basename, tmpdir, options) do |tmpname, n, opts|
      opts[:perm] = 0600
      @tmpfile = File.open(tmpname, @mode, opts)
      @opts = opts.freeze
    end
    ObjectSpace.define_finalizer(self, Remover.new(@tmpfile))

    super(@tmpfile)
  end
end  

在您的情况下,我认为您的代码中某处定义了 Array#to_hash 方法。

我遇到了同样的问题,出于某种原因,当一个方法有一个默认参数时,在本例中为 basename="",以及一个双参数,Ruby 调用 to_hash 函数在第一个参数上。

参见以下示例:

class Dummy
  def initialize(val = "", **options)
    puts "val = #{val}"
    # puts "Options: #{options}"
  end
end

class Array
  def to_hash
    puts "to_hash called on #{self}"
  end
end

Dummy.new(["Joe", "Bloe"])

这将输出

to_hash called on ["Joe", "Bloe"]
val = ["Joe", "Bloe"]

但是当 val 参数没有默认值时,您将得到:

val = ["Joe", "Bloe"]

请注意,TempFile#initialize 函数签名已从 Ruby 2.1 更改为 Ruby 2.2。

差异如下:

-  def initialize(basename, *rest)
+  def initialize(basename="", tmpdir=nil, mode: 0, **options)

请注意 basename 不再有默认值。