Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

동적 예외 매처로 레벨 업 '구조'

Ruby에서 복구 절을 사용할 때 복구하려는 예외의 종류를 지정할 수 있습니다. 다음과 같은 예외 클래스 목록을 제공하기만 하면 됩니다.

begin
  raise RuntimeError
rescue RuntimeError, NoMethodError
  puts "rescued!"
end

그러나 코드를 작성할 때 예외 클래스가 무엇인지 모른다면 어떻게 될까요? 가장 확실한 대답은 모든 예외를 구제하고 일종의 테스트를 수행한 다음 통과하지 못한 예외를 다시 발생시키는 것입니다. 다음과 같습니다.

begin
  raise "FUBAR! The ship's going down!"
rescue => e
  raise unless e.message =~ /^FUBAR/
  ... do something ...
end

하지만 그건 너무 지루해! 또한 매우 건조한 접근 방식이 아닙니다. 우리의 조건과 일치하는 예외만 구조하도록 구조 절에 어떻게든 지시할 수 있다면 훨씬 더 흥미로울 것입니다. 그리고 이것은 Ruby이기 때문에 할 수 있습니다!

구조가 예외를 일치시키는 방법

구조 블록 내에서 예외가 발생하면 루비 인터프리터는 사용자가 제공한 예외 클래스 목록에 대해 예외의 클래스를 확인합니다. 일치하는 항목이 있으면 예외가 구출됩니다.

일치하는 모양은 다음과 같습니다.

exception_classes_to_rescue.any? do |c|
  c === raised_exception.class
end

Ruby의 다른 모든 연산자와 마찬가지로 === 단순히 방법입니다. 이 경우 c의 메소드입니다. . 그래서 우리만의 ===를 정의한다면 무엇을 할 수 있을까요? 방법?

아래 예에서는 Anything이라는 클래스를 만들고 있습니다. 여기서  Anything === x x 값에 대해 true를 반환합니다. 이 클래스를 구출에 대한 인수로 제공하면 모든 예외가 구출됩니다.

class Anything
  def self.===(exception)
    true
  end
end

begin
  raise EOFError
rescue Anything
  puts "It rescues ANYTHING!"
end

모든 예외를 구제하는 훨씬 더 좋은 방법이 있지만 이 코드는 다음 두 가지를 보여주기 때문에 흥미롭습니다.

  1. Exception에서 상속하지 않는 구조 절 클래스를 제공할 수 있습니다. , ===를 구현하는 한

  2. ===를 제어하는 ​​경우 , 구조되는 예외를 제어할 수 있습니다.

메시지를 기반으로 예외 구조

우리가 지금 알고 있는 것을 알면 예외 메시지가 패턴과 일치하는 경우에만 예외를 구제하는 코드를 작성하는 것은 간단합니다.

class AllFoobarErrors
  def self.===(exception)
    # rescue all exceptions with messages starting with FOOBAR 
    exception.message =~ /^FOOBAR/
  end
end

begin
  raise EOFError, "FOOBAR: there was an eof!"
rescue AllFoobarErrors
  puts "rescued!"
end

사용자 정의 속성을 기반으로 예외 구조

예외 개체에 대한 액세스 권한이 있으므로 매처는 해당 개체에 포함된 모든 데이터를 사용할 수 있습니다.

"심각도"라는 사용자 지정 속성이 있는 예외가 있다고 잠시 상상해 보십시오. 예외의 "심각도가 낮은" 모든 항목을 삼키고 싶지만 "심각도가 높은" 항목은 모두 전달하도록 합니다. 다음과 같이 구현할 수 있습니다.

class Infraction < StandardError
  attr_reader :severity
  def initialize(severity)
    @severity = severity
  end
end

class LowSeverityInfractions
  def self.===(exception)
    exception.is_a?(Infraction) && exception.severity == :low
  end
end

begin
  raise Infraction.new(:low)
rescue LowSeverityInfractions
  puts "rescued!"
end

동적으로 만들기

이 모든 것이 꽤 멋지지만 많은 상용구 코드가 필요합니다. 매처마다 별도의 클래스를 수동으로 정의해야 하는 것은 과도한 것 같습니다. 다행히 약간의 메타프로그래밍을 사용하여 이것을 상당히 건조할 수 있습니다.

아래 예제에서 우리는 매처 클래스를 생성하는 메소드를 정의하고 있습니다. 블록을 통해 일치 논리를 제공하면 일치 생성기가 === 내부의 블록을 사용하는 새 클래스를 생성합니다. 방법.

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

소금 한 알

Ruby의 많은 멋진 트릭과 마찬가지로, 이 모든 것이 미친 것인지 아니면 훌륭한 아이디어인지 잘 판단할 수 없습니다. 어쩌면 둘 중 하나일 수도 있습니다. 이 기술을 첫 번째 선택으로 사용하는 것을 제안하지는 않지만 심각도에 따라 예외를 구하려는 위와 같은 상황에서 이 기술이 얼마나 유용한지 알 수 있습니다. 어쨌든, 이것은 도구 벨트의 또 다른 도구입니다!