>> 連載の目次は こちら!
■ Rubyの例外クラスの構成
Exception
|
|- StandardError
| |- TypeError とか RuntimeError とかの、アプリケーション側に起因する例外たち
| |- 僕らがアプリケーション向けに独自に作成する例外たち
|
|- その他の例外: メモリのエラーみたいな、システム側の例外。アプリケーション側では救えない類。
- 例外の基底クラスは Exception クラス
- その配下は、大きく StandardErrorクラス系と、それ以外に分かれる
- StandardErrorクラス系は、いわゆるアプリケーション側の例外
- それ以外はシステム側のエラーで、通常はアプリケーションでは復帰できない例外たち(メモリのエラーとか)
- 通常の例外クラスは、XxxError という命名が正しいのかな。XxxExceptionじゃなくて。
・rescueでキャッチできるのは StandardError系だけ
↑間違いです! 型を明示してrescueすれば、それ以外の例外もちゃんと拾えます。
コメント欄の @scivola さんの記述も参照してください。
■ begin〜rescue〜end (いわゆる try〜catch 的な)
● 一番シンプルに試してみる
begin
raise "oh my god !!" # (1)
rescue => e # (2)
p e # (3) #<RuntimeError: oh my god !!>
p $! # (4) #<RuntimeError: oh my god !!>
end
(1) raise で例外を投げる(throw的な)。例外クラスを指定しない場合、RuntimeError が投げられる
(2) rescue で catch する。例外の型を指定しない場合、StandardError系の全ての例外をキャッチする
(3) rescue が受け取った e が、例外オブジェクト。
(4) グローバル変数「$!」にも、例外オブジェクトがセットされている
● 型を指定してrescueしてみる
def exception_test(num)
begin
case num
when 1
# raiseする
raise ArgumentError.new, "bad argument"
when 2
# 0で割っちゃう
5 / 0
when 3
# 無いメソッド呼んじゃう
String.not_exist_method
else
# もうどうしようもないやつ
raise RuntimeError, "oh my god!"
end
# 例外の型を明示してresucue
rescue ArgumentError => e
print "rescue 1: "; p e
# 例外の型は複数指定してresucueできる
rescue ZeroDivisionError, NoMethodError => e
print "rescue 2: "; p e
# 例外の型を指定しなければ、StandardError系すべてresucueできる
# 以下は「rescue StandardError => e」って書くのと同じこと。
rescue => e
print "rescue 3: "; p e
end
end
exception_test(1)
# rescue 1: #<ArgumentError: bad argument>
exception_test(2)
# rescue 2: #<ZeroDivisionError: divided by 0>
exception_test(3)
# rescue 2: #<NoMethodError: undefined method `not_exist_method' for String:Class>
exception_test(4)
# rescue 3: #<RuntimeError: oh my god!>
■ 独自の例外クラスを定義する
# StandardError を継承する。もちろんここからさらにサブクラス階層化しても良し。
class OverWorkError < StandardError
attr_reader :hour
# 例外のnewで引数を受け取ってインスタンス変数で保持してみる
def initialize(hour)
@hour = hour
end
end
begin
(1..10).each do |i|
raise OverWorkError.new(i), "働きすぎ!" if (i > 8)
print i.to_s + ".."
end
rescue OverWorkError => e
# 例外オブジェクトからインスタンス変数を取得。messageメソッドでメッセージを取得
puts e.hour.to_s + "時間なんて" + e.message
end
# 実行結果
# 1..2..3..4..5..6..7..8..9時間なんて働きすぎ!
■ 例外が発生しなかった場合になんかする(else)
begin
print "予想外のことが全く起きないって..."
rescue => e
p e
else
puts "けっこう退屈かもねー"
end
■ 例外が発生しようがしまいがなんかする(ensure)
begin
print "世は全てこともなし..."
rescue => e
p e
ensure
puts "で、でもなんか言わせてー!"
end
■ 例外発生時にリトライする(retry)
i = 0
begin
i += 1
raise OverWorkError.new(i), "働きすぎ!"
rescue => e
# 無限ループに注意!なんか条件付きでretryすること。
retry if (i <= 8)
puts e.hour.to_s + "時間なんて" + e.message # 9時間なんて働きすぎ!
end
# 実行結果
# 9時間なんて働きすぎ!
■ 簡易なrescue(後置rescue)
raise OverWorkError.new(i), "働きすぎ!" rescue puts "おうちに帰ろう!"
# この場合、StandardError系をまとめてrescueする