You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在工厂中不使用文件夹具定义ActiveStorage附件?遇NoMethodError

ActiveStorage搭配Rack::Test::UploadedFile使用StringIO时的NoMethodError问题

问题场景

模型中定义了ActiveStorage附件:

has_one_attached :csv

尝试在FactoryBot工厂里用StringIO生成测试用的CSV附件:

csv do
  Rack::Test::UploadedFile.new(StringIO.new(<<~CSV), 'text/csv', original_filename: "file.csv")
    Some, Columns, Here
  CSV
end

根据Rack::Test的代码设计,这种用法应该被支持,但运行时抛出以下错误:

NoMethodError:
  private method `open' called for #<StringIO:0x00007f206ff88d38>
  
          tempfile.public_send(method_name, *args, &block)

错误根源

  1. ActiveStorage在处理附件时会调用open方法,而Rack::Test::UploadedFile会将未实现的方法委托给内部的tempfile对象(也就是我们传入的StringIO实例)。
  2. StringIO类仅提供了类方法open,但其实例没有公开的#open实例方法;而Tempfile实例自带公开的#open方法,这是两者的API差异。
  3. Rack::Test虽然允许传入StringIO,但没有处理这种API差异,导致ActiveStorage调用open时触发了StringIO的私有实例方法,最终报错。

结论

这是Rack::Test的兼容性Bug:它宣称支持StringIO作为UploadedFile的输入,但未抹平StringIO与Tempfile之间的API差异,导致依赖#open方法的ActiveStorage无法正常工作。

临时修复方案

给StringIO实例添加公开的#open方法,映射到它已有的#reopen方法:

csv do
  file = StringIO.new(<<~CSV)
    Some, Columns, Here
  CSV

  # 修复StringIO与Rack::Test::UploadedFile配合的Bug
  file.class_eval do
    def open = reopen
  end

  Rack::Test::UploadedFile.new(file, "text/csv", original_filename: "enrollments_changes_report.csv")
end

内容的提问来源于stack exchange,提问作者Greg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 02:55:33