Ловля исключений при использовании внешнего драгоценного камня

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

Каков наилучший способ сделать это?
Должен ли я написать свой собственный метод, который просто вызывает внешний гем, а также спасает исключения? Или есть другой способ сделать что-то вроде: "Когда бы ни возникало исключение такого типа где-либо в программе, обрабатывайте это так"?

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

1 ответ

Решение

Основной ответ на этот вопрос, вероятно, заключается в том, чтобы обернуть класс, с которым вы работаете; Ruby обеспечивает большую гибкость для этого, поскольку он имеет method_missing и довольно динамичную среду классов. Вот пример (который может быть или не быть фатальным, но демонстрирует принцип:

# A Foo class that throws a nasty exception somewhere.
class Foo
  class SpecialException < Exception; end

  def bar
    raise SpecialException.new("Barf!")
  end
end

# This will rescue all exceptions and optionally call a callback instead
# of raising.
class RescueAllTheThings
  def initialize(instance, callback=nil)
    @instance = instance
    @callback = callback
  end

  def method_missing(method, *args, &block)
    if @instance.respond_to? method
      begin
        @instance.send(method, *args, &block)
      rescue Exception => e
        @callback.call(e) if @callback
      end
    else
      super
    end
  end
end

# A normal non-wrapped Foo. Exceptions will propagate.
raw_foo = Foo.new

# We'll wrap it here with a rescue so that we don't exit when it raises.
begin
  raw_foo.bar
rescue Foo::SpecialException
  puts "Uncaught exception here! I would've exited without this local rescue!"
end

# Wrap the raw_foo instance with RescueAllTheThings, which will pass through
# all method calls, but will rescue all exceptions and optionally call the
# callback instead. Using lambda{} is a fancy way to create a temporary class
# with a #call method that runs the block of code passed. This code is executed
# in the context *here*, so local variables etc. are usable from wherever the
# lambda is placed.
safe_foo = RescueAllTheThings.new(raw_foo, lambda { |e| puts "Caught an exception: #{e.class}: #{e.message}" })

# No need to rescue anything, it's all handled!
safe_foo.bar

puts "Look ma, I didn't exit!"

То, имеет ли смысл использовать очень общую версию класса-оболочки, такую ​​как класс RescueAllTheThings выше, или что-то более конкретное для того, что вы пытаетесь обернуть, будет во многом зависеть от контекста и конкретных проблем, которые вы ищете решать.

Другие вопросы по тегам