packages feed

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 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