Tech Racho エンジニアの「?」を「!」に。
  • 開発

ThinReports (Prawn) を使うとRestClientのPOST/PUTが動作しなくなる

ある日、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

これで直ります。


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。