web-dev-qa-db-ja.com

引数を使用してカスタム例外を発生させる

モデルのカスタム例外をRailsの一種のラッパー例外として定義しています:(begin[code]rescue[raise custom exception]end

例外を発生させるとき、a)内部関数がエラーを発生させるモデルのインスタンス、およびb)キャッチされたエラーに関する情報を渡したいと思います。

これは、POST外部データソースからのリクエストにより生成されるモデルの自動インポートメソッドで進行中です。

tldr;例外を自分で定義した場合、どのように引数を例外に渡すことができますか?その例外に初期化メソッドがありますが、raise構文は例外クラスとメッセージのみを受け入れ、インスタンス化プロセスに渡されるオプションのパラメーターは受け入れないようです。

44
Chris Keele

newを使用して例外のインスタンスを作成します。

class CustomException < StandardError
  def initialize(data)
    @data = data
  end
end
# => nil 
raise CustomException.new(bla: "blupp")
# CustomException: CustomException
65
phoet

溶液:

_class FooError < StandardError
  attr_reader :foo

  def initialize(foo)
   super
   @foo = foo
  end
end
_

Rubocop Style Guide に従い、常にメッセージをraiseの2番目の引数として渡す場合、これが最良の方法です。

_raise FooError.new('foo'), 'bar'
_

次のようにfooを取得できます。

_rescue FooError => error
  error.foo     # => 'foo'
  error.message # => 'bar'
_

エラーメッセージをカスタマイズする場合は、次のように記述します。

_class FooError < StandardError
  attr_reader :foo

  def initialize(foo)
   super
   @foo = foo
  end

  def message
    "The foo is: #{foo}"
  end
end
_

fooが必要な場合、これはうまく機能します。 fooをオプションの引数にしたい場合は、読み続けてください。


説明:

raiseの2番目の引数としてメッセージを渡します

Rubocop Style Guide にあるように、メッセージと例外クラスは別々の引数として提供する必要があります。

_raise FooError.new('bar')
_

そして、バックトレースをraiseに渡したい場合、メッセージを2回渡さずにそれを行う方法はありません。

_raise FooError.new('bar'), 'bar', other_error.backtrace
_

この回答 が示すように、同じバックトレースと異なるメッセージまたはデータを持つ新しいインスタンスとして例外を再発生させる場合は、バックトレースを渡す必要があります。

FooErrorの実装

問題の核心は、fooがオプションの引数である場合、例外を発生させる2つの異なる方法があるということです。

_raise FooError.new('foo'), 'bar', backtrace # case 1
_

そして

_raise FooError, 'bar', backtrace # case 2
_

そして、FooErrorが両方で機能するようにします。

ケース1では、クラスではなくエラーインスタンスを指定したため、raiseは_'bar'_をエラーインスタンスのメッセージとして設定します。

ケース2の場合、raiseFooErrorをインスタンス化し、_'bar'_を唯一の引数として渡しますが、メッセージを設定しませんケース1のような初期化後。メッセージを設定するには、メッセージを唯一の引数として_FooError#initialize_でsuperを呼び出す必要があります。

したがって、ケース1では_FooError#initialize_は_'foo'_を受け取り、ケース2では_'bar'_を受け取ります。それは過負荷であり、一般的にこれらのケースを区別する方法はありません。これはRubyの設計上の欠陥です。したがって、fooがオプションの引数である場合、3つの選択肢があります。

(a)_FooError#initialize_に渡される値がfooまたはメッセージのいずれかであることを受け入れます。

(b)raiseでは、ケース1またはケース2スタイルのみを使用し、両方は使用しないでください。

(c)fooをキーワード引数にします。

fooをキーワード引数にしたくない場合は、(a)をお勧めします。上記のFooErrorの実装は、そのように動作するように設計されています。

ケース2スタイルを使用してraise a FooErrorを使用する場合、fooの値はメッセージであり、暗黙的にsuperに渡されます。 _FooError#initialize_にさらに引数を追加する場合は、明示的なsuper(foo)が必要です。

キーワード引数(h/t Lemon Cat's answer )を使用すると、コードは次のようになります。

_class FooError < StandardError
  attr_reader :foo

  def initialize(message, foo: nil)
   super(message)
   @foo = foo
  end
end
_

そして、レイズは次のようになります。

_raise FooError, 'bar', backtrace
raise FooError(foo: 'foo'), 'bar', backtrace
_
14
Max Wallace

エラーにコードを追加するサンプルコードを次に示します。

_class MyCustomError < StandardError
    attr_reader :code

    def initialize(code)
        @code = code
    end

    def to_s
        "[#{code}] #{super}"
    end
end
_

そしてそれを上げるには:raise MyCustomError.new(code), message

8
cyrilchampier