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

これで直ります。

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

baba

ゆとりプログラマー。 高校時代から趣味でプログラミングを初め、そのままコードを書き続けて現在に至る。慶應義塾大学環境情報学部(SFC)卒業。BPS設立初期に在学中から参加している最古参メンバーの一人。得意分野はWeb全般、Ruby on Rails、Androidアプリケーションなど。最近はBlinkと格闘中。軽度の資格マニアで、情報処理技術者試験(高度10区分)などを保有。

babaの書いた記事

週刊Railsウォッチ

インフラ

Rubyスタイルガイドを読む

BigBinary記事より

ActiveSupport探訪シリーズ