packages feed

conferer 0.3.0.0 → 0.4.0.0

raw patch · 38 files changed

+605/−605 lines, 38 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Conferer: Provider :: (Key -> IO (Maybe Text)) -> Provider
- Conferer: [getKeyInProvider] :: Provider -> Key -> IO (Maybe Text)
- Conferer: addProvider :: ProviderCreator -> Config -> IO Config
- Conferer: data Provider
- Conferer: type ProviderCreator = Config -> IO Provider
- Conferer.Core: addProvider :: ProviderCreator -> Config -> IO Config
- Conferer.Core: mkStandaloneProvider :: ProviderCreator -> IO Provider
- Conferer.Provider.CLIArgs: mkCLIArgsProvider :: ProviderCreator
- Conferer.Provider.CLIArgs: mkCLIArgsProvider' :: [String] -> ProviderCreator
- Conferer.Provider.CLIArgs: parseArgsIntoKeyValue :: [String] -> [(Key, Text)]
- Conferer.Provider.Env: keyToEnvVar :: Prefix -> Key -> Text
- Conferer.Provider.Env: mkEnvProvider :: Prefix -> ProviderCreator
- Conferer.Provider.Env: mkEnvProvider' :: LookupEnvFunc -> Prefix -> ProviderCreator
- Conferer.Provider.Env: type LookupEnvFunc = String -> IO (Maybe String)
- Conferer.Provider.Env: type Prefix = Text
- Conferer.Provider.Files: fromRight :: a -> Either e a -> a
- Conferer.Provider.Files: getFilePathFromEnv :: Config -> String -> IO FilePath
- Conferer.Provider.Mapping: mkMappingProvider :: Map Key Key -> ProviderCreator -> ProviderCreator
- Conferer.Provider.Mapping: mkMappingProvider' :: (Key -> Maybe Key) -> ProviderCreator -> ProviderCreator
- Conferer.Provider.Namespaced: mkNamespacedProvider :: Key -> ProviderCreator -> ProviderCreator
- Conferer.Provider.Null: mkNullProvider :: ProviderCreator
- Conferer.Provider.PropertiesFile: lineToKeyValue :: Text -> Maybe (Key, Text)
- Conferer.Provider.PropertiesFile: mkPropertiesFileProvider :: ProviderCreator
- Conferer.Provider.PropertiesFile: mkPropertiesFileProvider' :: Text -> ProviderCreator
- Conferer.Provider.Simple: mkMapProvider :: [(Key, Text)] -> ProviderCreator
- Conferer.Provider.Simple: mkMapProvider' :: Map Key Text -> ProviderCreator
- Conferer.Provider.Simple: mkPureMapProvider :: Map Key Text -> Provider
- Conferer.Types: Provider :: (Key -> IO (Maybe Text)) -> Provider
- Conferer.Types: [getKeyInProvider] :: Provider -> Key -> IO (Maybe Text)
- Conferer.Types: [providers] :: Config -> [Provider]
- Conferer.Types: data Provider
- Conferer.Types: type ProviderCreator = Config -> IO Provider
+ Conferer: Source :: (Key -> IO (Maybe Text)) -> Source
+ Conferer: [getKeyInSource] :: Source -> Key -> IO (Maybe Text)
+ Conferer: addSource :: SourceCreator -> Config -> IO Config
+ Conferer: data Source
+ Conferer: type SourceCreator = Config -> IO Source
+ Conferer.Core: addSource :: SourceCreator -> Config -> IO Config
+ Conferer.Core: mkStandaloneSource :: SourceCreator -> IO Source
+ Conferer.Source.CLIArgs: mkCLIArgsSource :: SourceCreator
+ Conferer.Source.CLIArgs: mkCLIArgsSource' :: [String] -> SourceCreator
+ Conferer.Source.CLIArgs: parseArgsIntoKeyValue :: [String] -> [(Key, Text)]
+ Conferer.Source.Env: keyToEnvVar :: Prefix -> Key -> Text
+ Conferer.Source.Env: mkEnvSource :: Prefix -> SourceCreator
+ Conferer.Source.Env: mkEnvSource' :: LookupEnvFunc -> Prefix -> SourceCreator
+ Conferer.Source.Env: type LookupEnvFunc = String -> IO (Maybe String)
+ Conferer.Source.Env: type Prefix = Text
+ Conferer.Source.Files: fromRight :: a -> Either e a -> a
+ Conferer.Source.Files: getFilePathFromEnv :: Config -> String -> IO FilePath
+ Conferer.Source.Mapping: mkMappingSource :: Map Key Key -> SourceCreator -> SourceCreator
+ Conferer.Source.Mapping: mkMappingSource' :: (Key -> Maybe Key) -> SourceCreator -> SourceCreator
+ Conferer.Source.Namespaced: mkNamespacedSource :: Key -> SourceCreator -> SourceCreator
+ Conferer.Source.Null: mkNullSource :: SourceCreator
+ Conferer.Source.PropertiesFile: lineToKeyValue :: Text -> Maybe (Key, Text)
+ Conferer.Source.PropertiesFile: mkPropertiesFileSource :: SourceCreator
+ Conferer.Source.PropertiesFile: mkPropertiesFileSource' :: Text -> SourceCreator
+ Conferer.Source.Simple: mkMapSource :: [(Key, Text)] -> SourceCreator
+ Conferer.Source.Simple: mkMapSource' :: Map Key Text -> SourceCreator
+ Conferer.Source.Simple: mkPureMapSource :: Map Key Text -> Source
+ Conferer.Types: Source :: (Key -> IO (Maybe Text)) -> Source
+ Conferer.Types: [getKeyInSource] :: Source -> Key -> IO (Maybe Text)
+ Conferer.Types: [sources] :: Config -> [Source]
+ Conferer.Types: data Source
+ Conferer.Types: type SourceCreator = Config -> IO Source
- Conferer.Types: Config :: [Provider] -> Map Key Text -> Config
+ Conferer.Types: Config :: [Source] -> Map Key Text -> Config

Files

README.md view
@@ -46,7 +46,7 @@ * Setting an environment variable called `AWESOMEAPP_SERVER_PORT=5555` * In a `config/dev.properties` file, you can have `server.port=5555` -And you may also get that value from different configuration providers like+And you may also get that value from different configuration sources like redis, json file, dhall file or whichever you may need.  ## Example 2: many different values with defaults@@ -110,14 +110,14 @@ * `--hedis.host=redis.example.com`: set hedis' connection host to `redis.example.com`  -## Existing providers+## Existing sources -Providers usually incur in many dependencies so they are split into different+Sources usually incur in many dependencies so they are split into different packages -* *[Json](https://hackage.haskell.org/package/conferer-provider-json)* (depends on `aeson`)-* *[Dhall](https://hackage.haskell.org/package/conferer-provider-dhall)* (depends on `dhall`)-* *[Yaml](https://hackage.haskell.org/package/conferer-provider-yaml)* (depends on `yaml`) +* *[Json](https://hackage.haskell.org/package/conferer-source-json)* (depends on `aeson`)+* *[Dhall](https://hackage.haskell.org/package/conferer-source-dhall)* (depends on `dhall`)+* *[Yaml](https://hackage.haskell.org/package/conferer-source-yaml)* (depends on `yaml`)   ## Existing FromConfig instances @@ -131,17 +131,17 @@  ## Utilities -There are as well some utilities to change providers:+There are as well some utilities to change sources: -* `Conferer.Provider.Namespace`: All keys must be namespaced and the namespace+* `Conferer.Source.Namespace`: All keys must be namespaced and the namespace   is striped for lookup-* `Conferer.Provider.Mapped`: Using a map key to maybe key you can change the+* `Conferer.Source.Mapped`: Using a map key to maybe key you can change the   name of a key or even hiding some key-* `Conferer.Provider.Simple`: Get keys from a hardcoded map key to string+* `Conferer.Source.Simple`: Get keys from a hardcoded map key to string  ## Future maybe things  * Interpolate keys with other keys: `{a: "db", b: "${a}_thing"}`, getting `b`   will give `"db_thing"` (maybe) even in different levels of configuration-* A LOT of providers+* A LOT of sources * A LOT of `FromConfig` implementations
conferer.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.18 --- This file has been generated from package.yaml by hpack version 0.31.2.+-- This file has been generated from package.yaml by hpack version 0.33.0. -- -- see: https://github.com/sol/hpack ----- hash: e9e6edef27397421e3ed20cd7b4269e086ded2fc7fd679dac79be16cf923d188+-- hash: 27fd2a5bad0744bb410d47e23efe3ef9a76a0bb35bd073e397f8f5f6c5c9a796  name:           conferer-version:        0.3.0.0+version:        0.4.0.0 synopsis:       Configuration management library  description:    Library to abstract the parsing of many haskell config values from different config sources@@ -28,14 +28,14 @@       Conferer       Conferer.Core       Conferer.FromConfig.Basics-      Conferer.Provider.CLIArgs-      Conferer.Provider.Env-      Conferer.Provider.Files-      Conferer.Provider.Mapping-      Conferer.Provider.Namespaced-      Conferer.Provider.Null-      Conferer.Provider.PropertiesFile-      Conferer.Provider.Simple+      Conferer.Source.CLIArgs+      Conferer.Source.Env+      Conferer.Source.Files+      Conferer.Source.Mapping+      Conferer.Source.Namespaced+      Conferer.Source.Null+      Conferer.Source.PropertiesFile+      Conferer.Source.Simple       Conferer.Types   other-modules:       Paths_conferer@@ -56,13 +56,13 @@   other-modules:       Conferer.FromConfig.BasicsSpec       Conferer.GenericsSpec-      Conferer.Provider.ArgsSpec-      Conferer.Provider.EnvSpec-      Conferer.Provider.MappingSpec-      Conferer.Provider.NamespacedSpec-      Conferer.Provider.NullSpec-      Conferer.Provider.PropertiesFileSpec-      Conferer.Provider.SimpleSpec+      Conferer.Source.ArgsSpec+      Conferer.Source.EnvSpec+      Conferer.Source.MappingSpec+      Conferer.Source.NamespacedSpec+      Conferer.Source.NullSpec+      Conferer.Source.PropertiesFileSpec+      Conferer.Source.SimpleSpec       ConfererSpec       Paths_conferer   hs-source-dirs:
src/Conferer.hs view
@@ -50,23 +50,23 @@   -- (eg: @warp@) you can get anything that implements 'FromConfig' (like   -- 'Warp.Settings')   ---  -- Internally a 'Config' is made of many 'Provider's which have a simpler+  -- Internally a 'Config' is made of many 'Source's which have a simpler   -- interface:   --   -- @-  -- 'getKeyInProvider' :: Provider -> Key -> IO (Maybe Text)+  -- 'getKeyInSource' :: Source -> Key -> IO (Maybe Text)   -- @   ---  -- Most configuration providers can be abstracted away as Map String String,+  -- Most configuration sources can be abstracted away as Map String String,   -- and they can use whatever logic they want to turn conferer keys (a list of-  -- strings) into a place to look for a string, (for example the env provider+  -- strings) into a place to look for a string, (for example the env source   -- requires a string to namespace the env vars that can affect the   -- configuration)   ---  -- Once you have your 'Provider' you can add it to a 'Config' using the-  -- 'addProvider' function. One final note: each provider has a different-  -- priority, which depends on when is was added to the config ('Provider's-  -- added later have lower priority) so the config searches keys in providers+  -- Once you have your 'Source' you can add it to a 'Config' using the+  -- 'addSource' function. One final note: each source has a different+  -- priority, which depends on when is was added to the config ('Source's+  -- added later have lower priority) so the config searches keys in sources   -- in the same order they were added.    module Conferer.Types@@ -75,14 +75,14 @@   , defaultConfigWithDefaults   , Key(..) -  -- * Providers-  , module Conferer.Provider.Env-  , module Conferer.Provider.Simple-  , module Conferer.Provider.Namespaced-  , module Conferer.Provider.Mapping-  , module Conferer.Provider.CLIArgs-  , module Conferer.Provider.Null-  , module Conferer.Provider.PropertiesFile+  -- * Sources+  , module Conferer.Source.Env+  , module Conferer.Source.Simple+  , module Conferer.Source.Namespaced+  , module Conferer.Source.Mapping+  , module Conferer.Source.CLIArgs+  , module Conferer.Source.Null+  , module Conferer.Source.PropertiesFile   -- * Re-Exports   , (&)   ) where@@ -90,15 +90,15 @@ import           Data.Text (Text) import           Data.Function ((&)) -import           Conferer.Core (emptyConfig, addProvider, getFromConfig, getFromRootConfig, getFromConfigWithDefault, safeGetFromConfig, safeGetFromConfigWithDefault, getKey, unsafeGetKey, (/.), withDefaults)-import           Conferer.Types (Config, Key(..), ProviderCreator, Provider(..), DefaultConfig(..), FromConfig(..))-import           Conferer.Provider.Env-import           Conferer.Provider.Simple-import           Conferer.Provider.Namespaced-import           Conferer.Provider.Mapping-import           Conferer.Provider.CLIArgs-import           Conferer.Provider.Null-import           Conferer.Provider.PropertiesFile+import           Conferer.Core (emptyConfig, addSource, getFromConfig, getFromRootConfig, getFromConfigWithDefault, safeGetFromConfig, safeGetFromConfigWithDefault, getKey, unsafeGetKey, (/.), withDefaults)+import           Conferer.Types (Config, Key(..), SourceCreator, Source(..), DefaultConfig(..), FromConfig(..))+import           Conferer.Source.Env+import           Conferer.Source.Simple+import           Conferer.Source.Namespaced+import           Conferer.Source.Mapping+import           Conferer.Source.CLIArgs+import           Conferer.Source.Null+import           Conferer.Source.PropertiesFile  -- | Default config which reads from command line arguments, env vars and -- property files@@ -111,7 +111,7 @@ defaultConfigWithDefaults :: Text -> [(Key, Text)] -> IO Config defaultConfigWithDefaults appName configMap =   pure (emptyConfig & withDefaults configMap)-  >>= addProvider (mkCLIArgsProvider)-  >>= addProvider (mkEnvProvider appName)-  >>= addProvider (mkPropertiesFileProvider)+  >>= addSource (mkCLIArgsSource)+  >>= addSource (mkEnvSource appName)+  >>= addSource (mkPropertiesFileSource) 
src/Conferer/Core.hs view
@@ -10,22 +10,22 @@ import           Data.Typeable (Typeable, Proxy(..), typeRep) import           Control.Exception (try, throw, throwIO, evaluate) -import           Conferer.Provider.Simple+import           Conferer.Source.Simple import           Conferer.Types  -- | Most Basic function to interact directly with a 'Config'. It always returns --   'Text' in the case of success and implements the logic to traverse---   providers inside the 'Config'.+--   sources inside the 'Config'. getKey :: Key -> Config -> IO (Maybe Text) getKey k config =-  go $ providers config ++ [mkPureMapProvider (defaults config)]+  go $ sources config ++ [mkPureMapSource (defaults config)]   where     go [] = return Nothing-    go (provider:providers) = do-      res <- getKeyInProvider provider k+    go (source:sources) = do+      res <- getKeyInSource source k       case res of         Just t -> return $ Just t-        Nothing -> go providers+        Nothing -> go sources   -- | Fetch a value from a config under some specific key that's parsed using the 'FromConfig'@@ -93,20 +93,20 @@ withDefaults configMap config =   config { defaults = Map.fromList configMap } --- | Instantiate a 'ProviderCreator' using the 'emptyConfig'-mkStandaloneProvider :: ProviderCreator -> IO Provider-mkStandaloneProvider mkProvider =-  mkProvider emptyConfig+-- | Instantiate a 'SourceCreator' using the 'emptyConfig'+mkStandaloneSource :: SourceCreator -> IO Source+mkStandaloneSource mkSource =+  mkSource emptyConfig  --- | Instantiate a 'Provider' using an 'ProviderCretor' and a 'Config' and add+-- | Instantiate a 'Source' using an 'SourceCretor' and a 'Config' and add --   to the config-addProvider :: ProviderCreator -> Config -> IO Config-addProvider mkProvider config = do-  newProvider <- mkProvider config+addSource :: SourceCreator -> Config -> IO Config+addSource mkSource config = do+  newSource <- mkSource config   return $     config-    { providers = providers config ++ [ newProvider ]+    { sources = sources config ++ [ newSource ]     }  -- | Same as 'getKey' but it throws if the 'Key' isn't found
− src/Conferer/Provider/CLIArgs.hs
@@ -1,43 +0,0 @@-module Conferer.Provider.CLIArgs-  (-    -- * Command line arguments Provider-    -- | This provider provides keys from the command line arguments passed into-    -- the program. It only accepts arguments with @--@ and an equals, for-    -- example: @./awesomeapp --warp.port=5000@-    mkCLIArgsProvider-    , mkCLIArgsProvider'-    , parseArgsIntoKeyValue-  )-where--import           Data.Text (Text)-import qualified Data.Text as Text-import           Data.Maybe (mapMaybe)-import           Data.String (fromString)-import           System.Environment (getArgs)--import Conferer.Types-import Conferer.Provider.Simple----- | Create a 'ProviderCreator' for CLIArgs from a argument list-mkCLIArgsProvider' :: [String] -> ProviderCreator-mkCLIArgsProvider' args = \config -> do-  let configMap = parseArgsIntoKeyValue args-  mkMapProvider configMap config---- | Same as 'mkCLIArgsProvider'' but using 'getArgs' to provide the argument--- list-mkCLIArgsProvider :: ProviderCreator-mkCLIArgsProvider = \config -> do-  args <- getArgs-  mkCLIArgsProvider' args config---- | Parse an argument list into a dictionary suitable for a 'Provider'-parseArgsIntoKeyValue :: [String] -> [(Key, Text)]-parseArgsIntoKeyValue =-  fmap (\(k, s) -> (fromString $ Text.unpack k, s)) .-  fmap (\s -> fmap (Text.drop 1) $ Text.breakOn "=" s).-  mapMaybe (Text.stripPrefix "--") .-  takeWhile (/= "--") .-  fmap Text.pack
− src/Conferer/Provider/Env.hs
@@ -1,67 +0,0 @@-module Conferer.Provider.Env-  (--- * Env Provider--- | This 'Provider' provides config values from env vars given a prefix that's--- used to avoid colliding with different system configuration------ For example if you use the 'Prefix' "awesomeapp" and get the 'Key'--- "warp.port" this provider will try to lookup the env var called--- @AWESOMEAPP_WARP_PORT@.---- * Usage--- | To use this provider simply choose a prefix and add it using the--- 'addProvider' function like:------ @--- config & 'addProvider' ('mkEnvProvider' "awesomeapp")--- @-    mkEnvProvider-    , mkEnvProvider'-    , Prefix-    , LookupEnvFunc-    , keyToEnvVar-  ) where---import           Data.Text (Text)-import qualified Data.Text as Text-import qualified System.Environment as System--import           Conferer.Types---- | 'ProviderCreator' for env 'Provider' that uses the real 'System.lookupEnv'--- function-mkEnvProvider :: Prefix -> ProviderCreator-mkEnvProvider prefix =-  mkEnvProvider' System.lookupEnv prefix---- | 'ProviderCreator' for env 'Provider' that allows parameterizing the--- function used to lookup for testing-mkEnvProvider' :: LookupEnvFunc -> Prefix -> ProviderCreator-mkEnvProvider' lookupEnv prefix = \_config ->-  return $-  Provider-  { getKeyInProvider = \k -> do-      let envVarName = Text.unpack $ keyToEnvVar prefix k-      fmap Text.pack <$> lookupEnv envVarName-  }---- | Type alias for the function to lookup env vars-type LookupEnvFunc = String -> IO (Maybe String)----- | A text to namespace env vars-type Prefix = Text---- | Get the env name from a prefix and a key by uppercasing and--- intercalating underscores------ >>> keyToEnVar "awesomeapp" "warp.port"--- "AWESOMEAPP_WARP_PORT"-keyToEnvVar :: Prefix -> Key -> Text-keyToEnvVar prefix (Path keys) =-  Text.toUpper-  $ Text.intercalate "_"-  $ filter (/= mempty)-  $ prefix : keys-
− src/Conferer/Provider/Files.hs
@@ -1,21 +0,0 @@-module Conferer.Provider.Files where--import qualified Data.Text as Text-import           Data.Maybe (fromMaybe)--import           Conferer.Types-import           Conferer.FromConfig.Basics ()--fromRight :: a -> Either e a -> a-fromRight a (Left _) = a-fromRight _ (Right a) = a--getFilePathFromEnv :: Config -> String -> IO FilePath-getFilePathFromEnv config extension = do-  env <- updateFromConfig "env" config "development"-  return $ mconcat-    [ "config/"-    , Text.unpack env-    , "."-    , extension-    ]
− src/Conferer/Provider/Mapping.hs
@@ -1,34 +0,0 @@-module Conferer.Provider.Mapping-  (-    -- * Namespaced higher-order provider-    -- | This provider takes a provider and returns a new provider that-    -- always transforms the key according to either a function for-    -- 'mkMappingProvider'' or a 'Map' for 'mkMappingProvider'-    mkMappingProvider-    , mkMappingProvider'-  )-where--import           Data.Map (Map)-import qualified Data.Map as Map--import           Conferer.Types---- | Create a 'ProviderCreator' using a function to transform the supplied keys--- and another 'ProviderCreator'-mkMappingProvider' :: (Key -> Maybe Key) -> ProviderCreator -> ProviderCreator-mkMappingProvider' mapper providerCreator config = do-  configProvider <- providerCreator config--  return $ Provider-    { getKeyInProvider = \k -> do-        case mapper k of-          Just newKey -> getKeyInProvider configProvider newKey-          Nothing -> return Nothing-    }---- | Create a 'ProviderCreator' using a 'Map' 'Key' 'Key' to transform the supplied keys--- and another 'ProviderCreator'-mkMappingProvider :: Map Key Key -> ProviderCreator -> ProviderCreator-mkMappingProvider configMap configProvider =-  mkMappingProvider' (`Map.lookup` configMap) configProvider
− src/Conferer/Provider/Namespaced.hs
@@ -1,23 +0,0 @@-module Conferer.Provider.Namespaced-  (-    -- * Namespaced higher-order provider-    -- | This provider takes a provider and returns a new provider that-    -- always checks that the 'Key' given always starts with certain 'Key'-    -- and then strips that prefix before consulting its inner Provider-    mkNamespacedProvider-  ) where--import           Data.List (stripPrefix)--import           Conferer.Types---- | Create a 'ProviderCreator' from a prefix and another 'ProviderCreator'-mkNamespacedProvider :: Key -> ProviderCreator -> ProviderCreator-mkNamespacedProvider (Path key) configCreator = \config -> do-  configProvider <- configCreator config-  return $ Provider-    { getKeyInProvider = \(Path k) -> do-        case stripPrefix key k of-          Just newKey -> getKeyInProvider configProvider (Path newKey)-          Nothing -> return Nothing-    }
− src/Conferer/Provider/Null.hs
@@ -1,18 +0,0 @@-module Conferer.Provider.Null-  (-    -- * Does Nothing Provider-    -- | The stub provider that never has a key-    mkNullProvider-  )-where--import           Conferer.Types---- | Create a null 'Provider'-mkNullProvider :: ProviderCreator-mkNullProvider _config =-  return $ Provider-  { getKeyInProvider =-      \_k -> do-        return Nothing-  }
− src/Conferer/Provider/PropertiesFile.hs
@@ -1,54 +0,0 @@-module Conferer.Provider.PropertiesFile where--import           Data.Text (Text)-import           Data.Function ((&))-import           System.Directory (doesFileExist)-import           Data.Maybe (catMaybes)-import qualified Data.Text as Text-import qualified Data.Text.IO as Text--import           Conferer.Types-import           Conferer.Provider.Files-import           Conferer.Provider.Null-import           Conferer.Provider.Simple---- | 'ProviderCreator' for properties file 'Provider' that read from a--- config file in @config/{env}.properties@ and parses it as a properties--- file with @some.key=a value@ lines-mkPropertiesFileProvider :: ProviderCreator-mkPropertiesFileProvider = \config -> do-  filePath <- getFilePathFromEnv config "properties"-  fileExists <- doesFileExist filePath-  if fileExists-    then do-      fileContent <- Text.readFile filePath-      mkPropertiesFileProvider' fileContent config-    else do-      mkNullProvider config----- | 'ProviderCreator' for properties file 'Provider' that only parses a--- given 'Text' as a properties file-mkPropertiesFileProvider' :: Text -> ProviderCreator-mkPropertiesFileProvider' fileContent =-  \config -> do-    let keyValues =-          fileContent-          & Text.lines-          & fmap lineToKeyValue-          & catMaybes-    mkMapProvider keyValues config----- | Transform a line into a key/value pair (or not)-lineToKeyValue :: Text -> Maybe (Key, Text)-lineToKeyValue line =-  Text.breakOn "=" line-  & (\(rawKey, rawValue) ->-      case Text.stripPrefix "=" rawValue of-        Just value ->-          Just (Path $ Text.splitOn "." rawKey, value)-        Nothing ->-          Nothing-    )-
− src/Conferer/Provider/Simple.hs
@@ -1,34 +0,0 @@-module Conferer.Provider.Simple-  (-    -- * Simple Provider-    -- | This provider provides values from a hardcoded Map passed at creation-    -- time that can not be changed afterwards, it's mostly used as a necessary-    -- utility-    mkMapProvider-  , mkMapProvider'-  , mkPureMapProvider-  ) where--import           Data.Map (Map)-import qualified Data.Map as Map-import           Data.Text (Text)--import           Conferer.Types---- | Make a 'ProviderCreator' from a 'Map'-mkMapProvider' :: Map Key Text -> ProviderCreator-mkMapProvider' configMap _config =-  return $ mkPureMapProvider configMap---- | Make a 'Provider' from a 'Map'-mkPureMapProvider :: Map Key Text -> Provider-mkPureMapProvider configMap =-  Provider-    { getKeyInProvider =-      \k -> do-        return $ Map.lookup k configMap-    }---- | Make a 'Provider' from 'List' of 'Key', 'Text' pairs-mkMapProvider :: [(Key, Text)] -> ProviderCreator-mkMapProvider = mkMapProvider' . Map.fromList
+ src/Conferer/Source/CLIArgs.hs view
@@ -0,0 +1,43 @@+module Conferer.Source.CLIArgs+  (+    -- * Command line arguments Source+    -- | This source provides keys from the command line arguments passed into+    -- the program. It only accepts arguments with @--@ and an equals, for+    -- example: @./awesomeapp --warp.port=5000@+    mkCLIArgsSource+    , mkCLIArgsSource'+    , parseArgsIntoKeyValue+  )+where++import           Data.Text (Text)+import qualified Data.Text as Text+import           Data.Maybe (mapMaybe)+import           Data.String (fromString)+import           System.Environment (getArgs)++import Conferer.Types+import Conferer.Source.Simple+++-- | Create a 'SourceCreator' for CLIArgs from a argument list+mkCLIArgsSource' :: [String] -> SourceCreator+mkCLIArgsSource' args = \config -> do+  let configMap = parseArgsIntoKeyValue args+  mkMapSource configMap config++-- | Same as 'mkCLIArgsSource'' but using 'getArgs' to provide the argument+-- list+mkCLIArgsSource :: SourceCreator+mkCLIArgsSource = \config -> do+  args <- getArgs+  mkCLIArgsSource' args config++-- | Parse an argument list into a dictionary suitable for a 'Source'+parseArgsIntoKeyValue :: [String] -> [(Key, Text)]+parseArgsIntoKeyValue =+  fmap (\(k, s) -> (fromString $ Text.unpack k, s)) .+  fmap (\s -> fmap (Text.drop 1) $ Text.breakOn "=" s).+  mapMaybe (Text.stripPrefix "--") .+  takeWhile (/= "--") .+  fmap Text.pack
+ src/Conferer/Source/Env.hs view
@@ -0,0 +1,67 @@+module Conferer.Source.Env+  (+-- * Env Source+-- | This 'Source' provides config values from env vars given a prefix that's+-- used to avoid colliding with different system configuration+--+-- For example if you use the 'Prefix' "awesomeapp" and get the 'Key'+-- "warp.port" this source will try to lookup the env var called+-- @AWESOMEAPP_WARP_PORT@.++-- * Usage+-- | To use this source simply choose a prefix and add it using the+-- 'addSource' function like:+--+-- @+-- config & 'addSource' ('mkEnvSource' "awesomeapp")+-- @+    mkEnvSource+    , mkEnvSource'+    , Prefix+    , LookupEnvFunc+    , keyToEnvVar+  ) where+++import           Data.Text (Text)+import qualified Data.Text as Text+import qualified System.Environment as System++import           Conferer.Types++-- | 'SourceCreator' for env 'Source' that uses the real 'System.lookupEnv'+-- function+mkEnvSource :: Prefix -> SourceCreator+mkEnvSource prefix =+  mkEnvSource' System.lookupEnv prefix++-- | 'SourceCreator' for env 'Source' that allows parameterizing the+-- function used to lookup for testing+mkEnvSource' :: LookupEnvFunc -> Prefix -> SourceCreator+mkEnvSource' lookupEnv prefix = \_config ->+  return $+  Source+  { getKeyInSource = \k -> do+      let envVarName = Text.unpack $ keyToEnvVar prefix k+      fmap Text.pack <$> lookupEnv envVarName+  }++-- | Type alias for the function to lookup env vars+type LookupEnvFunc = String -> IO (Maybe String)+++-- | A text to namespace env vars+type Prefix = Text++-- | Get the env name from a prefix and a key by uppercasing and+-- intercalating underscores+--+-- >>> keyToEnVar "awesomeapp" "warp.port"+-- "AWESOMEAPP_WARP_PORT"+keyToEnvVar :: Prefix -> Key -> Text+keyToEnvVar prefix (Path keys) =+  Text.toUpper+  $ Text.intercalate "_"+  $ filter (/= mempty)+  $ prefix : keys+
+ src/Conferer/Source/Files.hs view
@@ -0,0 +1,21 @@+module Conferer.Source.Files where++import qualified Data.Text as Text+import           Data.Maybe (fromMaybe)++import           Conferer.Types+import           Conferer.FromConfig.Basics ()++fromRight :: a -> Either e a -> a+fromRight a (Left _) = a+fromRight _ (Right a) = a++getFilePathFromEnv :: Config -> String -> IO FilePath+getFilePathFromEnv config extension = do+  env <- updateFromConfig "env" config "development"+  return $ mconcat+    [ "config/"+    , Text.unpack env+    , "."+    , extension+    ]
+ src/Conferer/Source/Mapping.hs view
@@ -0,0 +1,34 @@+module Conferer.Source.Mapping+  (+    -- * Namespaced higher-order source+    -- | This source takes a source and returns a new source that+    -- always transforms the key according to either a function for+    -- 'mkMappingSource'' or a 'Map' for 'mkMappingSource'+    mkMappingSource+    , mkMappingSource'+  )+where++import           Data.Map (Map)+import qualified Data.Map as Map++import           Conferer.Types++-- | Create a 'SourceCreator' using a function to transform the supplied keys+-- and another 'SourceCreator'+mkMappingSource' :: (Key -> Maybe Key) -> SourceCreator -> SourceCreator+mkMappingSource' mapper sourceCreator config = do+  configSource <- sourceCreator config++  return $ Source+    { getKeyInSource = \k -> do+        case mapper k of+          Just newKey -> getKeyInSource configSource newKey+          Nothing -> return Nothing+    }++-- | Create a 'SourceCreator' using a 'Map' 'Key' 'Key' to transform the supplied keys+-- and another 'SourceCreator'+mkMappingSource :: Map Key Key -> SourceCreator -> SourceCreator+mkMappingSource configMap configSource =+  mkMappingSource' (`Map.lookup` configMap) configSource
+ src/Conferer/Source/Namespaced.hs view
@@ -0,0 +1,23 @@+module Conferer.Source.Namespaced+  (+    -- * Namespaced higher-order source+    -- | This source takes a source and returns a new source that+    -- always checks that the 'Key' given always starts with certain 'Key'+    -- and then strips that prefix before consulting its inner Source+    mkNamespacedSource+  ) where++import           Data.List (stripPrefix)++import           Conferer.Types++-- | Create a 'SourceCreator' from a prefix and another 'SourceCreator'+mkNamespacedSource :: Key -> SourceCreator -> SourceCreator+mkNamespacedSource (Path key) configCreator = \config -> do+  configSource <- configCreator config+  return $ Source+    { getKeyInSource = \(Path k) -> do+        case stripPrefix key k of+          Just newKey -> getKeyInSource configSource (Path newKey)+          Nothing -> return Nothing+    }
+ src/Conferer/Source/Null.hs view
@@ -0,0 +1,18 @@+module Conferer.Source.Null+  (+    -- * Does Nothing Source+    -- | The stub source that never has a key+    mkNullSource+  )+where++import           Conferer.Types++-- | Create a null 'Source'+mkNullSource :: SourceCreator+mkNullSource _config =+  return $ Source+  { getKeyInSource =+      \_k -> do+        return Nothing+  }
+ src/Conferer/Source/PropertiesFile.hs view
@@ -0,0 +1,54 @@+module Conferer.Source.PropertiesFile where++import           Data.Text (Text)+import           Data.Function ((&))+import           System.Directory (doesFileExist)+import           Data.Maybe (catMaybes)+import qualified Data.Text as Text+import qualified Data.Text.IO as Text++import           Conferer.Types+import           Conferer.Source.Files+import           Conferer.Source.Null+import           Conferer.Source.Simple++-- | 'SourceCreator' for properties file 'Source' that read from a+-- config file in @config/{env}.properties@ and parses it as a properties+-- file with @some.key=a value@ lines+mkPropertiesFileSource :: SourceCreator+mkPropertiesFileSource = \config -> do+  filePath <- getFilePathFromEnv config "properties"+  fileExists <- doesFileExist filePath+  if fileExists+    then do+      fileContent <- Text.readFile filePath+      mkPropertiesFileSource' fileContent config+    else do+      mkNullSource config+++-- | 'SourceCreator' for properties file 'Source' that only parses a+-- given 'Text' as a properties file+mkPropertiesFileSource' :: Text -> SourceCreator+mkPropertiesFileSource' fileContent =+  \config -> do+    let keyValues =+          fileContent+          & Text.lines+          & fmap lineToKeyValue+          & catMaybes+    mkMapSource keyValues config+++-- | Transform a line into a key/value pair (or not)+lineToKeyValue :: Text -> Maybe (Key, Text)+lineToKeyValue line =+  Text.breakOn "=" line+  & (\(rawKey, rawValue) ->+      case Text.stripPrefix "=" rawValue of+        Just value ->+          Just (Path $ Text.splitOn "." rawKey, value)+        Nothing ->+          Nothing+    )+
+ src/Conferer/Source/Simple.hs view
@@ -0,0 +1,34 @@+module Conferer.Source.Simple+  (+    -- * Simple Source+    -- | This source provides values from a hardcoded Map passed at creation+    -- time that can not be changed afterwards, it's mostly used as a necessary+    -- utility+    mkMapSource+  , mkMapSource'+  , mkPureMapSource+  ) where++import           Data.Map (Map)+import qualified Data.Map as Map+import           Data.Text (Text)++import           Conferer.Types++-- | Make a 'SourceCreator' from a 'Map'+mkMapSource' :: Map Key Text -> SourceCreator+mkMapSource' configMap _config =+  return $ mkPureMapSource configMap++-- | Make a 'Source' from a 'Map'+mkPureMapSource :: Map Key Text -> Source+mkPureMapSource configMap =+  Source+    { getKeyInSource =+      \k -> do+        return $ Map.lookup k configMap+    }++-- | Make a 'Source' from 'List' of 'Key', 'Text' pairs+mkMapSource :: [(Key, Text)] -> SourceCreator+mkMapSource = mkMapSource' . Map.fromList
src/Conferer/Types.hs view
@@ -17,14 +17,14 @@  -- | Core interface for library provided configuration, basically consists of --   getting a 'Key' and informing returning a maybe signaling the value and---   if it's present in that specific provider-data Provider =-  Provider-  { getKeyInProvider :: Key -> IO (Maybe Text)+--   if it's present in that specific source+data Source =+  Source+  { getKeyInSource :: Key -> IO (Maybe Text)   } --- | The way to index 'Provider's, basically list of names that will be adapted---   to whatever the provider needs+-- | The way to index 'Source's, basically list of names that will be adapted+--   to whatever the source needs newtype Key   = Path { unKey :: [Text] }   deriving (Show, Eq, Ord)@@ -37,17 +37,17 @@ keyName = Text.intercalate "." . unKey  -- | Core type that the user of this library interact with, in the future it may---   contain more this besides a list of providers+--   contain more this besides a list of sources data Config =   Config-  { providers :: [Provider]+  { sources :: [Source]   , defaults :: Map Key Text   } --- | The type for creating a provider given a 'Config', some providers require a--- certain configuration to be initialized (for example: the redis provider+-- | The type for creating a source given a 'Config', some sources require a+-- certain configuration to be initialized (for example: the redis source -- needs connection info to connect to the server)-type ProviderCreator = Config -> IO Provider+type SourceCreator = Config -> IO Source  keyNotPresentError :: forall a. (Typeable a) => Key -> Proxy a -> FailedToFetchError keyNotPresentError key =@@ -62,7 +62,7 @@   configDef :: a  -- | Main typeclass for defining the way to get values from config, hiding the--- 'Text' based nature of the 'Provider's.+-- 'Text' based nature of the 'Source's. -- updated using a config, so for example a Warp.Settings can get updated from a config, -- but that doesn't make much sense for something like an 'Int' --
test/Conferer/FromConfig/BasicsSpec.hs view
@@ -13,7 +13,7 @@ import           Control.DeepSeq  configWith :: [(Key, Text)] -> IO Config-configWith keyValues = emptyConfig & addProvider (mkMapProvider keyValues)+configWith keyValues = emptyConfig & addSource (mkMapSource keyValues)  configParserError_ :: ConfigParsingError -> Bool configParserError_ = const True
test/Conferer/GenericsSpec.hs view
@@ -38,14 +38,14 @@       context "when no keys are set" $ do         it "returns the default" $ do           c <- emptyConfig-                & addProvider (mkMapProvider [ ])+                & addSource (mkMapSource [ ])            res <- fetch @Thing "somekey" c           res `shouldBe` Thing { thingA = 0, thingB = 0 }       context "when all keys are set" $ do         it "return the keys set" $ do           c <- emptyConfig-                & addProvider (mkMapProvider+                & addSource (mkMapSource                   [ ("somekey.a", "1")                   , ("somekey.b", "2")                   ])@@ -56,7 +56,7 @@       context "when some keys are set" $ do         it "uses the default and returns the keys set" $ do           c <- emptyConfig-                & addProvider (mkMapProvider+                & addSource (mkMapSource                   [ ("somekey.b", "2")                   ]) @@ -67,7 +67,7 @@       context "when none of the keys are set" $ do         it "returns the default of both records" $ do           c <- emptyConfig-                & addProvider (mkMapProvider+                & addSource (mkMapSource                   [ ])            res <- fetch @Bigger "somekey" c@@ -76,7 +76,7 @@       context "when some keys of the top record are set" $ do         it "returns the default for the inner record" $ do           c <- emptyConfig-                & addProvider (mkMapProvider+                & addSource (mkMapSource                   [ ("somekey.b", "30")                   ]) @@ -86,7 +86,7 @@       context "when some keys of the inner record are set" $ do         it "returns the inner record updated" $ do           c <- emptyConfig-                & addProvider (mkMapProvider+                & addSource (mkMapSource                   [ ("somekey.thing.a", "30")                   ]) @@ -96,7 +96,7 @@       context "when every key is set" $ do         it "returns everything with the right values" $ do           c <- emptyConfig-                & addProvider (mkMapProvider+                & addSource (mkMapSource                   [ ("somekey.thing.a", "10")                   , ("somekey.thing.b", "20")                   , ("somekey.b", "30")
− test/Conferer/Provider/ArgsSpec.hs
@@ -1,38 +0,0 @@-module Conferer.Provider.ArgsSpec where--import           Test.Hspec--import           Conferer--spec :: Spec-spec = do-  describe "with a mapping provider" $ do-    let mkConf args =-          emptyConfig-          & addProvider-          (mkCLIArgsProvider' args)--    it "gets a parameters with it's value if it starts with the right prefix" $ do-      c <- mkConf []-      res <- getKey "some.key" c-      res `shouldBe` Nothing--    it "with a value that begins with the right prefix it uses it" $ do-      c <- mkConf ["--some.key=value"]-      res <- getKey "some.key" c-      res `shouldBe` Just "value"--    it "with a reapeated value it uses the last one" $ do-      c <- mkConf ["--some.key=value", "--some.key=different value"]-      res <- getKey "some.key" c-      res `shouldBe` Just "different value"--    it "ignores values that don't start with the right prefix" $ do-      c <- mkConf ["some.key=value", "-some.key=value", "-Xsome.key=value", "some.key"]-      res <- getKey "some.key" c-      res `shouldBe` Nothing--    it "after encountering a -- it stops parsing parameters" $ do-      c <- mkConf ["--", "--some.key=value"]-      res <- getKey "some.key" c-      res `shouldBe` Nothing
− test/Conferer/Provider/EnvSpec.hs
@@ -1,41 +0,0 @@-module Conferer.Provider.EnvSpec where--import           Test.Hspec-import qualified Data.Map as Map--import Conferer--fakeLookupEnv :: [(String, String)] -> LookupEnvFunc-fakeLookupEnv fakeEnv = \envName ->-  return $ Map.lookup envName $ Map.fromList fakeEnv--spec :: Spec-spec = do-  describe "with an env config" $ do-    let-      mkEnvConfig =-        emptyConfig-        & addProvider-        (mkEnvProvider'-         (fakeLookupEnv-          [ ("TMUX","/tmp/tmux-1000/default,2822,0")-          , ("TMUX_PANE","%1")-          , ("TMUX_PLUGIN_MANAGER_PATH","/home/user/.tmux/plugins/")-          ])-         "TMUX"-        )-    it "getting an existent key returns unwraps top level value (wihtout \-       \children)" $ do-      c <- mkEnvConfig-      res <- getKey "." c-      res `shouldBe` Just "/tmp/tmux-1000/default,2822,0"--    it "getting an existent key for a child gets that value" $ do-      c <- mkEnvConfig-      res <- getKey "pane" c-      res `shouldBe` Just "%1"--    it "keys should always be consistent as to how the words are separated" $ do-      c <- mkEnvConfig-      res <- getKey "pane" c-      res `shouldBe` Just "%1"
− test/Conferer/Provider/MappingSpec.hs
@@ -1,43 +0,0 @@-module Conferer.Provider.MappingSpec where--import           Test.Hspec-import qualified Data.Map as Map-import           Data.Function ((&))--import           Conferer--spec :: Spec-spec = do-  describe "with a mapping provider" $ do-    it "getting an existent key in the original map but that's not mapped in the \-       \wrapper doesn't exist" $ do-      c <- emptyConfig-           & addProvider (mkMappingProvider Map.empty-                          $ mkMapProvider [("some.key", "some value")])-      res <- getKey "some.key" c-      res `shouldBe` Nothing--    it "getting a non existent key isn't there" $ do-      c <- emptyConfig-           & addProvider (mkMappingProvider (Map.fromList [("k", "key")])-                          $ mkMapProvider [("key", "75")])--      res <- getKey "xxxx" c-      res `shouldBe` Nothing--    it "getting an existent key that's mapped but doesn't exist on the \-       \inner provider isn't there" $ do-      c <- emptyConfig-           & addProvider (mkMappingProvider (Map.fromList [("another.key", "some.key")])-                          $ mkMapProvider [])--      res <- getKey "another.key" c-      res `shouldBe` Nothing--    it "getting an existent key that's mapped properly gets it and exists on \-       \the inner provider gets it" $ do-      c <- emptyConfig-           & addProvider (mkMappingProvider (Map.fromList [("another.key", "some.key")])-                          $ mkMapProvider [("some.key", "some value")])-      res <- getKey "another.key" c-      res `shouldBe` Just "some value"
− test/Conferer/Provider/NamespacedSpec.hs
@@ -1,21 +0,0 @@-module Conferer.Provider.NamespacedSpec where--import Test.Hspec--import Conferer--spec :: Spec-spec = do-  describe "namespaced config" $ do-    it "return nothing if the key doesn't match" $ do-      c <- emptyConfig-           & addProvider (mkNamespacedProvider "postgres"-                          $ mkMapProvider [("url", "some url")])-      res <- getKey "url" c-      res `shouldBe` Nothing-    it "returns the wrapped value" $ do-      c <- emptyConfig-           & addProvider (mkNamespacedProvider "postgres"-                          $ mkMapProvider [("url", "some url")])-      res <- getKey "postgres.url" c-      res `shouldBe` Just "some url"
− test/Conferer/Provider/NullSpec.hs
@@ -1,13 +0,0 @@-module Conferer.Provider.NullSpec where--import           Test.Hspec--import           Conferer--spec :: Spec-spec = do-  it "always fails to get a key" $ do-    c <- emptyConfig-         & addProvider mkNullProvider-    res <- getKey "some.key" c-    res `shouldBe` Nothing
− test/Conferer/Provider/PropertiesFileSpec.hs
@@ -1,39 +0,0 @@-module Conferer.Provider.PropertiesFileSpec where--import           Test.Hspec-import           Data.Text (Text)-import qualified Data.Text as Text-import qualified Data.Map as Map--import Conferer-import Conferer.Core-import Conferer.Provider.PropertiesFile--fakeLookupEnv :: [(String, String)] -> LookupEnvFunc-fakeLookupEnv fakeEnv = \envName ->-  return $ Map.lookup envName $ Map.fromList fakeEnv--spec :: Spec-spec = do-  describe "with a properties file config" $ do-    it "getting an existent key returns unwraps top level value (wihtout \-       \children)" $ do-      c <- mkStandaloneProvider $ mkPropertiesFileProvider' "some.key=a value"-      res <- getKeyInProvider c "some.key"-      res `shouldBe` Just "a value"--    it "getting an existent key for a child gets that value" $ do-      c <- mkStandaloneProvider $ mkPropertiesFileProvider' $ Text.unlines-                               [ "some.key=a value"-                               , "some.key=b"-                               ]-      res <- getKeyInProvider c "some.key"-      res `shouldBe` Just "b"--    it "keys should always be consistent as to how the words are separated" $ do-      c <- mkStandaloneProvider $ mkPropertiesFileProvider' $ Text.unlines-                               [ "some.key=a value"-                               , "some.key=b"-                               ]-      res <- getKeyInProvider c "another.key"-      res `shouldBe` Nothing
− test/Conferer/Provider/SimpleSpec.hs
@@ -1,22 +0,0 @@-module Conferer.Provider.SimpleSpec where--import Test.Hspec--import Conferer--spec :: Spec-spec = do-  describe "json provider" $ do-    let creator =-          emptyConfig-          & addProvider (mkMapProvider [ ("postgres.url", "some url")])--    it "getting a non existent key returns an empty config" $ do-      c <- creator-      res <- getKey "some.key" c-      res `shouldBe` Nothing--    it "getting an existent key returns unwraps the original map" $ do-      c <- creator-      res <- getKey "postgres.url" c-      res `shouldBe` Just "some url"
+ test/Conferer/Source/ArgsSpec.hs view
@@ -0,0 +1,38 @@+module Conferer.Source.ArgsSpec where++import           Test.Hspec++import           Conferer++spec :: Spec+spec = do+  describe "with a mapping source" $ do+    let mkConf args =+          emptyConfig+          & addSource+          (mkCLIArgsSource' args)++    it "gets a parameters with it's value if it starts with the right prefix" $ do+      c <- mkConf []+      res <- getKey "some.key" c+      res `shouldBe` Nothing++    it "with a value that begins with the right prefix it uses it" $ do+      c <- mkConf ["--some.key=value"]+      res <- getKey "some.key" c+      res `shouldBe` Just "value"++    it "with a reapeated value it uses the last one" $ do+      c <- mkConf ["--some.key=value", "--some.key=different value"]+      res <- getKey "some.key" c+      res `shouldBe` Just "different value"++    it "ignores values that don't start with the right prefix" $ do+      c <- mkConf ["some.key=value", "-some.key=value", "-Xsome.key=value", "some.key"]+      res <- getKey "some.key" c+      res `shouldBe` Nothing++    it "after encountering a -- it stops parsing parameters" $ do+      c <- mkConf ["--", "--some.key=value"]+      res <- getKey "some.key" c+      res `shouldBe` Nothing
+ test/Conferer/Source/EnvSpec.hs view
@@ -0,0 +1,41 @@+module Conferer.Source.EnvSpec where++import           Test.Hspec+import qualified Data.Map as Map++import Conferer++fakeLookupEnv :: [(String, String)] -> LookupEnvFunc+fakeLookupEnv fakeEnv = \envName ->+  return $ Map.lookup envName $ Map.fromList fakeEnv++spec :: Spec+spec = do+  describe "with an env config" $ do+    let+      mkEnvConfig =+        emptyConfig+        & addSource+        (mkEnvSource'+         (fakeLookupEnv+          [ ("TMUX","/tmp/tmux-1000/default,2822,0")+          , ("TMUX_PANE","%1")+          , ("TMUX_PLUGIN_MANAGER_PATH","/home/user/.tmux/plugins/")+          ])+         "TMUX"+        )+    it "getting an existent key returns unwraps top level value (wihtout \+       \children)" $ do+      c <- mkEnvConfig+      res <- getKey "." c+      res `shouldBe` Just "/tmp/tmux-1000/default,2822,0"++    it "getting an existent key for a child gets that value" $ do+      c <- mkEnvConfig+      res <- getKey "pane" c+      res `shouldBe` Just "%1"++    it "keys should always be consistent as to how the words are separated" $ do+      c <- mkEnvConfig+      res <- getKey "pane" c+      res `shouldBe` Just "%1"
+ test/Conferer/Source/MappingSpec.hs view
@@ -0,0 +1,43 @@+module Conferer.Source.MappingSpec where++import           Test.Hspec+import qualified Data.Map as Map+import           Data.Function ((&))++import           Conferer++spec :: Spec+spec = do+  describe "with a mapping source" $ do+    it "getting an existent key in the original map but that's not mapped in the \+       \wrapper doesn't exist" $ do+      c <- emptyConfig+           & addSource (mkMappingSource Map.empty+                          $ mkMapSource [("some.key", "some value")])+      res <- getKey "some.key" c+      res `shouldBe` Nothing++    it "getting a non existent key isn't there" $ do+      c <- emptyConfig+           & addSource (mkMappingSource (Map.fromList [("k", "key")])+                          $ mkMapSource [("key", "75")])++      res <- getKey "xxxx" c+      res `shouldBe` Nothing++    it "getting an existent key that's mapped but doesn't exist on the \+       \inner source isn't there" $ do+      c <- emptyConfig+           & addSource (mkMappingSource (Map.fromList [("another.key", "some.key")])+                          $ mkMapSource [])++      res <- getKey "another.key" c+      res `shouldBe` Nothing++    it "getting an existent key that's mapped properly gets it and exists on \+       \the inner source gets it" $ do+      c <- emptyConfig+           & addSource (mkMappingSource (Map.fromList [("another.key", "some.key")])+                          $ mkMapSource [("some.key", "some value")])+      res <- getKey "another.key" c+      res `shouldBe` Just "some value"
+ test/Conferer/Source/NamespacedSpec.hs view
@@ -0,0 +1,21 @@+module Conferer.Source.NamespacedSpec where++import Test.Hspec++import Conferer++spec :: Spec+spec = do+  describe "namespaced config" $ do+    it "return nothing if the key doesn't match" $ do+      c <- emptyConfig+           & addSource (mkNamespacedSource "postgres"+                          $ mkMapSource [("url", "some url")])+      res <- getKey "url" c+      res `shouldBe` Nothing+    it "returns the wrapped value" $ do+      c <- emptyConfig+           & addSource (mkNamespacedSource "postgres"+                          $ mkMapSource [("url", "some url")])+      res <- getKey "postgres.url" c+      res `shouldBe` Just "some url"
+ test/Conferer/Source/NullSpec.hs view
@@ -0,0 +1,13 @@+module Conferer.Source.NullSpec where++import           Test.Hspec++import           Conferer++spec :: Spec+spec = do+  it "always fails to get a key" $ do+    c <- emptyConfig+         & addSource mkNullSource+    res <- getKey "some.key" c+    res `shouldBe` Nothing
+ test/Conferer/Source/PropertiesFileSpec.hs view
@@ -0,0 +1,39 @@+module Conferer.Source.PropertiesFileSpec where++import           Test.Hspec+import           Data.Text (Text)+import qualified Data.Text as Text+import qualified Data.Map as Map++import Conferer+import Conferer.Core+import Conferer.Source.PropertiesFile++fakeLookupEnv :: [(String, String)] -> LookupEnvFunc+fakeLookupEnv fakeEnv = \envName ->+  return $ Map.lookup envName $ Map.fromList fakeEnv++spec :: Spec+spec = do+  describe "with a properties file config" $ do+    it "getting an existent key returns unwraps top level value (wihtout \+       \children)" $ do+      c <- mkStandaloneSource $ mkPropertiesFileSource' "some.key=a value"+      res <- getKeyInSource c "some.key"+      res `shouldBe` Just "a value"++    it "getting an existent key for a child gets that value" $ do+      c <- mkStandaloneSource $ mkPropertiesFileSource' $ Text.unlines+                               [ "some.key=a value"+                               , "some.key=b"+                               ]+      res <- getKeyInSource c "some.key"+      res `shouldBe` Just "b"++    it "keys should always be consistent as to how the words are separated" $ do+      c <- mkStandaloneSource $ mkPropertiesFileSource' $ Text.unlines+                               [ "some.key=a value"+                               , "some.key=b"+                               ]+      res <- getKeyInSource c "another.key"+      res `shouldBe` Nothing
+ test/Conferer/Source/SimpleSpec.hs view
@@ -0,0 +1,22 @@+module Conferer.Source.SimpleSpec where++import Test.Hspec++import Conferer++spec :: Spec+spec = do+  describe "json source" $ do+    let creator =+          emptyConfig+          & addSource (mkMapSource [ ("postgres.url", "some url")])++    it "getting a non existent key returns an empty config" $ do+      c <- creator+      res <- getKey "some.key" c+      res `shouldBe` Nothing++    it "getting an existent key returns unwraps the original map" $ do+      c <- creator+      res <- getKey "postgres.url" c+      res `shouldBe` Just "some url"
test/ConfererSpec.hs view
@@ -14,8 +14,8 @@   describe "with a config with a nested value" $ do     let mkConfig =           pure emptyConfig-          >>= addProvider (mkMapProvider [ ("postgres.url", "some url")])-          >>= addProvider (mkMapProvider [ ("postgres.url", "different url") , ("server.port", "4000")])+          >>= addSource (mkMapSource [ ("postgres.url", "some url")])+          >>= addSource (mkMapSource [ ("postgres.url", "different url") , ("server.port", "4000")])      it "getting a non existent key returns an empty config" $ do       c <- mkConfig