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

RubyのException#causeでネストされた例外の原因を追跡する方法

Rubyでは、例外をrescueして別の種類の例外として再送出(re-raise)するのは非常によくあるパターンです。ActionViewはその分かりやすい例の一つです。以前TracePointについて取り上げた記事でも触れましたが、ActionViewはテンプレート内で発生した例外をすべて握りつぶし、ActionView::Template::Errorとして再送出します。

しかし、この挙動だけでは不十分な場合があります。元の例外には問題解決につながる重要なデータが含まれていることが多く、どうしても元の例外そのものが必要になるのです。幸い、Ruby 2.1以降ではException#causeメソッドを使うことで、これを簡単に実現できます。

実際の動作を見てみましょう。次の例では、まずNoMethodErrorをraiseし、直後にそれをrescueしてRuntimeErrorをraiseしています。その後、外側でRuntimeErrorを捕捉し、#causeを使って元のNoMethodErrorを取得しています。

def fail_and_reraise
  raise NoMethodError
rescue
  raise RuntimeError
end

begin
  fail_and_reraise
rescue => e
  puts "#{ e } caused by #{ e.cause }"
end

ネストされたバックトレースとカスタム属性へのアクセス

#causeメソッドは、元の例外オブジェクトそのものを返します。つまり、元の例外が持っていたあらゆるメタデータにアクセスできるということです。もちろん、元のバックトレースも取得可能です。

次の例では、食べ物の名前を保持するカスタム例外EatingErrorを定義し、再送出後もその属性を参照できることを示しています。

class EatingError < StandardError
  attr_reader :food
  def initialize(food)
    @food = food
  end
end

def fail_and_reraise
  raise EatingError.new("soup")
rescue
  raise RuntimeError
end

begin
  fail_and_reraise
rescue => e
  puts "#{ e } caused by #{ e.cause } while eating #{ e.cause.food }"
  puts e.cause.backtrace.first
end

無限の彼方へ!

ここまでの例は1階層のネストでしたが、Rubyのネストされた例外は理論上いくらでも深くできます。とはいえ、実際に3〜4階層より深いネストが必要になる場面はまずないでしょう。

…とはいえ、せっかくなので面白半分に100階層の深さを持つネストされた例外を作ってみました。ただの遊びのコードなので、本番環境でこんなものを目にすることがないよう祈っています。

def recursively_raise(c=0)
  raise "Level #{ c }"
rescue => e
  if c < 100
    recursively_raise(c + 1)
  else
    recursively_print(e)
  end
end

def recursively_print(e)
  if e
    puts e
    recursively_print(e.cause)
  end
end

recursively_raise()

# ... Prints the following:
# Level 100
# Level 99
# Level 98
# etc.

  1. TCMallocを使ってRubyのメモリ割り当てをプロファイリングする方法

    Rubyでは、メモリの割り当てはどのように行われているのでしょうか? Rubyは「ページ(page)」と呼ばれる塊単位でメモリを確保し、新しく生成されたオブジェクトはこのページの中に格納されていきます。 しかし、 ページがいっぱいになると、さらなるメモリが必要になります。 そのときRubyは、malloc関数を使ってオペレーティングシステム(OS)に追加のメモリを要求します。 このmalloc関数はOS標準で提供されているものですが、代替となる実装に差し替えることも可能です。 その代表的な実装のひとつが、Google製のTCMallocです。 TCMallocは「gperftools(Goog

  2. Rubyでパーサーを自作する方法!StringScannerを使った実装手順を徹底解説

    パース(構文解析)とは、文字列の集まりから意味を読み取り、プログラムが扱える形のデータへと変換する技術です。正規表現でも文字列の解析は可能ですが、すべての場面に適しているわけではありません。 たとえば、正規表現でHTMLを解析するのはあまり良い方法ではないというのは、プログラミング界隈ではよく知られた話です。 Rubyにはnokogiriという強力なライブラリがあり、HTMLの解析はこれに任せられます。しかし、自分でパーサーを一から作ってみると、文字列処理や構文解析の仕組みについて多くのことを学べます。それでは早速始めていきましょう! Rubyでのパースの基本:StringScannerクラス