(旧タイトル:rescue節での上限回数付きretry(retryカウンタをbegin以前に宣言せずに))
#便利なGemをご紹介頂きました。
もともとの記事のコメント欄で @pasela さんに
retryableというgemを使うとスッキリ書けると思います。
とご紹介頂きました。
ソースが短いので、読んで使い方をまとめてみました(間違っていたらご指摘ください)。
大元の記事はオマケに成り下がりましたw
#retryableの使い方(v1.3.5)
(注:v2.0.0以上からメソッドが Kernel.#retryable
から Retryable.retryable
に変更されました。以下の記載はv1.3.5時点の記事なので読み替えてください。)
require 'retryable'
します。
Kernel.#retryable
に渡すブロックで、例外時にretryする処理を指定します。
具体例は @pasela さんが書いてくださったコメント欄や、retryableのRubyDoc
をご覧ください。
##Kernel.#retryable
の仮引数Hash
###繰り返し回数指定::tries
デフォルト値 n=2 。
Should an exception occur, it'll retry for (n-1) times.
###スリープ秒数::sleep
デフォルト値 1秒。
引数には数字またはProcオブジェクトが取れます。
retryable(:sleep => lambda { |n| 4**n }) { } # sleep 1, 4, 16, etc. each try
###捕捉する例外を指定::on
または:matching
大まかに:on
で捕捉する例外を指定して:matching
の正規表現で実際に例外捕捉するようです。
:matching
を指定しなければ:on
を満たす例外が捕捉されます。
:on
を指定しなければ、:on
のデフォルト値StandardError
に含まれた例外のうち:matching
を満たす例外が捕捉されます。
###例外捕捉時処理の指定::exception_cb
Procオブジェクトで渡します。引数には例外名が渡されます。
###後処理の指定::ensure
Procオブジェクトで渡します。引数にはリトライ回数が渡されます。
###ログ出力指定::task
:task
に処理名称を入れると、名称・リトライ回数・捕捉された例外名をログで表示します。
これ無くなってます(GithubのREADME.markdownにはまだ残ってますが)。
おそらく、リトライ回数と例外名がKernel.#retryable
のブロックパラメータへ渡せるので、ログ書くならご自由に、というような事ではないでしょうか。
retryable do |retries, exception|
puts "try #{retries} failed with exception: #{exception}" if retries > 0
pick_up_soap
end
##関連リンク
* [retryable | RubyGems.org | your community gem host](http://rubygems.org/gems/retryable)
* [File: README — Documentation for retryable](http://rubydoc.info/gems/retryable/)
* ~~[bronson/retryable](https://github.com/bronson/retryable)~~ [nfedyashev/retryable](https://github.com/nfedyashev/retryable)
* [Facebookから社内ADまで、外部サービスと連携するときに知っておくと役に立つライブラリ - よかろうもん!](http://interu.hatenablog.com/entry/20120404/1333546980)
<hr>
以下は元記事です。
#核となる部分
カウンタの初期化と加算とを行う`_retry = (_retry || 0) + 1`というイディオムがポイント。
```ruby
retry_upto = 5 # retry回数の上限
begin
something_do_with_error()
rescue => e
#カウンタの初期化、加算、上限確認
if _retry = (_retry || 0) + 1 and _retry < retry_upto
retry
else
#カウンタを念のためにリセット
_retry = nil
raise e
end
end
#具体例
あまり上手い例ではないですスミマセン。
retry_upto = 5
test = lambda {
loop do
begin
print (r = rand(10))
#rが0..6ならばraise、7..9ならばputs r
if r <= 6
raise
else
puts
end
rescue => e
print " -> retry: "
if _retry = (_retry || 0) + 1 and _retry < retry_upto
puts _retry
retry
else
_retry = nil
raise e
end
end
end
}
##実行結果例
> test.call
6 -> retry: 1
6 -> retry: 2
5 -> retry: 3
9
4 -> retry: 4
5 -> retry: RuntimeError:
> test.call
0 -> retry: 1
1 -> retry: 2
4 -> retry: 3
1 -> retry: 4
2 -> retry: RuntimeError:
> test.call
7
0 -> retry: 1
5 -> retry: 2
4 -> retry: 3
8
0 -> retry: 4
9
3 -> retry: RuntimeError:
#参考
本質的にこれをそのまま利用しています。
このやり方うまいなーと思いつつ、そのままブラウザのタブに残してたままにしてました。
そろそろタブを消したいのでQiitaにメモ書きしましたw
begin
@browser = Watir::Browser.new :phantomjs, :http_client => client
rescue Selenium::WebDriver::Error::WebDriverError => e
sleep 0.3
if _r = (_r || 0) + 1 and _r < 5
retry
else
raise e
end
end
[can't create browser object for phantomjs · Issue #222 · watir/watir-webdriver](https://github.com/watir/watir-webdriver/issues/222#issuecomment-27783207)
##以前書いた関連Qiita投稿
[Ruby - rescue 節で一定回数 retry したあとは次に移るループ - Qiita](http://qiita.com/riocampos/items/0870f1b682a62d6477de)
これを書いたあとに上記の書き方を知りました。