hscaffold 0.4.0.0 → 0.4.1.0
raw patch · 15 files changed
+257/−230 lines, 15 filesdep +exceptionssetup-changed
Dependencies added: exceptions
Files
- Setup.hs +2/−1
- bin/HsfilesFromDirectory.hs +1/−1
- hscaffold.cabal +7/−2
- src/Hscaffold.hs +18/−21
- src/Hscaffold/EDSL.hs +37/−61
- src/Hscaffold/Generator/Directory.hs +13/−16
- src/Hscaffold/Generator/Hsfiles.hs +10/−16
- src/Hscaffold/Interpreter/Common.hs +8/−8
- src/Hscaffold/Interpreter/Haskell.hs +25/−23
- src/Hscaffold/Interpreter/Hsfiles.hs +13/−11
- src/Hscaffold/Interpreter/IO.hs +51/−5
- src/Hscaffold/Types.hs +4/−5
- test/HscaffoldSpec.hs +67/−59
- test/SanitySpec.hs +1/−1
- test/Spec.hs +0/−0
Setup.hs view
@@ -1,2 +1,3 @@-import Distribution.Simple+import Distribution.Simple+ main = defaultMain
bin/HsfilesFromDirectory.hs view
@@ -6,7 +6,7 @@ main = do as <- getArgs case as of- (fp:_) -> go fp+ (fp : _) -> go fp [] -> error "Usage: hsfiles-from-directory <DIRECTORY>" where go fp = do
hscaffold.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack name: hscaffold-version: 0.4.0.0+version: 0.4.1.0 synopsis: Very simple file/directory structure scaffolding writer monad EDSL description: See our README on GitHub at <https://github.com/yamadapc/hscaffold> homepage: https://github.com/yamadapc/hscaffold#readme@@ -34,6 +34,8 @@ , filepath , unix , regex-compat+ , temporary+ , exceptions exposed-modules: Hscaffold Hscaffold.EDSL@@ -61,6 +63,8 @@ , filepath , unix , regex-compat+ , temporary+ , exceptions , hscaffold default-language: Haskell2010 @@ -78,10 +82,11 @@ , filepath , unix , regex-compat+ , temporary+ , exceptions , hspec , hscaffold , QuickCheck- , temporary other-modules: HscaffoldSpec SanitySpec
src/Hscaffold.hs view
@@ -5,63 +5,60 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-}+ module Hscaffold- (- -- * Running Hscaffold+ ( -- * Running Hscaffold runHscaffold- -- * Convert Hscaffold to HSFILES (@stack templates@)+ -- * Convert Hscaffold to HSFILES (@stack templates@) , toHsfiles , writeToHsfiles- -- * Convert HSFILES to Hscaffold+ -- * Convert HSFILES to Hscaffold , fromHsfiles , fromHsfilesW , readHsfiles-- -- * Convert a directory to Hscaffold+ -- * Convert a directory to Hscaffold , hscaffoldFromDirectory- -- * Compile Hscaffold to Haskell code+ -- * Compile Hscaffold to Haskell code , hscaffoldToHaskell-- -- ** Finer grained runners+ -- ** Finer grained runners , runAction , runWriter , runWriterT-- -- * EDSL Combinators+ -- * EDSL Combinators , directory , file , link , copy , touch-- -- ** Setting permissions+ -- ** Setting permissions , permissions , fileWith , directoryWith , copyWith , touchWith , Permissions(..)-- -- * Types+ -- * Types , ScaffoldMonadT , ScaffoldMonadIO , ScaffoldActionType(..) , ScaffoldAction , ScaffoldActionV , ScaffoldMonadET-- -- * Helpers+ -- * Utilities+ , withTemporaryHscaffold+ , withTemporaryHscaffold'+ , withTemporaryHscaffoldIO+ , withTemporaryHscaffoldIO'+ -- * Helpers , runHscaffoldIO , mkActionPath-- -- * Re-exports+ -- * Re-exports , Text , module Control.Monad.IO.Class , module Control.Monad.Writer , module System.Directory , module System.FilePath- )- where+ ) where import Control.Monad.IO.Class import Control.Monad.Writer
src/Hscaffold/EDSL.hs view
@@ -2,9 +2,9 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-}-module Hscaffold.EDSL- where +module Hscaffold.EDSL where+ import Control.Monad.Writer import Data.Text (Text) import System.Directory@@ -12,96 +12,72 @@ import Hscaffold.Types -- | Create a directory with the nested contents-directory- :: MonadWriter (ScaffoldAction e) m- => FilePath- -> WriterT (ScaffoldAction e) m b- -> m b+directory :: MonadWriter (ScaffoldAction e) m+ => FilePath+ -> WriterT (ScaffoldAction e) m b+ -> m b directory fp nested = do (x, nested') <- runWriterT nested- tell [Directory fp nested']+ tell [ Directory fp nested' ] return x -- | Create a directory with the nested contents and permissions-directoryWith- :: MonadWriter (ScaffoldAction e) m- => Permissions- -> FilePath- -> WriterT (ScaffoldAction e) m b- -> m b+directoryWith :: MonadWriter (ScaffoldAction e) m+ => Permissions+ -> FilePath+ -> WriterT (ScaffoldAction e) m b+ -> m b directoryWith perms fp nested = do x <- directory fp nested- tell [SetPermissions perms fp]+ tell [ SetPermissions perms fp ] return x -- | Create a file with the given contents-file- :: MonadWriter (ScaffoldAction e) m- => FilePath- -> Text- -> m ()-file fp txt = tell [File fp txt]+file :: MonadWriter (ScaffoldAction e) m => FilePath -> Text -> m ()+file fp txt = tell [ File fp txt ] -- | Create a file with the given contents and permissions-fileWith- :: MonadWriter (ScaffoldAction e) m- => Permissions- -> FilePath- -> Text- -> m ()+fileWith :: MonadWriter (ScaffoldAction e) m+ => Permissions+ -> FilePath+ -> Text+ -> m () fileWith perms fp txt = do file fp txt- tell [SetPermissions perms fp]+ tell [ SetPermissions perms fp ] -- | Set permissions on a filepath-permissions- :: MonadWriter (ScaffoldAction e) m- => FilePath- -> Permissions- -> m ()-permissions fp perms = tell [SetPermissions perms fp]+permissions :: MonadWriter (ScaffoldAction e) m+ => FilePath+ -> Permissions+ -> m ()+permissions fp perms = tell [ SetPermissions perms fp ] -- | Create a symbolic link between two filepaths-link- :: MonadWriter (ScaffoldAction e) m- => FilePath- -> FilePath- -> m ()-link fp1 fp2 = tell [Link fp1 fp2]+link :: MonadWriter (ScaffoldAction e) m => FilePath -> FilePath -> m ()+link fp1 fp2 = tell [ Link fp1 fp2 ] -- | Write the empty string to a file-touch- :: MonadWriter (ScaffoldAction e) m- => FilePath- -> m ()+touch :: MonadWriter (ScaffoldAction e) m => FilePath -> m () touch fp = file fp "" -- | Write the empty string to a file with given permissions-touchWith- :: MonadWriter (ScaffoldAction e) m- => Permissions- -> FilePath- -> m ()+touchWith :: MonadWriter (ScaffoldAction e) m => Permissions -> FilePath -> m () touchWith perms fp = fileWith perms fp "" -- | Copy a file from A to B -- -- Non-absolute paths are treated relative to the *current* root, nested blocks -- change the root-copy- :: MonadWriter (ScaffoldAction e) m- => FilePath- -> FilePath- -> m ()-copy fp1 fp2 = tell [Copy fp1 fp2]+copy :: MonadWriter (ScaffoldAction e) m => FilePath -> FilePath -> m ()+copy fp1 fp2 = tell [ Copy fp1 fp2 ] -- | Copy a file from A to B and set permissions on B, see 'copy'-copyWith- :: MonadWriter (ScaffoldAction e) m- => Permissions- -> FilePath- -> FilePath- -> m ()+copyWith :: MonadWriter (ScaffoldAction e) m+ => Permissions+ -> FilePath+ -> FilePath+ -> m () copyWith perms fp1 fp2 = do copy fp1 fp2 permissions fp2 perms
src/Hscaffold/Generator/Directory.hs view
@@ -1,5 +1,4 @@-module Hscaffold.Generator.Directory- where+module Hscaffold.Generator.Directory where import Data.List import qualified Data.Text.IO as Text@@ -15,16 +14,17 @@ -- | Converts a directory to scaffold actions with a custom filter function. By -- default we ignore directories starting with @.@-hscaffoldFromDirectoryWith- :: ([FilePath] -> [FilePath]) -> FilePath -> IO (ScaffoldAction e)-hscaffoldFromDirectoryWith = hscaffoldFromDirectoryWith' False+hscaffoldFromDirectoryWith :: ([FilePath] -> [FilePath])+ -> FilePath+ -> IO (ScaffoldAction e)+hscaffoldFromDirectoryWith =+ hscaffoldFromDirectoryWith' False -hscaffoldFromDirectoryWith'- :: Traversable t =>- Bool- -> ([FilePath] -> t FilePath)- -> FilePath- -> IO [ScaffoldActionType e]+hscaffoldFromDirectoryWith' :: Traversable t+ => Bool+ -> ([FilePath] -> t FilePath)+ -> FilePath+ -> IO [ScaffoldActionType e] hscaffoldFromDirectoryWith' isRecur p root = do ls <- p <$> getDirectoryContents root concat <$> mapM classify ls@@ -32,14 +32,11 @@ fromFile fp' fp = do txt <- Text.readFile fp' return [ File (normalise fp) txt ]- fromDir =- hscaffoldFromDirectoryWith' True p+ fromDir = hscaffoldFromDirectoryWith' True p classify fp = do let fp' = root </> fp isfl <- doesFileExist fp' isdir <- doesDirectoryExist fp' if isfl then fromFile fp' (if isRecur then fp' else fp)- else if isdir- then fromDir fp'- else return []+ else if isdir then fromDir fp' else return []
src/Hscaffold/Generator/Hsfiles.hs view
@@ -1,12 +1,12 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-module Hscaffold.Generator.Hsfiles- where+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-} +module Hscaffold.Generator.Hsfiles where+ import Control.Arrow import Control.Monad.Writer-import qualified Data.Text as Text--- TODO - Disable this on Windows+import qualified Data.Text as Text import System.FilePath import Text.Regex @@ -14,18 +14,12 @@ -- | Converts hsfiles to hscaffold actions fromHsfiles :: String -> ScaffoldAction e-fromHsfiles h =- let (_, r) = foldr helper ("", []) matches- in r+fromHsfiles h = snd $ foldr helper ("", []) matches where- helper (Just (fp:_), _) (lacc, current) =- ( ""- , File (normalise fp) lacc:current- )+ helper (Just (fp : _), _) (lacc, current) =+ ("", File (normalise fp) lacc : current) helper (_, l) (lacc, current) =- ( Text.stripEnd (l <> "\n" <> lacc)- , current- )+ (Text.stripEnd (l <> "\n" <> lacc), current) re = mkRegex "^{-# START_FILE (.+) #-}( +)?$" matches = map (matchRegex re &&& Text.pack) (lines h)
src/Hscaffold/Interpreter/Common.hs view
@@ -1,14 +1,14 @@-module Hscaffold.Interpreter.Common- where+module Hscaffold.Interpreter.Common where import System.FilePath -- | Very simple helper, if the second argument is absolute, returns it, -- otherwise, return it relative to the first argument-mkActionPath- :: FilePath- -- ^ The root- -> FilePath- -- ^ A filepath- -> FilePath+mkActionPath :: FilePath+ ->+ -- ^ The root+ FilePath+ ->+ -- ^ A filepath+ FilePath mkActionPath root fp = if isAbsolute fp then fp else root </> fp
src/Hscaffold/Interpreter/Haskell.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-}-module Hscaffold.Interpreter.Haskell- where +module Hscaffold.Interpreter.Haskell where+ import Control.Monad.Writer import Data.Text (Text) import qualified Data.Text as Text@@ -15,24 +15,26 @@ concatMap hscaffoldActionToHaskell hs hscaffoldActionToHaskell :: ScaffoldActionType e -> [Text]-hscaffoldActionToHaskell h = case h of- (File fp txt) ->- [ "file " <> Text.pack (show fp <> " " <> show txt) ]- (Directory fp nested) ->- let nestedCode = concatMap hscaffoldActionToHaskell nested- inestedCode = map (" " <>) nestedCode- in [ "directory " <> Text.pack (show fp) <> " $ do"- ] <> inestedCode- (Link fp1 fp2) ->- [ "link " <> Text.pack (show fp1) <> " "- <> Text.pack (show fp2)- ]- (SetPermissions p fp) ->- [ "permissions " <> Text.pack (show fp)- <> " (" <> Text.pack (show p) <> ")"- ]- (Copy fp1 fp2) ->- [ "copy " <> Text.pack (show fp1)- <> " " <> Text.pack (show fp2)- ]- _ -> []+hscaffoldActionToHaskell h =+ case h of+ (File fp txt) -> [ "file " <> Text.pack (show fp <> " " <> show txt) ]+ (Directory fp nested) ->+ let nestedCode = concatMap hscaffoldActionToHaskell nested+ inestedCode = map (" " <>) nestedCode+ in+ [ "directory " <> Text.pack (show fp) <> " $ do" ] <>+ inestedCode+ (Link fp1 fp2) -> [ "link " <> Text.pack (show fp1) <> " "+ <> Text.pack (show fp2)+ ]+ (SetPermissions p fp) ->+ [ "permissions " <> Text.pack (show fp)+ <> " (" <>+ Text.pack (show p) <>+ ")"+ ]+ (Copy fp1 fp2) -> [ "copy " <> Text.pack (show fp1)+ <> " " <>+ Text.pack (show fp2)+ ]+ _ -> []
src/Hscaffold/Interpreter/Hsfiles.hs view
@@ -1,33 +1,35 @@ {-# LANGUAGE OverloadedStrings #-}-module Hscaffold.Interpreter.Hsfiles- where +module Hscaffold.Interpreter.Hsfiles where+ import Control.Monad.Writer import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Text.IO as Text -import Hscaffold.Types- import Hscaffold.Interpreter.Common+import Hscaffold.Types -- | Run the scaffolding writer and return an @.hsfiles@ 'Text' to use with -- @stack-templates@ toHsfiles :: Writer ScaffoldActionV a -> Text-toHsfiles w =- let (_, ws) = runWriter w- in Text.stripEnd $ Text.unlines $- concatMap (actionToHsfile ".") ws+toHsfiles w = let (_, ws) = runWriter w+ in+ Text.stripEnd $+ Text.unlines $+ concatMap (actionToHsfile ".") ws -- | Convert a single scaffolding action to hsfiles -- -- Ignores everything but the 'file' directives actionToHsfile :: FilePath -> ScaffoldActionType e -> [Text] actionToHsfile root a = case a of- (File fp txt) ->- [ "{-# START_FILE " <> Text.pack (mkActionPath root fp) <> " #-}" ] <>+ (File fp txt) -> [ "{-# START_FILE " <> Text.pack (mkActionPath root fp) <>+ " #-}"+ ] <> Text.lines txt- (Directory fp nested) -> concatMap (actionToHsfile (mkActionPath root fp)) nested+ (Directory fp nested) ->+ concatMap (actionToHsfile (mkActionPath root fp)) nested _ -> [] -- | Shortcut for
src/Hscaffold/Interpreter/IO.hs view
@@ -1,16 +1,18 @@-module Hscaffold.Interpreter.IO- where+{-# LANGUAGE FlexibleContexts #-} +module Hscaffold.Interpreter.IO where++import Control.Monad.Catch import Control.Monad.IO.Class import Control.Monad.Writer import qualified Data.Text.IO as Text import System.Directory+import System.IO.Temp -- TODO - Disable this on Windows import System.Posix.Files -import Hscaffold.Types- import Hscaffold.Interpreter.Common+import Hscaffold.Types -- | Run the scaffolding writer on the IO monad with no extensions --@@ -35,7 +37,8 @@ -- | Run a single scaffolding action on the IO monad with no extensions runAction :: FilePath -> ScaffoldActionType e -> IO ()-runAction _ (ScaffoldActionTypeExtension _) = return ()+runAction _ (ScaffoldActionTypeExtension _) =+ return () runAction root (SetPermissions perms fp) = setPermissions (mkActionPath root fp) perms runAction root (Link fp1 fp2) =@@ -50,3 +53,46 @@ fp2' = mkActionPath root fp2 copyFile fp1' fp2' +-- | Creates a temporary directory with the scaffold and runs an action that+-- takes it as its argument.+--+-- Uses 'withSystemTempDirectory' under the hood.+--+-- @+-- withTemporaryHscaffold+-- (do+-- file "something" "something"+-- directory "something" $ file "something-else" "something"+-- )+-- (\tmp -> do+-- undefined+-- )+-- @+withTemporaryHscaffold :: (MonadMask m, MonadIO m)+ => ScaffoldMonadT m a+ -> (FilePath -> m b)+ -> m b+withTemporaryHscaffold =+ withTemporaryHscaffold' "hscaffold"++withTemporaryHscaffold' :: (MonadMask m, MonadIO m)+ => String+ -> ScaffoldMonadT m a+ -> (FilePath -> m b)+ -> m b+withTemporaryHscaffold' name scaffold action =+ withSystemTempDirectory name $+ \tmp -> do+ _ <- runHscaffold tmp scaffold+ action tmp++withTemporaryHscaffoldIO :: ScaffoldMonadIO a -> (FilePath -> IO b) -> IO b+withTemporaryHscaffoldIO =+ withTemporaryHscaffold++withTemporaryHscaffoldIO' :: String+ -> ScaffoldMonadIO a+ -> (FilePath -> IO b)+ -> IO b+withTemporaryHscaffoldIO' =+ withTemporaryHscaffold'
src/Hscaffold/Types.hs view
@@ -1,5 +1,4 @@-module Hscaffold.Types- where+module Hscaffold.Types where import Control.Monad.Writer import Data.Text (Text)@@ -22,11 +21,11 @@ -- | Type of actions scaffolding can make, 'ScaffoldActionTypeExtension' is open -- for extension through other data-types-data ScaffoldActionType e- = File FilePath Text+data ScaffoldActionType e =+ File FilePath Text | Link FilePath FilePath | Directory FilePath (ScaffoldAction e) | SetPermissions Permissions FilePath | Copy FilePath FilePath | ScaffoldActionTypeExtension e- deriving(Show, Eq, Ord)+ deriving (Show, Eq, Ord)
test/HscaffoldSpec.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-}-module HscaffoldSpec- where +module HscaffoldSpec where+ import Control.Exception import Control.Monad import Data.Maybe@@ -61,78 +61,86 @@ file "other/stuff-bang.hs" "stuff here" directory "other" $ do file "other-stuff-bang.hs" "even more stuff here"- in h `shouldBe` Text.stripEnd- (Text.unlines [ "{-# START_FILE ./stuff-bang.hs #-}"- , "stuff here"- , "{-# START_FILE ./other/stuff-bang.hs #-}"- , "stuff here"- , "{-# START_FILE ./other/other-stuff-bang.hs #-}"- , "even more stuff here"- ])+ in+ h `shouldBe`+ Text.stripEnd (Text.unlines [ "{-# START_FILE ./stuff-bang.hs #-}"+ , "stuff here"+ , "{-# START_FILE ./other/stuff-bang.hs #-}"+ , "stuff here"+ , "{-# START_FILE ./other/other-stuff-bang.hs #-}"+ , "even more stuff here"+ ]) describe "fromHsfiles" $ do it "works without surprises" $- let h = (init- (unlines- [ "{-# START_FILE ./stuff-bang.hs #-}"- , "stuff here"- , "{-# START_FILE ./other/stuff-bang.hs #-}"- , "stuff here"- , "{-# START_FILE ./other/other-stuff-bang.hs #-}"- , "even more stuff here"- ]))- in (fromHsfiles h :: ScaffoldActionV) `shouldBe`- [ File "stuff-bang.hs" "stuff here"- , File "other/stuff-bang.hs" "stuff here"- , File "other/other-stuff-bang.hs" "even more stuff here"- ]+ let h = (init (unlines [ "{-# START_FILE ./stuff-bang.hs #-}"+ , "stuff here"+ , "{-# START_FILE ./other/stuff-bang.hs #-}"+ , "stuff here"+ , "{-# START_FILE ./other/other-stuff-bang.hs #-}"+ , "even more stuff here"+ ]))+ in+ (fromHsfiles h :: ScaffoldActionV) `shouldBe`+ [ File "stuff-bang.hs" "stuff here"+ , File "other/stuff-bang.hs" "stuff here"+ , File "other/other-stuff-bang.hs" "even more stuff here"+ ] it "works without surprises" $- let h = (init- (unlines- [ "{-# START_FILE ./stuff-bang.hs #-}"- , "stuff here"- , "multiline"- , " ya"- , "{-# START_FILE ./other/stuff-bang.hs #-}"- , "stuff here"- , ""- , "{-# START_FILE ./other/other-stuff-bang.hs #-} "- , "even more stuff here"- , " here too"- ]))- in (fromHsfiles h :: ScaffoldActionV) `shouldBe`- [ File "stuff-bang.hs" "stuff here\nmultiline\n ya"- , File "other/stuff-bang.hs" "stuff here"- , File "other/other-stuff-bang.hs" "even more stuff here\n here too"- ]+ let h = (init (unlines [ "{-# START_FILE ./stuff-bang.hs #-}"+ , "stuff here"+ , "multiline"+ , " ya"+ , "{-# START_FILE ./other/stuff-bang.hs #-}"+ , "stuff here"+ , ""+ , "{-# START_FILE ./other/other-stuff-bang.hs #-} "+ , "even more stuff here"+ , " here too"+ ]))+ in+ (fromHsfiles h :: ScaffoldActionV) `shouldBe`+ [ File "stuff-bang.hs" "stuff here\nmultiline\n ya"+ , File "other/stuff-bang.hs" "stuff here"+ , File "other/other-stuff-bang.hs"+ "even more stuff here\n here too"+ ] describe "hscaffoldToHaskell" $ do it "works without surprises" $- let h = hscaffoldToHaskell $ execWriter $ do- file "stuff-bang.hs" "stuff here"- file "other/stuff-bang.hs" "stuff here"- directory "other" $ do- file "other-stuff-bang.hs" "even more stuff here"- in h `shouldBe` Text.stripEnd- (Text.unlines [ "file \"stuff-bang.hs\" \"stuff here\""- , "file \"other/stuff-bang.hs\" \"stuff here\""- , "directory \"other\" $ do"- , " file \"other-stuff-bang.hs\" \"even more stuff here\""- ])+ let h = hscaffoldToHaskell $+ execWriter $ do+ file "stuff-bang.hs" "stuff here"+ file "other/stuff-bang.hs" "stuff here"+ directory "other" $ do+ file "other-stuff-bang.hs" "even more stuff here"+ in+ h `shouldBe`+ Text.stripEnd (Text.unlines [ "file \"stuff-bang.hs\" \"stuff here\""+ , "file \"other/stuff-bang.hs\" \"stuff here\""+ , "directory \"other\" $ do"+ , " file \"other-stuff-bang.hs\" \"even more stuff here\""+ ]) describe "hscaffoldFromDirectory" $ do it "works without surprises" $ do h <- hscaffoldFromDirectory "./src" :: IO (ScaffoldActionV)- let ms = mapMaybe (\x -> case x of File fp _ -> Just fp; _ -> Nothing) h- ms `shouldContain` ["Hscaffold.hs"]+ let ms = mapMaybe (\x -> case x of+ File fp _ -> Just fp+ _ -> Nothing)+ h+ ms `shouldContain` [ "Hscaffold.hs" ] it "ignores git" $ do h <- hscaffoldFromDirectory "." :: IO (ScaffoldActionV)- let ms = mapMaybe (\x -> case x of File fp _ -> Just fp; _ -> Nothing) h- ms `shouldNotContain` [".git/index"]- ms `shouldContain` ["README.md"]- ms `shouldContain` ["test/Spec.hs"]+ let ms = mapMaybe (\x -> case x of+ File fp _ -> Just fp+ _ -> Nothing)+ h+ ms `shouldNotContain` [ ".git/index" ]+ ms `shouldContain` [ "README.md" ]+ ms `shouldContain` [ "test/Spec.hs" ] describe "the runner" $ describe "runAction" $ do
test/SanitySpec.hs view
@@ -1,6 +1,6 @@ module SanitySpec where -import Test.Hspec+import Test.Hspec spec = describe "when I have tests" $ it "I have sanity" $ True `shouldBe` True
test/Spec.hs view