app-settings 0.2.0.3 → 0.2.0.4
raw patch · 4 files changed
+151/−74 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- Data/AppSettings.hs +11/−0
- Data/Serialization.hs +37/−8
- app-settings.cabal +12/−1
- tests/Tests.hs +91/−65
Data/AppSettings.hs view
@@ -231,6 +231,17 @@ -- -- > testList= --+-- There is also another technique that you can use if you have too long+-- lines: you can put line breaks in the setting values if you start the+-- following lines with spaces, like so:+--+-- > testList=["list1",+-- > "list2", "list3"]+--+-- In that case don't use the ListSetting option. Also keep the leading+-- spaces consistent in the continuing lines. Note that the library will+-- automatically wrap settings longer than 80 characters when saving.+-- -- Once we declared the settings, we can read the configuration -- from disk (and your settings module should export your wrapper -- around the function offered by this library):
Data/Serialization.hs view
@@ -16,6 +16,7 @@ import Control.Exception (throwIO, Exception) import Data.Typeable (Typeable) import System.Directory (doesFileExist, copyFile)+import Control.Applicative ((<$>)) data SettingInfo = SettingInfo { value :: String, userSet :: Bool } deriving (Show, Eq) @@ -50,20 +51,42 @@ (a, SettingInfo {value=b, userSet=True})) configEntries comment :: T.GenParser st ConfigElement-comment = do- char '#'- many $ noneOf "\r\n"- many1 $ oneOf "\r\n"- return Comment+comment = char '#' >> finishLine >> return Comment configEntry :: T.GenParser st ConfigElement configEntry = do key <- many1 $ noneOf " \t=\r\n" char '='- val <- many $ noneOf "\r\n"+ val <- finishLine+ -- so now we finished the parsing of the classical+ -- key=value, however we support a little bit more,+ -- you can do something like that:+ -- key=[1,2,+ -- 3,4,+ -- 5, 6]+ -- In that case the value will be+ -- "[1,2,3,4,5, 6]"+ blanks <- many $ oneOf " \t"+ fullVal <- if null blanks+ then return val+ else do+ firstExtraLine <- finishLine+ rest <- concat <$> many (lineSkipSpaces $ length blanks)+ return $ val ++ firstExtraLine ++ rest+ return $ ConfigEntry key fullVal++finishLine :: T.GenParser st String+finishLine = do+ result <- many $ noneOf "\r\n" many1 $ oneOf "\r\n"- return $ ConfigEntry key val+ return result +lineSkipSpaces :: Int -> T.GenParser st String+lineSkipSpaces spaceCount = do+ count spaceCount (oneOf "\t ")+ result <- finishLine+ return result+ emptyLine :: T.GenParser st ConfigElement emptyLine = do many $ oneOf " \t"@@ -82,4 +105,10 @@ writeConfigEntry :: Handle -> String -> SettingInfo -> IO () writeConfigEntry handle key (SettingInfo sValue sUserSet) = do unless sUserSet $ hPutStr handle "# "- hPutStrLn handle $ key ++ "=" ++ sValue+ hPutStrLn handle $ key ++ "=" ++ (wrap sValue)++wrap :: String -> String+wrap str = if null rest+ then str+ else first ++ "\n " ++ wrap rest+ where (first, rest) = splitAt 80 str
app-settings.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: app-settings-version: 0.2.0.3+version: 0.2.0.4 synopsis: A library to manage application settings (INI file-like) description: A library to deal with application settings.@@ -66,6 +66,17 @@ like so: . > testList=+ .+ There is also another technique that you can use if you have too long+ lines: you can put line breaks in the setting values if you start the+ following lines with spaces, like so:+ .+ > testList=["list1",+ > "list2", "list3"]+ .+ In that case don't use the ListSetting option. Also keep the leading+ spaces consistent in the continuing lines. Note that the library will+ automatically wrap setting values longer than 80 characters when saving. . Once we declared the settings, we can read the configuration from disk (and your settings module should export your wrapper
tests/Tests.hs view
@@ -4,7 +4,7 @@ import Data.AppSettingsInternal import Test.Hspec-import Test.HUnit (assertBool)+import Test.HUnit (assertBool, assertEqual) import System.Directory (removeFile, copyFile, doesFileExist) import Control.Exception (try, SomeException)@@ -43,71 +43,73 @@ testInvalidFile testInvalidValue testNonExistingFile+ testReadLongSetting describe "save config" $ do testSaveUserSetAndDefaults createBakBeforeSaving+ wrapLongSettings describe "set setting" testSetListSetting testEmptyFileDefaults :: Spec testEmptyFileDefaults = it "parses correctly an empty file with defaults" $ do- readResult <- try $ readSettings (Path "tests/empty.config")- case readResult of- Right (_, GetSetting getSetting) -> do- getSetting textSizeFromWidth `shouldBe` 0.04- getSetting textSizeFromHeight `shouldBe` 12.4- getSetting textFill `shouldBe` (1,1,0,1)- getSetting testInlineList `shouldBe` ["inline1", "inline2", "inline3"]- getSetting testList `shouldBe` ["list1", "list2", "list3"]- Left (x :: SomeException) -> assertBool (show x) False+ readResult <- try $ readSettings (Path "tests/empty.config")+ case readResult of+ Right (_, GetSetting getSetting) -> do+ getSetting textSizeFromWidth `shouldBe` 0.04+ getSetting textSizeFromHeight `shouldBe` 12.4+ getSetting textFill `shouldBe` (1,1,0,1)+ getSetting testInlineList `shouldBe` ["inline1", "inline2", "inline3"]+ getSetting testList `shouldBe` ["list1", "list2", "list3"]+ Left (x :: SomeException) -> assertBool (show x) False testPartialFileDefaults :: Spec testPartialFileDefaults = it "parses correctly a partial file with defaults" $ do- readResult <- try $ readSettings (Path "tests/partial.config")- case readResult of- Right (_, GetSetting getSetting) -> do- getSetting textSizeFromWidth `shouldBe` 1.02- getSetting textSizeFromHeight `shouldBe` 12.4- getSetting textFill `shouldBe` (1,2,3,4)- getSetting testInlineList `shouldBe` ["un", "deux"]- getSetting testList `shouldBe` ["one", "two"]- Left (x :: SomeException) -> assertBool (show x) False+ readResult <- try $ readSettings (Path "tests/partial.config")+ case readResult of+ Right (_, GetSetting getSetting) -> do+ getSetting textSizeFromWidth `shouldBe` 1.02+ getSetting textSizeFromHeight `shouldBe` 12.4+ getSetting textFill `shouldBe` (1,2,3,4)+ getSetting testInlineList `shouldBe` ["un", "deux"]+ getSetting testList `shouldBe` ["one", "two"]+ Left (x :: SomeException) -> assertBool (show x) False testPartialFileDefaults2 :: Spec testPartialFileDefaults2 = it "parses correctly a partial2 file with defaults" $ do- readResult <- try $ readSettings (Path "tests/partial2.config")- case readResult of- Right (_, GetSetting getSetting) -> do- getSetting textSizeFromWidth `shouldBe` 1.02- getSetting textSizeFromHeight `shouldBe` 12.4- getSetting textFill `shouldBe` (1,2,3,4)- getSetting testInlineList `shouldBe` ["un", "deux"]- getSetting testList `shouldBe` []- Left (x :: SomeException) -> assertBool (show x) False+ readResult <- try $ readSettings (Path "tests/partial2.config")+ case readResult of+ Right (_, GetSetting getSetting) -> do+ getSetting textSizeFromWidth `shouldBe` 1.02+ getSetting textSizeFromHeight `shouldBe` 12.4+ getSetting textFill `shouldBe` (1,2,3,4)+ getSetting testInlineList `shouldBe` ["un", "deux"]+ getSetting testList `shouldBe` []+ Left (x :: SomeException) -> assertBool (show x) False testFileWithComments :: Spec testFileWithComments = it "parses correctly a partial file with comments" $ do- readResult <- try $ readSettings (Path "tests/test-save.txt")- case readResult of- Right (_, GetSetting getSetting) -> do- getSetting textSizeFromWidth `shouldBe` 1.02- getSetting textSizeFromHeight `shouldBe` 12.4- getSetting textFill `shouldBe` (1,2,3,4)- getSetting testInlineList `shouldBe` ["un", "deux"]- getSetting testList `shouldBe` ["one", "two"]- Left (x :: SomeException) -> assertBool (show x) False+ readResult <- try $ readSettings (Path "tests/test-save.txt")+ case readResult of+ Right (_, GetSetting getSetting) -> do+ getSetting textSizeFromWidth `shouldBe` 1.02+ getSetting textSizeFromHeight `shouldBe` 12.4+ getSetting textFill `shouldBe` (1,2,3,4)+ getSetting testInlineList `shouldBe` ["un", "deux"]+ getSetting testList `shouldBe` ["one", "two"]+ Left (x :: SomeException) -> assertBool (show x) False testInvalidValue :: Spec testInvalidValue = it "parses correctly a file with invalid values" $ do- readResult <- try $ readSettings (Path "tests/brokenvalue.config")- case readResult of- Right (_, GetSetting getSetting) -> do- -- give the default for the invalid value- getSetting textSizeFromWidth `shouldBe` 0.04- getSetting textSizeFromHeight `shouldBe` 12.4- getSetting textFill `shouldBe` (1,2,3,4)- getSetting testInlineList `shouldBe` ["inline1", "inline2", "inline3"]- getSetting testList `shouldBe` ["list1", "list2", "list3"]- Left (x :: SomeException) -> assertBool (show x) False+ readResult <- try $ readSettings (Path "tests/brokenvalue.config")+ case readResult of+ Right (_, GetSetting getSetting) -> do+ -- give the default for the invalid value+ getSetting textSizeFromWidth `shouldBe` 0.04+ getSetting textSizeFromHeight `shouldBe` 12.4+ getSetting textFill `shouldBe` (1,2,3,4)+ getSetting testInlineList `shouldBe` ["inline1", "inline2", "inline3"]+ getSetting testList `shouldBe` ["list1", "list2", "list3"]+ Left (x :: SomeException) -> assertBool (show x) False testNonExistingFile :: Spec testNonExistingFile = it "returns empty config for a non-existing file" $ do@@ -121,12 +123,22 @@ getSetting testList `shouldBe` ["list1", "list2", "list3"] Left (x :: SomeException) -> assertBool (show x) False +testReadLongSetting :: Spec+testReadLongSetting = it "Reads setting files with setting values wrapped over several lines" $ do+ readResult <- try $ readSettings (Path "tests/longsetting.conf") + case readResult of + Right (_, GetSetting getVal) -> do+ let expected = replicate 20 "longstring"+ let actual = getVal testInlineList+ assertEqual "doesn't match" expected actual+ Left (x :: SomeException) -> assertBool (show x) False+ testInvalidFile :: Spec testInvalidFile = it "reports errors properly for invalid files" $ do- readResult <- try $ readSettings (Path "tests/broken.config")- case readResult of- Left (_ :: SomeException) -> assertBool "ok" True- Right _ -> assertBool "did not get an error!" False+ readResult <- try $ readSettings (Path "tests/broken.config")+ case readResult of+ Left (_ :: SomeException) -> assertBool "ok" True+ Right _ -> assertBool "did not get an error!" False testSaveUserSetAndDefaults :: Spec testSaveUserSetAndDefaults = it "saves a file with user-set and default settings" $ do@@ -153,22 +165,36 @@ removeFile "p.config.bak" Left (x :: SomeException) -> assertBool (show x) False +wrapLongSettings :: Spec+wrapLongSettings = it "wraps long settings when saving" $ do+ readResult <- try $ readSettings (Path "tests/partial.config") + case readResult of + Right (conf, _) -> do+ let conf1 = setSetting conf testInlineList $ replicate 20 "longstring"+ saveSettings defaultConfig (Path "xx.config") conf1+ expected <- readFile "tests/longsetting.conf"+ actual <- readFile "xx.config"+ assertEqual "doesn't match" expected actual+ removeFile "xx.config"+ Left (x :: SomeException) -> assertBool (show x) False+ + testSetListSetting :: Spec testSetListSetting = it "overwrites correctly a list setting" $ do- readResult <- try $ readSettings (Path "tests/empty.config")- case readResult of- Right (conf, GetSetting getSetting) -> do- getSetting testList `shouldBe` ["list1", "list2", "list3"]- getSetting testInlineList `shouldBe` ["inline1", "inline2", "inline3"]- let conf0 = setSetting conf testList ["un"]- let conf1 = setSetting conf0 testInlineList ["one"]- getSetting' conf1 testList `shouldBe` ["un"]- getSetting' conf1 testInlineList `shouldBe` ["one"]- let conf2 = setSetting conf1 testList []- let conf3 = setSetting conf2 testInlineList []- getSetting' conf3 testList `shouldBe` []- getSetting' conf3 testInlineList `shouldBe` []- Left (x :: SomeException) -> assertBool (show x) False+ readResult <- try $ readSettings (Path "tests/empty.config")+ case readResult of+ Right (conf, GetSetting getSetting) -> do+ getSetting testList `shouldBe` ["list1", "list2", "list3"]+ getSetting testInlineList `shouldBe` ["inline1", "inline2", "inline3"]+ let conf0 = setSetting conf testList ["un"]+ let conf1 = setSetting conf0 testInlineList ["one"]+ getSetting' conf1 testList `shouldBe` ["un"]+ getSetting' conf1 testInlineList `shouldBe` ["one"]+ let conf2 = setSetting conf1 testList []+ let conf3 = setSetting conf2 testInlineList []+ getSetting' conf3 testList `shouldBe` []+ getSetting' conf3 testInlineList `shouldBe` []+ Left (x :: SomeException) -> assertBool (show x) False testIsKeyForListSetting :: Spec testIsKeyForListSetting = it "checks correctly whether a key is attached to a list setting" $ do