on master help sign in
Merge pull request #190 from krsch/master
63107a6Jasper Van der Jeugt
hakyll.cabal+1
@@ -132,6 +132,7 @@ Library
132 Hakyll.Core.Provider.Metadata
132 Hakyll.Core.Provider.Metadata
133 Hakyll.Core.Provider.MetadataCache
133 Hakyll.Core.Provider.MetadataCache
134 Hakyll.Core.Rules.Internal
134 Hakyll.Core.Rules.Internal
135+ Hakyll.Core.Rules.Default
135 Hakyll.Core.Runtime
136 Hakyll.Core.Runtime
136 Hakyll.Core.Store
137 Hakyll.Core.Store
137 Hakyll.Core.Util.File
138 Hakyll.Core.Util.File
src/Hakyll/Core/Metadata.hs+11
@@ -5,6 +5,7 @@ module Hakyll.Core.Metadata
5 , getMetadataField
5 , getMetadataField
6 , getMetadataField'
6 , getMetadataField'
7 , makePatternDependency
7 , makePatternDependency
8+ , metadataFiles
8 ) where
9 ) where
9
10
10
11
@@ -12,6 +13,7 @@ module Hakyll.Core.Metadata
12import Control.Monad (forM)
13import Control.Monad (forM)
13import Data.Map (Map)
14import Data.Map (Map)
14import qualified Data.Map as M
15import qualified Data.Map as M
16+import System.FilePath.Posix ((</>), takeDirectory)
15
17
16
18
17--------------------------------------------------------------------------------
19--------------------------------------------------------------------------------
@@ -61,3 +63,12 @@ makePatternDependency :: MonadMetadata m => Pattern -> m Dependency
61makePatternDependency pattern = do
63makePatternDependency pattern = do
62 matches' <- getMatches pattern
64 matches' <- getMatches pattern
63 return $ PatternDependency pattern matches'
65 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
20import Text.Parsec ((<?>))
20import Text.Parsec ((<?>))
21import qualified Text.Parsec as P
21import qualified Text.Parsec as P
22import Text.Parsec.String (Parser)
22import Text.Parsec.String (Parser)
23+import System.FilePath.Posix
24+import Control.Monad (liftM)
23
25
24
26
25--------------------------------------------------------------------------------
27--------------------------------------------------------------------------------
@@ -28,7 +30,7 @@ import Hakyll.Core.Metadata
28import Hakyll.Core.Provider.Internal
30import Hakyll.Core.Provider.Internal
29import Hakyll.Core.Util.Parser
31import Hakyll.Core.Util.Parser
30import Hakyll.Core.Util.String
32import Hakyll.Core.Util.String
31−
33+import Hakyll.Core.Identifier.Pattern
32
34
33--------------------------------------------------------------------------------
35--------------------------------------------------------------------------------
34loadMetadata :: Provider -> Identifier -> IO (Metadata, Maybe String)
36loadMetadata :: Provider -> Identifier -> IO (Metadata, Maybe String)
@@ -42,7 +44,9 @@ loadMetadata p identifier = do
42 Nothing -> return M.empty
44 Nothing -> return M.empty
43 Just mi' -> loadMetadataFile $ resourceFilePath p mi'
45 Just mi' -> loadMetadataFile $ resourceFilePath p mi'
44
46
47+ gmd <- loadGlobalMetadata p identifier
48+
45− return (M.union md emd, body)
49+ return (M.unions [md, gmd], body)
46 where
50 where
47 normal = setVersion Nothing identifier
51 normal = setVersion Nothing identifier
48 fp = resourceFilePath p identifier
52 fp = resourceFilePath p identifier
@@ -133,3 +137,32 @@ page = do
133 metadata' <- P.option [] metadataBlock
137 metadata' <- P.option [] metadataBlock
134 body <- P.many P.anyChar
138 body <- P.many P.anyChar
135 return (metadata', body)
139 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
35import Hakyll.Core.Provider
35import Hakyll.Core.Provider
36import Hakyll.Core.Routes
36import Hakyll.Core.Routes
37import Hakyll.Core.Rules.Internal
37import Hakyll.Core.Rules.Internal
38+import Hakyll.Core.Rules.Default
38import Hakyll.Core.Store (Store)
39import Hakyll.Core.Store (Store)
39import qualified Hakyll.Core.Store as Store
40import qualified Hakyll.Core.Store as Store
40import Hakyll.Core.Util.File
41import Hakyll.Core.Util.File
@@ -53,7 +54,7 @@ run config verbosity rules = do
53 provider <- newProvider store (shouldIgnoreFile config) $
54 provider <- newProvider store (shouldIgnoreFile config) $
54 providerDirectory config
55 providerDirectory config
55 Logger.message logger "Running rules..."
56 Logger.message logger "Running rules..."
56− ruleSet <- runRules rules provider
57+ ruleSet <- runRules (rules >> internalRules) provider
57
58
58 -- Get old facts
59 -- Get old facts
59 mOldFacts <- Store.get store factsKey
60 mOldFacts <- Store.get store factsKey
@@ -186,7 +187,7 @@ chase trail id'
186 config <- runtimeConfiguration <$> ask
187 config <- runtimeConfiguration <$> ask
187 Logger.debug logger $ "Processing " ++ show id'
188 Logger.debug logger $ "Processing " ++ show id'
188
189
189− let compiler = todo M.! id'
190+ let compiler = addMetadataDependencies >> todo M.! id'
190 read' = CompilerRead
191 read' = CompilerRead
191 { compilerConfig = config
192 { compilerConfig = config
192 , compilerUnderlying = id'
193 , 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)
12import qualified Hakyll.Core.Dependencies.Tests
12import qualified Hakyll.Core.Dependencies.Tests
13import qualified Hakyll.Core.Identifier.Tests
13import qualified Hakyll.Core.Identifier.Tests
14import qualified Hakyll.Core.Provider.Metadata.Tests
14import qualified Hakyll.Core.Provider.Metadata.Tests
15+import qualified Hakyll.Core.Provider.GlobalMetadata.Tests
15import qualified Hakyll.Core.Provider.Tests
16import qualified Hakyll.Core.Provider.Tests
16import qualified Hakyll.Core.Routes.Tests
17import qualified Hakyll.Core.Routes.Tests
17import qualified Hakyll.Core.Rules.Tests
18import qualified Hakyll.Core.Rules.Tests
@@ -32,6 +33,7 @@ main = defaultMain
32 [ Hakyll.Core.Dependencies.Tests.tests
33 [ Hakyll.Core.Dependencies.Tests.tests
33 , Hakyll.Core.Identifier.Tests.tests
34 , Hakyll.Core.Identifier.Tests.tests
34 , Hakyll.Core.Provider.Metadata.Tests.tests
35 , Hakyll.Core.Provider.Metadata.Tests.tests
36+ , Hakyll.Core.Provider.GlobalMetadata.Tests.tests
35 , Hakyll.Core.Provider.Tests.tests
37 , Hakyll.Core.Provider.Tests.tests
36 , Hakyll.Core.Routes.Tests.tests
38 , Hakyll.Core.Routes.Tests.tests
37 , Hakyll.Core.Rules.Tests.tests
39 , 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