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.
-
TCMallocを使ってRubyのメモリ割り当てをプロファイリングする方法
Rubyでは、メモリの割り当てはどのように行われているのでしょうか? Rubyは「ページ(page)」と呼ばれる塊単位でメモリを確保し、新しく生成されたオブジェクトはこのページの中に格納されていきます。 しかし、 ページがいっぱいになると、さらなるメモリが必要になります。 そのときRubyは、malloc関数を使ってオペレーティングシステム(OS)に追加のメモリを要求します。 このmalloc関数はOS標準で提供されているものですが、代替となる実装に差し替えることも可能です。 その代表的な実装のひとつが、Google製のTCMallocです。 TCMallocは「gperftools(Goog
-
Rubyでパーサーを自作する方法!StringScannerを使った実装手順を徹底解説
パース(構文解析)とは、文字列の集まりから意味を読み取り、プログラムが扱える形のデータへと変換する技術です。正規表現でも文字列の解析は可能ですが、すべての場面に適しているわけではありません。 たとえば、正規表現でHTMLを解析するのはあまり良い方法ではないというのは、プログラミング界隈ではよく知られた話です。 Rubyにはnokogiriという強力なライブラリがあり、HTMLの解析はこれに任せられます。しかし、自分でパーサーを一から作ってみると、文字列処理や構文解析の仕組みについて多くのことを学べます。それでは早速始めていきましょう! Rubyでのパースの基本:StringScannerクラス