Rubyカスタムエラークラス:メッセージ属性の継承


95

カスタム例外クラスに関する情報があまり見つからないようです。

私が知っていること

カスタムエラークラスを宣言し、それをから継承させるStandardErrorことができるため、次のようになりますrescue

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

それは機能しますか?それが機能する場合、これは物事の正しい方法ですか?


3
しないでくださいrescue Exception => e。これrescue => eは、から拡張されたデフォルトよりも広く、StandardErrorCtrl + Cを含むすべてをキャッチします。するよrescue MyCustomError => e
ライアンテイラー

1
@RyanTaylorより適切なアプローチのために質問を編集しました。
MarioDS 2014

回答:


121

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 Exceptionrescue MyCustomError見、それはRubyで`救助例外=> e`に悪いスタイルですなぜ?


構文全体を示したので、私はあなたの答えを受け入れます。ありがとう!
MarioDS 2013

1
ここではやってrescue Exceptionrescue MyCustomErrorますが、どうしてでしょうか?
Dfr

参考までに、最初の引数objectがオプションで、がraise MyCustomError, "a message"ないnew場合、「メッセージ」は設定されません。
hiroshi

カスタム例外クラスで発生したメッセージをどうにかして取得する方法はありますか?
Cyber​​Mew 2018年

@Cyber​​Mewどういう意味ですか?何をしたいですか?
ステファン

10

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_strではなく#to_s戦略をオーバーライドします。動作が異なります

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戦略のオーバーライド

これは、私がレールで使用した実装に最も近い戦略です。上述したように、使用demodualizeunderscore及び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

6

あなたの考えは正しいですが、あなたがそれを呼ぶ方法は間違っています。そのはず

raise MyCustomError.new(an_object, "A message")

さて、あなたが与えたメッセージはraiseキーワードか何かの2番目のパラメータだと思いました。
MarioDS 2013

initialize2つの引数を取るように再定義しました。new引数をに渡しますinitialize
佐和

または、括弧を省略できます。
澤沢2013

私はそのことを理解していますが、質問でリンクしたトピックのポスターは次のようにしていますraise(BillRowError.new(:roamingcalls, @index), "Roaming Calls field missing")。したがって、彼はraise2つのパラメーターで呼び出しBillRowErrorます。新しいオブジェクトと彼のメッセージです。:私はちょうど私はいつもこのようにそれを参照してください他のチュートリアルでは...構文で混乱していますraise Error, message
MarioDS

1
問題は、渡す引数の数ではありませんraise。それはかなり柔軟です。問題は、initialize2つの引数を取るように定義し、1つだけを与えたことです。あなたの例を見てください。BillRowError.new(:roamingcalls, @index)2つの引数が与えられます。
佐和

4

似たようなことをしたかった。オブジェクトを#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をオーバーライドしているようです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.