Обычные классы ошибок Ruby: наследование атрибута сообщения
Я не могу найти много информации о пользовательских классах исключений.
Что я знаю
Вы можете объявить свой собственный класс ошибок и наследовать его от 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
будет ли это работать, и если да, то это правильный способ сделать что-то?
Ответы
Ответ 1
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
, см. Почему это плохой стиль для` rescue Exception = > e` в Ruby?.
Ответ 2
Ваша идея правильная, но то, как вы ее называете, неверно. Это должно быть
raise MyCustomError.new(an_object, "A message")
Ответ 3
Учитывая, что документация rubi core Exception
, из которой наследуются все другие ошибки, говорится о #message
Возвращает результат вызова exception.to_s. Обычно это возвращается сообщение или имя исключения. Предоставляя метод to_str, исключения соглашаются на использование там, где ожидаются строки.
http://ruby-doc.org/core-1.9.3/Exception.html#method-i-message
Я бы выбрал переопределение to_s
/to_str
или инициализатор. Вот пример, где мы хотим знать, в основном, с точки зрения человека, когда внешняя служба ничего не сделала.
ПРИМЕЧАНИЕ. Во второй стратегии ниже используются методы строковой строки 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
Переопределение стратегии # инициализации
Это стратегия, ближайшая к реализациям, которые я использовал в рельсах. Как отмечено выше, он использует методы 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
Демо-инструмент
Это демонстрация, показывающая спасение и обмен сообщениями вышеупомянутой реализации. Класс, добавляющий исключения, - это поддельный API для Cloudinary. Просто выгрузите одну из вышеперечисленных стратегий в консоль рельсов, затем следуйте этим инструкциям.
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
Ответ 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.