on master help sign in
Merge pull request #190 from krsch/master

Implemented per-directory metadata support

commit63107a6f75335cabe828751d2bef4704667cad76 authorJasper Van der Jeugt <jaspervdj@gmail.com> dateThu 23 Jan 2014, 01:29 (13 years ago) parentbc360a3dbabe03
11 files changed+160−4
hakyll.cabal+1 src/Hakyll/Core/Metadata.hs+11 src/Hakyll/Core/Provider/Metadata.hs+35−2 src/Hakyll/Core/Rules/Default.hs+24 src/Hakyll/Core/Runtime.hs+3−2 tests/Hakyll/Core/Provider/GlobalMetadata/Tests.hs+31 tests/TestSuite.hs+2 tests/data/metadata+27 tests/data/posts/2013-10-18-metadata-test.md+4 tests/data/posts/2013-10-18-metadata-test.md.metadata+2 tests/data/posts/metadata+20
hakyll.cabal+1
@@ -132,6 +132,7 @@ Library
132132 Hakyll.Core.Provider.Metadata
133133 Hakyll.Core.Provider.MetadataCache
134134 Hakyll.Core.Rules.Internal
135+ Hakyll.Core.Rules.Default
135136 Hakyll.Core.Runtime
136137 Hakyll.Core.Store
137138 Hakyll.Core.Util.File
src/Hakyll/Core/Metadata.hs+11
@@ -5,6 +5,7 @@ module Hakyll.Core.Metadata
55 , getMetadataField
66 , getMetadataField'
77 , makePatternDependency
8+ , metadataFiles
89 ) where
910
1011
@@ -12,6 +13,7 @@ module Hakyll.Core.Metadata
1213import Control.Monad (forM)
1314import Data.Map (Map)
1415import qualified Data.Map as M
16+import System.FilePath.Posix ((</>), takeDirectory)
1517
1618
1719--------------------------------------------------------------------------------
@@ -61,3 +63,12 @@ makePatternDependency :: MonadMetadata m => Pattern -> m Dependency
6163makePatternDependency pattern = do
6264 matches' <- getMatches pattern
6365 return $ PatternDependency pattern matches'
66+
67+--------------------------------------------------------------------------------
68+-- | Returns a list of all directory-wise metadata files, subdir first, global last
69+metadataFiles :: Identifier -> [Identifier]
70+metadataFiles identifier = local : go (takeDirectory $ toFilePath identifier) where
71+ go "." = [fromFilePath "metadata"]
72+ go dir = fromFilePath (dir </> "metadata") : go (takeDirectory dir)
73+ local = fromFilePath $ toFilePath identifier ++ ".metadata"
74+
src/Hakyll/Core/Provider/Metadata.hs+35−2
@@ -20,6 +20,8 @@ import System.IO as IO
2020import Text.Parsec ((<?>))
2121import qualified Text.Parsec as P
2222import Text.Parsec.String (Parser)
23+import System.FilePath.Posix
24+import Control.Monad (liftM)
2325
2426
2527--------------------------------------------------------------------------------
@@ -28,7 +30,7 @@ import Hakyll.Core.Metadata
2830import Hakyll.Core.Provider.Internal
2931import Hakyll.Core.Util.Parser
3032import Hakyll.Core.Util.String
31−
33+import Hakyll.Core.Identifier.Pattern
3234
3335--------------------------------------------------------------------------------
3436loadMetadata :: Provider -> Identifier -> IO (Metadata, Maybe String)
@@ -42,7 +44,9 @@ loadMetadata p identifier = do
4244 Nothing -> return M.empty
4345 Just mi' -> loadMetadataFile $ resourceFilePath p mi'
4446
45− return (M.union md emd, body)
47+ gmd <- loadGlobalMetadata p identifier
48+
49+ return (M.unions [md, gmd], body)
4650 where
4751 normal = setVersion Nothing identifier
4852 fp = resourceFilePath p identifier
@@ -133,3 +137,32 @@ page = do
133137 metadata' <- P.option [] metadataBlock
134138 body <- P.many P.anyChar
135139 return (metadata', body)
140+
141+
142+--------------------------------------------------------------------------------
143+-- | Load directory-wise metadata
144+loadGlobalMetadata :: Provider -> Identifier -> IO Metadata
145+loadGlobalMetadata p fp = liftM M.fromList $ loadgm fp where
146+ loadgm :: Identifier -> IO [(String, String)]
147+ loadgm = liftM concat . mapM loadOne . reverse . filter (resourceExists p) . metadataFiles
148+ loadOne mfp =
149+ let path = resourceFilePath p mfp
150+ dir = takeDirectory $ toFilePath mfp
151+ -- TODO: It might be better to print warning and continue
152+ in either (error.show) (findMetadata dir) . P.parse namedMetadata path <$> readFile path
153+ findMetadata dir =
154+ concatMap snd . filter (flip matches fp . fromGlob . normalise . combine dir . fst)
155+
156+namedMetadata :: Parser [(String, [(String, String)])]
157+namedMetadata = liftA2 (:) (namedMetadataBlock False) $ P.many $ namedMetadataBlock True
158+
159+namedMetadataBlock :: Bool -> Parser (String, [(String, String)])
160+namedMetadataBlock isNamed = do
161+ name <- if isNamed
162+ then P.many1 (P.char '-') *> P.many inlineSpace *> P.manyTill P.anyChar newline
163+ else pure "**"
164+ metadata' <- metadata
165+ P.skipMany P.space
166+ return (name, metadata')
167+
168+
src/Hakyll/Core/Rules/Default.hsnew file+24
@@ -0,0 +1,24 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+module Hakyll.Core.Rules.Default
3+ ( internalRules
4+ , addMetadataDependencies
5+ )
6+where
7+import Hakyll.Core.Rules
8+import Hakyll.Core.Compiler
9+import Hakyll.Core.Compiler.Internal (compilerTellDependencies)
10+import Hakyll.Core.Metadata (getMatches, metadataFiles)
11+import Hakyll.Core.Identifier.Pattern(fromList)
12+
13+internalRules :: Rules ()
14+internalRules = do
15+ match "metadata" $ compile $ makeItem ()
16+ match "**/metadata" $ compile $ makeItem ()
17+ match "**.metadata" $ compile $ makeItem ()
18+
19+--------------------------------------------------------------------------------
20+addMetadataDependencies :: Compiler ()
21+addMetadataDependencies =
22+ compilerTellDependencies . map IdentifierDependency =<< getMatches . fromList =<< fmap metadataFiles getUnderlying
23+
24+
src/Hakyll/Core/Runtime.hs+3−2
@@ -35,6 +35,7 @@ import qualified Hakyll.Core.Logger as Logger
3535import Hakyll.Core.Provider
3636import Hakyll.Core.Routes
3737import Hakyll.Core.Rules.Internal
38+import Hakyll.Core.Rules.Default
3839import Hakyll.Core.Store (Store)
3940import qualified Hakyll.Core.Store as Store
4041import Hakyll.Core.Util.File
@@ -53,7 +54,7 @@ run config verbosity rules = do
5354 provider <- newProvider store (shouldIgnoreFile config) $
5455 providerDirectory config
5556 Logger.message logger "Running rules..."
56− ruleSet <- runRules rules provider
57+ ruleSet <- runRules (rules >> internalRules) provider
5758
5859 -- Get old facts
5960 mOldFacts <- Store.get store factsKey
@@ -186,7 +187,7 @@ chase trail id'
186187 config <- runtimeConfiguration <$> ask
187188 Logger.debug logger $ "Processing " ++ show id'
188189
189− let compiler = todo M.! id'
190+ let compiler = addMetadataDependencies >> todo M.! id'
190191 read' = CompilerRead
191192 { compilerConfig = config
192193 , compilerUnderlying = id'
tests/Hakyll/Core/Provider/GlobalMetadata/Tests.hsnew file+31
@@ -0,0 +1,31 @@
1+--------------------------------------------------------------------------------
2+{-# LANGUAGE OverloadedStrings #-}
3+module Hakyll.Core.Provider.GlobalMetadata.Tests
4+ ( tests
5+ ) where
6+
7+--------------------------------------------------------------------------------
8+import qualified Data.Map as M
9+import Control.Monad (forM_)
10+import Test.Framework (Test, testGroup)
11+import Test.HUnit (Assertion, (@=?))
12+
13+
14+--------------------------------------------------------------------------------
15+import Hakyll.Core.Provider (resourceMetadata)
16+import TestSuite.Util
17+
18+--------------------------------------------------------------------------------
19+tests :: Test
20+tests = testGroup "Hakyll.Core.Provider.GlobalMetadata.Tests" $
21+ fromAssertions "page" [testPage]
22+
23+testPage :: Assertion
24+testPage = do
25+ store <- newTestStore
26+ provider <- newTestProvider store
27+
28+ metadata <- resourceMetadata provider "posts/2013-10-18-metadata-test.md"
29+ forM_ ["1", "2", "3", "4", "5", "6", "7", "8"] $ \a ->
30+ Just a @=? M.lookup ('a':a) metadata
31+
tests/TestSuite.hs+2
@@ -12,6 +12,7 @@ import Test.Framework (defaultMain)
1212import qualified Hakyll.Core.Dependencies.Tests
1313import qualified Hakyll.Core.Identifier.Tests
1414import qualified Hakyll.Core.Provider.Metadata.Tests
15+import qualified Hakyll.Core.Provider.GlobalMetadata.Tests
1516import qualified Hakyll.Core.Provider.Tests
1617import qualified Hakyll.Core.Routes.Tests
1718import qualified Hakyll.Core.Rules.Tests
@@ -32,6 +33,7 @@ main = defaultMain
3233 [ Hakyll.Core.Dependencies.Tests.tests
3334 , Hakyll.Core.Identifier.Tests.tests
3435 , Hakyll.Core.Provider.Metadata.Tests.tests
36+ , Hakyll.Core.Provider.GlobalMetadata.Tests.tests
3537 , Hakyll.Core.Provider.Tests.tests
3638 , Hakyll.Core.Routes.Tests.tests
3739 , Hakyll.Core.Rules.Tests.tests
tests/data/metadatanew file+27
@@ -0,0 +1,27 @@
1+--- posts/2013-10-18-metadata-test.md
2+a1: 8
3+a2: 8
4+a3: 8
5+a4: 8
6+a5: 8
7+a6: 8
8+a7: 8
9+a8: 8
10+
11+--- posts/*
12+a1: 7
13+a2: 7
14+a3: 7
15+a4: 7
16+a5: 7
17+a6: 7
18+a7: 7
19+
20+--- **
21+a1: 6
22+a2: 6
23+a3: 6
24+a4: 6
25+a5: 6
26+a6: 6
27+
tests/data/posts/2013-10-18-metadata-test.mdnew file+4
@@ -0,0 +1,4 @@
1+---
2+a1: 1
3+---
4+Nothing interesting here.
tests/data/posts/2013-10-18-metadata-test.md.metadatanew file+2
@@ -0,0 +1,2 @@
1+a1: 2
2+a2: 2
tests/data/posts/metadatanew file+20
@@ -0,0 +1,20 @@
1+--- **
2+a1: 5
3+a2: 5
4+a3: 5
5+a4: 5
6+a5: 5
7+
8+--- *
9+a1: 4
10+a2: 4
11+a3: 4
12+a4: 4
13+
14+--- 2013-10-18-metadata-test.md
15+a1: 3
16+a2: 3
17+a3: 3
18+
19+--- nonexistent
20+a3: 0