tutorial - ruby throw argument error




Benutzerdefinierte Ruby-Fehlerklassen: Vererbung des Nachrichtenattributs (3)

Ich kann anscheinend nicht viele Informationen über benutzerdefinierte Ausnahmeklassen finden.

Was ich weiß

Sie können Ihre benutzerdefinierte Fehlerklasse deklarieren und sie von StandardError erben lassen, so dass es rescue d sein kann:

class MyCustomError < StandardError
end

So können Sie es erhöhen mit:

raise MyCustomError, "A message"

und später, hole diese Botschaft bei der Rettung

rescue MyCustomError => e
  puts e.message # => "A message"

Was ich nicht weiß

Ich möchte meine Ausnahme einige benutzerdefinierte Felder geben, aber ich möchte das message von der übergeordneten Klasse erben. Ich habe zu diesem Thema @message dass @message keine Instanzvariable der Ausnahmeklasse ist, also mache ich mir Sorgen, dass meine Vererbung nicht funktioniert.

Kann mir jemand mehr Details dazu sagen? Wie würde ich eine benutzerdefinierte Fehlerklasse mit einem object implementieren? Ist folgendes richtig:

class MyCustomError < StandardError
  attr_reader :object
  def initialize(message, object)
    super(message)
    @object = object
  end
end

Und dann:

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

bekommen:

rescue MyCustomError => e
  puts e.message # => "A message"
  puts e.object # => anObject

Wird es funktionieren, und wenn ja, ist das der richtige Weg, Dinge zu tun?


Angesichts dessen, was die Ruby-Core-Dokumentation von Exception , von der alle anderen Fehler erben, besagt #message

Gibt das Ergebnis des Aufrufs von exception.to_s zurück. Normalerweise gibt dies die Nachricht oder den Namen der Ausnahme zurück. Durch die Bereitstellung einer to_str-Methode stimmen Ausnahmen überein, wo Strings erwartet werden.

http://ruby-doc.org/core-1.9.3/Exception.html#method-i-message

Ich würde entscheiden, to_s / to_str oder den Initialisierer neu zu definieren. Hier ist ein Beispiel, in dem wir auf eine meist menschenverständliche Art und Weise wissen wollen, wann ein externer Dienst etwas versäumt hat.

HINWEIS: Bei der zweiten Strategie werden die hübschen String-Methoden der Schiene verwendet, wie zum Beispiel das demodualize , was ein wenig kompliziert und daher in einer Ausnahme möglicherweise unklug ist. Sie könnten der Methodensignatur auch weitere Argumente hinzufügen, falls Sie dies benötigen.

Überschreibt #to_s Strategie nicht #to_str, es funktioniert anders

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

Konsolenausgabe

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

Überschreiben der # Initialisierungsstrategie

Dies ist die Strategie, die den Implementierungen in Schienen am nächsten kommt. Wie oben erwähnt, werden die ActiveSupport Methoden demodualize , underscore und humanize . Aber dies könnte leicht entfernt werden, wie in der vorherigen Strategie.

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

Konsolenausgabe

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

Demowerkzeug

Dies ist eine Demo, um die Rettung und das Messaging der obigen Implementierung zu zeigen. Die Klasse, die die Ausnahmen auslöst, ist eine gefälschte API zu Cloudinary. Werfen Sie einfach eine der obigen Strategien in Ihre Rails-Konsole, gefolgt von diesem.

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

Deine Idee ist richtig, aber wie du es nennst ist falsch. Es sollte sein

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

raise setzt die Nachricht bereits so, dass Sie sie nicht an den Konstruktor übergeben müssen:

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

Ich habe die rescue Exception durch rescue MyCustomError , siehe Warum ist es ein schlechter Stil, Exception => e` in Ruby zu retten? .







custom-exceptions