ある日、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
これで直ります。