packages feed

shikensu 0.3.3 → 0.3.4

raw patch · 16 files changed

+370/−478 lines, 16 filesdep −shikensudep ~aesondep ~basedep ~bytestring

Dependencies removed: shikensu

Dependency ranges changed: aeson, base, bytestring, directory, filepath, flow, text, unordered-containers

Files

shikensu.cabal view
@@ -1,98 +1,55 @@-name: shikensu-version: 0.3.3-category: File IO--synopsis:-  A small toolset for building static websites--description:-  Please see README.md--homepage:-  https://github.com/icidasset/shikensu#README--license: MIT-license-file: LICENSE--author: Steven Vandevelde-maintainer: icid.asset@gmail.com--build-type: Simple-cabal-version: >= 1.10+-- This file has been generated from package.yaml by hpack version 0.17.0.+--+-- see: https://github.com/sol/hpack +name:           shikensu+version:        0.3.4+synopsis:       Run a sequence of functions on in-memory representations of files+description:    See README at <https://github.com/icidasset/shikensu#readme>+category:       Filesystem+homepage:       https://github.com/icidasset/shikensu#readme+bug-reports:    https://github.com/icidasset/shikensu/issues+maintainer:     Steven Vandevelde <icid.asset@gmail.com>+license:        MIT+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10  source-repository head   type: git   location: https://github.com/icidasset/shikensu --library-  default-language:-    Haskell2010--  default-extensions:-    OverloadedStrings-+test-suite spec+  type: exitcode-stdio-1.0+  main-is: Test.hs   hs-source-dirs:-    src--  exposed-modules:-    Shikensu,-    Shikensu.Contrib,-    Shikensu.Contrib.IO,-    Shikensu.Internal.Utilities,-    Shikensu.Metadata,-    Shikensu.Sorting,-    Shikensu.Types,-    Shikensu.Utilities-+      tests+      src+  default-extensions: DisambiguateRecordFields DuplicateRecordFields OverloadedStrings   build-depends:-    aeson == 1.0.*,-    base >= 4.8 && < 5,-    bytestring == 0.10.*,-    directory == 1.3.*,-    filepath == 1.4.*,-    flow == 1.0.*,-    Glob == 0.7.*,-    unordered-containers == 0.2.*,-    text >= 1.2 && < 2---test-suite tests-  default-language:-    Haskell2010--  default-extensions:-    OverloadedStrings--  ghc-options:-    -Wall -threaded -rtsopts -with-rtsopts=-N--  type:-    exitcode-stdio-1.0--  hs-source-dirs:-    tests--  main-is:-    Test.hs-+      aeson == 1.0.*+    , base >= 4.9 && < 5+    , bytestring == 0.10.*+    , directory == 1.3.*+    , filepath == 1.4.*+    , flow == 1.0.*+    , Glob == 0.7.*+    , unordered-containers == 0.2.*+    , text == 1.2.*+    , tasty == 0.11.*+    , tasty-hunit == 0.9.*   other-modules:-    Test.Contrib,-    Test.Example,-    Test.Helpers,-    Test.Shikensu,-    Test.Utilities--  build-depends:-    aeson,-    base,-    bytestring,-    directory,-    filepath,-    flow,-    shikensu,-    tasty == 0.11.*,-    tasty-hunit == 0.9.*,-    text,-    unordered-containers+      Test.Contrib+      Test.Example+      Test.Helpers+      Test.Shikensu+      Test.Utilities+      Shikensu+      Shikensu.Contrib+      Shikensu.Contrib.IO+      Shikensu.Internal.Types+      Shikensu.Internal.Utilities+      Shikensu.Metadata+      Shikensu.Sorting+      Shikensu.Utilities+  default-language: Haskell2010
src/Shikensu.hs view
@@ -4,25 +4,26 @@  -} module Shikensu-    ( list+    ( forkDefinition+    , list     , listF     , listRelative     , listRelativeF--    , forkDefinition     , makeDefinition     , makeDictionary+    , module Shikensu.Internal.Types     ) where +import Data.Monoid ((<>)) import Flow+import Shikensu.Internal.Types import Shikensu.Internal.Utilities-import Shikensu.Types import System.FilePath  import qualified Data.HashMap.Strict as HashMap (empty)-import qualified Data.List as List (concat, map, zip)+import qualified Data.List as List (concatMap, map, zip) import qualified System.Directory as Dir (canonicalizePath)-+import qualified System.FilePath.Glob as Glob (compile, globDir1)   -- IO@@ -37,40 +38,42 @@ 4. We make a Dictionary out of each tuple (this also needs the path). 5. Merge the dictionaries into one dictionary. +> list ["*.md"] "/root/articles"+ -}-list :: [Pattern] -> FilePath -> IO Dictionary+list :: [String] -> FilePath -> IO Dictionary list patterns rootDir =     patterns-        |> compilePatterns-        |> globDir rootDir+        |> List.map (Glob.compile)+        |> List.map (flip Glob.globDir1 $ rootDir)+        |> sequence         |> fmap (List.zip patterns)-        |> fmap (List.map (uncurry . makeDictionary $ rootDir))-        |> fmap (List.concat)+        |> fmap (List.concatMap $ makeDictionary rootDir)   {-| Flipped version of `list`. -}-listF :: FilePath -> [Pattern] -> IO Dictionary+listF :: FilePath -> [String] -> IO Dictionary listF = flip list   {-| Same as `list`, but given a relative directory.  > listRelative ["*.md"] "./articles"+ -}-listRelative :: [Pattern] -> FilePath -> IO Dictionary+listRelative :: [String] -> FilePath -> IO Dictionary listRelative patterns relativePath =     Dir.canonicalizePath relativePath >>= list patterns   {-| Flipped version `listRelative`. -}-listRelativeF :: FilePath -> [Pattern] -> IO Dictionary+listRelativeF :: FilePath -> [String] -> IO Dictionary listRelativeF = flip listRelative   - -- PURE  @@ -101,7 +104,7 @@  - the root path `/Users/icidasset/Projects/shikensu` - the pattern `example/**/*.md`-- the workspace path `example/test/hello.md`+- the absolute path `/Users/icidasset/Projects/shikensu/example/test/hello.md`  > Definition >     { basename = "hello"@@ -118,31 +121,34 @@ >     }  -}-makeDefinition :: FilePath -> Pattern -> FilePath -> Definition-makeDefinition _rootDirname _pattern _workspacePath =+makeDefinition :: FilePath -> String -> FilePath -> Definition+makeDefinition rootDirname pattern absolutePath =     let-        workingDir  = cleanPath . (commonDirectory) $ _pattern-        localPath   = cleanPath . (stripPrefix workingDir) . cleanPath $ _workspacePath+        workingDirname      = commonDirectory pattern+        rootWorkingDirname  = combine rootDirname workingDirname++        theAbsolutePath     = normalise absolutePath+        theLocalPath        = dropDrive (stripPrefix rootWorkingDirname theAbsolutePath)     in         Definition-            { basename        = takeBaseName localPath-            , dirname         = takeDirName localPath-            , extname         = takeExtension localPath-            , pattern         = _pattern-            , rootDirname     = dropTrailingPathSeparator _rootDirname-            , workingDirname  = workingDir+            { basename        = takeBaseName theLocalPath+            , dirname         = takeDirName theLocalPath+            , extname         = takeExtension theLocalPath+            , pattern         = pattern+            , rootDirname     = rootDirname+            , workingDirname  = workingDirname              -- Additional properties             , content         = Nothing             , metadata        = HashMap.empty-            , parentPath      = compileParentPath $ takeDirName localPath-            , pathToRoot      = compilePathToRoot $ takeDirName localPath+            , parentPath      = compileParentPath $ takeDirName theLocalPath+            , pathToRoot      = compilePathToRoot $ takeDirName theLocalPath             }    {-| Make a Dictionary. -}-makeDictionary :: FilePath -> Pattern -> [FilePath] -> Dictionary-makeDictionary _rootDirname _pattern =-    List.map (makeDefinition _rootDirname _pattern)+makeDictionary :: FilePath -> (String, [FilePath]) -> Dictionary+makeDictionary rootDirname (pattern, filepaths) =+    List.map (makeDefinition rootDirname pattern) filepaths
src/Shikensu/Contrib.hs view
@@ -25,11 +25,11 @@     ) where  import Data.ByteString (ByteString)-import Flow+import Data.Monoid ((<>)) import Shikensu (forkDefinition)-import Shikensu.Internal.Utilities (cleanPath, compileParentPath, compilePathToRoot)+import Shikensu.Internal.Types+import Shikensu.Internal.Utilities (compileParentPath, compilePathToRoot) import Shikensu.Metadata (transposeToMetadata)-import Shikensu.Types import System.FilePath (FilePath, combine)  import qualified Data.HashMap.Strict as HashMap (empty, union)@@ -41,7 +41,7 @@ -} clearMetadata :: Dictionary -> Dictionary clearMetadata =-    fmap (clearMetadataDef)+    fmap clearMetadataDef   clearMetadataDef :: Definition -> Definition@@ -62,11 +62,11 @@ clone existingPath newPath dict =     let         makeNew = \def acc ->-            if (localPath def) == existingPath-                then acc ++ [forkDefinition newPath def]-                else acc+            if localPath def == existingPath+               then acc <> [forkDefinition newPath def]+               else acc     in-        dict ++ (foldr makeNew [] dict)+        dict <> foldr makeNew [] dict   @@ -77,7 +77,7 @@ -} copyPropsToMetadata :: Dictionary -> Dictionary copyPropsToMetadata =-    fmap (copyPropsToMetadataDef)+    fmap copyPropsToMetadataDef   copyPropsToMetadataDef :: Definition -> Definition@@ -93,7 +93,7 @@ -} exclude :: FilePath -> Dictionary -> Dictionary exclude path =-    filter (\def -> (localPath def) /= path)+    filter (\def -> localPath def /= path)   @@ -127,16 +127,16 @@  permalinkDef :: String -> Definition -> Definition permalinkDef newBasename def =-    if (basename def) /= newBasename then-        let-            newDirname = cleanPath $ combine (dirname def) (basename def)-        in-            def-                { basename    = newBasename-                , dirname     = newDirname-                , parentPath  = compileParentPath $ newDirname-                , pathToRoot  = compilePathToRoot $ newDirname-                }+    if basename def /= newBasename then+       let+           newDirname = combine (dirname def) (basename def)+       in+           def+               { basename    = newBasename+               , dirname     = newDirname+               , parentPath  = compileParentPath newDirname+               , pathToRoot  = compilePathToRoot newDirname+               }     else         def @@ -153,12 +153,12 @@ prefixDirnameDef :: String -> Definition -> Definition prefixDirnameDef prefix def =     let-        newDirname = prefix ++ (dirname def)+        newDirname = prefix <> dirname def     in         def             { dirname     = newDirname-            , parentPath  = compileParentPath $ newDirname-            , pathToRoot  = compilePathToRoot $ newDirname+            , parentPath  = compileParentPath newDirname+            , pathToRoot  = compilePathToRoot newDirname             }  @@ -178,9 +178,9 @@  renameDef :: FilePath -> FilePath -> Definition -> Definition renameDef oldPath newPath def =-  if (localPath def) == oldPath-    then forkDefinition newPath def-    else def+  if localPath def == oldPath+     then forkDefinition newPath def+     else def   @@ -199,7 +199,7 @@  renameExtDef :: String -> String -> Definition -> Definition renameExtDef oldExtname newExtname def =-    if (extname def) == oldExtname+    if extname def == oldExtname         then def { extname = newExtname }         else def 
src/Shikensu/Contrib/IO.hs view
@@ -9,8 +9,7 @@     ) where  import Data.Maybe (fromMaybe)-import Flow-import Shikensu.Types+import Shikensu.Internal.Types import System.Directory (createDirectoryIfMissing, doesFileExist) import System.FilePath (FilePath, joinPath, takeDirectory) @@ -25,7 +24,7 @@ -} read :: Dictionary -> IO Dictionary read =-    Utilities.mapIO (readDef)+    Utilities.mapIO readDef   readDef :: Definition -> IO Definition
+ src/Shikensu/Internal/Types.hs view
@@ -0,0 +1,71 @@+{-| Types and path helpers.+    This is re-exported in the main `Shikensu` module.++-}+module Shikensu.Internal.Types where++import Data.Aeson ((.=), toJSON)+import Data.ByteString (ByteString)+import Data.Monoid ((<>))+import System.FilePath (joinPath)++import qualified Data.Aeson as Aeson (Object, ToJSON, object)+++{-| A file definition, along with some additional properties.+-}+data Definition =+    Definition+        { basename :: String+        , dirname :: FilePath+        , extname :: String+        , pattern :: String+        , rootDirname :: FilePath+        , workingDirname :: FilePath++        -- Additional properties+        , content :: Maybe ByteString+        , metadata :: Metadata+        , parentPath :: Maybe FilePath+        , pathToRoot :: FilePath+        } deriving (Eq, Show)+++instance Aeson.ToJSON Definition where+    toJSON def =+        Aeson.object+            [ "basename"        .= basename def+            , "dirname"         .= dirname def+            , "extname"         .= extname def+            , "pattern"         .= pattern def+            , "workingDirname"  .= workingDirname def+            , "parentPath"      .= parentPath def+            , "pathToRoot"      .= pathToRoot def+            ]++++-- Type aliases+++type Dictionary = [Definition]+type Metadata = Aeson.Object++++-- Path functions+++absolutePath :: Definition -> String+absolutePath def =+    joinPath [rootDirname def, workspacePath def]+++localPath :: Definition -> String+localPath def =+    joinPath [dirname def, basename def <> extname def]+++workspacePath :: Definition -> String+workspacePath def =+    joinPath [workingDirname def, localPath def]
src/Shikensu/Internal/Utilities.hs view
@@ -2,12 +2,9 @@  -} module Shikensu.Internal.Utilities-    ( cleanPath-    , commonDirectory-    , compilePatterns+    ( commonDirectory     , compileParentPath     , compilePathToRoot-    , globDir     , replaceSingleDot     , stripPrefix     , takeDirName@@ -15,37 +12,23 @@  import Data.Maybe (fromMaybe) import Flow-import Shikensu.Types (Pattern) import System.FilePath  import qualified Data.List as List (map, stripPrefix)+import qualified Data.Tuple as Tuple (fst) import qualified System.FilePath.Glob as Glob  -{-| "Clean up" a path.--> `/directory/./nested/` -> `directory/nested`-> `./` -> ``-> `.` -> ``---}-cleanPath :: FilePath -> FilePath-cleanPath =-    normalise .> dropTrailingPathSeparator .> dropDrive .> replaceSingleDot---{-| Get the common directory from a Pattern.--}-commonDirectory :: Pattern -> FilePath-commonDirectory pattern =-    (Glob.compile .> Glob.commonDirectory .> fst) pattern---{-| Compile a list of `Pattern`s to `Glob.Pattern`s.+{-| Get the common directory from a pattern. -}-compilePatterns :: [Pattern] -> [Glob.Pattern]-compilePatterns =-    List.map Glob.compile+commonDirectory :: String -> FilePath+commonDirectory =+    Glob.compile+    .> Glob.commonDirectory+    .> Tuple.fst+    .> normalise+    .> dropTrailingPathSeparator+    .> replaceSingleDot   {-| Path to parent, when there is one.@@ -76,21 +59,11 @@     else         dirname             |> splitDirectories-            |> fmap (\_ -> "..")+            |> fmap (const "..")             |> joinPath             |> addTrailingPathSeparator  -{-| List contents of a directory using a glob pattern.-Returns a relative path, based on the given rootDirname.--}-globDir :: FilePath -> [Glob.Pattern] -> IO [[FilePath]]-globDir rootDir patterns =-    Glob.globDir patterns rootDir-        |> fmap (fst)-        |> fmap (List.map . List.map . makeRelative $ rootDir)-- {-| If the path is a single dot, return an empty string. Otherwise return the path. -}@@ -110,4 +83,4 @@ -} takeDirName :: FilePath -> FilePath takeDirName =-    replaceSingleDot . takeDirectory+    takeDirectory .> replaceSingleDot
src/Shikensu/Metadata.hs view
@@ -7,11 +7,10 @@     ) where  import Flow-import Shikensu.Types+import Shikensu.Internal.Types  import qualified Data.Aeson as Aeson (FromJSON, Result(..), ToJSON, fromJSON, toJSON) import qualified Data.HashMap.Strict as HashMap (empty)-   {-| Transpose metadata.
src/Shikensu/Sorting.hs view
@@ -5,7 +5,7 @@     ( sortByAbsolutePath     ) where -import Shikensu.Types (Definition, absolutePath)+import Shikensu.Internal.Types (Definition, absolutePath)   {-| Sort by absolutePath.
− src/Shikensu/Types.hs
@@ -1,70 +0,0 @@-{-| Types and path helpers.---}-module Shikensu.Types where--import Data.Aeson (ToJSON, Object, (.=), object, toJSON)-import Data.ByteString (ByteString)-import System.FilePath (joinPath)---{-| A file definition, along with some additional properties.--}-data Definition =-    Definition-        { basename :: String-        , dirname :: FilePath-        , extname :: String-        , pattern :: Pattern-        , rootDirname :: FilePath-        , workingDirname :: FilePath--        -- Additional properties-        , content :: Maybe ByteString-        , metadata :: Metadata-        , parentPath :: Maybe FilePath-        , pathToRoot :: FilePath-        } deriving (Eq, Show)---instance ToJSON Definition where-    toJSON def =-        object-            [ "basename"        .= basename def-            , "dirname"         .= dirname def-            , "extname"         .= extname def-            , "pattern"         .= pattern def-            , "workingDirname"  .= workingDirname def-            , "parentPath"      .= parentPath def-            , "pathToRoot"      .= pathToRoot def-            ]------- Type aliases---type Dictionary = [Definition]-type Metadata = Object-type Pattern = String------- Path functions---absolutePath :: Definition -> String-absolutePath def =-    joinPath [rootDirname def, workspacePath def]---localPath :: Definition -> String-localPath def =-    joinPath [dirname def, (basename def) ++ (extname def)]---workspacePath :: Definition -> String-workspacePath def =-    joinPath [workingDirname def, localPath def]
src/Shikensu/Utilities.hs view
@@ -1,24 +1,25 @@ module Shikensu.Utilities-    ( io-    , mapIO-    , lsequence-+    ( (!~>)     , (~>)-    , (!~>)+    , io+    , lsequence+    , mapIO     ) where  import Data.Aeson (FromJSON, ToJSON, fromJSON)+import Data.Maybe (fromMaybe)+import Data.Monoid ((<>)) import Data.Text (Text) import Flow-import Shikensu.Types+import Shikensu.Internal.Types  import qualified Data.Aeson as Json (Object, Result(..), encode) import qualified Data.HashMap.Strict as HashMap (lookup) import qualified Data.List as List (unzip, zip)-import qualified Data.Tuple as Tuple (fst, snd)-import qualified Data.Text as Text (concat, unpack)+import qualified Data.Text as Text (unpack) import qualified Data.Text.Lazy as Lazy.Text (unpack) import qualified Data.Text.Lazy.Encoding as Lazy.Text (decodeUtf8)+import qualified Data.Tuple as Tuple (fst, snd)   -- IO@@ -45,7 +46,7 @@  From multiple IO monads to a single IO monad. -}-lsequence :: Monad m => [( String, m a )] -> m [( String, a )]+lsequence :: Monad m => [( id, m a )] -> m [( id, a )] lsequence list =     let         unzippedList =@@ -84,8 +85,8 @@     case (obj ~> key) of         Just x  -> x         Nothing -> error <|-            "Could not find the key `" ++ (Text.unpack key)         ++ "` " ++-            "on the metadata object `" ++ (aesonObjectToString obj) ++ "` using (!~>)"+            "Could not find the key `" <> Text.unpack key         <> "` " <>+            "on the metadata object `" <> aesonObjectToString obj <> "` using (!~>)"   
tests/Test.hs view
@@ -1,23 +1,28 @@-import Control.Exception (catch)-import System.Directory (removeDirectoryRecursive)+module Main where++import System.Directory (canonicalizePath, removePathForcibly) import System.FilePath (combine) import Test.Contrib import Test.Example-import Test.Helpers (ioErrorHandler, rootPath) import Test.Shikensu import Test.Tasty import Test.Utilities   main :: IO ()-main =-    rootPath-        >>= \r -> catch-            (removeDirectoryRecursive (combine r "tests/build"))-            (ioErrorHandler)-        >> defaultMain tests+main = do+    root        <- canonicalizePath "./"+    _           <- removePathForcibly (combine root "tests/build") +    defaultMain tests + tests :: TestTree tests =-    testGroup "Tests" [shikensuTests, contribTests, exampleTests, utilityTests]+    testGroup+        "Tests"+        [ shikensuTests+        , contribTests+        , exampleTests+        , utilityTests+        ]
tests/Test/Contrib.hs view
@@ -1,4 +1,6 @@-module Test.Contrib (contribTests) where+module Test.Contrib+    ( contribTests+    ) where  import Data.ByteString (ByteString) import Flow@@ -13,12 +15,11 @@ import qualified Data.HashMap.Strict as HashMap (fromList, lookup) import qualified Data.List as List (head, reverse) import qualified Data.Text as Text (pack, unpack)-import qualified Data.Text.IO as Text (readFile) import qualified Data.Text.Encoding as Text (decodeUtf8)+import qualified Data.Text.IO as Text (readFile) import qualified Shikensu import qualified Shikensu.Contrib as Contrib import qualified Shikensu.Contrib.IO as Contrib.IO-import qualified Shikensu.Types as Shikensu   contribTests :: TestTree@@ -38,13 +39,12 @@   - -- Test data  -list :: Shikensu.Pattern -> IO Shikensu.Dictionary+list :: String -> IO Shikensu.Dictionary list pattern =-    rootPath >>= Shikensu.list [pattern]+    Shikensu.listRelative [pattern] "."   example_md :: IO Shikensu.Dictionary@@ -64,7 +64,6 @@   - -- Tests  @@ -72,10 +71,10 @@ testClone =     let         dictionary = fmap (Contrib.clone "example.md" "cloned.md") example_md-        definition = fmap (List.head . List.reverse) dictionary+        definition = fmap (List.reverse .> List.head) dictionary     in         testCase "Should `clone`"-        $ definition `rmap` Shikensu.localPath >>= assertEq "cloned.md"+        $ assertDef definition Shikensu.localPath "cloned.md"   @@ -83,10 +82,10 @@ testExclude =     let         dictionary = fmap (Contrib.exclude "example.md") example_md-        length_ = fmap length dictionary+        dictLength = fmap length dictionary     in         testCase "Should `exclude`"-        $ length_ >>= assertEq 0+        $ assertEqm dictLength 0   @@ -108,33 +107,35 @@         -- 1. Insert C         -- 2. Replace with A         -- 3. Insert B-        dictionary = example_md-            <&> ( Contrib.insertMetadata (HashMap.fromList [ (keyC, valueC) ])-               .> Contrib.replaceMetadata (HashMap.fromList [ (keyA, valueA) ])-               .> Contrib.copyPropsToMetadata-               .> Contrib.insertMetadata (HashMap.fromList [ (keyB, valueB) ])+        dictionary = fmap+            ( id+                .> Contrib.insertMetadata   (HashMap.fromList [ (keyC, valueC) ])+                .> Contrib.replaceMetadata  (HashMap.fromList [ (keyA, valueA) ])+                .> Contrib.copyPropsToMetadata+                .> Contrib.insertMetadata   (HashMap.fromList [ (keyB, valueB) ])             )+            example_md -        definition = fmap (List.head . List.reverse) dictionary+        definition      = fmap (List.reverse .> List.head) dictionary -        lookupTitle = \def -> HashMap.lookup keyA (Shikensu.metadata def)-        lookupHello = \def -> HashMap.lookup keyB (Shikensu.metadata def)-        lookupRemoved = \def -> HashMap.lookup keyC (Shikensu.metadata def)-        lookupBasename = \def -> HashMap.lookup keyBase (Shikensu.metadata def)+        lookupTitle     = \def -> HashMap.lookup keyA (Shikensu.metadata def)+        lookupHello     = \def -> HashMap.lookup keyB (Shikensu.metadata def)+        lookupRemoved   = \def -> HashMap.lookup keyC (Shikensu.metadata def)+        lookupBasename  = \def -> HashMap.lookup keyBase (Shikensu.metadata def)     in         testGroup             "Metadata"             [ testCase "Should no longer have `removed` key"-            $ definition `rmap` lookupRemoved >>= assertEq Nothing+            $ assertDef definition lookupRemoved Nothing              , testCase "Should have `hello` key"-            $ definition `rmap` lookupHello >>= assertEq (Just valueB)+            $ assertDef definition lookupHello (Just valueB)              , testCase "Should have `title` key"-            $ definition `rmap` lookupTitle >>= assertEq (Just valueA)+            $ assertDef definition lookupTitle (Just valueA)              , testCase "Should have `basename` key"-            $ definition `rmap` lookupBasename >>= assertEq (Just valueBase)+            $ assertDef definition lookupBasename (Just valueBase)             ]  @@ -143,18 +144,18 @@ testPermalink =     let         dictionary = fmap (Contrib.permalink "index") example_md-        definition = fmap (List.head) dictionary+        definition = fmap List.head dictionary     in         testGroup             "Permalink"             [ testCase "Should have the correct `localPath`"-            $ definition `rmap` Shikensu.localPath >>= assertEq "example/index.md"+            $ assertDef definition Shikensu.localPath "example/index.md"              , testCase "Should have the correct `parentPath`"-            $ definition `rmap` Shikensu.parentPath >>= assertEq (Just "../")+            $ assertDef definition Shikensu.parentPath (Just "../")              , testCase "Should have the correct `pathToRoot`"-            $ definition `rmap` Shikensu.pathToRoot >>= assertEq "../"+            $ assertDef definition Shikensu.pathToRoot "../"             ]  @@ -168,13 +169,13 @@         testGroup             "PrefixDirname"             [ testCase "Should have the correct `dirname`"-            $ definition `rmap` Shikensu.dirname >>= assertEq "prefix/fixtures"+            $ assertDef definition Shikensu.dirname "prefix/fixtures"              , testCase "Should have the correct `pathToRoot`"-            $ definition `rmap` Shikensu.pathToRoot >>= assertEq "../../"+            $ assertDef definition Shikensu.pathToRoot "../../"              , testCase "Should have the correct `parentPath`"-            $ definition `rmap` Shikensu.parentPath >>= assertEq (Just "../")+            $ assertDef definition Shikensu.parentPath (Just "../")             ]  @@ -182,14 +183,11 @@ testRead :: TestTree testRead =     let-        dictionary = example_md >>= Contrib.IO.read-        definition = fmap List.head dictionary+        theResult  = Just (Text.pack "# Example\n")+        definition = fmap List.head (example_md >>= Contrib.IO.read)     in         testCase "Should `read`"-        $ definition-            <&> Shikensu.content-            <&> fmap Text.decodeUtf8-            >>= assertEq (Just (Text.pack "# Example\n"))+        $ assertDef definition (Shikensu.content .> fmap Text.decodeUtf8) theResult   @@ -197,10 +195,10 @@ testRename =     let         dictionary = fmap (Contrib.rename "example.md" "renamed.md") example_md-        definition = fmap (List.head) dictionary+        definition = fmap List.head dictionary     in         testCase "Should `rename`"-        $ definition `rmap` Shikensu.localPath >>= assertEq "renamed.md"+        $ assertDef definition Shikensu.localPath "renamed.md"   @@ -208,25 +206,22 @@ testRenameExt =     let         dictionary = fmap (Contrib.renameExt ".md" ".html") example_md-        definition = fmap (List.head) dictionary+        definition = fmap List.head dictionary     in         testCase "Should `renameExt`"-        $ definition `rmap` Shikensu.extname >>= assertEq ".html"+        $ assertDef definition Shikensu.extname ".html"    testRenderContent :: TestTree testRenderContent =     let+        theResult  = Just (Text.pack "<html># Example\n</html>")         dictionary = fmap (Contrib.renderContent renderer) (example_md >>= Contrib.IO.read)-        definition = fmap (List.head) dictionary-        expectedResult = Just (Text.pack "<html># Example\n</html>")+        definition = fmap List.head dictionary     in         testCase "Should `renderContent`"-        $ definition-            <&> Shikensu.content-            <&> fmap Text.decodeUtf8-            >>= assertEq expectedResult+        $ assertDef definition (Shikensu.content .> fmap Text.decodeUtf8) theResult   @@ -234,11 +229,13 @@ testWrite =     let         destination = "tests/build/"-        dictionary = list "tests/**/example.md" >>= Contrib.IO.read >>= Contrib.IO.write destination+        dictionary  = list "tests/**/example.md"+            >>= Contrib.IO.read+            >>= Contrib.IO.write destination+         definition = fmap List.head dictionary     in         testCase "Should `write`"-        $ definition-            <&> Shikensu.rootDirname-            >>= \r -> Text.readFile (joinPath [r, destination, "fixtures/example.md"])-            >>= \c -> assertEq "# Example\n" (Text.unpack c)+        $ fmap Shikensu.rootDirname definition+            >>= \r -> readFile (joinPath [r, destination, "fixtures/example.md"])+            >>= \c -> assertEq "# Example\n" c
tests/Test/Example.hs view
@@ -1,33 +1,40 @@-module Test.Example (exampleTests) where+module Test.Example+    ( exampleTests+    ) where +import Data.ByteString (ByteString)+import Data.Monoid ((<>)) import Data.Text (Text)+import Flow+import Prelude hiding (read)+import Shikensu (Definition(..), Dictionary(..), list, makeDefinition)+import Shikensu.Contrib (clone, copyPropsToMetadata, permalink, renameExt, renderContent)+import Shikensu.Contrib.IO (read, write) import System.Directory (canonicalizePath) import Test.Helpers import Test.Tasty import Test.Tasty.HUnit  import qualified Data.Text.Encoding as Text (decodeUtf8, encodeUtf8)-import qualified Shikensu -import Data.ByteString (ByteString)-import Flow-import Prelude hiding (read)-import Shikensu.Types-import Shikensu.Contrib-import Shikensu.Contrib.IO (read, write) - exampleTests :: TestTree exampleTests =-    let-        path = canonicalizePath "./tests"-        dict = path >>= dictionary_io-        test = path >>= \p ->-            read [Shikensu.makeDefinition p "fixtures/*.md" "fixtures/example.md"]-            >>= flow-    in-        testCase "Example test"-        $ dict >>= \d -> test >>= assertEq d+    testCase "Example test" $ dictionaries >>= uncurry assertEq+++dictionaries :: IO (Dictionary, Dictionary)+dictionaries = do+    root            <- canonicalizePath "./tests"+    dictA           <- dictionary_io root++    let absolute    = root <> "/fixtures/example.md"+    let dictB       = [Shikensu.makeDefinition root "fixtures/*.md" absolute]++    dictB_Read      <- read dictB+    dictB_Final     <- flow dictB_Read++    return (dictA, dictB_Final)   
tests/Test/Helpers.hs view
@@ -1,30 +1,24 @@ module Test.Helpers-    ( (<&>)-    , rmap+    ( assertDef     , assertEq-    , ioErrorHandler-    , rootPath-    , sort-    , testsPath+    , assertEqm+    , define     ) where +import Flow+import Shikensu import Shikensu.Sorting (sortByAbsolutePath)-import Shikensu.Types (Dictionary)-import System.Directory (canonicalizePath)-import System.FilePath (combine) import Test.Tasty.HUnit (Assertion, assertEqual) -import qualified Data.List as List+import qualified Data.List as List (head, sortBy)  -(<&>) :: Functor f => f a -> (a -> b) -> f b-(<&>) =-    flip fmap+-- Assertions  -rmap :: Functor f => f a -> (a -> b) -> f b-rmap =-    (<&>)+assertDef :: (Eq a, Show a) => IO Definition -> (Definition -> a) -> a -> Assertion+assertDef definition recordFn value =+    fmap recordFn definition >>= assertEq value   assertEq :: (Eq a, Show a) => a -> a -> Assertion@@ -32,21 +26,18 @@     assertEqual ""  -ioErrorHandler :: IOError -> IO ()-ioErrorHandler _ =-    putStrLn ""+assertEqm :: (Eq a, Show a) => IO a -> a -> Assertion+assertEqm a b =+    a >>= assertEqual "" b  -rootPath :: IO FilePath-rootPath =-    canonicalizePath "./" --sort :: Dictionary -> Dictionary-sort =-    List.sortBy sortByAbsolutePath+-- Shortcuts  -testsPath :: IO FilePath-testsPath =-    fmap ((flip combine) "tests") rootPath+define :: String -> String -> IO Definition+define pattern root =+    root+        |> Shikensu.listRelative [pattern]+        |> fmap (List.sortBy sortByAbsolutePath)+        |> fmap List.head
tests/Test/Shikensu.hs view
@@ -1,4 +1,6 @@-module Test.Shikensu (shikensuTests) where+module Test.Shikensu+    ( shikensuTests+    ) where  import Test.Helpers import Test.Tasty@@ -6,14 +8,16 @@  import qualified Data.List as List (head) import qualified Shikensu-import qualified Shikensu.Types as Shikensu   shikensuTests :: TestTree shikensuTests = testGroup     "Shikensu tests"-    [testRegular, testDot, testWithoutWd, testRootFile, testRelative]-+    [ testRegular+    , testDot+    , testWithoutWd+    , testRootFile+    ]   @@ -23,29 +27,23 @@ testRegular :: TestTree testRegular =     let-        pattern = "tests/**/*.md"-        dictionary = fmap sort $ rootPath >>= Shikensu.list [pattern]-        definition = fmap List.head dictionary-        localPath = "fixtures/example.md"+        definition = define "tests/**/*.md" "."     in         testGroup "Test regular"             [ testCase "Should have the correct basename"-            $ definition `rmap` Shikensu.basename >>= assertEq "example"+            $ assertDef definition Shikensu.basename "example"              , testCase "Should have the correct dirname"-            $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures"+            $ assertDef definition Shikensu.dirname "fixtures"              , testCase "Should have the correct extname"-            $ definition `rmap` Shikensu.extname >>= assertEq ".md"--            , testCase "Should have the correct localPath"-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath+            $ assertDef definition Shikensu.extname ".md"              , testCase "Should have the correct pattern"-            $ definition `rmap` Shikensu.pattern >>= assertEq pattern+            $ assertDef definition Shikensu.pattern "tests/**/*.md"              , testCase "Should have the correct workingDirname"-            $ definition `rmap` Shikensu.workingDirname >>= assertEq "tests"+            $ assertDef definition Shikensu.workingDirname "tests"              ] @@ -53,29 +51,23 @@ testDot :: TestTree testDot =     let-        pattern = "./tests/**/*.md"-        dictionary = fmap sort $ rootPath >>= Shikensu.list [pattern]-        definition = fmap List.head dictionary-        localPath = "fixtures/example.md"+        definition = define "./tests/**/*.md" "."     in         testGroup "Test dot"             [ testCase "Should have the correct basename"-            $ definition `rmap` Shikensu.basename >>= assertEq "example"+            $ assertDef definition Shikensu.basename "example"              , testCase "Should have the correct dirname"-            $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures"+            $ assertDef definition Shikensu.dirname "fixtures"              , testCase "Should have the correct extname"-            $ definition `rmap` Shikensu.extname >>= assertEq ".md"--            , testCase "Should have the correct localPath"-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath+            $ assertDef definition Shikensu.extname ".md"              , testCase "Should have the correct pattern"-            $ definition `rmap` Shikensu.pattern >>= assertEq pattern+            $ assertDef definition Shikensu.pattern "./tests/**/*.md"              , testCase "Should have the correct workingDirname"-            $ definition `rmap` Shikensu.workingDirname >>= assertEq "tests"+            $ assertDef definition Shikensu.workingDirname "tests"              ] @@ -83,29 +75,23 @@ testWithoutWd :: TestTree testWithoutWd =     let-        pattern = "**/*.md"-        dictionary = fmap sort $ testsPath >>= Shikensu.list [pattern]-        definition = fmap List.head dictionary-        localPath = "fixtures/example.md"+        definition = define "**/*.md" "./tests"     in         testGroup "Test without workingDirname"             [ testCase "Should have the correct basename"-            $ definition `rmap` Shikensu.basename >>= assertEq "example"+            $ assertDef definition Shikensu.basename "example"              , testCase "Should have the correct dirname"-            $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures"+            $ assertDef definition Shikensu.dirname "fixtures"              , testCase "Should have the correct extname"-            $ definition `rmap` Shikensu.extname >>= assertEq ".md"--            , testCase "Should have the correct localPath"-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath+            $ assertDef definition Shikensu.extname ".md"              , testCase "Should have the correct pattern"-            $ definition `rmap` Shikensu.pattern >>= assertEq pattern+            $ assertDef definition Shikensu.pattern "**/*.md"              , testCase "Should have the correct workingDirname"-            $ definition `rmap` Shikensu.workingDirname >>= assertEq ""+            $ assertDef definition Shikensu.workingDirname ""              ] @@ -113,45 +99,22 @@ testRootFile :: TestTree testRootFile =     let-        pattern = "*.md"-        dictionary = fmap sort $ rootPath >>= Shikensu.list [pattern]-        definition = fmap List.head dictionary-        localPath = "CHANGELOG.md"+        definition = define "*.md" "."     in         testGroup "Test file in root path"             [ testCase "Should have the correct basename"-            $ definition `rmap` Shikensu.basename >>= assertEq "CHANGELOG"+            $ assertDef definition Shikensu.basename "CHANGELOG"              , testCase "Should have the correct dirname"-            $ definition `rmap` Shikensu.dirname >>= assertEq ""+            $ assertDef definition Shikensu.dirname ""              , testCase "Should have the correct extname"-            $ definition `rmap` Shikensu.extname >>= assertEq ".md"--            , testCase "Should have the correct localPath"-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath+            $ assertDef definition Shikensu.extname ".md"              , testCase "Should have the correct pattern"-            $ definition `rmap` Shikensu.pattern >>= assertEq pattern+            $ assertDef definition Shikensu.pattern "*.md"              , testCase "Should have the correct workingDirname"-            $ definition `rmap` Shikensu.workingDirname >>= assertEq ""--            ]---testRelative :: TestTree-testRelative =-    let-        pattern = "*.md"-        dictionary = fmap sort $ Shikensu.listRelative [pattern] "./"-        definition = fmap List.head dictionary-        localPath = "CHANGELOG.md"-    in-        testGroup "Test relative"-            [ testCase "Should have the correct rootDirname"-            $ definition `rmap` Shikensu.rootDirname >>= (\x -> rootPath >>= assertEq x)+            $ assertDef definition Shikensu.workingDirname "" -            , testCase "Should have the correct localPath"-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath             ]
tests/Test/Utilities.hs view
@@ -1,7 +1,9 @@-module Test.Utilities (utilityTests) where+module Test.Utilities+    ( utilityTests+    ) where  import Flow-import Shikensu.Types+import Shikensu import Shikensu.Utilities as Utils import Test.Helpers import Test.Tasty@@ -10,15 +12,13 @@ import qualified Data.HashMap.Strict as HashMap (singleton) import qualified Data.List as List (head) import qualified Data.Tuple as Tuple (fst)-import qualified Shikensu import qualified Shikensu.Contrib as Contrib   utilityTests :: TestTree utilityTests = testGroup     "Utility tests"-    [testSequencing, testThunder]-+    [ testSequencing, testMetadataAccessors ]   @@ -30,40 +30,33 @@     let         result =             Utils.lsequence-                [ ( "a", rootPath >>= Shikensu.list ["tests/fixtures/example.md"] )-                , ( "b", rootPath >>= Shikensu.list ["tests/fixtures/example.md"] )+                [ ( "a", Shikensu.listRelative ["tests/fixtures/example.md"] "." )+                , ( "b", Shikensu.listRelative ["tests/fixtures/example.md"] "." )                 ]     in         testCase "Test lsequence"-        $ (List.head .> Tuple.fst) <$> result >>= assertEq "a"+        $ fmap (List.head .> Tuple.fst) result >>= assertEq "a"  -testThunder :: TestTree-testThunder =+testMetadataAccessors :: TestTree+testMetadataAccessors =     let-        flow =-            Contrib.insertMetadata (HashMap.singleton "a" "Hi!")-            .> return--        dictionary =-            rootPath-                >>= Shikensu.list ["tests/fixtures/example.md"]-                >>= flow--        def =-            fmap List.head dictionary+        definition =+            "."+                |> define "tests/fixtures/example.md"+                |> fmap (Contrib.insertMetadataDef $ HashMap.singleton "a" "Hi!")          resultExisting =-            fmap (\d -> metadata d ~> "a" :: Maybe String) def+            fmap (\d -> metadata d ~> "a" :: Maybe String) definition          resultNonExisting =-            fmap (\d -> metadata d ~> "b" :: Maybe String) def+            fmap (\d -> metadata d ~> "b" :: Maybe String) definition     in         testGroup             "Test (~>)"             [ testCase "Existing"-            $ resultExisting >>= assertEq (Just "Hi!")+            $ assertEqm resultExisting (Just "Hi!")              , testCase "Non-existing"-            $ resultNonExisting >>= assertEq (Nothing)+            $ assertEqm resultNonExisting Nothing             ]