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メソッドを使えば同じ型に限って、違う値を返せるけど、
例外と普通の型を交互に投げたい時ってどうするんだろう。
Jpmobileで作ったスマホサイトをスマホから見た時、PCビューを表示するリンクを作る
jpmobile (4.0.0)
rails (4.0.0)
ruby2.0
ロジック
特定のクッキーをもっていれば、Jpmobileのビュー自動振り分け機能を無効にしてPCビューを表示する感じ。
実装
before_actionで自動振り分け機能を無効化する
rails4なのでconcernを使ってみる。
app/controllers/concerns/jpmobile.rb
module Concerns module Jpmobile extend ActiveSupport::Concern included do before_action :force_pc_view before_filter :disable_mobile_view_if_tablet end # クッキーの中を見てスマホでPCビューを表示する def force_pc_view if cookies[:force_pc_view] == 't' disable_mobile_view! # 自動振り分け無効 end end # タブレットは常にPCビューを表示する def disable_mobile_view_if_tablet if request.mobile && request.mobile.tablet? disable_mobile_view! end end end end
作ったmoduleを取り込む。
class ApplicationController < ActionController::Base include Jpmobile::ViewSelector # 自動振り分けする include Concerns::Jpmobile # 追加 end
ルーティング
get '/toggle_pc_view' => 'home#toggle_pc_view', as: :toggle_pc_view
toggle_pc_viewアクションを作る
class HomeController < ApplicationController def toggle_pc_view if cookies[:force_pc_view] == 't' cookies.delete(:force_pc_view) else cookies[:force_pc_view] = 't' end redirect_to params['current_path'] end end
スマホテンプレートに表示する『リンク』を作る
app/views/layouts/application_smart_phone.html.erbのフッターあたりへ。
<%= link_to 'PC版の表示', toggle_pc_view_path(current_path: request.path) %>
PCビューを見ている時にスマホビューへ誘導するリンクを作る
app/views/shared/smart_phone/_toggle_pc_view_link.html.erb
<% if request.mobile && request.mobile.tablet? %> <%# なに表示しない %> <% else %> <% if request.smart_phone? %> <%= link_to 'スマートフォン版の表示', toggle_pc_view_path(current_path: request.path) %> <% end %> <% end %>
このパーシャルをPC用テンプレートに貼る。
app/views/layouts/application.html.erb
... <%= render 'shared/smart_phone/toggle_pc_view_link' %> # 追加 <%= yield %> <%= render 'shared/smart_phone/toggle_pc_view_link' %> # 追加 ...
おしまい。