ある日、RestClientを使っていたコードが、タイムアウトして例外を吐くようになりました。
# 通信部分はstubにしていたため、自動テストで発見できず検知が少し遅れました。
undefined method `closed?' for #<Hash:0x00000008af8108>
少しRestClientのソースコードを追いかけてみると、PayloadのクラスがRestClient::Payload::Streamedになっています。
ここはUrlEncodedかMultipartになっていて欲しいところ。
原因はここにありました。
# rest-client/lib/restclient/payload.rb def generate(params) if params.is_a?(String) Base.new(params) elsif params.respond_to?(:read) Streamed.new(params) elsif params if params.delete(:multipart) == true || has_file?(params) Multipart.new(params) else UrlEncoded.new(params) end else nil end end
Hashがreadメソッドを持っていると動作しませんね、これは。
そして、ThinReports→Prawn→PdfReader→Hasheryという依存関係により、Hasheryが読み込まれ、これはreadを定義しています。
# Gemfile.lock thinreports (0.7.6) prawn (>= 0.12.0, <= 0.12.0) prawn (0.12.0) pdf-reader (>= 0.9.0) pdf-reader (1.2.0) Ascii85 (~> 1.0.0) hashery (~> 2.0) ruby-rc4
# https://github.com/rubyworks/hashery/blob/master/lib/hashery/core_ext.rb class Hash # ... alias :read :[] # ... end
仕方ないのでモンキーパッチを当てましょう。
# config/initializers/restclient_patch.rb module RestClient::Payload def generate(params) if params.is_a?(String) Base.new(params) elsif params.respond_to?(:read) && !params.is_a?(Hash) # Hashは例外 Streamed.new(params) elsif params if params.delete(:multipart) == true || has_file?(params) Multipart.new(params) else UrlEncoded.new(params) end else nil end end end
これで直ります。