packages feed

config-manager 0.1.0.0 → 0.2.0.0

raw patch · 8 files changed

+165/−11 lines, 8 filesdep +timePVP ok

version bump matches the API change (PVP)

Dependencies added: time

API changes (from Hackage documentation)

+ Data.ConfigManager.Types: class Configured a
+ Data.ConfigManager.Types: convert :: Configured a => Value -> Maybe a
- Data.ConfigManager: lookup :: Read a => Name -> Config -> Either Text a
+ Data.ConfigManager: lookup :: Configured a => Name -> Config -> Either Text a

Files

Data/ConfigManager.hs view
@@ -22,6 +22,9 @@   -- ** Comments   -- $comments +  -- ** Example+  -- $example+   -- * Configuration loading   readConfig @@ -31,7 +34,6 @@   ) where  import Prelude hiding (lookup)-import Text.Read (readMaybe)  import Data.Text (Text) import qualified Data.Text as T@@ -39,6 +41,7 @@  import qualified Data.ConfigManager.Reader as R import Data.ConfigManager.Types+import Data.ConfigManager.Instances ()  -- | Load a 'Config' from a given 'FilePath'. @@ -47,13 +50,13 @@  -- | Lookup for the value associated to a name. -lookup :: Read a => Name -> Config -> Either Text a+lookup :: Configured a => Name -> Config -> Either Text a lookup name config =   case M.lookup name (hashMap config) of     Nothing ->       Left . T.concat $ ["Value not found for Key ", name]     Just value ->-      case readMaybe . T.unpack $ value of+      case convert value of         Nothing -> Left . T.concat $ ["Reading error for key ", name]         Just result -> Right result @@ -80,8 +83,11 @@ -- > a_double = 4.0 -- > thatIsABoolean = True -- > a_double = 5.0+-- > diffTime = 1 day+-- > otherDiffTime = 3 hours ----- If two or more bindings have the same name, only the last one is kept.+-- * If two or more bindings have the same name, only the last one is kept.+-- * Accepted duration values are seconds, minutes, hours, days and weeks.  -- $import --@@ -96,3 +102,35 @@ -- -- > # Comment -- > x = 8 # Another comment++-- $example+--+-- From application.conf:+--+-- > port = 3000+-- > mailFrom = "no-reply@mail.com"+-- > currency = "$"+-- > expiration = 30 minutes+--+-- Read the configuration:+--+-- > import qualified Data.ConfigManager as Conf+-- > import Data.Time.Clock (DiffTime)+-- >+-- > data Conf = Conf+-- >   { port :: Int+-- >   , mailFrom :: String+-- >   , currency :: String+-- >   , expiration :: DiffTime+-- >   } deriving (Read, Eq, Show)+-- >+-- > getConfig :: IO (Either Text Conf)+-- > getConfig =+-- >   (flip fmap) (Conf.readConfig "application.conf") (\configOrError -> do+-- >     conf <- configOrError+-- >     Conf <$>+-- >       Conf.lookup "port" conf <*>+-- >       Conf.lookup "mailFrom" conf <*>+-- >       Conf.lookup "currency" conf <*>+-- >       Conf.lookup "expiration" conf+-- >   )
+ Data/ConfigManager/Instances.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE IncoherentInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Data.ConfigManager.Instances+  () where++import qualified Data.Text as T+import Data.Time.Clock (DiffTime, NominalDiffTime)++import Text.Read (readMaybe)++import Data.ConfigManager.Types.Internal+import Data.ConfigManager.Parser.Duration (parseDuration)++instance Configured DiffTime where+  convert value = parseDuration value++instance Configured NominalDiffTime where+  convert value = realToFrac <$> parseDuration value++instance Read a => Configured a where+  convert = readMaybe . T.unpack
+ Data/ConfigManager/Parser/Duration.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.ConfigManager.Parser.Duration+  ( parseDuration+  ) where++import Control.Applicative ((<|>))++import Data.Text (Text)+import qualified Data.Text as T+import Data.Time.Clock (DiffTime)+import qualified Data.Time.Clock as Time++import Text.Read (readMaybe)++parseDuration :: Text -> Maybe DiffTime+parseDuration input =+  case T.splitOn " " input of+    [count, unit] -> do+      n <- readMaybe . T.unpack $ count+      let matchDuration singularUnit pluralUnit seconds =+            if ((n == 0 || n == 1) && unit == singularUnit) || (n > 1 && unit == pluralUnit)+              then Just . Time.secondsToDiffTime $ n * seconds+              else Nothing+      (matchDuration "second" "seconds" second)+        <|> (matchDuration "minute" "minutes" minute)+        <|> (matchDuration "hour" "hours" hour)+        <|> (matchDuration "day" "days" day)+        <|> (matchDuration "week" "weeks" week)+    _ -> Nothing+  where+    second = 1+    minute = 60 * second+    hour = 60 * minute+    day = 24 * hour+    week = 7 * day
Data/ConfigManager/Reader.hs view
@@ -43,8 +43,8 @@     Binding name value ->       return . Right . Config $ M.insert name value (hashMap config)     Import requirement path -> do-      eitherConfig <- readConfig requirement (fileDir </> path)-      case eitherConfig of+      configOrError <- readConfig requirement (fileDir </> path)+      case configOrError of         Left errorMessage ->           return . Left $ errorMessage         Right importedConfig ->
Data/ConfigManager/Types.hs view
@@ -12,6 +12,8 @@   , Name   , Value   , Requirement(..)+  , Configured+  , convert   ) where  import Data.ConfigManager.Types.Internal
Data/ConfigManager/Types/Internal.hs view
@@ -4,10 +4,11 @@   , Name   , Value   , Requirement(..)+  , Configured+  , convert   ) where  import Data.Text (Text)- import Data.HashMap.Strict  -- | Configuration data.@@ -37,3 +38,9 @@   Required   | Optional   deriving (Eq, Read, Show)++-- | This class represents types that can be converted /from/ a value /to/ a+-- destination type++class Configured a where+  convert :: Value -> Maybe a
config-manager.cabal view
@@ -1,5 +1,5 @@ name:                  config-manager-version:               0.1.0.0+version:               0.2.0.0 synopsis:              Configuration management description:   A configuration management library which supports:@@ -9,6 +9,9 @@   * required or optional imports,   .   * comments.+  .+  For details of the configuration file format, see+  <http://hackage.haskell.org/packages/archive/config-manager/latest/doc/html/Data-ConfigManager.html>. homepage:              https://gitlab.com/guyonvarch/config-manager bug-reports:           https://gitlab.com/guyonvarch/config-manager/issues license:               GPL-3@@ -26,13 +29,16 @@                        Data.ConfigManager.Types   other-modules:       Data.ConfigManager.Reader                        Data.ConfigManager.Parser+                       Data.ConfigManager.Instances                        Data.ConfigManager.Types.Internal+                       Data.ConfigManager.Parser.Duration   ghc-options:         -Wall   build-depends:       base < 5,                        text,                        unordered-containers,                        parsec,-                       filepath+                       filepath,+                       time   default-language:    Haskell2010  source-repository      head@@ -52,5 +58,6 @@                        test-framework-hunit,                        temporary,                        directory,-                       unordered-containers+                       unordered-containers,+                       time   default-language:    Haskell2010
tests/Test.hs view
@@ -18,6 +18,8 @@ import Data.ConfigManager import Data.ConfigManager.Types (Config(..)) import qualified Data.Text as T+import Data.Time.Clock (DiffTime)+import qualified Data.Time.Clock as Time  import Helper (forceGetConfig, getConfig, eitherToMaybe) @@ -32,6 +34,7 @@   , testCase "value" valueAssertion   , testCase "skip" skipAssertion   , testCase "import" importAssertion+  , testCase "duration" durationAssertion   ]  bindingAssertion :: Assertion@@ -41,7 +44,7 @@    oneBinding <- forceGetConfig "x = \"foo\""   assertEqual "one binding present" (Right "foo") (lookup "x" oneBinding)-  assertBool "one binding missing" (isLeft $ (lookup "y" oneBinding :: Either Text Int))+  assertBool "one binding missing" (isLeft (lookup "y" oneBinding :: Either Text Int))   assertEqual "one binding count" 1 (M.size . hashMap $ oneBinding)    multipleBindings <- forceGetConfig $ T.unlines@@ -133,3 +136,40 @@     , "x = 4"     ]   assertEqual "missing optional config" (Right 4) (lookup "x" missingOptionalConfig)++durationAssertion :: Assertion+durationAssertion = do+  config <- forceGetConfig $ T.unlines+    [ "a = 1 second"+    , "b = 5 seconds"+    , "c = 1 minute"+    , "d = 10 minutes"+    , "e = 1 hour"+    , "f = 7 hours"+    , "g = 1 day"+    , "h = 2 days"+    , "i = 1 week"+    , "j = 9 weeks"+    , ""+    , "k = 1 minutes"+    , "l = 20  weeks"+    ]++  let second = 1+  let minute = 60 * second+  let hour = 60 * minute+  let day = 24 * hour+  let week = 7 * day++  assertEqual "a" (Right (Time.secondsToDiffTime $ 1 * second)) (lookup "a" config)+  assertEqual "b" (Right (Time.secondsToDiffTime $ 5 * second)) (lookup "b" config)+  assertEqual "c" (Right (Time.secondsToDiffTime $ 1 * minute)) (lookup "c" config)+  assertEqual "d" (Right (Time.secondsToDiffTime $ 10 * minute)) (lookup "d" config)+  assertEqual "e" (Right (Time.secondsToDiffTime $ 1 * hour)) (lookup "e" config)+  assertEqual "f" (Right (Time.secondsToDiffTime $ 7 * hour)) (lookup "f" config)+  assertEqual "g" (Right (Time.secondsToDiffTime $ 1 * day)) (lookup "g" config)+  assertEqual "h" (Right (Time.secondsToDiffTime $ 2 * day)) (lookup "h" config)+  assertEqual "i" (Right (Time.secondsToDiffTime $ 1 * week)) (lookup "i" config)+  assertEqual "j" (Right (Time.secondsToDiffTime $ 9 * week)) (lookup "j" config)+  assertBool "k" (isLeft (lookup "k" config :: Either Text DiffTime))+  assertBool "l" (isLeft (lookup "l" config :: Either Text DiffTime))