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

Ruby의 예외에 대한 초보자 가이드

다른 날 나는 초보자를 위해 작성된 Ruby 예외에 대한 소개를 찾고 있었습니다. 기본적인 Ruby 구문을 알고 있지만 예외가 무엇인지 또는 왜 유용한지 잘 모르는 사람들을 위한 것입니다. 나는 그것을 찾을 수 없었고, 그래서 내가 직접 가보기로 결정했습니다. 유용하게 사용하시기 바랍니다. 혼란스러운 점이 있으면 @StarrHorne으로 저에게 트윗해 주십시오. :)

예외란 무엇입니까?

예외는 예기치 않은 이벤트를 처리하는 Ruby의 방식입니다.

코드에 오타를 만들어 SyntaxError와 같은 메시지와 함께 프로그램이 충돌하는 경우 또는 NoMethodError , 그럼 당신은 행동에서 예외를 보았습니다.

Ruby에서 예외를 발생시키면 세상이 멈추고 프로그램이 종료되기 시작합니다. 아무 것도 프로세스를 중지하지 않으면 프로그램이 결국 오류 메시지와 함께 종료됩니다.

여기 예가 있습니다. 아래 코드에서는 0으로 나누려고 합니다. 이것은 불가능하므로 Ruby는 ZeroDivisionError라는 예외를 발생시킵니다. . 프로그램이 종료되고 오류 메시지가 인쇄됩니다.

1 / 0
# Program crashes and outputs: "ZeroDivisionError: divided by 0"

충돌 프로그램은 사용자를 화나게 만드는 경향이 있습니다. 따라서 일반적으로 이 종료 프로세스를 중지하고 오류에 지능적으로 대응하기를 원합니다.

이것을 "구출", "처리" 또는 "캐치" 예외라고 합니다. 그들은 모두 같은 것을 의미합니다. Ruby에서 수행하는 방법은 다음과 같습니다.

begin
  # Any exceptions in here... 
  1/0
rescue
  # ...will cause this code to run
  puts "Got an exception, but I'm responding intelligently!"
  do_something_intelligent()
end

# This program does not crash.
# Outputs: "Got an exception, but I'm responding intelligently!"

예외는 여전히 발생하지만 "구출"되었기 때문에 프로그램이 충돌하지 않습니다. Ruby는 종료하는 대신 구조 블록에서 코드를 실행하여 메시지를 출력합니다.

이것은 좋지만 한 가지 큰 제한이 있습니다. 무엇인지 알려주지 않고 "문제가 발생했습니다."라고 알려줍니다. 잘못됐다.

무엇이 잘못되었는지에 대한 모든 정보는 예외 개체에 포함될 것입니다.

예외 개체

예외 객체는 일반적인 Ruby 객체입니다. 그들은 당신이 방금 구한 예외에 대한 "무슨 일이 일어났는지"에 대한 모든 데이터를 보유합니다.

예외 개체를 가져오려면 약간 다른 구조 구문을 사용합니다.

# Rescues all errors, an puts the exception object in `e`
rescue => e

# Rescues only ZeroDivisionError and puts the exception object in `e`
rescue ZeroDivisionError => e

위의 두 번째 예에서 ZeroDivisionError e에 있는 개체의 클래스입니다. . 우리가 이야기한 예외의 모든 "유형"은 실제로 클래스 이름에 불과합니다.

예외 개체는 유용한 디버그 데이터도 보유합니다. ZeroDivisionError에 대한 예외 개체를 살펴보겠습니다. .

begin
  # Any exceptions in here... 
  1/0
rescue ZeroDivisionError => e
  puts "Exception Class: #{ e.class.name }"
  puts "Exception Message: #{ e.message }"
  puts "Exception Backtrace: #{ e.backtrace }"
end

# Outputs:
# Exception Class: ZeroDivisionError
# Exception Message: divided by 0
# Exception Backtrace: ...backtrace as an array...

대부분의 Ruby 예외와 마찬가지로 클래스 이름과 함께 메시지와 역추적을 포함합니다.

자신의 예외 제기

지금까지 우리는 예외를 구하는 것에 대해서만 이야기했습니다. 자체 예외를 트리거할 수도 있습니다. 이 과정을 "기름"이라고 합니다. raise를 호출하면 됩니다. 방법.

자신의 예외를 발생시킬 때 사용할 예외 유형을 선택하게 됩니다. 또한 오류 메시지를 설정할 수 있습니다.

다음은 예입니다.

begin
  # raises an ArgumentError with the message "you messed up!"
  raise ArgumentError.new("You messed up!")
rescue ArgumentError => e  
  puts e.message
end

# Outputs: You messed up! 

보시다시피 새 오류 개체( ArgumentError ) 사용자 정의 메시지("당신은 엉망입니다!")와 함께 raise에 전달합니다. 방법.

이것은 Ruby입니다. raise 여러 가지 방법으로 호출할 수 있습니다.

# This is my favorite because it's so explicit
raise RuntimeError.new("You messed up!")

# ...produces the same result
raise RuntimeError, "You messed up!"

# ...produces the same result. But you can only raise 
# RuntimeErrors this way
raise "You messed up!"

사용자 정의 예외 만들기

Ruby의 기본 제공 예외는 훌륭하지만 가능한 모든 사용 사례를 다루지는 않습니다.

사용자 시스템을 구축 중이고 사용자가 사이트의 출입 금지 부분에 액세스하려고 할 때 예외를 발생시키려면 어떻게 해야 합니까? Ruby의 표준 예외는 어느 것도 적합하지 않으므로 가장 좋은 방법은 새로운 종류의 예외를 만드는 것입니다.

사용자 정의 예외를 만들려면 StandardError에서 상속하는 새 클래스를 만드세요. .

class PermissionDeniedError < StandardError

end

raise PermissionDeniedError.new()

이것은 그냥 일반적인 Ruby 클래스입니다. 즉, 다른 클래스와 마찬가지로 메서드와 데이터를 추가할 수 있습니다. "action"이라는 속성을 추가해 보겠습니다.

class PermissionDeniedError < StandardError

  attr_reader :action

  def initialize(message, action)
    # Call the parent's constructor to set the message
    super(message)

    # Store the action in an instance variable
    @action = action
  end

end

# Then, when the user tries to delete something they don't
# have permission to delete, you might do something like this:
raise PermissionDeniedError.new("Permission Denied", :delete)

클래스 계층

StandardError를 서브클래싱하여 맞춤 예외를 만들었습니다. , 자체적으로 Exception을 서브클래싱합니다. .

실제로 Ruby에서 예외의 클래스 계층 구조를 보면 결국 Exception으로 다시 연결된다는 것을 알 수 있습니다. . 여기, 내가 당신에게 그것을 증명할 것입니다. 다음은 계층적으로 표시되는 대부분의 Ruby 기본 제공 예외입니다.

Exception
 NoMemoryError
 ScriptError
   LoadError
   NotImplementedError
   SyntaxError
 SignalException
   Interrupt
 StandardError
   ArgumentError
   IOError
     EOFError
   IndexError
   LocalJumpError
   NameError
     NoMethodError
   RangeError
     FloatDomainError
   RegexpError
   RuntimeError
   SecurityError
   SystemCallError
   SystemStackError
   ThreadError
   TypeError
   ZeroDivisionError
 SystemExit

물론 이 모든 것을 외울 필요는 없습니다. 이 계층 구조에 대한 아이디어는 특정한 이유 때문에 매우 중요하기 때문에 여러분에게 그것들을 보여주고 있습니다.

특정 클래스의 오류를 구하면 하위 클래스의 오류도 구합니다.

다시 해보자...

rescue StandardError할 때 , StandardError 클래스로 예외를 구출할 뿐만 아니라 그러나 그 자식들도 마찬가지입니다. 차트를 보면 많은 것을 알 수 있습니다. ArgumentError , IOError

rescue Exception하려는 경우 , 모든 단일 예외를 구제할 수 있으며 이는 매우 나쁜 생각입니다.

모든 예외 구조(나쁜 방법)

소리를 지르려면 스택 오버플로로 이동하여 다음과 같은 코드를 게시하세요.

// Don't do this 
begin
  do_something()
rescue Exception => e
  ...
end

위의 코드는 모든 예외를 구제합니다. 하지마! 그것은 당신의 프로그램을 이상한 방식으로 깨뜨릴 것입니다.

Ruby는 오류 이외의 예외를 사용하기 때문입니다. 또한 이를 사용하여 "신호"라는 운영 체제의 메시지를 처리합니다. 프로그램을 종료하기 위해 "ctrl-c"를 누른 적이 있다면 신호를 사용한 것입니다. 모든 예외를 억제하면 해당 신호도 억제됩니다.

또한 구문 오류와 같이 실제로 프로그램이 충돌을 일으키는 예외 유형도 있습니다. 그것들을 억제하면 오타나 다른 실수를 할 때 절대 알 수 없습니다.

모든 오류 구조(올바른 방법)

돌아가서 클래스 계층 차트를 보면 구조하려는 모든 오류가 StandardError의 자식임을 알 수 있습니다.

즉, "모든 오류"를 구하려면 StandardError를 구해야 합니다. .

begin
  do_something()
rescue StandardError => e
  # Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone. 
end

사실, 예외 클래스를 지정하지 않으면 Ruby는 StandardError를 의미한다고 가정합니다.

begin
  do_something()
rescue => e
  # This is the same as rescuing StandardError
end

특정 오류 구조(최상의 접근)

이제 모든 오류를 구하는 방법을 알았으므로 일반적으로 나쁜 생각, 코드 냄새, 유해한 것으로 간주 등을 알아야 합니다.

이는 일반적으로 어떤 특정 예외를 구해야 하는지 파악하기에는 너무 게으르다는 신호입니다. 그리고 그들은 거의 항상 당신을 괴롭히기 위해 돌아올 것입니다.

그러니 시간을 갖고 제대로 하세요. 특정 예외를 구출하십시오.

begin
  do_something()
rescue Errno::ETIMEDOUT => e
  // This will only rescue Errno::ETIMEDOUT exceptions
end

동일한 구조 블록에서 여러 종류의 예외를 구조할 수도 있으므로 변명의 여지가 없습니다. :)

begin
  do_something()
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED => e
end