diff --git a/bower.json b/bower.json index 391c8eb..b67a41d 100644 --- a/bower.json +++ b/bower.json @@ -15,11 +15,11 @@ "package.json" ], "dependencies": { - "purescript-aff": "^2.0.1", - "purescript-refs": "^2.0.0" + "purescript-aff": "^3.0.0", + "purescript-refs": "^3.0.0" }, "devDependencies": { - "purescript-test-unit": "^10.0.1", - "purescript-parallel": "^2.1.0" + "purescript-test-unit": "^11.0.0", + "purescript-parallel": "^3.0.0" } } diff --git a/package.json b/package.json index 93546eb..5196343 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "test": "pulp test" }, "devDependencies": { - "pulp": "^9.0.1", - "purescript": "^0.10.1", - "purescript-psa": "^0.3.9", - "rimraf": "^2.5.4" + "pulp": "^11.0.0", + "purescript": "^0.11.4", + "purescript-psa": "^0.5.1", + "rimraf": "^2.6.1" } } diff --git a/src/Control/Monad/Aff/Reattempt.purs b/src/Control/Monad/Aff/Reattempt.purs index c23c557..d3f5b7b 100644 --- a/src/Control/Monad/Aff/Reattempt.purs +++ b/src/Control/Monad/Aff/Reattempt.purs @@ -2,11 +2,12 @@ module Control.Monad.Aff.Reattempt where import Prelude -import Control.Monad.Aff (Aff(), forkAff, later', cancel) -import Control.Monad.Eff.Ref (newRef, readRef, writeRef, REF()) +import Control.Monad.Aff (Aff, forkAff, delay, cancel) +import Control.Monad.Eff.Ref (newRef, readRef, writeRef, REF) import Control.Monad.Eff.Class (liftEff) import Control.Monad.Error.Class (catchError, throwError) -import Control.Monad.Eff.Exception (Error(), error) +import Control.Monad.Eff.Exception (Error, error) +import Data.Time.Duration (Milliseconds) -- | `reattempt` repeatedly attempts to run the provided `Aff` until either an attempt -- | succeeds or the provided timeout elapses. @@ -18,10 +19,12 @@ import Control.Monad.Eff.Exception (Error(), error) -- | When an attempt to run the provided `Aff` succeeds the `Aff` returned by `reattempt` -- | will succeed. When no attempts succeed the `Aff` returned by `reattempt` will fail -- | with the `Error` raised by the last attempt. -reattempt ∷ ∀ e a. Int → Aff (ref ∷ REF | e) a → Aff (ref ∷ REF | e) a +reattempt ∷ ∀ e a. Milliseconds → Aff (ref ∷ REF | e) a → Aff (ref ∷ REF | e) a reattempt ms aff = do elapsed ← liftEff $ newRef false - forkedTimeout ← forkAff (later' ms $ liftEff $ writeRef elapsed true) + forkedTimeout ← forkAff do + delay ms + liftEff $ writeRef elapsed true let attempt = aff `catchError` \error → do shouldRethrow ← liftEff $ readRef elapsed if shouldRethrow @@ -29,5 +32,5 @@ reattempt ms aff = do else attempt result ← attempt -- Process continues after returned aff succeeds if forked timeout isn't cancelled - cancel forkedTimeout (error "") + _ ← cancel forkedTimeout (error "") pure result diff --git a/test/Main.purs b/test/Main.purs index 9ce327b..edfd84a 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -2,31 +2,33 @@ module Test.Main where import Prelude import Control.Alt ((<|>)) -import Control.Monad.Aff.AVar (makeVar', takeVar, putVar, AVar(), AVAR()) -import Control.Monad.Aff (attempt, later, later', Aff) +import Control.Monad.Aff (attempt, delay, Aff) +import Control.Monad.Aff.AVar (makeVar', takeVar, putVar, AVar, AVAR) +import Control.Monad.Aff.Reattempt (reattempt) import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Ref (REF) import Control.Monad.Eff.Console (CONSOLE) +import Control.Monad.Eff.Ref (REF) import Control.Parallel.Class (parallel) import Control.Plus (empty) - +import Data.Array (head, tail) import Data.Either as Either +import Data.Int (toNumber) import Data.Maybe (maybe, fromMaybe) -import Data.Array (head, tail) import Data.Newtype (unwrap) +import Data.Time.Duration (Milliseconds(..)) import Data.Unfoldable (replicate) import Test.Unit (test, suite) import Test.Unit.Assert (assert) import Test.Unit.Console (TESTOUTPUT) import Test.Unit.Main (runTest) -import Control.Monad.Aff.Reattempt (reattempt) - -failAffsForDurationAndNumberOfAttempts ∷ ∀ e. Int → Int → Array (Aff e Unit) +failAffsForDurationAndNumberOfAttempts ∷ ∀ e. Milliseconds → Int → Array (Aff e Unit) failAffsForDurationAndNumberOfAttempts timeout attemptCount = seq where - seq = replicate attemptCount (later' interval (empty)) - interval = timeout / attemptCount + seq = replicate attemptCount do + delay interval + empty + interval = Milliseconds (unwrap timeout / toNumber attemptCount) affDouble ∷ ∀ e. AVar (Array (Aff (avar ∷ AVAR | e) Unit)) → Aff (avar ∷ AVAR | e) Unit affDouble affsVar = do @@ -37,29 +39,36 @@ affDouble affsVar = do main ∷ ∀ eff. Eff (console ∷ CONSOLE, testOutput ∷ TESTOUTPUT, ref ∷ REF, avar ∷ AVAR | eff) Unit main = runTest do test "When the Aff never succeeds the returned Aff should fail" do - result ← attempt $ reattempt 100 (later empty) + result ← attempt $ reattempt (Milliseconds 100.0) do + delay (Milliseconds 10.0) + empty assert "The returned Aff did not fail" $ Either.isLeft result test "When the timeout will elapse before any attempts to run the Aff are successful the returned Aff should fail" do - seq ← makeVar' $ failAffsForDurationAndNumberOfAttempts 1000 10 - result ← attempt $ reattempt 100 (affDouble seq) + seq ← makeVar' $ failAffsForDurationAndNumberOfAttempts (Milliseconds 1000.0) 10 + result ← attempt $ reattempt (Milliseconds 100.0) (affDouble seq) assert "The returned Aff did not fail" $ Either.isLeft result test "When the Aff always succeeds the returned Aff must be successful" do - result ← attempt $ reattempt 10000 (later' 1000 $ pure unit) + result ← attempt $ reattempt (Milliseconds 10000.0) do + delay (Milliseconds 1000.0) + pure unit assert "The returned Aff failed" $ Either.isRight result suite "When the Aff will succeed during an attempt started before the timeout will elapse" do test "The returned Aff should be successful" do - seq ← makeVar' $ failAffsForDurationAndNumberOfAttempts 100 10 - result ← attempt $ reattempt 100000000 (affDouble seq) + seq ← makeVar' $ failAffsForDurationAndNumberOfAttempts (Milliseconds 100.0) 10 + result ← attempt $ reattempt (Milliseconds 100000000.0) (affDouble seq) assert "The returned Aff failed" $ Either.isRight result test "The returned Aff should not wait for the timeout to elapse in order to succeed" do - seq ← makeVar' $ failAffsForDurationAndNumberOfAttempts 100 10 - let parReattempt = parallel (reattempt 100000000 (affDouble seq) $> true) - let parLater = parallel (later' 1000 $ pure false) + seq ← makeVar' $ failAffsForDurationAndNumberOfAttempts (Milliseconds 100.0) 10 + let + parReattempt = parallel (reattempt (Milliseconds 100000000.0) (affDouble seq) $> true) + parLater = parallel do + delay (Milliseconds 1000.0) + pure false result ← unwrap $ parReattempt <|> parLater assert "The returned Aff failed" result