dotenv 0.1.0.9 → 0.2.0.0
raw patch · 8 files changed
+141/−62 lines, 8 filesdep +megaparsecdep +textdep −parsecdep −parseerror-eqdep ~basedep ~optparse-applicative
Dependencies added: megaparsec, text
Dependencies removed: parsec, parseerror-eq
Dependency ranges changed: base, optparse-applicative
Files
- CHANGELOG.md +10/−0
- LICENSE +1/−1
- dotenv.cabal +21/−15
- spec/Configuration/Dotenv/ParseSpec.hs +11/−7
- spec/Configuration/DotenvSpec.hs +22/−3
- spec/fixtures/.dotenv +1/−0
- src/Configuration/Dotenv.hs +33/−11
- src/Configuration/Dotenv/Parse.hs +42/−25
+ CHANGELOG.md view
@@ -0,0 +1,10 @@+## Dotenv 0.2.0.0++* Changed public interfaces to use Data.Text.+* Added function `parseFile` to read dotenv file without modifying the+ environment. Thanks to Daisuke Fujimura (Github: fujimura) for making this+ contribution.+ +## Dotenv 0.1.0.0++* First public release.
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2015 Justin Leitgeb+Copyright (c) 2016 Justin Leitgeb Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
dotenv.cabal view
@@ -1,5 +1,5 @@ name: dotenv-version: 0.1.0.9+version: 0.2.0.0 synopsis: Loads environment variables from dotenv files homepage: https://github.com/stackbuilders/dotenv-hs description:@@ -34,23 +34,26 @@ license: MIT license-file: LICENSE author: Justin Leitgeb-maintainer: justin@stackbuilders.com-copyright: 2015 Stack Builders Inc.+maintainer: hackage@stackbuilders.com+copyright: 2015-2016 Stack Builders Inc. category: Configuration build-type: Simple-extra-source-files: spec/fixtures/.dotenv+extra-source-files: spec/fixtures/.dotenv+ , CHANGELOG.md cabal-version: >=1.10 bug-reports: https://github.com/stackbuilders/dotenv-hs/issues executable dotenv main-is: Main.hs- -- other-modules:+ other-modules: Configuration.Dotenv+ Configuration.Dotenv.Parse -- other-extensions:- build-depends: base >=4.5 && <4.9+ build-depends: base >=4.5 && <5.0 , base-compat >= 0.4- , optparse-applicative >=0.12 && < 0.13- , parsec >= 3.1.0 && <= 3.2+ , optparse-applicative >=0.11 && < 0.13+ , megaparsec >= 4.2.0 && < 4.5.0 , process+ , text hs-source-dirs: src default-language: Haskell2010@@ -59,9 +62,10 @@ exposed-modules: Configuration.Dotenv.Parse , Configuration.Dotenv - build-depends: base >=4.5 && <4.9+ build-depends: base >=4.5 && <5.0 , base-compat >= 0.4- , parsec >= 3.1.0 && <= 3.2+ , megaparsec >= 4.2.0 && < 4.5.0+ , text hs-source-dirs: src default-language: Haskell2010@@ -74,12 +78,14 @@ main-is: Spec.hs other-modules: Configuration.DotenvSpec , Configuration.Dotenv.ParseSpec+ , Configuration.Dotenv+ , Configuration.Dotenv.Parse - build-depends: base >=4.5 && <4.9- , base-compat >= 0.4- , parsec >= 3.1.0 && <= 3.2- , parseerror-eq- , hspec+ build-depends: base >=4.5 && <5.0+ , base-compat >= 0.4+ , megaparsec >= 4.2.0 && < 4.5.0+ , text+ , hspec default-language: Haskell2010 ghc-options: -Wall
spec/Configuration/Dotenv/ParseSpec.hs view
@@ -1,15 +1,19 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE OverloadedStrings #-} -module Configuration.Dotenv.ParseSpec (spec) where+module Configuration.Dotenv.ParseSpec (main, spec) where import Configuration.Dotenv.Parse (configParser) -import Test.Hspec (it, describe, shouldBe, Spec)+import Test.Hspec (it, describe, shouldBe, Spec, hspec) -import Text.Parsec (ParseError, parse)+import Text.Megaparsec (ParseError, parse) -import Text.ParseErrorEq ()+import qualified Data.Text as T +main :: IO ()+main = hspec spec+ spec :: Spec spec = describe "parse" $ do it "parses unquoted values" $@@ -44,7 +48,7 @@ it "does not parse variables with hyphens in the name" $ isLeft (parseConfig "FOO-BAR=foobar") `shouldBe` True - it "parses variables with '_' in the name" $+ it "parses variables with \"_\" in the name" $ parseConfig "FOO_BAR=foobar" `shouldBe` Right [("FOO_BAR", "foobar")] it "parses variables with digits after the first character" $@@ -69,7 +73,7 @@ it "ignores inline comments after quoted arguments" $ parseConfig "FOO=\"bar\" # this is foo" `shouldBe` Right [("FOO", "bar")] - it "allows # in quoted values" $+ it "allows \"#\" in quoted values" $ parseConfig "foo=\"bar#baz\" # comment" `shouldBe` Right [("foo", "bar#baz")] @@ -85,5 +89,5 @@ isLeft ( Left _ ) = True isLeft _ = False -parseConfig :: String -> Either ParseError [(String, String)]+parseConfig :: T.Text -> Either ParseError [(T.Text, T.Text)] parseConfig = parse configParser "(unknown)"
spec/Configuration/DotenvSpec.hs view
@@ -1,13 +1,19 @@-module Configuration.DotenvSpec (spec) where+{-# LANGUAGE OverloadedStrings #-} -import Configuration.Dotenv (load, loadFile)+module Configuration.DotenvSpec (main, spec) where +import Configuration.Dotenv (load, loadFile, parseFile)+ import Test.Hspec import System.Environment.Compat (lookupEnv, setEnv, unsetEnv)+import Control.Monad (liftM) -{-# ANN module "HLint: ignore Reduce duplication" #-}+{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-} +main :: IO ()+main = hspec spec+ spec :: Spec spec = do describe "load" $ after_ (unsetEnv "foo") $ do@@ -53,3 +59,16 @@ loadFile True "spec/fixtures/.dotenv" lookupEnv "DOTENV" `shouldReturn` Just "true"++ describe "parseFile" $ after_ (unsetEnv "DOTENV") $ do+ it "returns variables from a file without changing the environment" $ do+ lookupEnv "DOTENV" `shouldReturn` Nothing++ liftM head (parseFile "spec/fixtures/.dotenv") `shouldReturn`+ ("DOTENV", "true")++ lookupEnv "DOTENV" `shouldReturn` Nothing++ it "recognizes unicode characters" $+ liftM (!! 1) (parseFile "spec/fixtures/.dotenv") `shouldReturn`+ ("UNICODE_TEST", "Manabí")
spec/fixtures/.dotenv view
@@ -1,1 +1,2 @@ DOTENV=true+UNICODE_TEST=Manabí
src/Configuration/Dotenv.hs view
@@ -1,16 +1,29 @@-module Configuration.Dotenv (load, loadFile) where+-- |+-- Module : Configuration.Dotenv+-- Copyright : © 2015–2016 Stack Builders Inc.+-- License : MIT+--+-- Maintainer : Stack Builders <hackage@stackbuilders.com>+-- Stability : experimental+-- Portability : portable+--+-- This module contains common functions to load and read dotenv files. +module Configuration.Dotenv (load, loadFile, parseFile) where+ import System.Environment.Compat (lookupEnv, setEnv) import Configuration.Dotenv.Parse (configParser) -import Text.Parsec (parse)+import Text.Megaparsec (parse) +import qualified Data.Text as T+ -- | Loads the given list of options into the environment. Optionally -- override existing variables with values from Dotenv files. load :: Bool -- ^ Override existing settings?- -> [(String, String)] -- ^ List of values to be set in environment+ -> [(T.Text, T.Text)] -- ^ List of values to be set in environment -> IO () load override = mapM_ (applySetting override) @@ -20,22 +33,31 @@ Bool -- ^ Override existing settings? -> FilePath -- ^ A file containing options to load into the environment -> IO ()-loadFile override f = do+loadFile override f = load override =<< parseFile f++-- | Parses the given dotenv file and returns values /without/ adding them to+-- the environment.+parseFile ::+ FilePath -- ^ A file containing options to read+ -> IO [(T.Text, T.Text)] -- ^ Variables contained in the file+parseFile f = do contents <- readFile f - case parse configParser f contents of+ case parse configParser f (T.pack contents) of Left e -> error $ "Failed to read file" ++ show e- Right options -> load override options-+ Right options -> return options -applySetting :: Bool -> (String, String) -> IO ()+applySetting :: Bool -> (T.Text, T.Text) -> IO () applySetting override (key, value) = if override then- setEnv key value+ setEnv key' value' else do- res <- lookupEnv key+ res <- lookupEnv key' case res of- Nothing -> setEnv key value+ Nothing -> setEnv key' value' Just _ -> return ()++ where key' = T.unpack key+ value' = T.unpack value
src/Configuration/Dotenv/Parse.hs view
@@ -1,67 +1,84 @@+-- |+-- Module : Configuration.Dotenv.Parse+-- Copyright : © 2015–2016 Stack Builders Inc.+-- License : MIT+--+-- Maintainer : Stack Builders <hackage@stackbuilders.com>+-- Stability : experimental+-- Portability : portable+--+-- Parser for files in dotenv format. These files generally consist of lines+-- with the form key=value. Comments and blank lines are also supported. More+-- information on the dotenv format can be found in the project README and the+-- test suite.+ module Configuration.Dotenv.Parse (configParser) where -import Text.Parsec ((<|>), (<?>), anyChar, char, many, manyTill, try)-import Text.Parsec.Combinator (eof)-import Text.Parsec.String (Parser)-import Text.ParserCombinators.Parsec.Char- (digit, letter, newline, noneOf, oneOf)+import Text.Megaparsec ((<?>), anyChar, char, eof, manyTill, try)+import Text.Megaparsec.Text (Parser)+import Text.Megaparsec.Char+ (digitChar, letterChar, newline, noneOf, oneOf) -import Control.Applicative ((<*), (*>), (<$>))+import Control.Applicative+import Prelude+ import Data.Maybe (catMaybes)-import Control.Monad (liftM2)+import Control.Monad (liftM, liftM2) +import qualified Data.Text as T+ -- | Returns a parser for a Dotenv configuration file. Accepts key -- and value arguments separated by "=". Comments are allowed on -- lines by themselves and on blank lines.-configParser :: Parser [(String, String)]+configParser :: Parser [(T.Text, T.Text)] configParser = catMaybes <$> many envLine <* eof -envLine :: Parser (Maybe (String, String))+envLine :: Parser (Maybe (T.Text, T.Text)) envLine = (comment <|> blankLine) *> return Nothing <|> Just <$> optionLine -blankLine :: Parser String-blankLine = many verticalSpace <* newline <?> "blank line"+blankLine :: Parser T.Text+blankLine = liftM T.pack (many verticalSpace <* newline) <?> "blank line" -optionLine :: Parser (String, String)+optionLine :: Parser (T.Text, T.Text) optionLine = liftM2 (,) (many verticalSpace *> variableName <* variableValueSeparator) value -- | Variables must start with a letter or underscore, and may contain -- letters, digits or '_' character after the first character.-variableName :: Parser String-variableName = liftM2 (:) (letter <|> char '_')- (many (letter <|> char '_' <|> digit <?>+variableName :: Parser T.Text+variableName = liftM T.pack (liftM2 (:) (letterChar <|> char '_')+ (many (letterChar <|> char '_' <|> digitChar <?> unwords [ "valid non-leading shell variable character (alphanumeric,"- , "digit or underscore)" ]))+ , "digit or underscore)" ]))) <?> unwords [ "shell variable name (letter or underscore followed" , "by alphanumeric characters or underscores)" ] -value :: Parser String+value :: Parser T.Text value = quotedValue <|> unquotedValue <?> "variable value" -quotedValue :: Parser String+quotedValue :: Parser T.Text quotedValue = (quotedWith '\'' <|> quotedWith '\"') <* (comment *> return () <|> many verticalSpace *> endOfLineOrInput) <?> "variable value surrounded with single or double quotes" -unquotedValue :: Parser String+unquotedValue :: Parser T.Text unquotedValue =- manyTill anyChar (comment <|> many verticalSpace <* endOfLineOrInput)+ liftM T.pack (manyTill anyChar (liftM T.unpack comment <|> many verticalSpace <* endOfLineOrInput)) -- | Based on a commented-string parser in: -- http://hub.darcs.net/navilan/XMonadTasks/raw/Data/Config/Lexer.hs-quotedWith :: Char -> Parser String-quotedWith c = char c *> many chr <* (char c <?> "closing quote character")+quotedWith :: Char -> Parser T.Text+quotedWith c = liftM T.pack (char c *> many chr <* (char c <?> "closing quote character")) where chr = esc <|> noneOf [c] esc = escape *> char c <?> "escape character" -comment :: Parser String-comment = try (many verticalSpace *> char '#')- *> manyTill anyChar endOfLineOrInput+comment :: Parser T.Text+comment = liftM T.pack (try (many verticalSpace *> char '#')+ *> manyTill anyChar endOfLineOrInput) <?> "comment" endOfLineOrInput :: Parser ()