on master help sign in
Added ReaderT to our stack.
commit4bc34b8a98ffa1e7f3478a596b73c4ab12d9cb1b authorJasper Van der Jeugt <jaspervdj@gmail.com> dateThu 14 Jan 2010, 20:46 (17 years ago) parent332f2f9
11 files changed+101−54
examples/brochure/hakyll.hs+2−2 hakyll.cabal+1 src/Text/Hakyll.hs+23−9 src/Text/Hakyll/File.hs+6−2 src/Text/Hakyll/Hakyll.hs+15 src/Text/Hakyll/Page.hs+13−11 src/Text/Hakyll/Render.hs+24−20 src/Text/Hakyll/Render/Internal.hs+9−6 src/Text/Hakyll/Renderable.hs+2−1 src/Text/Hakyll/Renderables.hs+4−2 src/Text/Hakyll/Tags.hs+2−1
examples/brochure/hakyll.hs+2−2
@@ -1,9 +1,9 @@
1−import Text.Hakyll (hakyll)
1+import Text.Hakyll (hakyll, defaultHakyllConfiguration)
22import Text.Hakyll.File (directory)
33import Text.Hakyll.Render (css, static, renderChain)
44import Text.Hakyll.Renderables (createPagePath)
55
6−main = hakyll $ do
6+main = hakyll defaultHakyllConfiguration $ do
77 directory css "css"
88 render "about.markdown"
99 render "index.markdown"
hakyll.cabal+1
@@ -36,6 +36,7 @@ library
3636 time >= 1,
3737 parallel >= 1
3838 exposed-modules: Text.Hakyll
39+ Text.Hakyll.Hakyll
3940 Text.Hakyll.Render
4041 Text.Hakyll.Renderable
4142 Text.Hakyll.Renderables
src/Text/Hakyll.hs+23−9
@@ -1,28 +1,42 @@
11module Text.Hakyll
2− ( hakyll
2+ ( defaultHakyllConfiguration
3+ , hakyll
34 ) where
45
6+import Control.Monad.Reader (runReaderT)
7+import qualified Data.Map as M
8+
59import Network.Hakyll.SimpleServer (simpleServer)
10+import Text.Hakyll.Hakyll
611
712import System.Environment (getArgs, getProgName)
813import System.Directory (doesDirectoryExist, removeDirectoryRecursive)
914
15+-- | Default hakyll configuration.
16+defaultHakyllConfiguration :: HakyllConfiguration
17+defaultHakyllConfiguration = HakyllConfiguration
18+ { hakyllDestination = "_site"
19+ , hakyllGlobalContext = M.empty
20+ }
21+
1022-- | Main function to run hakyll.
11−hakyll :: IO () -> IO ()
12−hakyll buildFunction = do
23+hakyll :: HakyllConfiguration -> Hakyll () -> IO ()
24+hakyll configuration buildFunction = do
1325 args <- getArgs
14− case args of ["build"] -> build buildFunction
26+ case args of ["build"] -> build'
1527 ["clean"] -> clean
16− ["preview", p] -> build buildFunction >> server (read p)
17− ["preview"] -> build buildFunction >> server 8000
28+ ["preview", p] -> build' >> server (read p)
29+ ["preview"] -> build' >> server 8000
1830 ["server", p] -> server (read p)
1931 ["server"] -> server 8000
2032 _ -> help
33+ where
34+ build' = build configuration buildFunction
2135
2236-- | Build the site.
23−build :: IO () -> IO ()
24−build buildFunction = do putStrLn "Generating..."
25− buildFunction
37+build :: HakyllConfiguration -> Hakyll () -> IO ()
38+build configuration buildFunction = do putStrLn "Generating..."
39+ runReaderT buildFunction configuration
2640
2741-- | Clean up directories.
2842clean :: IO ()
src/Text/Hakyll/File.hs+6−2
@@ -17,6 +17,8 @@ import System.Directory
1717import System.FilePath
1818import Control.Monad
1919import Data.List (isPrefixOf)
20+import Text.Hakyll.Hakyll (Hakyll)
21+import Control.Monad.Reader (liftIO)
2022
2123-- | Auxiliary function to remove pathSeparators form the start. We don't deal
2224-- with absolute paths here. We also remove $root from the start.
@@ -90,8 +92,10 @@ havingExtension :: String -> [FilePath] -> [FilePath]
9092havingExtension extension = filter ((==) extension . takeExtension)
9193
9294-- | Perform an IO action on every file in a given directory.
93−directory :: (FilePath -> IO ()) -> FilePath -> IO ()
94−directory action dir = getRecursiveContents dir >>= mapM_ action
95+directory :: (FilePath -> Hakyll ()) -> FilePath -> Hakyll ()
96+directory action dir = do
97+ contents <- liftIO $ getRecursiveContents dir
98+ mapM_ action contents
9599
96100-- | Check if a cache file is still valid.
97101isCacheValid :: FilePath -> [FilePath] -> IO Bool
src/Text/Hakyll/Hakyll.hsnew file+15
@@ -0,0 +1,15 @@
1+module Text.Hakyll.Hakyll
2+ ( HakyllConfiguration (..)
3+ , Hakyll
4+ ) where
5+
6+import Text.Hakyll.Context (Context)
7+import System.FilePath (FilePath)
8+import Control.Monad.Reader (ReaderT)
9+
10+data HakyllConfiguration = HakyllConfiguration
11+ { hakyllDestination :: FilePath
12+ , hakyllGlobalContext :: Context
13+ }
14+
15+type Hakyll = ReaderT HakyllConfiguration IO
src/Text/Hakyll/Page.hs+13−11
@@ -11,10 +11,12 @@ import qualified Data.List as L
1111import Data.Maybe (fromMaybe)
1212
1313import Control.Parallel.Strategies (rnf, ($|))
14+import Control.Monad.Reader (liftIO)
1415
1516import System.FilePath (FilePath, takeExtension)
1617import System.IO
1718
19+import Text.Hakyll.Hakyll (Hakyll)
1820import Text.Hakyll.File
1921import Text.Hakyll.Util (trim)
2022import Text.Hakyll.Context (Context)
@@ -66,9 +68,9 @@ renderFunction ext = writeHtmlString writerOptions
6668 readFunction _ = readMarkdown
6769
6870-- | Read metadata header from a file handle.
69−readMetaData :: Handle -> IO [(String, String)]
71+readMetaData :: Handle -> Hakyll [(String, String)]
7072readMetaData handle = do
71− line <- hGetLine handle
73+ line <- liftIO $ hGetLine handle
7274 if isDelimiter line
7375 then return []
7476 else do others <- readMetaData handle
@@ -81,8 +83,8 @@ isDelimiter :: String -> Bool
8183isDelimiter = L.isPrefixOf "---"
8284
8385-- | Used for caching of files.
84−cachePage :: Page -> IO ()
85−cachePage page@(Page mapping) = do
86+cachePage :: Page -> Hakyll ()
87+cachePage page@(Page mapping) = liftIO $ do
8688 let destination = toCache $ getURL page
8789 makeDirectories destination
8890 handle <- openFile destination WriteMode
@@ -98,21 +100,21 @@ cachePage page@(Page mapping) = do
98100-- | Read a page from a file. Metadata is supported, and if the filename
99101-- has a .markdown extension, it will be rendered using pandoc. Note that
100102-- pages are not templates, so they should not contain $identifiers.
101−readPage :: FilePath -> IO Page
103+readPage :: FilePath -> Hakyll Page
102104readPage pagePath = do
103105 -- Check cache.
104− getFromCache <- isCacheValid cacheFile [pagePath]
106+ getFromCache <- liftIO $ isCacheValid cacheFile [pagePath]
105107 let path = if getFromCache then cacheFile else pagePath
106108
107109 -- Read file.
108− handle <- openFile path ReadMode
109− line <- hGetLine handle
110+ handle <- liftIO $ openFile path ReadMode
111+ line <- liftIO $ hGetLine handle
110112 (metaData, body) <-
111113 if isDelimiter line
112114 then do md <- readMetaData handle
113− b <- hGetContents handle
115+ b <- liftIO $ hGetContents handle
114116 return (md, b)
115− else do b <- hGetContents handle
117+ else do b <- liftIO $ hGetContents handle
116118 return ([], line ++ "\n" ++ b)
117119
118120 -- Render file
@@ -123,7 +125,7 @@ readPage pagePath = do
123125 , ("path", pagePath)
124126 ] ++ metaData
125127
126− seq (($|) id rnf rendered) $ hClose handle
128+ seq (($|) id rnf rendered) $ liftIO $ hClose handle
127129
128130 -- Cache if needed
129131 if getFromCache then return () else cachePage page
src/Text/Hakyll/Render.hs+24−20
@@ -11,10 +11,12 @@ module Text.Hakyll.Render
1111 ) where
1212
1313import Control.Monad (unless, mapM)
14+import Control.Monad.Reader (liftIO)
1415
1516import System.Directory (copyFile)
1617import System.IO
1718
19+import Text.Hakyll.Hakyll (Hakyll)
1820import Text.Hakyll.Context (ContextManipulation)
1921import Text.Hakyll.Page
2022import Text.Hakyll.Renderable
@@ -26,17 +28,17 @@ import Text.Hakyll.Render.Internal
2628-- | Execute an IO action only when the cache is invalid.
2729depends :: FilePath -- ^ File to be rendered or created.
2830 -> [FilePath] -- ^ Files the render depends on.
29− -> IO () -- ^ IO action to execute when the file is out of date.
30− -> IO ()
31+ -> Hakyll () -- ^ IO action to execute when the file is out of date.
32+ -> Hakyll ()
3133depends file dependencies action = do
32− valid <- isCacheValid (toDestination file) dependencies
34+ valid <- liftIO $ isCacheValid (toDestination file) dependencies
3335 unless valid action
3436
3537-- | Render to a Page.
3638render :: Renderable a
3739 => FilePath -- ^ Template to use for rendering.
3840 -> a -- ^ Renderable object to render with given template.
39− -> IO Page -- ^ The body of the result will contain the render.
41+ -> Hakyll Page -- ^ The body of the result will contain the render.
4042render = renderWith id
4143
4244-- | Render to a Page. This function allows you to manipulate the context
@@ -45,15 +47,15 @@ renderWith :: Renderable a
4547 => ContextManipulation -- ^ Manipulation to apply on the context.
4648 -> FilePath -- ^ Template to use for rendering.
4749 -> a -- ^ Renderable object to render with given template.
48− -> IO Page -- ^ The body of the result will contain the render.
50+ -> Hakyll Page -- ^ The body of the result will contain the render.
4951renderWith manipulation templatePath renderable = do
50− template <- readFile templatePath
52+ template <- liftIO $ readFile templatePath
5153 context <- toContext renderable
5254 return $ fromContext $ pureRenderWith manipulation template context
5355
5456-- | Render each renderable with the given template, then concatenate the
5557-- result.
56−renderAndConcat :: Renderable a => FilePath -> [a] -> IO String
58+renderAndConcat :: Renderable a => FilePath -> [a] -> Hakyll String
5759renderAndConcat = renderAndConcatWith id
5860
5961-- | Render each renderable with the given template, then concatenate the
@@ -63,41 +65,43 @@ renderAndConcatWith :: Renderable a
6365 => ContextManipulation
6466 -> FilePath
6567 -> [a]
66− -> IO String
68+ -> Hakyll String
6769renderAndConcatWith manipulation templatePath renderables = do
68− template <- readFile templatePath
70+ template <- liftIO $ readFile templatePath
6971 contexts <- mapM toContext renderables
7072 return $ pureRenderAndConcatWith manipulation template contexts
7173
7274-- | Chain a render action for a page with a number of templates. This will
7375-- also write the result to the site destination. This is the preferred way
7476-- to do general rendering.
75−renderChain :: Renderable a => [FilePath] -> a -> IO ()
77+renderChain :: Renderable a => [FilePath] -> a -> Hakyll ()
7678renderChain = renderChainWith id
7779
7880-- | A more custom render chain that allows you to specify a
7981-- "ContextManipulation" which to apply on the context when it is read first.
8082renderChainWith :: Renderable a
81− => ContextManipulation -> [FilePath] -> a -> IO ()
83+ => ContextManipulation -> [FilePath] -> a -> Hakyll ()
8284renderChainWith manipulation templatePaths renderable =
83− depends (getURL renderable) (getDependencies renderable ++ templatePaths) $
84− do templates <- mapM readFile templatePaths
85− context <- toContext renderable
86− let result = pureRenderChainWith manipulation templates context
87− writePage $ fromContext result
85+ depends (getURL renderable) dependencies render'
86+ where
87+ dependencies = (getDependencies renderable) ++ templatePaths
88+ render' = do templates <- liftIO $ mapM readFile templatePaths
89+ context <- toContext renderable
90+ let result = pureRenderChainWith manipulation templates context
91+ writePage $ fromContext result
8892
8993-- | Mark a certain file as static, so it will just be copied when the site is
9094-- generated.
91−static :: FilePath -> IO ()
92−static source = depends destination [source] action
95+static :: FilePath -> Hakyll ()
96+static source = depends destination [source] (liftIO action)
9397 where
9498 destination = toDestination source
9599 action = do makeDirectories destination
96100 copyFile source destination
97101
98102-- | Render a css file, compressing it.
99−css :: FilePath -> IO ()
100−css source = depends destination [source] css'
103+css :: FilePath -> Hakyll ()
104+css source = depends destination [source] (liftIO css')
101105 where
102106 destination = toDestination source
103107 css' = do contents <- readFile source
src/Text/Hakyll/Render/Internal.hs+9−6
@@ -11,6 +11,8 @@ module Text.Hakyll.Render.Internal
1111
1212import qualified Data.Map as M
1313import Text.Hakyll.Context (Context, ContextManipulation)
14+import Control.Monad.Reader (ask, liftIO)
15+import Control.Monad (liftM)
1416import Data.List (isPrefixOf, foldl')
1517import Data.Char (isAlpha)
1618import Data.Maybe (fromMaybe)
@@ -18,6 +20,7 @@ import Control.Parallel.Strategies (rnf, ($|))
1820import Text.Hakyll.Renderable
1921import Text.Hakyll.Page
2022import Text.Hakyll.File
23+import Text.Hakyll.Hakyll (Hakyll, hakyllGlobalContext)
2124
2225-- | Substitutes `$identifiers` in the given string by values from the given
2326-- "Context". When a key is not found, it is left as it is. You can here
@@ -79,13 +82,13 @@ pureRenderChainWith manipulation templates context =
7982
8083-- | Write a page to the site destination. Final action after render
8184-- chains and such.
82−writePage :: Page -> IO ()
85+writePage :: Page -> Hakyll ()
8386writePage page = do
87+ globalContext <- liftM hakyllGlobalContext ask
8488 let destination = toDestination url
85− makeDirectories destination
86− writeFile destination body
89+ context = (M.singleton "root" $ toRoot url) `M.union` globalContext
90+ liftIO $ makeDirectories destination
91+    -- Substitute $root here, just before writing.
92+ liftIO $ writeFile destination $ finalSubstitute (getBody page) context
8793 where
8894 url = getURL page
89−    -- Substitute $root here, just before writing.
90−    body = finalSubstitute (getBody page)
91−                           (M.singleton "root" $ toRoot url)
src/Text/Hakyll/Renderable.hs+2−1
@@ -2,13 +2,14 @@ module Text.Hakyll.Renderable
22 ( Renderable(toContext, getDependencies, getURL)
33 ) where
44
5+import Text.Hakyll.Hakyll (Hakyll)
56import System.FilePath (FilePath)
67import Text.Hakyll.Context (Context)
78
89-- | A class for datatypes that can be rendered to pages.
910class Renderable a where
1011 -- | Get a context to do substitutions with.
11− toContext :: a -> IO Context
12+ toContext :: a -> Hakyll Context
1213
1314 -- | Get the dependencies for the renderable. This is used for cache
1415 -- invalidation.
src/Text/Hakyll/Renderables.hs+4−2
@@ -5,6 +5,7 @@ module Text.Hakyll.Renderables
55 , createPagePath
66 ) where
77
8+import Text.Hakyll.Hakyll (Hakyll)
89import System.FilePath (FilePath)
910import qualified Data.Map as M
1011import Text.Hakyll.Page
@@ -15,13 +16,14 @@ import Text.Hakyll.File
1516data CustomPage = CustomPage
1617 { url :: String,
1718 dependencies :: [FilePath],
18− mapping :: [(String, Either String (IO String))]
19+ mapping :: [(String, Either String (Hakyll String))]
1920 }
2021
2122-- | Create a custom page.
2223createCustomPage :: String -- ^ Destination of the page, relative to _site.
2324 -> [FilePath] -- ^ Dependencies of the page.
24− -> [(String, Either String (IO String))] -- ^ Key - value mapping for rendering.
25+ -> [(String, Either String (Hakyll String))] -- ^ Key - value
26+ -- mapping.
2527 -> CustomPage
2628createCustomPage = CustomPage
2729
src/Text/Hakyll/Tags.hs+2−1
@@ -9,6 +9,7 @@ module Text.Hakyll.Tags
99import qualified Data.Map as M
1010import Data.List (intercalate)
1111import Control.Monad (foldM)
12+import Text.Hakyll.Hakyll (Hakyll)
1213
1314import Text.Hakyll.Context (ContextManipulation, renderValue)
1415import Text.Hakyll.Regex
@@ -19,7 +20,7 @@ import Control.Arrow (second)
1920-- | Read a tag map. This creates a map from tags to page paths. This function
2021-- assumes the tags are located in the `tags` metadata field, separated by
2122-- commas.
22−readTagMap :: [FilePath] -> IO (M.Map String [FilePath])
23+readTagMap :: [FilePath] -> Hakyll (M.Map String [FilePath])
2324readTagMap paths = foldM addPaths M.empty paths
2425 where
2526 addPaths current path = do