Kernel.#openの例外処理

ruby2.0

特に理由がないのでmechanizeは使わずopenを使ってスクレイピングしてます。歌丸です。

コード

これは記事一覧ページの存在を確認をするためのメソッド。

  private
  def not_found_index_page?(_error_count = 0)
    is_error_under_5 = ->{ _error_count += 1; _error_count < 5 }

    begin
      open(current_index_page_url)
      return false
    rescue Timeout::Error
      retry if is_error_under_5.call
    rescue SocketError # ホストがないときとか
    rescue OpenURI::HTTPError => e
      case e.io.status[0]
      when /5../
        retry if is_error_under_5.call
      when /4../
      end
    rescue Exception => e
      puts e
    end

    return true
  end
テスト

スタブぐぐるの結構大変だった。

  describe "#not_found_index_page?" do
    let(:lh){ Lifehacker.new('business') }
    subject { lh.send('not_found_index_page?') }
    context "openメソッドが正常終了する時" do
      before { lh.stub(:open).and_return("") }
      it { should be_false }
    end
    context "例外が発生するとき" do
      describe "Timeout::Error" do
        let(:e) { Timeout::Error }
        before { lh.stub(:open).and_raise(e) }
        it { should be_true }
      end
      describe "SocketError" do
        let(:e) { SocketError }
        before { lh.stub(:open).and_raise(e) }
        it { should be_true }
      end
      describe "OpenURI::HTTPError" do
        let(:io) { double('io') }
        describe "5XX" do
          describe "ずっと5XXが返ってくる時" do
            before do
              io.stub_chain(:status,:[]).with(0).and_return('502')
              lh.stub(:open).and_raise(OpenURI::HTTPError.new('', io))
            end
            it { should be_true }
          end
          describe "最初だけ5XXで以降は正常になること" do
            before do
              pending "例外なげたら次は文字列を返したい"
              io.stub_chain(:status,:[]).with(0).and_return('509')
              lh.stub(:open).and_raise(OpenURI::HTTPError.new('', io)).and_return("") # できてない
            end
            it { should be_true }
          end
        end
        describe "4XX" do
          before do
            io.stub_chain(:status,:[]).with(0).and_return('402')
            lh.stub(:open).and_raise(OpenURI::HTTPError.new('', io))
          end
          it { should be_true }
        end
      end
    end
  end

スタブ、and_returnメソッドを使えば同じ型に限って、違う値を返せるけど、
例外と普通の型を交互に投げたい時ってどうするんだろう。