2017-01-11 18 views
2

Тестирование обновления до Ruby 2.3.3 для нашего приложения Rails 3.2.22.2 и получение странной ситуации, когда мы передаем массив как первый аргумент Tempfile.new, но он заканчивается как хэш.Ruby 2.3.3: Weird Tempfile.new ([имя, префикс]) basename преобразован в hash

Я пропатчен 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' 

Должен быть драгоценным камнем или чем-то еще, но не может понять, почему это происходит или где или что меняет поведение.

Любые идеи или предложения по отладке?

+1

Каков результат 'puts caller' внутри вашего исправленного tempfile.rb? –

ответ

2

В вашем случае я думаю, что где-то в вашем коде установлен метод 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 не имеет значения по умолчанию больше.

+0

Большое вам спасибо за ответ !!!! Это очень хорошая находка! Некоторое время боролся с этим. – bschaeffer

0

Просто попробовал это в своей консоли и не получил никаких ошибок. Попробуйте несколько вещей,

  1. Убедитесь, что вы используете рубин 2.3 или выше в вашем рельсового приложение, потому что я считаю, что метод make_tmpname был обработан по-другому раньше.
  2. Убедитесь, что котировки вокруг .csv являются котировками, а не тильдой `.
  3. я получить такую ​​же ошибку с рубином 2.3.1, если я делаю это Tempfile.new(['test', /re/])

Я надеюсь, что это помогает, в конце дня, что вызывает вашу ошибку этот метод try_convert который возвращается nil для второго аргумент вы переходите на Tempfile.new

+0

'\' '- это обратная сторона (или [серьезный акцент] (http://www.fileformat.info/info/unicode/char/0060/index.htm)). '~' является [тильдой] (http://www.fileformat.info/info/unicode/char/007e/index.htm). –

+0

Я знал, что кто-то меня исправит, но не мог вспомнить имя, спасибо чувак. –

+0

Спасибо за отзыв. Код выше - это код в приложении no '~' или '\' ', и я использую 2.3.3 для создания ошибки. Когда вы говорите, что вы получаете ту же ошибку с помощью регулярного выражения, вы имеете в виду, что вы видите, что '[]' args преобразуются в хэш? – bschaeffer

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