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

動的例外マッチャーを使用したレベルアップ`rescue`

Rubyでrescue句を使用する場合、どの種類の例外をレスキューするかを指定できます。あなたがする必要があるのは、次のような例外クラスのリストを提供することだけです:

begin
  raise RuntimeError
rescue RuntimeError, NoMethodError
  puts "rescued!"
end

しかし、コードを記述したときに例外クラスがどうなるかわからない場合はどうでしょうか。最も明白な答えは、すべての例外をレスキューし、ある種のテストを実行してから、合格しなかった例外を再発生させることです。このようなもの:

begin
  raise "FUBAR! The ship's going down!"
rescue => e
  raise unless e.message =~ /^FUBAR/
  ... do something ...
end

しかし、それはとても退屈です!さらに、それはあまりドライなアプローチではありません。どういうわけか、条件に一致する例外のみをレスキューするようにレスキュー句に指示できれば、はるかに興味深いでしょう。そしてこれはRubyなので、私たちはそれを行うことができます!

レスキューが例外と一致する方法

レスキューブロック内で例外が発生すると、rubyインタープリターは、指定された例外クラスのリストに対して例外のクラスをチェックします。一致するものがある場合、例外は救出されます。

マッチングは次のようになります:

exception_classes_to_rescue.any? do |c|
  c === raised_exception.class
end

Rubyの他のすべての演算子と同様に、=== 単なる方法です。この場合、それはcのメソッドです。 。では、独自の===を定義したらどうなるでしょうか。 方法は?

以下の例では、Anythingという名前のクラスを作成しています。 ここで、Anything === x xの任意の値に対してtrueを返します。このクラスをレスキューの引数として指定すると、すべての例外がレスキューされます。

class Anything
  def self.===(exception)
    true
  end
end

begin
  raise EOFError
rescue Anything
  puts "It rescues ANYTHING!"
end

すべての例外を救済するためのはるかに優れた方法がありますが、このコードは2つのことを示しているので興味深いものです。

  1. Exceptionから継承しないレスキュー句クラスを指定できます 、===を実装している限り

  2. ===を制御する場合 、どの例外をレスキューするかを制御できます。

メッセージに基づいて例外を救済する

私たちが今知っていることを知っているので、例外のメッセージがパターンに一致する場合にのみ例外を救済するコードを書くのは簡単です。

class AllFoobarErrors
  def self.===(exception)
    # rescue all exceptions with messages starting with FOOBAR 
    exception.message =~ /^FOOBAR/
  end
end

begin
  raise EOFError, "FOOBAR: there was an eof!"
rescue AllFoobarErrors
  puts "rescued!"
end
カスタム属性に基づいて例外をレスキューする

例外オブジェクトにアクセスできるため、マッチャーはそのオブジェクト内に含まれるすべてのデータを使用できます。

「重大度」と呼ばれるカスタム属性を持つ例外があると少し想像してみてください。例外の「重大度の低い」発生をすべて飲み込みたいが、「重大度の高い」発生はすべて通過させます。あなたはそのようにそれを実装するかもしれません:

class Infraction < StandardError
  attr_reader :severity
  def initialize(severity)
    @severity = severity
  end
end

class LowSeverityInfractions
  def self.===(exception)
    exception.is_a?(Infraction) && exception.severity == :low
  end
end

begin
  raise Infraction.new(:low)
rescue LowSeverityInfractions
  puts "rescued!"
end
動的にする

これらはすべてかなりクールですが、多くの定型コードが含まれています。マッチャーごとに個別のクラスを手動で定義する必要があるのは過剰なようです。幸い、少しのメタプログラミングを使用することで、これをかなり乾かすことができます。

以下の例では、マッチャークラスを生成するメソッドを定義しています。ブロックを介してマッチングロジックを提供すると、マッチングジェネレーターが===内のブロックを使用する新しいクラスを作成します。 メソッド。

def exceptions_matching(&block)
  Class.new do
    def self.===(other)
      @block.call(other)
    end
  end.tap do |c|
    c.instance_variable_set(:@block, block)
  end
end

begin
  raise "FOOBAR: We're all doomed!"
rescue exceptions_matching { |e| e.message =~ /^FOOBAR/ }
  puts "rescued!"
end
一粒の塩

Rubyの多くのクールなトリックのように、これがすべて狂気なのか素晴らしいアイデアなのかを完全に判断することはできません。多分それは両方の少しです。この手法を最初の選択肢として利用することは絶対にお勧めしませんが、上記のような重大度に基づいて例外を救済したい状況でどのように役立つかはわかります。いずれにせよ、それはあなたのツールベルトの別のツールです!


  1. Pryでの例外の処理

    あなたが私のようなら、あなたはRailsコンソールをよく使います。そして今では、PryがRailsコンソールに起こる最良のことであることに誰もが同意していると思います...まあ、これまで。 組み込みのこじ開けは、昔ながらのIRBに比べて、例外を除いて作業をはるかに簡単にするいくつかの非常に優れた機能です。 完全なバックトレースを表示 Pry(またはそのことについてはIRB)で例外が発生すると、バックトレースの短縮バージョンが表示されます。通常はこれで十分ですが、常にそうとは限りません。 pryでは、wtf -vコマンドを使用して、最新の例外の完全なバックトレースを確認できます。 -vフラ

  2. ダイナミック ロック機能で Windows 10 を保護する方法

    Windows 10 が追加した新機能の 1 つに、「動的ロック」があります。これは、Windows 10 で提供されるもう 1 つの非表示の機能であり、Creators の更新で開始されました。しかし、多くのユーザーはまだこの機能を認識していません. この記事では、動的ロックとは何か、またその使用方法について説明します。 Windows 10 の「ダイナミック ロック」とは? 動的ロックは、Microsoft によって追加された Windows 10 の機能です。この機能は、ユーザーが Bluetooth 経由でコンピュータまたはラップトップをロックするのに役立ちます。コンピューターをロ