In Anbetracht dessen, worüber die Ruby-Core-Dokumentation Exception
, von der alle anderen Fehler erben, aussagt#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 Angabe einer to_str-Methode erklären sich Ausnahmen damit einverstanden, dort verwendet zu werden, wo Strings erwartet werden.
http://ruby-doc.org/core-1.9.3/Exception.html#method-i-message
Ich würde mich für eine Neudefinition to_s
/ to_str
oder den Initialisierer entscheiden. Hier ist ein Beispiel, in dem wir auf meist menschlich lesbare Weise wissen möchten, wann ein externer Dienst etwas nicht getan hat.
HINWEIS: In der folgenden zweiten Strategie werden die Rails-Methoden für hübsche Zeichenfolgen verwendet, z. B. demodualize
, die möglicherweise etwas kompliziert und daher in einer Ausnahme möglicherweise unklug sind. Sie können der Methodensignatur bei Bedarf auch weitere Argumente hinzufügen.
#To_s Strategie überschreiben 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
# Initialize Strategy überschreiben
Dies ist die Strategie, die den Implementierungen am nächsten kommt, die ich in Schienen verwendet habe. Wie oben erwähnt, verwendet es das demodualize
, underscore
undhumanize
ActiveSupport
Methoden. Dies könnte jedoch wie in der vorherigen Strategie leicht entfernt werden.
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
Demo-Tool
Dies ist eine Demo, die zeigt, wie die oben genannte Implementierung gerettet und übermittelt wird. Die Klasse, die die Ausnahmen auslöst, ist eine gefälschte API für Cloudinary. Legen Sie einfach eine der oben genannten Strategien in Ihre Rails-Konsole ein, gefolgt von dieser.
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
rescue Exception => e
. Es ist breiter als die Standardeinstellung, vonrescue => e
der ausStandardError
alles erfasst wird, einschließlich Strg + C. Ich würde es tunrescue MyCustomError => e
.