Как спасти все исключения в определенном пространстве имен?
Есть ли способ спасти все исключения в определенном пространстве имен?
Например, я хочу спасти все исключения Errno:: * (Errno:: ECONNRESET, Errno:: ETIMEDOUT). Я могу пойти дальше и перечислить их все в моей строке исключения, но мне было интересно, могу ли я что-то сделать.
begin
# my code
rescue Errno
# handle exception
end
Вышеупомянутая идея, похоже, не работает, поэтому есть что-то подобное, что может работать?
Ответы
Ответ 1
Все Errno
подклассы исключений SystemCallError
:
Модуль Errno
создается динамически для сопоставления этих ошибок операционной системы с классами Ruby, причем каждый номер ошибки генерирует свой собственный подкласс SystemCallError
. Поскольку подкласс создается в модуле Errno
, его имя запустится Errno::
.
Итак, вы можете поймать SystemCallError
, а затем выполнить простую проверку имени:
rescue SystemCallError => e
raise e if(e.class.name.start_with?('Errno::'))
# do your thing...
end
Ответ 2
Все классы под Errno являются подклассами SystemCallError. И все подклассы SystemCallError являются классами под Errno. 2 набора идентичны, поэтому просто спасение SystemCallError. Это предполагает, что вы не используете внешнюю библиотеку, которая добавляется к одной, а не к другой.
Проверьте идентификатор 2-х наборов (используя active_support):
Errno.constants.map {|name|
Errno.const_get(name)
}.select{|const|
Class === const
}.uniq.map(&:to_s).sort ==
SystemCallError.subclasses.map(&:to_s).sort
Это возвращает true
для меня.
Итак, применительно к вашему примеру:
begin
# my code
rescue SystemCallError
# handle exception
end
Ответ 3
Вот еще одна интересная альтернатива . Может быть адаптирован к тому, что вы хотите.
Вставка наиболее интересной части:
def match_message(regexp)
lambda{ |error| regexp === error.message }
end
begin
raise StandardError, "Error message about a socket."
rescue match_message(/socket/) => error
puts "Error #{error} matches /socket/; ignored."
end
Смотрите исходный сайт для решения ruby 1.8.7.
Оказывается, лямбда не приняла мои последние версии рубинов. Кажется, что вариант состоит в том, чтобы использовать то, что работало в 1.8.7, но это медленнее (для создания нового класса во всех сравнениях. Поэтому я не рекомендую его использовать и даже не пробовал:
def exceptions_matching(&block)
Class.new do
def self.===(other)
@block.call(other)
end
end.tap do |c|
c.instance_variable_set(:@block, block)
end
end
begin
raise "FOOBAR: We're all doomed!"
rescue exceptions_matching { |e| e.message =~ /^FOOBAR/ }
puts "rescued!"
end
Если кто-то знает, когда рубин удалил лямбда-поддержку в rescue
, прокомментируйте.
Ответ 4
Вот более общее решение, если вы хотите спасти некоторые типы Errno, а не другие.
Создайте настраиваемый модуль, который будет включен всеми классами ошибок, которые мы хотим спасти
module MyErrnoModule; end
Настройте этот массив по своему вкусу, вплоть до "каждого" вызова.
Errno.constants.map {|name|
Errno.const_get(name)
}.select{|const|
Class === const
}.uniq.each {|klass|
klass.class_eval {
include MyErrnoModule
}
}
Тест:
begin
raise Errno::EPERM
rescue MyErrnoModule
p "rescued #{$!.inspect}"
end
Результат теста:
"rescued #<Errno::EPERM: Operation not permitted>"
Я бы предположил, что это работает немного лучше, чем решение, которое должно проверять имя исключения.