모든 오류가 치명적인 것은 아닙니다. 일부는 다시 시도해야 함을 나타냅니다. 다행히 Ruby는 "다시 시도"하기 쉽게 해주는 몇 가지 흥미로운 메커니즘을 제공합니다. 이 게시물에서는 이러한 메커니즘과 실제 세계에서 어떻게 작동하는지 살펴보겠습니다.
retry
소개
좋아 - 이것은 일종의 명백한 것이지만 그것이 존재한다는 것을 아는 경우에만 가능합니다. 개인적으로 나는 유쾌한 "재시도" 키워드에 대해 배우기 전에 Ruby 경력에 푹 빠져 있었습니다.
재시도는 Ruby의 예외 구조 시스템에 내장되어 있습니다. 아주 간단합니다. 복구 블록에서 "재시도"를 사용하면 복구된 코드 섹션이 다시 실행됩니다. 예를 들어 보겠습니다.
begin
retries ||= 0
puts "try ##{ retries }"
raise "the roof"
rescue
retry if (retries += 1) < 3
end
# ... outputs the following:
# try #0
# try #1
# try #2
여기서 주의할 사항이 몇 가지 있습니다.
-
재시도가 호출되면 모두 시작과 구조 사이의 코드가 다시 실행됩니다. 확실히 그렇습니다 아님 "중단된 부분부터 다시 시작" 또는 이와 유사한 것입니다.
-
재시도를 제한하는 메커니즘을 제공하지 않으면 무한 루프가 발생합니다.
-
시작 및 복구 블록의 코드는 상위 범위의 동일한 재시도 변수에 액세스할 수 있습니다.
retry
문제
재시도는 훌륭하지만 몇 가지 제한 사항이 있습니다. 가장 중요한 것은 전체 시작 블록이 다시 실행된다는 것입니다. 그러나 때로는 그것이 이상적이지 않습니다.
예를 들어 단일 메서드 호출을 사용하여 Twitter, Facebook 및 기타 여러 사이트에 상태 업데이트를 게시할 수 있는 gem을 사용하고 있다고 상상해 보십시오. 다음과 같이 보일 수 있습니다.
SocialMedia.post_to_all("Zomg! I just ate the biggest hamburger")
# ...posts to Twitter API
# ...posts to Facebook API
# ...etc
API 중 하나가 응답하지 않으면 gem은 SocialMedia::TimeoutError를 발생시키고 중단합니다. 이 예외를 포착하고 다시 시도하면 재시도가 처음부터 다시 시작되기 때문에 중복 게시물이 생성됩니다.
begin
SocialMedia.post_to_all("Zomg! I just ate the biggest hamburger")
rescue SocialMedia::TimeoutError
retry
end
# ...posts to Twitter API
# facebook error
# ...posts to Twitter API
# facebook error
# ...posts to Twitter API
# and so on
우리가 gem에게 "페이스북을 건너뛰고 게시할 API 목록을 계속 아래로 내려가세요"라고 말할 수 있다면 좋지 않을까요?
다행스럽게도 Ruby를 사용하면 바로 이 작업을 수행할 수 있습니다.
참고:물론 이 문제에 대한 진정한 해결책은 소셜 미디어 라이브러리를 다시 설계하는 것입니다. 하지만 이것은 제가 보여드릴 기술의 유일한 사용 사례와는 거리가 멉니다.
구조 계속
계속하는 것은 사람들을 놀라게 하는 경향이 있습니다. 그러나 그것은 자주 사용되지 않고 약간 이상해 보이기 때문입니다. 하지만 기본 사항을 이해하면 정말 간단합니다.
연속은 비디오 게임에서처럼 코드의 '저장 지점'과 같습니다. 나가서 다른 작업을 수행한 다음 저장 지점으로 다시 이동할 수 있습니다. 그러면 모든 것이 원래대로 유지됩니다.
...그렇습니다. 완벽한 비유는 아니지만 일종의 효과가 있습니다. 몇 가지 코드를 살펴보겠습니다.
require "continuation"
counter = 0
continuation = callcc { |c| c } # define our savepoint
puts(counter += 1)
continuation.call(continuation) if counter < 5 # jump back to our savepoint
몇 가지 이상한 점을 눈치채셨을 수도 있습니다. 그것들을 살펴봅시다:
-
callcc 메소드를 사용하여 Continuation 객체를 생성합니다. 이에 대한 명확한 OO 구문은 없습니다.
-
처음
continuation
변수가 할당되면callcc
의 반환 값으로 설정됩니다. 의 블록. 그렇기 때문에 블록이 있어야 합니다. -
저장점으로 돌아갈 때마다
continuation
변수는call
을 전달하는 인수에 할당됩니다. 방법. 그래서 이상한 모양의continuation.call(continuation)
을 사용합니다. 구문.
예외에 계속 추가
계속을 사용하여 skip
를 추가할 것입니다. 모든 예외에 대한 방법. 아래 예는 작동 방식을 보여줍니다. 예외를 구할 때마다 skip
를 호출할 수 있어야 합니다. , 예외를 발생시킨 코드가 발생하지 않은 것처럼 작동합니다.
begin
raise "the roof"
puts "The exception was ignored"
rescue => e
e.skip
end
# ...outputs "The exception was ignored"
이렇게 하려면 몇 가지 죄를 범해야 합니다. Exception
그냥 수업입니다. 즉, skip
를 추가하기 위해 돈을 벌 수 있습니다. 방법.
class Exception
attr_accessor :continuation
def skip
continuation.call
end
end
이제 continuation
을 설정해야 합니다. 모든 예외에 대한 속성. raise
재정의할 수 있는 방법일 뿐입니다.
BTW, 아래 코드는 Advi의 뛰어난 슬라이드 데크인 Things You Didn't know about Exceptions에서 거의 그대로 가져온 것입니다. 이보다 더 나은 구현 방법을 생각할 수 없었습니다.
require 'continuation'
module StoreContinuationOnRaise
def raise(*args)
callcc do |continuation|
begin
super
rescue Exception => e
e.continuation = continuation
super(e)
end
end
end
end
class Object
include StoreContinuationOnRaise
end
이제 skip
를 호출할 수 있습니다. 모든 예외에 대한 메서드이며 예외가 발생하지 않은 것과 같습니다.