packages feed

fold-debounce-conduit 0.1.0.5 → 0.2.0.0

raw patch · 4 files changed

+94/−67 lines, 4 filesdep ~basedep ~conduitdep ~hspecPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, conduit, hspec, resourcet, stm, transformers

API changes (from Hackage documentation)

- Data.Conduit.FoldDebounce: alwaysResetTimer :: Opts i o -> Bool
+ Data.Conduit.FoldDebounce: alwaysResetTimer :: () => Opts i o -> Bool
- Data.Conduit.FoldDebounce: debounce :: (MonadResource m, MonadBaseControl IO m) => Args i o -> Opts i o -> Source m i -> Source m o
+ Data.Conduit.FoldDebounce: debounce :: (MonadResource m, MonadUnliftIO m) => Args i o -> Opts i o -> ConduitT () i m () -> ConduitT () o m ()
- Data.Conduit.FoldDebounce: delay :: Opts i o -> Int
+ Data.Conduit.FoldDebounce: delay :: () => Opts i o -> Int

Files

ChangeLog.md view
@@ -1,5 +1,15 @@ # Revision history for fold-debounce-conduit +## 0.2.0.0  -- 2018-02-03++* Support `conduit-1.3.0` and `resourcet-1.2.0`. This causes major+  update of this package, too.+* Drop support for old GHCs and old dependency packages, to support+  the latest `conduit` and `resourcet`.+* Drop version upper bounds for `hspec`, because it's stable in most+  part.++ ## 0.1.0.5  -- 2017-01-24  * Confirmed test with `hspec-2.4.0`
fold-debounce-conduit.cabal view
@@ -1,5 +1,5 @@ name:                   fold-debounce-conduit-version:                0.1.0.5+version:                0.2.0.0 author:                 Toshio Ito <debug.ito@gmail.com> maintainer:             Toshio Ito <debug.ito@gmail.com> license:                BSD3@@ -20,12 +20,12 @@   exposed-modules:      Data.Conduit.FoldDebounce   -- other-modules:   default-extensions:   FlexibleContexts-  build-depends:        base >=4.6.0 && <5.0,-                        conduit >=1.2.4 && <1.3,+  build-depends:        base >=4.9.0 && <4.11,+                        conduit >=1.3.0 && <1.4,                         fold-debounce >=0.2.0 && <0.3,-                        resourcet >=1.1.5 && <1.2,-                        stm >=2.4.2 && <2.5,-                        transformers >=0.3.0 && <0.6,+                        resourcet >=1.2.0 && <1.3,+                        stm >=2.4.4.1 && <2.5,+                        transformers >=0.5.2 && <0.6,                         transformers-base >=0.4.4 && <0.5  -- executable fold-debounce-conduit@@ -46,7 +46,7 @@   other-modules:        Data.Conduit.FoldDebounceSpec   build-depends:        base, fold-debounce-conduit, stm,                         conduit, transformers, resourcet,-                        hspec >=2.1.7 && <2.5+                        hspec >=2.1.7  source-repository head   type:                 git
src/Data/Conduit/FoldDebounce.hs view
@@ -7,15 +7,16 @@ -- -- > module Main (main) where -- > --- > import Data.Conduit (Source, Sink, yield, ($$))+-- > import Data.Conduit (ConduitT, yield, runConduit, (.|)) -- > import qualified Data.Conduit.List as CL+-- > import Data.Void (Void) -- > import Control.Concurrent (threadDelay) -- > import Control.Monad.IO.Class (liftIO) -- > import Control.Monad.Trans.Resource (ResourceT, runResourceT) -- >  -- > import qualified Data.Conduit.FoldDebounce as F -- > --- > fastSource :: Int -> Source (ResourceT IO) Int+-- > fastSource :: Int -> ConduitT () Int (ResourceT IO) () -- > fastSource max_num = fastStream' 0 where -- >   fastStream' count = do -- >     yield count@@ -25,20 +26,20 @@ -- >         liftIO $ threadDelay 100000 -- >         fastStream' (count + 1) -- > --- > printSink :: Show a => Sink a (ResourceT IO) ()+-- > printSink :: Show a => ConduitT a Void (ResourceT IO) () -- > printSink = CL.mapM_ (liftIO . putStrLn . show) -- >  -- > main :: IO () -- > main = do -- >   putStrLn "-- Before debounce"--- >   runResourceT $ fastSource 10 $$ printSink+-- >   runResourceT $ runConduit $ fastSource 10 .| printSink -- >   let debouncer = F.debounce F.Args { F.cb = undefined, -- anything will do -- >                                       F.fold = (\list num -> list ++ [num]), -- >                                       F.init = [] } -- >                              F.def { F.delay = 500000 } -- >   putStrLn "-- After debounce"--- >   runResourceT $ debouncer (fastSource 10) $$ printSink---+-- >   runResourceT $ runConduit $ debouncer (fastSource 10) .| printSink+--  -- Result: -- -- > -- Before debounce@@ -58,13 +59,13 @@ -- > [5,6,7,8,9] -- > [10] ----- This module regulates (slows down) data stream from conduit--- 'Source' using "Control.FoldDebounce".+-- This module regulates (slows down) data stream from conduit source+-- using "Control.FoldDebounce". ----- The data from the original 'Source' (type @i@) are pulled and--- folded together to create an output data (type @o@). The output--- data then comes out of the debounced 'Source' in a predefined--- interval (specified by 'delay' option).+-- The data from the original source (type @i@) are pulled and folded+-- together to create an output data (type @o@). The output data then+-- comes out of the debounced source in a predefined interval+-- (specified by 'delay' option). -- -- See "Control.FoldDebounce" for detail. module Data.Conduit.FoldDebounce (@@ -83,12 +84,13 @@ import Prelude hiding (init) import Control.Monad (void) import Data.Monoid (Monoid)+import Data.Void (Void)  import Control.FoldDebounce (Args(Args,cb,fold,init),                              Opts, delay, alwaysResetTimer, def) import qualified Control.FoldDebounce as F-import Data.Conduit (Source, Sink, await, yieldOr, ($$))-import Control.Monad.Trans.Resource (MonadResource, MonadBaseControl,+import Data.Conduit (ConduitT, await, (.|), bracketP, yield, runConduit)+import Control.Monad.Trans.Resource (MonadResource, MonadUnliftIO,                                      allocate, register, release, resourceForkIO, runResourceT) import Control.Monad.Trans.Class (lift) import Control.Monad.IO.Class (MonadIO, liftIO)@@ -96,41 +98,44 @@                                atomically,                                TVar, readTVar, newTVarIO, writeTVar) --- | Debounce conduit 'Source' with "Control.FoldDebounce". The data--- stream from the original 'Source' (type @i@) is debounced and--- folded into the data stream of the type @o@.+-- | Debounce conduit source with "Control.FoldDebounce". The data+-- stream from the original source (type @i@) is debounced and folded+-- into the data stream of the type @o@. ----- Note that the original 'Source' is connected to a 'Sink' in another--- thread. You may need some synchronization if the original 'Source'+-- Note that the original source is connected to a sink in another+-- thread. You may need some synchronization if the original source -- has side-effects.-debounce :: (MonadResource m, MonadBaseControl IO m)+debounce :: (MonadResource m, MonadUnliftIO m)             => Args i o -- ^ mandatory argument for FoldDebounce. 'cb'                         -- field is ignored, so you can set anything                         -- to that.             -> Opts i o -- ^ optional argument for FoldDebounce-            -> Source m i -- ^ original 'Source'-            -> Source m o -- ^ debounced 'Source'-debounce args opts src = do-  out_chan <- liftIO $ newTChanIO-  (out_termed_key, out_termed) <- allocate (liftIO $ newTVarIO False) (liftIO . atomically . flip writeTVar True)-  let retSource = do-        mgot <- liftIO $ atomically $ readTChan out_chan-        case mgot of-          OutFinished -> return ()-          OutData got -> yieldOr got (release out_termed_key) >> retSource-  lift $ runResourceT $ do-    void $ register $ atomically $ writeTChan out_chan OutFinished-    (_, trig) <- allocate (F.new args { F.cb = atomically . writeTChan out_chan . OutData }-                                 opts)-                          (F.close)-    void $ resourceForkIO $ lift (src $$ trigSink trig out_termed)-  retSource-+            -> ConduitT () i m () -- ^ original source+            -> ConduitT () o m () -- ^ debounced source+debounce args opts src = bracketP initOutTermed finishOutTermed debounceWith+  where+    initOutTermed = newTVarIO False+    finishOutTermed = atomically . flip writeTVar True+    debounceWith out_termed = do+      out_chan <- liftIO $ newTChanIO+      lift $ runResourceT $ do+        void $ register $ atomically $ writeTChan out_chan OutFinished+        (_, trig) <- allocate (F.new args { F.cb = atomically . writeTChan out_chan . OutData }+                                     opts)+                              (F.close)+        void $ resourceForkIO $ lift $ runConduit (src .| trigSink trig out_termed)+      keepYield out_chan+    keepYield out_chan = do+      mgot <- liftIO $ atomically $ readTChan out_chan+      case mgot of+       OutFinished -> return ()+       OutData got -> yield got >> keepYield out_chan+       -- | Internal data type for output channel. data OutData o = OutData o                | OutFinished -trigSink :: (MonadIO m) => F.Trigger i o -> TVar Bool -> Sink i m ()+trigSink :: (MonadIO m) => F.Trigger i o -> TVar Bool -> ConduitT i Void m () trigSink trig out_termed = trigSink' where   trigSink' = do     mgot <- await@@ -153,7 +158,7 @@ forMonoid = F.forMonoid undefined  -- | 'Args' that discards input events. The data stream from the--- debounced 'Source' indicates the presence of data from the original--- 'Source'.+-- debounced source indicates the presence of data from the original+-- source. forVoid :: Args i () forVoid = F.forVoid undefined
test/Data/Conduit/FoldDebounceSpec.hs view
@@ -8,7 +8,7 @@ import Control.Monad (forM_, void) import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Resource (ResourceT, runResourceT, register)-import Data.Conduit (Source, ConduitM, ($$), yield, addCleanup)+import Data.Conduit (ConduitT, (.|), yield, bracketP, runConduit) import qualified Data.Conduit.FoldDebounce as F import qualified Data.Conduit.List as CL import Data.Maybe (isJust)@@ -18,23 +18,33 @@ main :: IO () main = hspec spec -delayedSource :: [(Int, a)] -> Source (ResourceT IO) a+delayedSource :: [(Int, a)] -> ConduitT () a (ResourceT IO) () delayedSource [] = return () delayedSource ((delay, item):rest) = do   liftIO $ threadDelay delay   yield item   delayedSource rest -periodicSource :: Int -> [a] -> Source (ResourceT IO) a+periodicSource :: Int -> [a] -> ConduitT () a (ResourceT IO) () periodicSource interval items = delayedSource $ zip (repeat interval) items -terminationDetector :: IO (TVar Bool, (ConduitM i o (ResourceT IO) r -> ConduitM i o (ResourceT IO) r))+terminationDetector :: IO (TVar Bool, (ConduitT i o (ResourceT IO) r -> ConduitT i o (ResourceT IO) r)) terminationDetector = do   terminated <- newTVarIO False-  return (terminated,-          addCleanup (\completed -> if not completed then liftIO $ atomically $ writeTVar terminated True else return () ) )+  return (terminated, makeDetector terminated)+  where+    makeDetector terminated orig = bracketP initCompletion setTermination runAction+      where+        initCompletion = newTVarIO False+        runAction v_completed = do+          ret <- orig+          liftIO $ atomically $ writeTVar v_completed True+          return ret+        setTermination v_completed = atomically $ do+          completed <- readTVar v_completed+          writeTVar terminated $ not completed -attachResource :: Source (ResourceT IO) a -> IO (TVar Bool, Source (ResourceT IO) a)+attachResource :: ConduitT () a (ResourceT IO) () -> IO (TVar Bool, ConduitT () a (ResourceT IO) ()) attachResource src = do   released <- newTVarIO False   let src' = do@@ -42,10 +52,10 @@         src   return (released, src') -debSum :: Int -> Source (ResourceT IO) Int -> Source (ResourceT IO) Int+debSum :: Int -> ConduitT () Int (ResourceT IO) () -> ConduitT () Int (ResourceT IO) () debSum delay = F.debounce F.Args { F.init = 0, F.fold = (+), F.cb = undefined } F.def { F.delay = delay } -debMonoid :: Monoid i => Int -> Source (ResourceT IO) i -> Source (ResourceT IO) i+debMonoid :: Monoid i => Int -> ConduitT () i (ResourceT IO) () -> ConduitT () i (ResourceT IO) () debMonoid delay = F.debounce F.forMonoid F.def { F.delay = delay }  shouldSatisfyEventually :: Int -> TVar a -> (a -> Bool) -> IO ()@@ -64,19 +74,20 @@ spec = do   describe "debounce" $ do     it "should fold inputs" $ do-      ret <- runResourceT $ debSum 500000 (periodicSource 10000 [1..10]) $$ CL.consume+      ret <- runResourceT $ runConduit $ debSum 500000 (periodicSource 10000 [1..10]) .| CL.consume       ret `shouldBe` [sum [1..10]]     it "should debounce source" $ do       let s = delayedSource [(1000, "a"), (1000, "b"), (200000, "c"), (1000, "d"), (1000, "e"), (200000, "f")]-      ret <- runResourceT $ debMonoid 100000 s $$ CL.consume+      ret <- runResourceT $ runConduit $ debMonoid 100000 s .| CL.consume       ret `shouldBe` ["ab", "cde", "f"]     it "should terminate debounced Source immediately if the original Source terminates immediately" $ do-      ret <- timeout 50000000 $ runResourceT $ debMonoid 60000000 (CL.sourceList ["A", "B", "C", "D", "E"]) $$ CL.consume+      ret <- timeout 50000000 $ runResourceT $ runConduit+             $ debMonoid 60000000 (CL.sourceList ["A", "B", "C", "D", "E"]) .| CL.consume       ret `shouldBe` Just ["ABCDE"]     it "should terminate the Sink for the original Source if the Sink for the debounced Source terminates" $ do       (terminated, detector) <- terminationDetector       let orig_source = detector $ periodicSource 10000 (repeat "a")-      ret <- runResourceT $ debMonoid 50000 orig_source $$ CL.take 4+      ret <- runResourceT $ runConduit $ debMonoid 50000 orig_source .| CL.take 4       length ret `shouldBe` 4       forM_ ret (`shouldContain` "a")       terminated `shouldSatisfyEventually'` (== True)@@ -85,7 +96,7 @@       -- i.e., we just let it terminate the thread. So we'll see the       -- error message while running the test.       let s = (periodicSource 1000 ["a", "b"]) >> error "Exception in origSource" >> (periodicSource 1000 ["c", "d"])-      ret <- runResourceT $ debMonoid 100000 s $$ CL.consume+      ret <- runResourceT $ runConduit $ debMonoid 100000 s .| CL.consume       ret `shouldBe` ["ab"]     it "should terminated the original Source gracefully if the downstream Sink throws exception" $ do       (terminated, detector) <- terminationDetector@@ -94,7 +105,7 @@             taken <- CL.take 4             _ <- error "Exception in retSink"             return taken-      runResourceT (debMonoid 50000 orig_source $$ ret_sink) `shouldThrow` errorCall "Exception in retSink"+      (runResourceT $ runConduit $ debMonoid 50000 orig_source .| ret_sink) `shouldThrow` errorCall "Exception in retSink"       terminated `shouldSatisfyEventually'` (== True)     it "should connect the original Source in another thread" $ do       this_thread <- myThreadId@@ -103,14 +114,14 @@             liftIO $ atomically . writeTVar orig_thread_t . Just =<< myThreadId             yield 10             return ()-      ret <- runResourceT $ debSum 10000 orig_source $$ CL.consume+      ret <- runResourceT $ runConduit $ debSum 10000 orig_source .| CL.consume       ret `shouldBe` [10]       orig_thread <- atomically (readTVar orig_thread_t)       orig_thread `shouldSatisfy` isJust       orig_thread `shouldSatisfy` (/= Just this_thread)     it "should release the resource in the original Source when the original Source finishes" $ do       (released, orig_source) <- attachResource $ periodicSource 10000 [1,2,3,4]-      ret <- runResourceT $ debSum 500000 orig_source $$ CL.consume+      ret <- runResourceT $ runConduit $ debSum 500000 orig_source .| CL.consume       ret `shouldBe` [10]       released `shouldSatisfyEventually'` (== True)     it "should release the resource in the original Source when the downstream Sink throws exception" $ do@@ -119,14 +130,15 @@             taken <- CL.take 4             _ <- error "Exception in downstream"             return taken-      runResourceT (debMonoid 500000 orig_source $$ sink) `shouldThrow` errorCall "Exception in downstream"+      (runResourceT $ runConduit $ debMonoid 500000 orig_source .| sink)+        `shouldThrow` errorCall "Exception in downstream"       released `shouldSatisfyEventually'` (== True)     it "should release the resource in the original Source when the original Source throws exception" $ do       -- Because the error is not handled, we'll see the error message       -- while running the test. (See above for the case "original       -- Source throwing exception")       (released, orig_source) <- attachResource (periodicSource 10000 ["a", "b"] >> error "Exception in source")-      ret <- runResourceT $ debMonoid 500000 orig_source $$ CL.consume+      ret <- runResourceT $ runConduit $ debMonoid 500000 orig_source .| CL.consume       ret `shouldBe` ["ab"]       released `shouldSatisfyEventually'` (== True)