Skip to content
Browse files

add error-hook for post-try code evaluation, and manual control of re…

…tries
  • Loading branch information...
1 parent eeb0a3b commit 8f259b7d0f00ed18ffdb8edaafa1620d40a2294e @joegallo committed Jun 1, 2011
Showing with 62 additions and 17 deletions.
  1. +22 −17 src/robert/bruce.clj
  2. +40 −0 test/robert/test/bruce.clj
View
39 src/robert/bruce.clj
@@ -6,7 +6,8 @@
:tries 5
:decay identity
:catch Exception
- :try 1}) ;; try is not overrideable
+ :try 1 ;; try is not overrideable
+ :error-hook (constantly nil)})
(defn double [x]
(* 2 x))
@@ -85,25 +86,29 @@ number as a result"
(defn retry
"internal function that will actually retry with the specified options"
[options f]
- (try
- (binding [*try* (:try options)
- *first-try* (= 1 (:try options))
- *last-try* (= 1 (:tries options))
- *error* (::error options)]
+ (binding [*try* (:try options)
+ *first-try* (= 1 (:try options))
+ *last-try* (= 1 (:tries options))
+ *error* (::error options)]
+ (try
(let [ret (f)]
(if (instance? IObj ret)
(vary-meta ret assoc :tries *try*)
- ret)))
- (catch Throwable t
- (let [options (-> options
- update-tries
- (assoc ::error t))]
- (if (try-again? options t)
- (do
- (when-let [sleep (:sleep options)]
- (Thread/sleep (long sleep)))
- #(retry (update-sleep options) f))
- (throw t))))))
+ ret))
+ (catch Throwable t
+ (let [options (-> options
+ update-tries
+ (assoc ::error t))
+ continue ((:error-hook options) t)]
+ (if (or (true? continue)
+ (and (not (false? continue))
+ (try-again? options t)))
+ (do
+ (when-let [sleep (:sleep options)]
+ (Thread/sleep (long sleep)))
+ #(retry (update-sleep options) f))
+ (throw t)))))))
+
(defn try-try-again
"if at first you don't succeed, intelligent retry trampolining"
View
40 test/robert/test/bruce.clj
@@ -138,3 +138,43 @@
(let [err (Exception.)]
(reset! e err)
(throw err)))))))))
+
+(deftest test-error-hook
+ (testing "is called after each failure"
+ (let [tries (atom [])
+ record-tries (fn [_] (swap! tries conj *try*))]
+ (is (thrown? Exception
+ (try-try-again {:sleep nil
+ :error-hook record-tries}
+ #(throw (Exception.)))))
+ (is (= [1 2 3 4 5] @tries))))
+ (testing "receives the thrown exception"
+ (let [e1 (Exception.)
+ e2 (atom nil)]
+ (is (thrown? Exception
+ (try-try-again {:sleep nil
+ :tries 1
+ :error-hook #(reset! e2 %)}
+ #(throw e1))))
+ (is (= e1 @e2))))
+ (testing "can cancel retries by returning false"
+ (let [tries (atom [])]
+ (is (thrown? Exception
+ (try-try-again {:sleep nil
+ :tries 10
+ :error-hook (constantly false)}
+ #(do
+ (swap! tries conj *try*)
+ (throw (Exception.))))))
+ (is (= [1] @tries))))
+ (testing "can force retries by returning true"
+ (let [tries (atom [])]
+ (is (thrown? Exception
+ (try-try-again {:sleep nil
+ :tries 1
+ :error-hook (fn [e]
+ (not (= (count @tries) 10)))}
+ #(do
+ (swap! tries conj *try*)
+ (throw (Exception.))))))
+ (is (= (range 1 11) @tries)))))

0 comments on commit 8f259b7

Please sign in to comment.
Something went wrong with that request. Please try again.