簡體   English   中英

為什么 Ruby open-uri 的 open 在我的單元測試中返回一個 StringIO,而在我的控制器中返回一個 FileIO?

[英]Why does Ruby open-uri's open return a StringIO in my unit test, but a FileIO in my controller?

我繼承了一個 Rails 2.2.2 應用程序,它在 Amazon S3 上存儲用戶上傳的圖像。 基於attachment_fu 的Photo模型提供了一個rotate方法,它使用open-uri從S3 和MiniMagick 檢索圖像來執行旋轉。

rotate方法包含這一行來檢索用於 MiniMagick 的圖像:

temp_image = MiniMagick::Image.from_file(open(self.public_filename).path)

self.public_filename返回類似

http://s3.amazonaws.com/bucketname/photos/98/photo.jpg

檢索圖像並旋轉它在生產和開發中運行的應用程序中工作得很好。 但是,單元測試失敗

TypeError: can't convert nil into String
    /Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `initialize'
    /Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `open'
    /Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `from_file'

原因是當在單元測試的上下文中調用模型方法時, open(self.public_filename)返回一個包含圖像數據的StringIO對象。 該對象的path方法返回nil並且MiniMagick::Image.from_file

當從PhotosController調用這個完全相同的模型方法時, open(self.public_filename)返回一個FileIO實例,該實例綁定到一個名為/tmp/open-uri7378-0的文件,該文件包含圖像數據。

想着一定是測試和開發的環境有差異,就在開發環境下啟動了控制台。 但就像在單元測試中一樣, open('http://...')返回一個StringIO而不是一個FileIO

我已經通過 open-uri 和所有相關的特定於應用程序的代碼追蹤了我的方式,但找不到差異的原因。

open-uri 庫使用一個常量來設置 StringIO 對象的 10KB 大小限制。

> OpenURI::Buffer::StringMax
=> 10240 

您可以將此設置更改為 0 以防止 open-uri 創建 StringIO 對象。 相反,這將強制它始終生成一個臨時文件。

只需將其放入初始化程序中即可:

# Don't allow downloaded files to be created as StringIO. Force a tempfile to be created.
require 'open-uri'
OpenURI::Buffer.send :remove_const, 'StringMax' if OpenURI::Buffer.const_defined?('StringMax')
OpenURI::Buffer.const_set 'StringMax', 0

您不能直接設置常量。 您需要實際刪除常量,然后再次設置它(如上),否則您將收到警告:

warning: already initialized constant StringMax

2012 年12 月 18 日更新:Rails 3 默認不需要 OpenURI,因此您需要在初始值設定項的頂部添加require 'open-uri' 我更新了上面的代碼以反映這種變化。

負責此操作的代碼位於 open-uri 的 Buffer 類中。 它首先創建一個 StringIO 對象,並且僅當數據超過特定大小 (10 KB) 時才在本地文件系統中創建一個實際的臨時文件。

我假設您的測試加載的任何數據都足夠小以保存在 StringIO 中,並且您在實際應用程序中使用的圖像足夠大以保證 TempFile。 解決方案是使用兩個類共有的方法,特別是 read 方法,以及 MiniMagick::Image#from_blob:

temp_image = MiniMagick::Image.from_blob(open(self.public_filename, &:read))

現在可以直接設置OpenURI::Buffer::StringMax

require 'open-uri'
OpenURI::Buffer::StringMax = 0

但有一個警告:

warning: already initialized constant OpenURI::Buffer::StringMax

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM