packages feed

cron 0.2.2 → 0.2.3

raw patch · 4 files changed

+154/−8 lines, 4 filesdep +mtldep +old-localedep +transformersdep ~hspecdep ~timePVP ok

version bump matches the API change (PVP)

Dependencies added: mtl, old-locale, transformers

Dependency ranges changed: hspec, time

API changes (from Hackage documentation)

+ System.Cron.Schedule: Job :: CronSchedule -> (IO ()) -> Job
+ System.Cron.Schedule: ParseError :: String -> ScheduleError
+ System.Cron.Schedule: ScheduleT :: StateT Jobs (ExceptT ScheduleError m) a -> ScheduleT m a
+ System.Cron.Schedule: addJob :: MonadSchedule m => IO () -> String -> m ()
+ System.Cron.Schedule: class MonadSchedule m
+ System.Cron.Schedule: data Job
+ System.Cron.Schedule: data ScheduleError
+ System.Cron.Schedule: execSchedule :: Schedule () -> IO [ThreadId]
+ System.Cron.Schedule: instance (Monad m, Functor m) => Applicative (ScheduleT m)
+ System.Cron.Schedule: instance Functor m => Functor (ScheduleT m)
+ System.Cron.Schedule: instance Monad m => Monad (ScheduleT m)
+ System.Cron.Schedule: instance Monad m => MonadError ScheduleError (ScheduleT m)
+ System.Cron.Schedule: instance Monad m => MonadSchedule (ScheduleT m)
+ System.Cron.Schedule: instance Monad m => MonadState Jobs (ScheduleT m)
+ System.Cron.Schedule: instance Show Job
+ System.Cron.Schedule: instance Show ScheduleError
+ System.Cron.Schedule: newtype ScheduleT m a
+ System.Cron.Schedule: runSchedule :: Schedule a -> Either ScheduleError (a, [Job])
+ System.Cron.Schedule: runScheduleT :: ScheduleT m a -> m (Either ScheduleError (a, [Job]))
+ System.Cron.Schedule: type Schedule = ScheduleT Identity
+ System.Cron.Schedule: unSchedule :: ScheduleT m a -> StateT Jobs (ExceptT ScheduleError m) a

Files

README.md view
@@ -31,7 +31,28 @@      make docs +#### Scheduler+Cron offers a scheduling monad which can be found in `System.Cron.Schedule`. This monad transform allows you to declare a set of jobs (of the type `IO ()`) that will be executed at intervals defined by cron strings.++```haskell+main :: IO ()+main = do+    ...+    tids <- execSchedule $ do+        addJob job1 "* * * * *"+        addJob job2 "0 * * * *"+    print tids+    ...++job1 :: IO ()+job1 = putStrLn "Job 1"++job2 :: IO ()+job2 = putStrLn "Job 2"+```+ ## Contributors  * [Simon Hengel](https://github.com/sol) * [Alberto Valverde](https://github.com/albertov)+* [Andrew Rademacher](https://github.com/AndrewRademacher)
changelog view
@@ -1,3 +1,5 @@+# 0.2.3+* Add new in-process task runner feature. Credit to @AndrewRademacher # 0.2.2 * Fix edge case when day of month and day of week are both specified. Credit to @joelwilliamson # 0.2.1
cron.cabal view
@@ -1,5 +1,5 @@ Name:                cron-Version:             0.2.2+Version:             0.2.3 Description:   Cron data structure and Attoparsec parser. The idea is to embed it in larger   systems which want to roll their own scheduled tasks in a format that people@@ -34,27 +34,31 @@  library  Exposed-modules: System.Cron,-                  System.Cron.Parser+                  System.Cron.Parser,+                  System.Cron.Schedule  Ghc-Options:     -Wall  Hs-source-dirs:  src  build-depends:   base            >= 4 && < 5,                   attoparsec      >= 0.10,                   text            >= 0.11 && < 2,-                  time            >= 1.4+                  time            >= 1.4,+                  old-locale      >= 1.0,+                  mtl             >= 2.2  test-suite spec   Type:           exitcode-stdio-1.0   Main-Is:        Spec.hs   Hs-Source-Dirs: test-  Build-Depends:  base >= 4 && < 5,+  Build-Depends:  base                  >= 4 && < 5,                   cron,-                  hspec == 1.3.*,+                  hspec                 >= 1.3,                   QuickCheck,                   derive,                   hspec-expectations,-                  attoparsec      >= 0.10,-                  text            >= 0.11 && < 2,-                  time            >= 1.4+                  attoparsec            >= 0.10,+                  text                  >= 0.11 && < 2,+                  time                  >= 1.4,+                  transformers          >= 0.4  source-repository head   Type:     git
+ src/System/Cron/Schedule.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables        #-}++--------------------------------------------------------------------+-- |+-- Module      : System.Cron.Schedule+-- Description : Monad stack for scheduling jobs to be executed by cron rules.+-- Copyright   : (c) Andrew Rademacher 2014+-- License     : MIT+--+-- Maintainer: Andrew Rademacher <andrewrademacher@gmail.com>+-- Portability: portable+--+-- > main :: IO ()+-- > main = do+-- >        ...+-- >        tids <- execSchedule $ do+-- >            addJob job1 "* * * * *"+-- >            addJob job2 "0 * * * *"+-- >        print tids+-- >        ...+-- >+-- > job1 :: IO ()+-- > job1 = putStrLn "Job 1"+-- >+-- > job2 :: IO ()+-- > job2 = putStrLn "Job 2"+--+--------------------------------------------------------------------++module System.Cron.Schedule+    ( Job (..)+    , ScheduleError (..)+    , Schedule+    , ScheduleT (..)++    , MonadSchedule (..)++    , runSchedule+    , runScheduleT++    , execSchedule+    ) where++import           Control.Applicative+import           Control.Concurrent+import           Control.Monad.Except+import           Control.Monad.Identity+import           Control.Monad.State+import           Data.Attoparsec.Text   (parseOnly)+import           Data.Text              (pack)+import           Data.Time+import           System.Cron+import           System.Cron.Parser+import           System.Locale++{- Scheduleing monad -}++data Job = Job CronSchedule (IO ())+type Jobs = [Job]++instance Show Job where+    show (Job c _) = "(Job " ++ show c ++ ")"++data ScheduleError = ParseError String+                   deriving (Show)++type Schedule = ScheduleT Identity++newtype ScheduleT m a = ScheduleT { unSchedule :: StateT Jobs (ExceptT ScheduleError m) a }+        deriving ( Functor, Applicative, Monad+                 , MonadState Jobs+                 , MonadError ScheduleError+                 )++runSchedule :: Schedule a -> Either ScheduleError (a, [Job])+runSchedule = runIdentity . runScheduleT++runScheduleT :: ScheduleT m a -> m (Either ScheduleError (a, [Job]))+runScheduleT = runExceptT . flip runStateT [] . unSchedule++class MonadSchedule m where+    addJob ::  IO () -> String -> m ()++instance (Monad m) => MonadSchedule (ScheduleT m) where+    addJob a t = do s :: Jobs <- get+                    case parseOnly cronSchedule (pack t) of+                        Left  e  -> throwError $ ParseError e+                        Right t' -> put $ Job t' a : s++{- Monitoring Engine -}++execSchedule :: Schedule () -> IO [ThreadId]+execSchedule s = let res = runSchedule s+                  in case res of+                        Left  e         -> print e >> return []+                        Right (_, jobs) -> mapM forkJob jobs++forkJob :: Job -> IO ThreadId+forkJob (Job s a) = forkIO $ forever $ do+            now <- getCurrentTime+            findNextMinuteDelay >>= threadDelay+            when (scheduleMatches s now) a++findNextMinuteDelay :: IO Int+findNextMinuteDelay = do+        now <- getCurrentTime+        let f    = formatTime defaultTimeLocale fmtFront now+            m    = (read (formatTime defaultTimeLocale fmtMinutes now) :: Int) + 1+            r    = f ++ ":" ++ if length (show m) == 1 then "0" ++ show m else show m+            next = readTime defaultTimeLocale fmtRead r :: UTCTime+        newNow <- getCurrentTime    -- Compensates for the time spent calculating what the next minute is.+        let diff  = diffUTCTime next newNow+            delay = round (realToFrac (diff * 1000000) :: Double) :: Int+        return delay+    where fmtFront   = "%F %H"+          fmtMinutes = "%M"+          fmtRead    = "%F %H:%M"