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 之类的东西,但我终究无法弄清楚为什么会发生这种情况,或者是什么地方或什么改变了行为。
关于如何调试有什么想法或建议吗?
刚刚在我的控制台上试过了,没有错误。尝试一些事情,
- 确保您在 rail 应用程序中使用 ruby 2.3 或更高版本,因为我相信之前对方法
make_tmpname
的处理方式不同。
- 确保
.csv
周围的引号是引号而不是波浪号 `.
- 如果我这样做 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
不再有默认值。
正在为我们的 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 之类的东西,但我终究无法弄清楚为什么会发生这种情况,或者是什么地方或什么改变了行为。
关于如何调试有什么想法或建议吗?
刚刚在我的控制台上试过了,没有错误。尝试一些事情,
- 确保您在 rail 应用程序中使用 ruby 2.3 或更高版本,因为我相信之前对方法
make_tmpname
的处理方式不同。 - 确保
.csv
周围的引号是引号而不是波浪号 `. - 如果我这样做 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
不再有默认值。