gitson 0.5.0 → 0.5.1
raw patch · 5 files changed
+112/−131 lines, 5 filesdep −HUnitdep −QuickCheckdep −hastachedep ~Globdep ~criteriondep ~doctestPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies removed: HUnit, QuickCheck, hastache, statistics
Dependency ranges changed: Glob, criterion, doctest, hspec
API changes (from Hackage documentation)
- Gitson: documentIdFromName :: MonadIO i => FilePath -> String -> i (Maybe Int)
+ Gitson: documentIdFromName :: (MonadIO i, Functor i) => FilePath -> String -> i (Maybe Int)
- Gitson: documentNameFromId :: MonadIO i => FilePath -> Int -> i (Maybe String)
+ Gitson: documentNameFromId :: (MonadIO i, Functor i) => FilePath -> Int -> i (Maybe String)
- Gitson: listCollections :: MonadIO i => i [FilePath]
+ Gitson: listCollections :: (MonadIO i, Functor i) => i [FilePath]
- Gitson: listDocumentKeys :: MonadIO i => FilePath -> i [FileName]
+ Gitson: listDocumentKeys :: (MonadIO i, Functor i) => FilePath -> i [FileName]
- Gitson: listEntries :: (MonadIO i, FromJSON a) => FilePath -> i [a]
+ Gitson: listEntries :: (MonadIO i, Functor i, FromJSON a) => FilePath -> i [a]
- Gitson: readDocument :: (MonadIO i, FromJSON a) => FilePath -> FileName -> i (Maybe a)
+ Gitson: readDocument :: (MonadIO i, Functor i, FromJSON a) => FilePath -> FileName -> i (Maybe a)
- Gitson: readDocumentById :: (MonadIO i, FromJSON a) => FilePath -> Int -> i (Maybe a)
+ Gitson: readDocumentById :: (MonadIO i, Functor i, FromJSON a) => FilePath -> Int -> i (Maybe a)
- Gitson: readDocumentByName :: (MonadIO i, FromJSON a) => FilePath -> String -> i (Maybe a)
+ Gitson: readDocumentByName :: (MonadIO i, Functor i, FromJSON a) => FilePath -> String -> i (Maybe a)
- Gitson: saveDocument :: (MonadIO i, ToJSON a) => FilePath -> FileName -> a -> TransactionWriter i ()
+ Gitson: saveDocument :: (MonadIO i, Functor i, ToJSON a) => FilePath -> FileName -> a -> TransactionWriter i ()
- Gitson: saveDocumentById :: (MonadIO i, ToJSON a) => FilePath -> Int -> a -> TransactionWriter i ()
+ Gitson: saveDocumentById :: (MonadIO i, Functor i, ToJSON a) => FilePath -> Int -> a -> TransactionWriter i ()
- Gitson: saveDocumentByName :: (MonadIO i, ToJSON a) => FilePath -> String -> a -> TransactionWriter i ()
+ Gitson: saveDocumentByName :: (MonadIO i, Functor i, ToJSON a) => FilePath -> String -> a -> TransactionWriter i ()
- Gitson: saveNextDocument :: (MonadIO i, ToJSON a) => FilePath -> FileName -> a -> TransactionWriter i ()
+ Gitson: saveNextDocument :: (MonadIO i, Functor i, ToJSON a) => FilePath -> FileName -> a -> TransactionWriter i ()
- Gitson: transaction :: (MonadIO i, MonadBaseControl IO i) => FilePath -> TransactionWriter i () -> i ()
+ Gitson: transaction :: (MonadIO i, Functor i, MonadBaseControl IO i) => FilePath -> TransactionWriter i () -> i ()
Files
- README.md +3/−14
- gitson.cabal +7/−11
- library/Gitson.hs +55/−61
- library/Gitson/Util.hs +3/−3
- test-suite/GitsonSpec.hs +44/−42
README.md view
@@ -1,4 +1,4 @@-# gitson [](https://hackage.haskell.org/package/gitson) [](https://travis-ci.org/myfreeweb/gitson) [](https://coveralls.io/r/myfreeweb/gitson) [](https://www.tldrlegal.com/l/apache2)+# gitson [](https://hackage.haskell.org/package/gitson) [](https://travis-ci.org/myfreeweb/gitson) [](https://coveralls.io/r/myfreeweb/gitson) [](https://tldrlegal.com/license/-isc-license) A simple document store library for Git + JSON, based on [Aeson]. Uses command line git, at least for now.@@ -104,16 +104,5 @@ ## License -Copyright 2014 Greg V <floatboth@me.com>--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at-- http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.+Copyright 2014-2015 Greg V <greg@unrelenting.technology>+Available under the ISC license, see the `COPYING` file
gitson.cabal view
@@ -1,5 +1,5 @@ name: gitson-version: 0.5.0+version: 0.5.1 synopsis: A document store library for Git + JSON. description: A simple document store library for Git + JSON, based on Aeson. Uses command line git. Transactions use flock, so it's safe even across completely separate programs! category: Database, JSON, Git@@ -7,14 +7,14 @@ author: Greg V copyright: 2014-2015 Greg V <greg@unrelenting.technology> maintainer: greg@unrelenting.technology-license: Apache-2.0+license: OtherLicense license-file: COPYING build-type: Simple cabal-version: >= 1.18 extra-source-files: README.md tested-with:- GHC == 7.8.2+ GHC == 7.8.3 source-repository head type: git@@ -45,8 +45,8 @@ test-suite examples build-depends: base >= 4.0.0.0 && < 5- , Glob == 0.*- , doctest >= 0.8+ , Glob+ , doctest default-language: Haskell2010 ghc-options: -threaded -Wall -Werror hs-source-dirs: test-suite@@ -61,9 +61,7 @@ , process , directory , aeson- , hspec == 1.*- , HUnit- , QuickCheck+ , hspec default-language: Haskell2010 ghc-options: -threaded -Wall -Werror -fhpc hs-source-dirs: test-suite@@ -80,9 +78,7 @@ , random , gitson , aeson- , criterion == 0.6.*- , hastache < 0.6- , statistics < 0.11+ , criterion default-language: Haskell2010 hs-source-dirs: benchmark main-is: Bench.hs
library/Gitson.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE Safe, FlexibleContexts #-}+{-# LANGUAGE Safe, FlexibleContexts, UnicodeSyntax #-} -- | Gitson is a simple document store library for Git + JSON. module Gitson (@@ -21,13 +21,14 @@ import System.Directory import System.Lock.FLock-import Control.Applicative ((<$>))+import Control.Applicative import Control.Exception (try, IOException) import Control.Error.Util (hush) import Control.Monad.Trans.Writer import Control.Monad.Trans.Control import Control.Monad.IO.Class-import Data.Maybe (fromMaybe, catMaybes)+import Control.Monad (liftM)+import Data.Maybe (fromMaybe, mapMaybe) import Data.List (find, isSuffixOf) import Text.Printf (printf) import qualified Data.ByteString.Lazy as BL@@ -39,133 +40,126 @@ type IdAndName = (Int, String) type FileName = String-type Finder = ([(IdAndName, FileName)] -> Maybe (IdAndName, FileName))+type Finder = [(IdAndName, FileName)] → Maybe (IdAndName, FileName) -splitFindDocument :: (MonadIO i) => FilePath -> Finder -> i (Maybe (IdAndName, FileName))-splitFindDocument collection finder = listDocumentKeys collection >>=- return . finder . catMaybes . map (\x -> intoFunctor (maybeReadIntString x) x)+splitFindDocument ∷ (MonadIO i, Functor i) ⇒ FilePath → Finder → i (Maybe (IdAndName, FileName))+splitFindDocument collection finder = + finder . mapMaybe (\x → intoFunctor (maybeReadIntString x) x) <$> listDocumentKeys collection -documentFullKey :: (MonadIO i) => FilePath -> Finder -> i (Maybe FileName)-documentFullKey collection finder = splitFindDocument collection finder >>= return . (snd <$>)+documentFullKey ∷ (MonadIO i, Functor i) ⇒ FilePath → Finder → i (Maybe FileName)+documentFullKey collection finder = (snd <$>) <$> splitFindDocument collection finder -findById :: Int -> Finder+findById ∷ Int → Finder findById i = find $ (== i) . fst . fst -findByName :: String -> Finder-findByName n = find $ (isSuffixOf n) . snd . fst+findByName ∷ String → Finder+findByName n = find $ isSuffixOf n . snd . fst -- | Creates a git repository under a given path.-createRepo :: FilePath -> IO ()+createRepo ∷ FilePath → IO () createRepo path = do createDirectoryIfMissing True path insideDirectory path $ shell "git" ["init"] -- | Executes a blocking transaction on a repository, committing the results to git.-transaction :: (MonadIO i, MonadBaseControl IO i) => FilePath -> TransactionWriter i () -> i ()+transaction ∷ (MonadIO i, Functor i, MonadBaseControl IO i) ⇒ FilePath → TransactionWriter i () → i () transaction repoPath action = insideDirectory repoPath $ do liftIO $ writeFile lockPath "" withLock lockPath Exclusive Block $ do- writeActions <- execWriterT action+ writeActions ← execWriterT action shell "git" ["stash"] -- it's totally ok to do this without changes liftIO $ sequence_ writeActions shell "git" ["add", "--all"] shell "git" ["commit", "-m", "Gitson transaction"] shell "git" ["stash", "pop"] -combineKey :: IdAndName -> FileName-combineKey (n, s) = zeroPad n ++ "-" ++ s- where zeroPad :: Int -> String- zeroPad x = printf "%06d" x+combineKey ∷ IdAndName → FileName+combineKey (n, s) = printf "%06d-%s" n s -writeDocument :: ToJSON a => FilePath -> FileName -> a -> IO ()+writeDocument ∷ ToJSON a ⇒ FilePath → FileName → a → IO () writeDocument collection key content = BL.writeFile (documentPath collection key) (encode content) -- | Adds a write action to a transaction.-saveDocument :: (MonadIO i, ToJSON a) => FilePath -> FileName -> a -> TransactionWriter i ()-saveDocument collection key content = do+saveDocument ∷ (MonadIO i, Functor i, ToJSON a) ⇒ FilePath → FileName → a → TransactionWriter i ()+saveDocument collection key content = tell [createDirectoryIfMissing True collection, writeDocument collection key content] -- | Adds a write action to a transaction. -- The key will start with a numeric id, incremented from the last id in the collection.-saveNextDocument :: (MonadIO i, ToJSON a) => FilePath -> FileName -> a -> TransactionWriter i ()-saveNextDocument collection key content = do+saveNextDocument ∷ (MonadIO i, Functor i, ToJSON a) ⇒ FilePath → FileName → a → TransactionWriter i ()+saveNextDocument collection key content = tell [createDirectoryIfMissing True collection, listDocumentKeys collection >>= return . nextKeyId >>=- \nextId -> writeDocument collection (combineKey (nextId, key)) content]+ \nextId → writeDocument collection (combineKey (nextId, key)) content] -- | Adds a write action to a transaction. -- Will update the document with the given numeric id.-saveDocumentById :: (MonadIO i, ToJSON a) => FilePath -> Int -> a -> TransactionWriter i ()-saveDocumentById collection i content = do+saveDocumentById ∷ (MonadIO i, Functor i, ToJSON a) ⇒ FilePath → Int → a → TransactionWriter i ()+saveDocumentById collection i content = tell [documentFullKey collection (findById i) >>=- \k -> case k of- Just key -> writeDocument collection key content- Nothing -> return ()]+ \k → case k of+ Just key → writeDocument collection key content+ Nothing → return ()] -- | Adds a write action to a transaction. -- Will update the document with the given numeric id.-saveDocumentByName :: (MonadIO i, ToJSON a) => FilePath -> String -> a -> TransactionWriter i ()-saveDocumentByName collection n content = do+saveDocumentByName ∷ (MonadIO i, Functor i, ToJSON a) ⇒ FilePath → String → a → TransactionWriter i ()+saveDocumentByName collection n content = tell [documentFullKey collection (findByName n) >>=- \k -> case k of- Just key -> writeDocument collection key content- Nothing -> return ()]+ \k → case k of+ Just key → writeDocument collection key content+ Nothing → return ()] -- | Lists collections in the current repository.-listCollections :: (MonadIO i) => i [FilePath]+listCollections ∷ (MonadIO i, Functor i) ⇒ i [FilePath] listCollections = liftIO $ do- contents <- try (getDirectoryContents =<< getCurrentDirectory) :: IO (Either IOException [FilePath])+ contents ← try (getDirectoryContents =<< getCurrentDirectory) ∷ IO (Either IOException [FilePath]) filterDirs $ fromMaybe [] $ hush contents -- | Lists document keys in a collection.-listDocumentKeys :: (MonadIO i) => FilePath -> i [FileName]+listDocumentKeys ∷ (MonadIO i, Functor i) ⇒ FilePath → i [FileName] listDocumentKeys collection = liftIO $ do- contents <- try (getDirectoryContents collection) :: IO (Either IOException [String])- return $ filterFilenamesAsKeys $ fromMaybe [] $ hush contents+ contents ← try (getDirectoryContents collection) ∷ IO (Either IOException [String])+ return . filterFilenamesAsKeys . fromMaybe [] $ hush contents -- | Lists entries in a collection.-listEntries :: (MonadIO i, FromJSON a) => FilePath -> i [a]+listEntries ∷ (MonadIO i, Functor i, FromJSON a) ⇒ FilePath → i [a] listEntries collection = liftIO $ do- ks <- listDocumentKeys collection- maybes <- mapM (readDocument collection) ks- return $ fromMaybe [] $ sequence maybes+ maybes ← mapM (readDocument collection) =<< listDocumentKeys collection+ return . fromMaybe [] $ sequence maybes -- | Reads a document from a collection by key.-readDocument :: (MonadIO i, FromJSON a) => FilePath -> FileName -> i (Maybe a)+readDocument ∷ (MonadIO i, Functor i, FromJSON a) ⇒ FilePath → FileName → i (Maybe a) readDocument collection key = liftIO $ do- jsonString <- try (BL.readFile $ documentPath collection key) :: IO (Either IOException BL.ByteString)+ jsonString ← try (BL.readFile $ documentPath collection key) ∷ IO (Either IOException BL.ByteString) return $ decode =<< hush jsonString -readDocument' :: (MonadIO i, FromJSON a) => FilePath -> Maybe FileName -> i (Maybe a)+readDocument' ∷ (MonadIO i, Functor i, FromJSON a) ⇒ FilePath → Maybe FileName → i (Maybe a) readDocument' collection key = liftIO $ case key of- Just key -> readDocument collection key- Nothing -> return Nothing+ Just key → readDocument collection key+ Nothing → return Nothing -- | Reads a document from a collection by numeric id (for example, key "00001-hello" has id 1).-readDocumentById :: (MonadIO i, FromJSON a) => FilePath -> Int -> i (Maybe a)+readDocumentById ∷ (MonadIO i, Functor i, FromJSON a) ⇒ FilePath → Int → i (Maybe a) readDocumentById collection i =- documentFullKey collection (findById i) >>=- readDocument' collection+ readDocument' collection =<< documentFullKey collection (findById i) -- | Reads a document from a collection by name (for example, key "00001-hello" has name "hello").-readDocumentByName :: (MonadIO i, FromJSON a) => FilePath -> String -> i (Maybe a)+readDocumentByName ∷ (MonadIO i, Functor i, FromJSON a) ⇒ FilePath → String → i (Maybe a) readDocumentByName collection n =- documentFullKey collection (findByName n) >>=- readDocument' collection+ readDocument' collection =<< documentFullKey collection (findByName n) -- | Returns a document's id by name (for example, "hello" will return 23 when key "00023-hello" exists). -- Does not read the document!-documentIdFromName :: (MonadIO i) => FilePath -> String -> i (Maybe Int)+documentIdFromName ∷ (MonadIO i, Functor i) ⇒ FilePath → String → i (Maybe Int) documentIdFromName collection n =- splitFindDocument collection (findByName n) >>=- return . (fst <$> fst <$>)+ (fst <$> fst <$>) <$> splitFindDocument collection (findByName n) -- | Returns a document's name by id (for example, 23 will return "hello" when key "00023-hello" exists). -- Does not read the document!-documentNameFromId :: (MonadIO i) => FilePath -> Int -> i (Maybe String)+documentNameFromId ∷ (MonadIO i, Functor i) ⇒ FilePath → Int → i (Maybe String) documentNameFromId collection i =- splitFindDocument collection (findById i) >>=- return . (drop 1 . snd <$> fst <$>)+ (drop 1 . snd <$> fst <$>) <$> splitFindDocument collection (findById i)
library/Gitson/Util.hs view
@@ -36,13 +36,13 @@ -- | Filters a list of file paths, leaving only paths to existing non-hidden directories. filterDirs :: [FilePath] -> IO [FilePath]-filterDirs = (filterM doesDirectoryExist) . filter (not . isPrefixOf ".")+filterDirs = filterM doesDirectoryExist . filter (not . isPrefixOf ".") -- | Returns an IO action that switches the current directory to a given path, -- executes the given IO action and switches the current directory back. insideDirectory :: (MonadIO i) => FilePath -> i a -> i a insideDirectory path action = do- prevPath <- liftIO $ getCurrentDirectory+ prevPath <- liftIO getCurrentDirectory liftIO $ setCurrentDirectory path result <- action liftIO $ setCurrentDirectory prevPath@@ -93,7 +93,7 @@ -- >>> nextKeyId ["1-hell0-w0rld-123456.json", "002-my-second-post.json"] -- 3 nextKeyId :: [String] -> Int-nextKeyId = (+1) . maxOrZero . catMaybes . map maybeReadInt+nextKeyId = (+1) . maxOrZero . mapMaybe maybeReadInt where maybeReadInt x = fst <$> maybeReadIntString x maxOrZero [] = 0 maxOrZero xs = maximum xs
test-suite/GitsonSpec.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TemplateHaskell, UnicodeSyntax #-} module GitsonSpec (spec) where @@ -12,10 +12,12 @@ import Gitson import Gitson.Util (insideDirectory, lastCommitText) -data Thing = Thing { val :: Int } deriving (Eq, Show, Ord)+{-# ANN module ("HLint: ignore Redundant do" :: String) #-}++data Thing = Thing { val ∷ Int } deriving (Eq, Show, Ord) $(deriveJSON defaultOptions ''Thing) -spec :: Spec+spec ∷ Spec spec = before setup $ after cleanup $ do context "transaction" $ do describe "saveDocument" $ do@@ -23,14 +25,14 @@ transaction "tmp/repo" $ do saveDocument "things" "first-thing" Thing {val = 1} saveDocument "things" "second-thing" Thing {val = 2}- liftIO $ (readFile "things/first-thing.json") `shouldThrow` anyIOException- liftIO $ (readFile "things/second-thing.json") `shouldThrow` anyIOException+ liftIO $ readFile "things/first-thing.json" `shouldThrow` anyIOException+ liftIO $ readFile "things/second-thing.json" `shouldThrow` anyIOException insideDirectory "tmp/repo" $ do- first <- readFile "things/first-thing.json"+ first ← readFile "things/first-thing.json" first `shouldBe` "{\n \"val\": 1\n}"- second <- readFile "things/second-thing.json"+ second ← readFile "things/second-thing.json" second `shouldBe` "{\n \"val\": 2\n}"- commitMsg <- lastCommitText+ commitMsg ← lastCommitText commitMsg `shouldBe` "Gitson transaction" describe "saveNextDocument" $ do@@ -39,108 +41,108 @@ saveNextDocument "things" "hello" Thing {val = 1} saveNextDocument "things" "world" Thing {val = 2} insideDirectory "tmp/repo" $ do- first <- readFile "things/000001-hello.json"+ first ← readFile "things/000001-hello.json" first `shouldBe` "{\n \"val\": 1\n}"- second <- readFile "things/000002-world.json"+ second ← readFile "things/000002-world.json" second `shouldBe` "{\n \"val\": 2\n}" describe "saveDocumentById" $ do it "updates an entry by specified id" $ do createDirectoryIfMissing True "tmp/repo/things"- _ <- liftIO $ writeFile "tmp/repo/things/000004-second-thing.json" "{\"val\":1}"+ _ ← liftIO $ writeFile "tmp/repo/things/000004-second-thing.json" "{\"val\":1}" transaction "tmp/repo" $ do saveDocumentById "things" 4 Thing {val = 2} insideDirectory "tmp/repo" $ do- second <- readFile "things/000004-second-thing.json"+ second ← readFile "things/000004-second-thing.json" second `shouldBe` "{\n \"val\": 2\n}" describe "saveDocumentByName" $ do it "updates an entry by specified name" $ do createDirectoryIfMissing True "tmp/repo/things"- _ <- liftIO $ writeFile "tmp/repo/things/000004-second-thing.json" "{\"val\":1}"+ _ ← liftIO $ writeFile "tmp/repo/things/000004-second-thing.json" "{\"val\":1}" transaction "tmp/repo" $ do saveDocumentByName "things" "second-thing" Thing {val = 2} insideDirectory "tmp/repo" $ do- second <- readFile "things/000004-second-thing.json"+ second ← readFile "things/000004-second-thing.json" second `shouldBe` "{\n \"val\": 2\n}" describe "readDocument" $ do it "returns Just the document when reading an document by key" $ do createDirectoryIfMissing True "tmp/repo/things"- _ <- writeFile "tmp/repo/things/second-thing.json" "{\"val\":2}"- content <- readDocument "tmp/repo/things" "second-thing" :: IO (Maybe Thing)+ _ ← writeFile "tmp/repo/things/second-thing.json" "{\"val\":2}"+ content ← readDocument "tmp/repo/things" "second-thing" ∷ IO (Maybe Thing) content `shouldBe` Just Thing {val = 2} it "returns Nothing when reading by a nonexistent key" $ do- content <- readDocument "tmp/repo/things" "totally-not-a-thing" :: IO (Maybe Thing)+ content ← readDocument "tmp/repo/things" "totally-not-a-thing" ∷ IO (Maybe Thing) content `shouldBe` Nothing describe "readDocumentById" $ do it "returns Just the document when reading an document by id" $ do createDirectoryIfMissing True "tmp/repo/things"- _ <- writeFile "tmp/repo/things/000004-second-thing.json" "{\"val\":1}"- content <- readDocumentById "tmp/repo/things" 4 :: IO (Maybe Thing)+ _ ← writeFile "tmp/repo/things/000004-second-thing.json" "{\"val\":1}"+ content ← readDocumentById "tmp/repo/things" 4 ∷ IO (Maybe Thing) content `shouldBe` Just Thing {val = 1} it "returns Nothing when reading by a nonexistent id" $ do- content <- readDocumentById "tmp/repo/things" 1 :: IO (Maybe Thing)+ content ← readDocumentById "tmp/repo/things" 1 ∷ IO (Maybe Thing) content `shouldBe` Nothing describe "readDocumentByName" $ do it "returns Just the document when reading an document by name" $ do createDirectoryIfMissing True "tmp/repo/things"- _ <- writeFile "tmp/repo/things/000098-second-thing.json" "{\"val\":1}"- content <- readDocumentByName "tmp/repo/things" "second-thing" :: IO (Maybe Thing)+ _ ← writeFile "tmp/repo/things/000098-second-thing.json" "{\"val\":1}"+ content ← readDocumentByName "tmp/repo/things" "second-thing" ∷ IO (Maybe Thing) content `shouldBe` Just Thing {val = 1} it "returns Nothing when reading by a nonexistent name" $ do- content <- readDocumentByName "tmp/repo/things" "yolo" :: IO (Maybe Thing)+ content ← readDocumentByName "tmp/repo/things" "yolo" ∷ IO (Maybe Thing) content `shouldBe` Nothing describe "documentIdFromName" $ do it "returns Just the document id from the document's name" $ do createDirectoryIfMissing True "tmp/repo/things"- _ <- writeFile "tmp/repo/things/000098-some-thing.json" "THIS SHOULD NOT BE READ"- i <- documentIdFromName "tmp/repo/things" "some-thing"+ _ ← writeFile "tmp/repo/things/000098-some-thing.json" "THIS SHOULD NOT BE READ"+ i ← documentIdFromName "tmp/repo/things" "some-thing" i `shouldBe` Just 98 it "returns Nothing when run with a nonexistent name" $ do- i <- documentIdFromName "tmp/repo/things" "yolo"+ i ← documentIdFromName "tmp/repo/things" "yolo" i `shouldBe` Nothing describe "documentNameFromId" $ do it "returns Just the document name from the document's id" $ do createDirectoryIfMissing True "tmp/repo/things"- _ <- writeFile "tmp/repo/things/000098-some-thing.json" "THIS SHOULD NOT BE READ"- i <- documentNameFromId "tmp/repo/things" 98+ _ ← writeFile "tmp/repo/things/000098-some-thing.json" "THIS SHOULD NOT BE READ"+ i ← documentNameFromId "tmp/repo/things" 98 i `shouldBe` Just "some-thing" it "returns Nothing when run with a nonexistent id" $ do- i <- documentNameFromId "tmp/repo/things" 123+ i ← documentNameFromId "tmp/repo/things" 123 i `shouldBe` Nothing describe "listDocumentKeys" $ do it "returns a list of document keys when listing a collection" $ do createDirectoryIfMissing True "tmp/repo/things"- _ <- writeFile "tmp/repo/things/first-thing.json" "{}"- _ <- writeFile "tmp/repo/things/second-thing.json" "{}"- list <- listDocumentKeys "tmp/repo/things"+ _ ← writeFile "tmp/repo/things/first-thing.json" "{}"+ _ ← writeFile "tmp/repo/things/second-thing.json" "{}"+ list ← listDocumentKeys "tmp/repo/things" sort list `shouldBe` ["first-thing", "second-thing"] it "returns an empty when listing a nonexistent collection" $ do- list <- listDocumentKeys "nonsense"+ list ← listDocumentKeys "nonsense" list `shouldBe` [] describe "listEntries" $ do it "returns a list of entries when listing a collection" $ do createDirectoryIfMissing True "tmp/repo/things"- _ <- writeFile "tmp/repo/things/first-thing.json" "{\"val\":1}"- _ <- writeFile "tmp/repo/things/second-thing.json" "{\"val\":2}"- list <- listEntries "tmp/repo/things" :: IO [Thing]+ _ ← writeFile "tmp/repo/things/first-thing.json" "{\"val\":1}"+ _ ← writeFile "tmp/repo/things/second-thing.json" "{\"val\":2}"+ list ← listEntries "tmp/repo/things" ∷ IO [Thing] sort list `shouldBe` [Thing {val = 1}, Thing {val = 2}] it "returns an empty list when listing a nonexistent collection" $ do- list <- listEntries "nonsense" :: IO [Thing]+ list ← listEntries "nonsense" ∷ IO [Thing] list `shouldBe` [] describe "listCollections" $ do@@ -148,17 +150,17 @@ createDirectoryIfMissing True "tmp/repo/bits" createDirectoryIfMissing True "tmp/repo/pieces" insideDirectory "tmp/repo" $ do- list <- listCollections+ list ← listCollections sort list `shouldBe` ["bits", "pieces"] it "returns an empty list when listing an empty repo" $ do createDirectoryIfMissing True "tmp/repo" insideDirectory "tmp/repo" $ do- list <- listCollections+ list ← listCollections sort list `shouldBe` [] -setup :: IO ()+setup ∷ IO () setup = createRepo "tmp/repo" -cleanup :: IO ()-cleanup = void (try (removeDirectoryRecursive "tmp/repo") :: IO (Either IOException ()))+cleanup ∷ ActionWith ()+cleanup () = void (try (removeDirectoryRecursive "tmp/repo") ∷ IO (Either IOException ()))