Skip to content
This repository has been archived by the owner on Jan 17, 2020. It is now read-only.

Commit

Permalink
Update for PureScript 0.11
Browse files Browse the repository at this point in the history
  • Loading branch information
garyb committed Apr 24, 2017
1 parent 965fd36 commit 14a1116
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 33 deletions.
8 changes: 4 additions & 4 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
15 changes: 9 additions & 6 deletions src/Control/Monad/Aff/Reattempt.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -18,16 +19,18 @@ 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
then throwError (error Error)
else attempt
result ← attempt
-- Process continues after returned aff succeeds if forked timeout isn't cancelled
cancel forkedTimeout (error "")
_ ← cancel forkedTimeout (error "")
pure result
47 changes: 28 additions & 19 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down

0 comments on commit 14a1116

Please sign in to comment.