Ruby 2.3.3:奇怪的Tempfile.new([name,prefix])basename转换为hash

时间:2017-01-11 18:48:50

标签: ruby-on-rails ruby ruby-on-rails-3.2 ruby-2.3

为我们的Rails 3.2.22.2应用程序测试升级到Ruby 2.3.3,并得到一个奇怪的情况,我们将一个数组作为Tempfile.new的第一个参数传递,但它最终成为一个哈希。 / p>

我已修补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'

要成为一个宝石或其他东西,但无法弄清楚为什么会发生这种情况或者改变行为的地方或内容。

有关如何调试的任何想法或建议?

3 个答案:

答案 0 :(得分:2)

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

我遇到了同样的问题,并且由于某种原因,当一个方法有一个默认参数(在本例中为basename="")和一个双splatted参数时,Ruby会在第一个参数上调用to_hash函数。 / p>

请参阅以下示例:

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不再具有默认值。

答案 1 :(得分:0)

在我的控制台中试过这个,没有错误。尝试一些事情,

  1. 确保您在铁路应用中使用ruby 2.3或更高版本,因为我认为方法make_tmpname之前的处理方式不同。
  2. 确保.csv周围的引号是引号,而不是代字号。
  3. 如果我这样做Tempfile.new(['test', /re/])
  4. ,我会遇到与ruby 2.3.1相同的错误

    我希望这会有所帮助,在一天结束时,导致错误的是这个方法try_convert,它会为您传递给nil的第二个参数返回Tempfile.new

答案 2 :(得分:0)

这是我修复它的方式。

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