Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyで例外が発生したときに再試行する方法

すべてのエラーが致命的というわけではありません。再試行する必要があることを示すものもあります。幸い、Rubyには、「再試行」を容易にするいくつかの興味深いメカニズムが用意されていますが、すべてが明白またはよく知られているわけではありません。この投稿では、これらのメカニズムと、それらが実際にどのように機能するかを見ていきます。

retryの紹介

わかりました-これは明らかですが、それが存在することを知っている場合に限ります。個人的には、楽しい「再試行」キーワードについて学ぶ前は、Rubyのキャリアに精通していました。

再試行は、Rubyの例外レスキューシステムに組み込まれています。とても簡単です。レスキューブロックで「再試行」を使用すると、レスキューされたコードのセクションが再度実行されます。例を見てみましょう。

begin
  retries ||= 0
  puts "try ##{ retries }"
  raise "the roof"
rescue
  retry if (retries += 1) < 3
end

# ... outputs the following:
# try #0
# try #1
# try #2

ここで注意すべき点がいくつかあります:

  • 再試行が呼び出されると、すべて beginとrescueの間のコードが再度実行されます。それは間違いなくします ない 「中断したところから再開する」など。

  • 再試行を制限するメカニズムを提供しないと、無限ループになってしまいます。

  • beginブロックとrescueブロックの両方のコードは、親スコープの同じ再試行変数にアクセスできます。

retryの問題

再試行は素晴らしいですが、いくつかの制限があります。主なものは、beginブロック全体が再実行されることです。しかし、それが理想的でない場合もあります。

たとえば、単一のメソッド呼び出しを使用して、Twitter、Facebook、および他の多くのサイトにステータスの更新を投稿できるgemを使用していると想像してください。このように見えるかもしれません。

SocialMedia.post_to_all("Zomg! I just ate the biggest hamburger")

# ...posts to Twitter API
# ...posts to Facebook API
# ...etc

APIの1つが応答しない場合、gemはSocialMedia::TimeoutErrorを発生させて中止します。この例外をキャッチして再試行すると、再試行が最初からやり直されるため、投稿が重複してしまいます。

begin
  SocialMedia.post_to_all("Zomg! I just ate the biggest hamburger")
rescue SocialMedia::TimeoutError
  retry
end

# ...posts to Twitter API
# facebook error
# ...posts to Twitter API
# facebook error
# ...posts to Twitter API
# and so on

宝石に「Facebookをスキップして、投稿するAPIのリストを続けてください」と伝えることができたら素晴らしいと思いませんか。

幸いなことに、Rubyではまさにそれが可能です。

注:もちろん、この問題の本当の解決策は、ソーシャルメディアライブラリを再構築することです。しかし、これは、これから紹介するテクニックの唯一のユースケースではありません。

レスキューへの継続

継続は人々を怖がらせる傾向があります。しかし、それはそれらがあまり頻繁に使用されておらず、少し奇妙に見えるからです。しかし、基本を理解すれば、それらは非常に単純です。

継続は、ビデオゲームの場合と同様に、コードの「保存ポイント」のようなものです。立ち去って他のことをした後、セーブポイントに戻ると、すべてがそのままになります。

...わかりました。完全な例えではありませんが、ある程度は機能します。いくつかのコードを見てみましょう:

require "continuation"
counter = 0
continuation = callcc { |c| c } # define our savepoint
puts(counter += 1)
continuation.call(continuation) if counter < 5 # jump back to our savepoint

あなたはいくつかの奇妙なことに気づいたかもしれません。それらを見ていきましょう:

  • callccメソッドを使用してContinuationオブジェクトを作成します。このためのクリーンなOO構文はありません。

  • 初めてのcontinuation 変数が割り当てられ、callccの戻り値に設定されます のブロック。そのため、ブロックが存在する必要があります。

  • セーブポイントに戻るたびに、continuation 変数には、callを渡す引数が割り当てられます 方法。そのため、奇妙な外観のcontinuation.call(continuation)を使用します 構文。

例外への継続の追加

継続を使用してskipを追加します すべての例外へのメソッド。以下の例は、それがどのように機能するかを示しています。例外を救済するときはいつでも、skipを呼び出すことができるはずです。 、これにより、例外を発生させたコードが発生しなかったように動作します。

begin
  raise "the roof"
  puts "The exception was ignored"
rescue => e
  e.skip
end

# ...outputs "The exception was ignored"

これを行うには、いくつかの罪を犯さなければなりません。 Exception ただのクラスです。つまり、monekypatchを実行して、skipを追加できます。 メソッド。

class Exception
  attr_accessor :continuation
  def skip
    continuation.call
  end
end

次に、continuationを設定する必要があります すべての例外の属性。 raiseであることが判明しました は単なるメソッドであり、オーバーライドできます。

ところで、以下のコードは、Adviの優れたスライドデッキ「例外について知らなかったもの」からほぼ逐語的に引用されています。これよりも優れた実装方法は考えられませんでした:

require 'continuation'
module StoreContinuationOnRaise
  def raise(*args)
    callcc do |continuation|
      begin
        super
      rescue Exception => e
        e.continuation = continuation
        super(e)
      end
    end
  end
end

class Object
  include StoreContinuationOnRaise
end

これで、skipを呼び出すことができます 例外のメソッドであり、例外が発生しなかったようになります。


  1. マトリックスとは何ですか?Rubyでそれを使用する方法は?

    マトリックスは、スプレッドシートのようなデータを保存および操作するために使用できる2D(2次元)配列です。 次の用途に使用できます : テーブルゲーム(チェス、チェッカーなど)でボードを表す 統計とデータ分析 プロットとグラフの生成 これは強力なデータ構造であるため、使用方法を学ぶのに役立ちます。 Rubyでマトリックスを作成する方法 配列を使用して行列を作成できます。 このように : matrix = [ [1,2,3], [4,5,6], [7,8,9] ] これにより、3×3のマトリックスが生成されます。これは、2次元データをボードまたは位置のセットとして保

  2. RubyでStructとOpenStructを使用する方法

    Rubyの構造体とは何ですか? 構造体は組み込みのRubyクラスであり、値オブジェクトを生成する新しいクラスを作成するために使用されます。値オブジェクトは、関連する属性を一緒に格納するために使用されます。 ここに例があります : Point 2つの座標(x &y 。 このデータはさまざまな方法で表すことができます。 いいね : 配列[10, 20] ハッシュ{ x: 10, y: 10 } オブジェクトPoint.new(10, 20) 複数のPointを使用する場合 、オブジェクトアプローチを使用することをお勧めします。 しかし… これら2つの値を一緒に格納するた