yaml 0.8.15.3 → 0.8.16
raw patch · 3 files changed
+153/−1 lines, 3 filesdep +semigroupsPVP ok
version bump matches the API change (PVP)
Dependencies added: semigroups
API changes (from Hackage documentation)
+ Data.Yaml.Config: applyCurrentEnv :: Bool -> Value -> IO Value
+ Data.Yaml.Config: applyEnvValue :: Bool -> HashMap Text Text -> Value -> Value
+ Data.Yaml.Config: data EnvUsage
+ Data.Yaml.Config: getCurrentEnv :: IO (HashMap Text Text)
+ Data.Yaml.Config: ignoreEnv :: EnvUsage
+ Data.Yaml.Config: instance Data.Semigroup.Semigroup Data.Yaml.Config.MergedValue
+ Data.Yaml.Config: loadYamlSettings :: FromJSON settings => [FilePath] -> [Value] -> EnvUsage -> IO settings
+ Data.Yaml.Config: requireCustomEnv :: HashMap Text Text -> EnvUsage
+ Data.Yaml.Config: requireEnv :: EnvUsage
+ Data.Yaml.Config: useCustomEnv :: HashMap Text Text -> EnvUsage
+ Data.Yaml.Config: useEnv :: EnvUsage
Files
- ChangeLog.md +7/−0
- Data/Yaml/Config.hs +143/−0
- yaml.cabal +3/−1
ChangeLog.md view
@@ -1,3 +1,10 @@+## 0.8.16++Add env variable parsing. `loadYamlSettings` can read config values from the environment with Yaml that specifies an env var.+The syntax is++`var: _env:ENV_VAR:default`+ ## 0.8.15.3 * Give a warning when compiling with GHCJS
+ Data/Yaml/Config.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+-- | Functionality for using YAML as configuration files+--+-- In particular, merging environment variables with yaml values+--+-- 'loadYamlSettings' is a high-level API for loading YAML and merging environment variables.+-- A yaml value of @_env:ENV_VAR:default@ will lookup the environment variable @ENV_VAR@.+--+-- On a historical note, this code was taken directly from the yesod web framework's configuration module.+module Data.Yaml.Config+ ( applyCurrentEnv+ , getCurrentEnv+ , applyEnvValue+ , loadYamlSettings+ , EnvUsage+ , ignoreEnv+ , useEnv+ , requireEnv+ , useCustomEnv+ , requireCustomEnv+ ) where+++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative ((<$>))+import Data.Monoid+#endif+import Data.Semigroup+import Data.List.NonEmpty (nonEmpty)+import Data.Aeson+import qualified Data.HashMap.Strict as H+import Data.Text (Text, pack)+import System.Environment (getEnvironment)+import Control.Arrow ((***))+import Control.Monad (forM)+import Control.Exception (throwIO)+import Data.Text.Encoding (encodeUtf8)+import qualified Data.Yaml as Y+import Data.Maybe (fromMaybe)+import qualified Data.Text as T++newtype MergedValue = MergedValue { getMergedValue :: Value }++instance Semigroup MergedValue where+ MergedValue x <> MergedValue y = MergedValue $ mergeValues x y++-- | Left biased+mergeValues :: Value -> Value -> Value+mergeValues (Object x) (Object y) = Object $ H.unionWith mergeValues x y+mergeValues x _ = x++applyEnvValue :: Bool -- ^ require an environment variable to be present?+ -> H.HashMap Text Text -> Value -> Value+applyEnvValue requireEnv' env =+ goV+ where+ goV (Object o) = Object $ goV <$> o+ goV (Array a) = Array (goV <$> a)+ goV (String t1) = fromMaybe (String t1) $ do+ t2 <- T.stripPrefix "_env:" t1+ let (name, t3) = T.break (== ':') t2+ mdef = fmap parseValue $ T.stripPrefix ":" t3+ Just $ case H.lookup name env of+ Just val ->+ -- If the default value parses as a String, we treat the+ -- environment variable as a raw value and do not parse it.+ -- This means that things like numeric passwords just work.+ -- However, for originally numerical or boolean values (e.g.,+ -- port numbers), we still perform a normal YAML parse.+ --+ -- For details, see:+ -- https://github.com/yesodweb/yesod/issues/1061+ case mdef of+ Just (String _) -> String val+ _ -> parseValue val+ Nothing ->+ case mdef of+ Just val | not requireEnv' -> val+ _ -> Null+ goV v = v++ parseValue val = fromMaybe (String val) $ Y.decode $ encodeUtf8 val++getCurrentEnv :: IO (H.HashMap Text Text)+getCurrentEnv = fmap (H.fromList . map (pack *** pack)) getEnvironment++applyCurrentEnv :: Bool -- ^ require an environment variable to be present?+ -> Value -> IO Value+applyCurrentEnv requireEnv' orig = flip (applyEnvValue requireEnv') orig <$> getCurrentEnv++data EnvUsage = IgnoreEnv+ | UseEnv+ | RequireEnv+ | UseCustomEnv (H.HashMap Text Text)+ | RequireCustomEnv (H.HashMap Text Text)++ignoreEnv, useEnv, requireEnv :: EnvUsage+ignoreEnv = IgnoreEnv+useEnv = UseEnv+requireEnv = RequireEnv++useCustomEnv, requireCustomEnv :: H.HashMap Text Text -> EnvUsage+useCustomEnv = UseCustomEnv+requireCustomEnv = RequireCustomEnv++-- | Load the settings from the following three sources:+--+-- * Run time config files+--+-- * Run time environment variables+--+-- * The default compile time config file+loadYamlSettings+ :: FromJSON settings+ => [FilePath] -- ^ run time config files to use, earlier files have precedence+ -> [Value] -- ^ any other values to use, usually from compile time config. overridden by files+ -> EnvUsage+ -> IO settings+loadYamlSettings runTimeFiles compileValues envUsage = do+ runValues <- forM runTimeFiles $ \fp -> do+ eres <- Y.decodeFileEither fp+ case eres of+ Left e -> do+ putStrLn $ "loadYamlSettings: Could not parse file as YAML: " ++ fp+ throwIO e+ Right value -> return value++ value' <-+ case nonEmpty $ map MergedValue $ runValues ++ compileValues of+ Nothing -> error "loadYamlSettings: No configuration provided"+ Just ne -> return $ getMergedValue $ sconcat ne+ value <-+ case envUsage of+ IgnoreEnv -> return $ applyEnvValue False mempty value'+ UseEnv -> applyCurrentEnv False value'+ RequireEnv -> applyCurrentEnv True value'+ UseCustomEnv env -> return $ applyEnvValue False env value'+ RequireCustomEnv env -> return $ applyEnvValue True env value'++ case fromJSON value of+ Error s -> error $ "Could not convert to AppSettings: " ++ s+ Success settings -> return settings
yaml.cabal view
@@ -1,5 +1,5 @@ name: yaml-version: 0.8.15.3+version: 0.8.16 license: BSD3 license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>, Anton Ageev <antage@gmail.com>,Kirill Simonov @@ -53,10 +53,12 @@ , filepath , directory , enclosed-exceptions+ , semigroups exposed-modules: Text.Libyaml Data.Yaml Data.Yaml.Aeson Data.Yaml.Builder+ Data.Yaml.Config Data.Yaml.Pretty Data.Yaml.Parser Data.Yaml.Include