Подтвердить что ты не робот

Как спасти все исключения в определенном пространстве имен?

Есть ли способ спасти все исключения в определенном пространстве имен?

Например, я хочу спасти все исключения Errno:: * (Errno:: ECONNRESET, Errno:: ETIMEDOUT). Я могу пойти дальше и перечислить их все в моей строке исключения, но мне было интересно, могу ли я что-то сделать.

begin
  # my code
rescue Errno
  # handle exception
end

Вышеупомянутая идея, похоже, не работает, поэтому есть что-то подобное, что может работать?

4b9b3361

Ответ 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>"

Я бы предположил, что это работает немного лучше, чем решение, которое должно проверять имя исключения.