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

Rubyの例外に関する初心者向けガイド

先日、初心者向けに書かれたRuby例外の紹介を探していました。基本的なRuby構文は知っているが、例外とは何か、なぜそれが役立つのかよくわからない人です。見つからなかったので、自分で試してみることにしました。お役に立てば幸いです。紛らわしい点がありましたら、@StarrHorneまでお気軽にツイートしてください。 :)

例外とは何ですか?

例外は、予期しないイベントを処理するRubyの方法です。

コードにタイプミスをしたことがあり、プログラムがクラッシュしてSyntaxErrorのようなメッセージが表示された場合 またはNoMethodError 、その後、動作中の例外を見てきました。

Rubyで例外を発生させると、ワールドが停止し、プログラムがシャットダウンし始めます。プロセスを停止するものが何もない場合、プログラムは最終的にエラーメッセージで終了します。

これが例です。以下のコードでは、ゼロで除算しようとしています。これは不可能であるため、RubyはZeroDivisionErrorという例外を発生させます。 。プログラムは終了し、エラーメッセージを出力します。

1 / 0
# Program crashes and outputs: "ZeroDivisionError: divided by 0"

プログラムがクラッシュすると、ユーザーは怒りを覚える傾向があります。したがって、通常、このシャットダウンプロセスを停止し、エラーにインテリジェントに対応する必要があります。

これは、例外の「レスキュー」、「処理」、または「キャッチ」と呼ばれます。それらはすべて同じことを意味します。これがRubyでのやり方です:

begin
  # Any exceptions in here... 
  1/0
rescue
  # ...will cause this code to run
  puts "Got an exception, but I'm responding intelligently!"
  do_something_intelligent()
end

# This program does not crash.
# Outputs: "Got an exception, but I'm responding intelligently!"

例外は引き続き発生しますが、「レスキュー」されたため、プログラムがクラッシュすることはありません。 Rubyは終了する代わりに、メッセージを出力するレスキューブロックでコードを実行します。

これは素晴らしいことですが、大きな制限が1つあります。 何が起こったのかを知らせずに、「何かがうまくいかなかった」ことを教えてくれます。 間違えた。

何がうまくいかなかったかに関するすべての情報は、例外オブジェクトに含まれます。

例外オブジェクト

例外オブジェクトは通常のRubyオブジェクトです。それらは、あなたが救出したばかりの例外を除いて、「何が起こったのか」に関するすべてのデータを保持しています。

例外オブジェクトを取得するには、わずかに異なるレスキュー構文を使用します。

# Rescues all errors, an puts the exception object in `e`
rescue => e

# Rescues only ZeroDivisionError and puts the exception object in `e`
rescue ZeroDivisionError => e

上記の2番目の例では、ZeroDivisionError e内のオブジェクトのクラスです 。これまでに説明した例外の「タイプ」はすべて、実際には単なるクラス名です。

例外オブジェクトは、有用なデバッグデータも保持します。 ZeroDivisionErrorの例外オブジェクトを見てみましょう。 。

begin
  # Any exceptions in here... 
  1/0
rescue ZeroDivisionError => e
  puts "Exception Class: #{ e.class.name }"
  puts "Exception Message: #{ e.message }"
  puts "Exception Backtrace: #{ e.backtrace }"
end

# Outputs:
# Exception Class: ZeroDivisionError
# Exception Message: divided by 0
# Exception Backtrace: ...backtrace as an array...

ほとんどのRuby例外と同様に、クラス名とともにメッセージとバックトレースが含まれています。

独自の例外を発生させる

これまでは、例外の救済についてのみ説明してきました。独自の例外をトリガーすることもできます。このプロセスは「レイズ」と呼ばれます。 raiseを呼び出すことでそれを行います 方法。

独自の例外を発生させると、使用する例外のタイプを選択できます。エラーメッセージを設定することもできます。

次に例を示します:

begin
  # raises an ArgumentError with the message "you messed up!"
  raise ArgumentError.new("You messed up!")
rescue ArgumentError => e  
  puts e.message
end

# Outputs: You messed up! 

ご覧のとおり、新しいエラーオブジェクト(ArgumentError)を作成しています。 )カスタムメッセージ(「めちゃくちゃになりました!」)を付けて、raiseに渡します。 メソッド。

これはRubyであり、raise いくつかの方法で呼び出すことができます:

# This is my favorite because it's so explicit
raise RuntimeError.new("You messed up!")

# ...produces the same result
raise RuntimeError, "You messed up!"

# ...produces the same result. But you can only raise 
# RuntimeErrors this way
raise "You messed up!"
カスタム例外の作成

Rubyの組み込みの例外は素晴らしいですが、考えられるすべてのユースケースを網羅しているわけではありません。

ユーザーシステムを構築していて、ユーザーがサイトの立ち入り禁止部分にアクセスしようとしたときに例外を発生させたい場合はどうなりますか? Rubyの標準的な例外はどれも当てはまらないので、最善の策は新しい種類の例外を作成することです。

カスタム例外を作成するには、StandardErrorから継承する新しいクラスを作成するだけです。 。

class PermissionDeniedError < StandardError

end

raise PermissionDeniedError.new()

これは通常のRubyクラスです。つまり、他のクラスと同じように、メソッドとデータを追加できます。 「アクション」という属性を追加しましょう:

class PermissionDeniedError < StandardError

  attr_reader :action

  def initialize(message, action)
    # Call the parent's constructor to set the message
    super(message)

    # Store the action in an instance variable
    @action = action
  end

end

# Then, when the user tries to delete something they don't
# have permission to delete, you might do something like this:
raise PermissionDeniedError.new("Permission Denied", :delete)
クラス階層

StandardErrorをサブクラス化してカスタム例外を作成しました 、それ自体がExceptionをサブクラス化します 。

実際、Rubyの例外のクラス階層を見ると、最終的にはExceptionに戻ることがわかります。 。ここで、私はあなたにそれを証明します。これらはRubyの組み込み例外のほとんどであり、階層的に表示されます:

Exception
 NoMemoryError
 ScriptError
   LoadError
   NotImplementedError
   SyntaxError
 SignalException
   Interrupt
 StandardError
   ArgumentError
   IOError
     EOFError
   IndexError
   LocalJumpError
   NameError
     NoMethodError
   RangeError
     FloatDomainError
   RegexpError
   RuntimeError
   SecurityError
   SystemCallError
   SystemStackError
   ThreadError
   TypeError
   ZeroDivisionError
 SystemExit

もちろん、これらすべてを覚える必要はありません。この階層の考え方は特定の理由で非常に重要であるため、これらを紹介します。

特定のクラスのエラーをレスキューすると、その子クラスのエラーもレスキューされます。

もう一度試してみましょう...

StandardErrorを救助するとき 、クラスStandardErrorで例外をレスキューするだけではありません しかし、その子供たちのものも。グラフを見ると、それがたくさんあることがわかります:ArgumentErrorIOError 、など。

rescue Exception場合 、すべての例外を救済することになりますが、これは非常に悪い考えです

すべての例外の救済(悪い方法)

怒鳴られたい場合は、スタックオーバーフローに移動し、次のようなコードを投稿してください:

// Don't do this 
begin
  do_something()
rescue Exception => e
  ...
end

上記のコードはすべての例外を救済します。 やらないでください! それは奇妙な方法であなたのプログラムを壊します。

これは、Rubyがエラー以外の例外を使用するためです。また、「シグナル」と呼ばれるオペレーティングシステムからのメッセージを処理するためにそれらを使用します。 「ctrl-c」を押してプログラムを終了したことがある場合は、シグナルを使用したことがあります。すべての例外を抑制することにより、それらの信号も抑制します。

また、構文エラーなど、プログラムを実際にクラッシュさせる可能性のあるいくつかの種類の例外もあります。それらを抑制した場合、タイプミスやその他の間違いをいつ犯したかはわかりません。

すべてのエラーの救済(正しい方法)

戻ってクラス階層チャートを見ると、救済したいすべてのエラーがStandardErrorの子であることがわかります。

つまり、「すべてのエラー」を救済したい場合は、StandardErrorを救済する必要があります。 。

begin
  do_something()
rescue StandardError => e
  # Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone. 
end

実際、例外クラスを指定しない場合、RubyはStandardErrorを意味すると想定します

begin
  do_something()
rescue => e
  # This is the same as rescuing StandardError
end

特定のエラーの救済(最善のアプローチ)

すべてのエラーを救済する方法がわかったので、それは通常、悪い考え、コードの臭い、有害と見なされるなどであることを知っておく必要があります。

これらは通常、どの特定の例外を救済する必要があるかを理解するのが面倒だったことを示しています。そして、彼らはほとんどの場合、あなたを悩ませるために戻ってきます。

ですから、時間をかけて正しく実行してください。特定の例外を救助します。

begin
  do_something()
rescue Errno::ETIMEDOUT => e
  // This will only rescue Errno::ETIMEDOUT exceptions
end

同じレスキューブロックで複数の種類の例外をレスキューすることもできるので、言い訳はできません。 :)

begin
  do_something()
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED => e
end

  1. Rubyで例外にコンテキストデータを追加する方法

    標準のバックトレース/エラーメッセージの組み合わせでは不十分な場合があります。エラーの原因を特定するために、追加のデータが必要になる場合があります。幸い、Rubyで行うのはとても簡単です。 エラーメッセージのカスタマイズ エラーにコンテキスト情報を追加する最も簡単な方法は、それを例外のメッセージに追加することです。以下の例では、例外をキャッチし、新しいメッセージで再発生させています: begin raise foo rescue => e raise e.class, bar end # RuntimeError: bar このアプローチの良い使用例は、テンプレートをレン

  2. Rubyでの関数型プログラミング(完全ガイド)

    関数型プログラミングについて聞いたばかりで、いくつか質問があるかもしれません。 いいね… 関数型プログラミングとは正確には何ですか? オブジェクト指向プログラミングと比較してどうですか? Rubyで関数型プログラミングを使用する必要がありますか? これらの質問に答えて、これがどのように機能するかをよりよく理解できるようにします。 関数型プログラミングとは これは単なる流行や派手な言葉ではなく、長い間存在していた実際のプログラミングパラダイムですが、最近人気を取り戻しています。 そして、このパラダイムの背後にある基本的な考え方は、あなたが思っているよりも理解しやすいです。 関数型