モデルのカスタム例外をRailsの一種のラッパー例外として定義しています:(begin[code]rescue[raise custom exception]end
)
例外を発生させるとき、a)内部関数がエラーを発生させるモデルのインスタンス、およびb)キャッチされたエラーに関する情報を渡したいと思います。
これは、POST外部データソースからのリクエストにより生成されるモデルの自動インポートメソッドで進行中です。
tldr;例外を自分で定義した場合、どのように引数を例外に渡すことができますか?その例外に初期化メソッドがありますが、raise
構文は例外クラスとメッセージのみを受け入れ、インスタンス化プロセスに渡されるオプションのパラメーターは受け入れないようです。
newを使用して例外のインスタンスを作成します。
class CustomException < StandardError
def initialize(data)
@data = data
end
end
# => nil
raise CustomException.new(bla: "blupp")
# CustomException: CustomException
_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の場合、raise
はFooError
をインスタンス化し、_'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
_
エラーにコードを追加するサンプルコードを次に示します。
_class MyCustomError < StandardError
attr_reader :code
def initialize(code)
@code = code
end
def to_s
"[#{code}] #{super}"
end
end
_
そしてそれを上げるには:raise MyCustomError.new(code), message