カスタム例外クラスに関する多くの情報を見つけることができないようです。
私が知っていること
カスタムエラークラスを宣言し、StandardError
から継承させることができるため、rescue
dになります。
class MyCustomError < StandardError
end
これにより、以下を使用してレイズできます。
raise MyCustomError, "A message"
その後、救助時にそのメッセージを受け取ります
rescue MyCustomError => e
puts e.message # => "A message"
わからないこと
例外をいくつかのカスタムフィールドに渡したいが、親クラスからmessage
属性を継承したい。 このトピックについて@message
は例外クラスのインスタンス変数ではないため、継承が機能しないことが心配です。
誰も私にこれの詳細を教えてもらえますか? object
属性を持つカスタムエラークラスを実装するにはどうすればよいですか?次は正しいですか:
class MyCustomError < StandardError
attr_reader :object
def initialize(message, object)
super(message)
@object = object
end
end
その後:
raise MyCustomError.new(anObject), "A message"
取得するため:
rescue MyCustomError => e
puts e.message # => "A message"
puts e.object # => anObject
それは機能しますか、それが機能する場合、これは物事を行う正しい方法ですか?
raise
はすでにメッセージを設定しているため、コンストラクタに渡す必要はありません。
class MyCustomError < StandardError
attr_reader :object
def initialize(object)
@object = object
end
end
begin
raise MyCustomError.new("an object"), "a message"
rescue MyCustomError => e
puts e.message # => "a message"
puts e.object # => "an object"
end
rescue Exception
をrescue MyCustomError
に置き換えました。 を参照してください。Rubyで「例外=> e」を「レスキューする」のが悪いスタイルなのはなぜですか? 。
他のすべてのエラーが継承するException
のRubyコアドキュメントの内容を考えると、#message
に関する記述
Exception.to_sを呼び出した結果を返します。通常、これは例外のメッセージまたは名前を返します。 to_strメソッドを提供することにより、文字列が予想される場所で例外を使用することに同意しています。
http://Ruby-doc.org/core-1.9.3/Exception.html#method-i-message
to_s
/to_str
または初期化子の再定義を選択します。以下は、外部サービスが何かを実行できなかった場合に、ほとんど人間が読める方法で知りたい例です。
注:次の2番目の戦略では、Railsきれいな文字列メソッド(demodualize
など)を使用します。必要に応じて、メソッドシグネチャにさらに引数を追加することもできます。
#to_s戦略のオーバーライド#to_strではなく、異なる動作をします
module ExternalService
class FailedCRUDError < ::StandardError
def to_s
'failed to crud with external service'
end
end
class FailedToCreateError < FailedCRUDError; end
class FailedToReadError < FailedCRUDError; end
class FailedToUpdateError < FailedCRUDError; end
class FailedToDeleteError < FailedCRUDError; end
end
コンソール出力
begin; raise ExternalService::FailedToCreateError; rescue => e; e.message; end
# => "failed to crud with external service"
begin; raise ExternalService::FailedToCreateError, 'custom message'; rescue => e; e.message; end
# => "failed to crud with external service"
begin; raise ExternalService::FailedToCreateError.new('custom message'); rescue => e; e.message; end
# => "failed to crud with external service"
raise ExternalService::FailedToCreateError
# ExternalService::FailedToCreateError: failed to crud with external service
#initialize Strategyのオーバーライド
これは、私がRailsで使用した実装に最も近い戦略です。上記のように、demodualize
、underscore
、およびhumanize
ActiveSupport
メソッドを使用します。ただし、前の戦略のように、これは簡単に削除できます。
module ExternalService
class FailedCRUDError < ::StandardError
def initialize(service_model=nil)
super("#{self.class.name.demodulize.underscore.humanize} using #{service_model.class}")
end
end
class FailedToCreateError < FailedCRUDError; end
class FailedToReadError < FailedCRUDError; end
class FailedToUpdateError < FailedCRUDError; end
class FailedToDeleteError < FailedCRUDError; end
end
コンソール出力
begin; raise ExternalService::FailedToCreateError; rescue => e; e.message; end
# => "Failed to create error using NilClass"
begin; raise ExternalService::FailedToCreateError, Object.new; rescue => e; e.message; end
# => "Failed to create error using Object"
begin; raise ExternalService::FailedToCreateError.new(Object.new); rescue => e; e.message; end
# => "Failed to create error using Object"
raise ExternalService::FailedCRUDError
# ExternalService::FailedCRUDError: Failed crud error using NilClass
raise ExternalService::FailedCRUDError.new(Object.new)
# RuntimeError: ExternalService::FailedCRUDError using Object
デモツール
これは、上記の実装のレスキューとメッセージングを示すデモです。例外を発生させるクラスは、Cloudinaryに対する偽のAPIです。上記の戦略の1つをRailsコンソールにダンプし、その後に続きます。
require 'Rails' # only needed for second strategy
module ExternalService
class FailedCRUDError < ::StandardError
def initialize(service_model=nil)
@service_model = service_model
super("#{self.class.name.demodulize.underscore.humanize} using #{@service_model.class}")
end
end
class FailedToCreateError < FailedCRUDError; end
class FailedToReadError < FailedCRUDError; end
class FailedToUpdateError < FailedCRUDError; end
class FailedToDeleteError < FailedCRUDError; end
end
# Stub service representing 3rd party cloud storage
class Cloudinary
def initialize(*error_args)
@error_args = error_args.flatten
end
def create_read_update_or_delete
begin
try_and_fail
rescue ExternalService::FailedCRUDError => e
e.message
end
end
private def try_and_fail
raise *@error_args
end
end
errors_map = [
# Without an arg
ExternalService::FailedCRUDError,
ExternalService::FailedToCreateError,
ExternalService::FailedToReadError,
ExternalService::FailedToUpdateError,
ExternalService::FailedToDeleteError,
# Instantiated without an arg
ExternalService::FailedCRUDError.new,
ExternalService::FailedToCreateError.new,
ExternalService::FailedToReadError.new,
ExternalService::FailedToUpdateError.new,
ExternalService::FailedToDeleteError.new,
# With an arg
[ExternalService::FailedCRUDError, Object.new],
[ExternalService::FailedToCreateError, Object.new],
[ExternalService::FailedToReadError, Object.new],
[ExternalService::FailedToUpdateError, Object.new],
[ExternalService::FailedToDeleteError, Object.new],
# Instantiated with an arg
ExternalService::FailedCRUDError.new(Object.new),
ExternalService::FailedToCreateError.new(Object.new),
ExternalService::FailedToReadError.new(Object.new),
ExternalService::FailedToUpdateError.new(Object.new),
ExternalService::FailedToDeleteError.new(Object.new),
].inject({}) do |errors, args|
begin
errors.merge!( args => Cloudinary.new(args).create_read_update_or_delete)
rescue => e
binding.pry
end
end
if defined?(pp) || require('pp')
pp errors_map
else
errors_map.each{ |set| puts set.inspect }
end
あなたの考えは正しいが、あなたがそれを呼ぶ方法は間違っている。そのはず
raise MyCustomError.new(an_object, "A message")
似たようなことをしたかった。オブジェクトを#newに渡し、渡されたオブジェクトの処理に基づいてメッセージを設定したかった。次の作品。
class FooError < StandardError
attr_accessor :message # this is critical!
def initialize(stuff)
@message = stuff.reverse
end
end
begin
raise FooError.new("!dlroW olleH")
rescue FooError => e
puts e.message #=> Hello World!
end
attr_accessor :message
を宣言しないと機能しません。 OPの問題に対処するために、メッセージを追加の引数として渡し、好きなものを保存することもできます。重要な部分は#messageをオーバーライドしているようです。