1. ホーム
  2. スクリプト・コラム
  3. ルビートピックス

Rubyでの例外処理コードの記述例

2022-02-03 06:13:11

単一の例外は、例外をキャッチして繰り返し投げるときにのみ fail キーワードを使用します (ここでは失敗しているのではなく、正確に意図的に例外を投げているためです)。

  begin
   fail 'Oops'
  rescue => error
   raise if error.message ! = 'Oops'
  end



    fail/raiseに正確なRuntimeErrorを指定しないでください。

 # bad
  fail RuntimeError, 'message'

  # good - signals a RuntimeError by default
  fail 'message'



    例外インスタンスではなく、fail/raiseの2つの引数として、例外クラスとメッセージを指定します。

 # bad
  fail SomeException.new('message')
  # Note that there is no way to do `fail SomeException.new('message'), backtrace`.

  # good
  fail SomeException, 'message'
  # Consistent with `fail SomeException, 'message', backtrace`.



    ensureブロックの中でreturnしてはいけない。ensure ブロックの中で明示的にメソッドから return すると、例外がスローされても return が優先され、例外がスローされていないにもかかわらず return してしまいます。実際、例外は黙って逃げていきます。

 def foo
   begin
    fail
   ensure
    return 'very bad idea'
   end
  end


    可能な限り暗黙のbeginブロックを使用する。

 # bad
  def foo
   begin
    # main logic goes here
   rescue
    # failure handling goes here
   end
  end

  # good
  def foo
   # main logic goes here
  rescue
   # failure handling goes here
  end



    コンティンジェンシー方式により、ビギン・ブロックの使用を削減する。(Avdi Grimmの造語)により、begin blockの使用を減らす。

  # bad
  begin
   something_that_might_fail
  rescue IOError
   # handle IOError
  end

  begin
   something_else_that_might_fail
  rescue IOError
   # handle IOError
  end

  # good
  def with_io_error_handling
    yield
  rescue IOError
   # handle IOError
  end

  with_io_error_handling { something_that_might_fail }

  with_io_error_handling { something_else_that_might_fail }



    例外出力を抑止しない。

  # bad
  begin
   # an exception occurs here
  rescue SomeError
   # the rescue clause does absolutely nothing
  end

  # bad
  do_something rescue nil



    レスキューモディファイアの形は使わないでください。

 # bad - this catches exceptions of StandardError class and its descendant classes
  read_file rescue handle_error($!)

  # good - this catches only the exceptions of Errno::ENOENT class and its descendant classes
  def foo
   read_file
  rescue Errno::ENOENT => ex
   handle_error(ex)
  end



    フローを制御するために例外を使わないでください。

 # bad
  begin
   n / d
  rescue ZeroDivisionError
   puts "Cannot divide by 0!"
  end

  # good
  if d.zero?
   puts "Cannot divide by 0!"
  else
   n / d
  end



    トップレベルの)Exception クラスをインターセプトすることは常に避けるべきでしょう。ここで (ruby 自身が) シグナルをキャッチして exit を呼び出し、 kill -9 でプロセスを終了させる必要があります。

  # bad
  begin
   # calls to exit and kill signals will be caught (except kill -9)
   exit
  rescue Exception
   puts "you didn't really want to exit, right?"
   # exception handling
  end

  # good
  begin
   # a blind rescue rescues from StandardError, not Exception as many
   # programmers assume.
  rescue => e
   # exception handling
  end

  # also good
  begin
   # an exception occurs here

  rescue StandardError => e
   # exception handling
  end



    より具体的な例外をレスキューチェーンの上に配置し、そうでない場合はレスキューされません。

  # bad
  begin
   # some code
  rescue Exception => e
   # some handling
  rescue StandardError => e
   # some handling
  end

  # good
  begin
   # some code
  rescue StandardError => e
   # some handling
  rescue Exception => e
   # some handling
  end



    プログラムが取得した外部資源を ensure ブロックで解放します。

 f = File.open('testfile')
  begin
   # ... process
  rescue
   # ... handle error
  ensure
   f.close unless f.nil?
  end


    必要な場合を除き、新しい例外クラスを導入するのではなく、可能な限りRuby標準ライブラリの例外クラスを使用します。(自分自身の例外クラスを派生させるのではなく)