summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Hakyll/Core/Util/Parser.hs2
-rw-r--r--src/Hakyll/Web/Feed.hs4
-rw-r--r--src/Hakyll/Web/Template.hs60
-rw-r--r--src/Hakyll/Web/Template/Context.hs35
-rw-r--r--src/Hakyll/Web/Template/Internal.hs7
-rw-r--r--src/Hakyll/Web/Template/Read.hs15
-rw-r--r--tests/Hakyll/Web/Template/Context/Tests.hs7
-rw-r--r--tests/Hakyll/Web/Template/Tests.hs11
-rw-r--r--tests/data/template.html13
-rw-r--r--tests/data/template.html.out15
10 files changed, 120 insertions, 49 deletions
diff --git a/src/Hakyll/Core/Util/Parser.hs b/src/Hakyll/Core/Util/Parser.hs
index afa72c1..7fb3b50 100644
--- a/src/Hakyll/Core/Util/Parser.hs
+++ b/src/Hakyll/Core/Util/Parser.hs
@@ -22,4 +22,4 @@ metadataKey = do
--------------------------------------------------------------------------------
reservedKeys :: [String]
-reservedKeys = ["if", "else","endif"]
+reservedKeys = ["if", "else", "endif", "for", "sep", "endfor"]
diff --git a/src/Hakyll/Web/Feed.hs b/src/Hakyll/Web/Feed.hs
index da2fd5d..d394243 100644
--- a/src/Hakyll/Web/Feed.hs
+++ b/src/Hakyll/Web/Feed.hs
@@ -97,7 +97,9 @@ renderFeed feedPath itemPath config itemContext items = do
-- recent.
updatedField = field "updated" $ \_ -> case items of
[] -> return "Unknown"
- (x : _) -> unContext itemContext' "updated" x
+ (x : _) -> unContext itemContext' "updated" x >>= \cf -> case cf of
+ ListField _ _ -> fail "Hakyll.Web.Feed.renderFeed: Internal error"
+ StringField s -> return s
--------------------------------------------------------------------------------
diff --git a/src/Hakyll/Web/Template.hs b/src/Hakyll/Web/Template.hs
index 8e3859a..45d1b92 100644
--- a/src/Hakyll/Web/Template.hs
+++ b/src/Hakyll/Web/Template.hs
@@ -39,13 +39,13 @@ module Hakyll.Web.Template
, applyTemplate
, loadAndApplyTemplate
, applyAsTemplate
- , applyTemplateWith
) where
--------------------------------------------------------------------------------
import Control.Monad (liftM)
-import Control.Monad.Error (MonadError(..))
+import Control.Monad.Error (MonadError (..))
+import Data.List (intercalate)
import Data.Monoid (mappend)
import Prelude hiding (id)
@@ -73,13 +73,44 @@ applyTemplate :: Template -- ^ Template
-> Item a -- ^ Page
-> Compiler (Item String) -- ^ Resulting item
applyTemplate tpl context item = do
- -- Appending missingField gives better error messages
- let context' k x = unContext (context `mappend` missingField) k x
- body <- applyTemplateWith context' tpl item
+ body <- applyTemplate' tpl context item
return $ itemSetBody body item
--------------------------------------------------------------------------------
+applyTemplate' :: Template -- ^ Template
+ -> Context a -- ^ Context
+ -> Item a -- ^ Page
+ -> Compiler String -- ^ Resulting item
+applyTemplate' tpl context x = go tpl
+ where
+ context' = unContext (context `mappend` missingField)
+ go = liftM concat . mapM applyElem . unTemplate
+
+ applyElem (Chunk c) = return c
+ applyElem Escaped = return "$"
+ applyElem (Key k) = context' k x >>= getString k
+ applyElem (If k t mf) = (context' k x >> go t) `catchError` handler
+ where
+ handler _ = case mf of
+ Nothing -> return ""
+ Just f -> go f
+ applyElem (For k b s) = context' k x >>= \cf -> case cf of
+ StringField _ -> fail $
+ "Hakyll.Web.Template.applyTemplateWith: expected ListField but " ++
+ "got StringField for key " ++ show k
+ ListField c xs -> do
+ sep <- maybe (return "") go s
+ bs <- mapM (applyTemplate' b c) xs
+ return $ intercalate sep bs
+
+ getString _ (StringField s) = return s
+ getString k (ListField _ _) = fail $
+ "Hakyll.Web.Template.applyTemplateWith: expected StringField but " ++
+ "got ListField for key " ++ show k
+
+
+--------------------------------------------------------------------------------
-- | The following pattern is so common:
--
-- > tpl <- loadBody "templates/foo.html"
@@ -109,22 +140,3 @@ applyAsTemplate :: Context String -- ^ Context
applyAsTemplate context item =
let tpl = readTemplate $ itemBody item
in applyTemplate tpl context item
-
-
---------------------------------------------------------------------------------
--- | Overloaded apply template function to work in an arbitrary Monad.
-applyTemplateWith :: MonadError e m
- => (String -> a -> m String)
- -> Template -> a -> m String
-applyTemplateWith context tpl x = go tpl
- where
- go = liftM concat . mapM applyElem . unTemplate
-
- applyElem (Chunk c) = return c
- applyElem Escaped = return "$"
- applyElem (Key k) = context k x
- applyElem (If k t mf) = (context k x >> go t) `catchError` handler
- where
- handler _ = case mf of
- Nothing -> return ""
- Just f -> go f
diff --git a/src/Hakyll/Web/Template/Context.hs b/src/Hakyll/Web/Template/Context.hs
index 74c2e8d..7b2d920 100644
--- a/src/Hakyll/Web/Template/Context.hs
+++ b/src/Hakyll/Web/Template/Context.hs
@@ -1,10 +1,11 @@
--------------------------------------------------------------------------------
+{-# LANGUAGE ExistentialQuantification #-}
module Hakyll.Web.Template.Context
- ( Context (..)
- , mapContext
+ ( ContextField (..)
+ , Context (..)
, field
, constField
- , functionField
+ , listField
, defaultContext
, teaserContext
@@ -46,8 +47,15 @@ import Hakyll.Web.Html
--------------------------------------------------------------------------------
+-- | Mostly for internal usage
+data ContextField
+ = StringField String
+ | forall a. ListField (Context a) [Item a]
+
+
+--------------------------------------------------------------------------------
newtype Context a = Context
- { unContext :: String -> Item a -> Compiler String
+ { unContext :: String -> Item a -> Compiler ContextField
}
@@ -58,13 +66,13 @@ instance Monoid (Context a) where
--------------------------------------------------------------------------------
-mapContext :: (String -> String) -> Context a -> Context a
-mapContext f (Context g) = Context $ \k i -> f <$> g k i
+field' :: String -> (Item a -> Compiler ContextField) -> Context a
+field' key value = Context $ \k i -> if k == key then value i else empty
--------------------------------------------------------------------------------
field :: String -> (Item a -> Compiler String) -> Context a
-field key value = Context $ \k i -> if k == key then value i else empty
+field key value = field' key (fmap StringField . value)
--------------------------------------------------------------------------------
@@ -73,12 +81,8 @@ constField key = field key . const . return
--------------------------------------------------------------------------------
-functionField :: String -> ([String] -> Item a -> Compiler String) -> Context a
-functionField name value = Context $ \k i -> case words k of
- [] -> empty
- (n : args)
- | n == name -> value args i
- | otherwise -> empty
+listField :: String -> Context a -> Compiler [Item a] -> Context b
+listField key c xs = field' key $ \_ -> fmap (ListField c) xs
--------------------------------------------------------------------------------
@@ -91,6 +95,7 @@ defaultContext =
titleField "title" `mappend`
missingField
+
--------------------------------------------------------------------------------
-- | A context with "teaser" key which contain a teaser of the item.
-- The item is loaded from the given snapshot (which should be saved
@@ -116,7 +121,7 @@ bodyField key = field key $ return . itemBody
metadataField :: Context String
metadataField = Context $ \k i -> do
value <- getMetadataField (itemIdentifier i) k
- maybe empty return value
+ maybe empty (return . StringField) value
--------------------------------------------------------------------------------
@@ -135,7 +140,7 @@ pathField key = field key $ return . toFilePath . itemIdentifier
--------------------------------------------------------------------------------
-- | This title field takes the basename of the underlying file by default
titleField :: String -> Context a
-titleField key = mapContext takeBaseName $ pathField key
+titleField key = field key $ return . takeBaseName . toFilePath . itemIdentifier
--------------------------------------------------------------------------------
diff --git a/src/Hakyll/Web/Template/Internal.hs b/src/Hakyll/Web/Template/Internal.hs
index f939566..1c81670 100644
--- a/src/Hakyll/Web/Template/Internal.hs
+++ b/src/Hakyll/Web/Template/Internal.hs
@@ -37,7 +37,8 @@ data TemplateElement
= Chunk String
| Key String
| Escaped
- | If String Template (Maybe Template) -- key, then branch, else branch
+ | If String Template (Maybe Template) -- key, then branch, else branch
+ | For String Template (Maybe Template) -- key, body, separator
deriving (Show, Eq, Typeable)
@@ -47,11 +48,13 @@ instance Binary TemplateElement where
put (Key key) = putWord8 1 >> put key
put (Escaped) = putWord8 2
put (If key t f) = putWord8 3 >> put key >> put t >> put f
+ put (For key b s) = putWord8 4 >> put key >> put b >> put s
get = getWord8 >>= \tag -> case tag of
0 -> Chunk <$> get
1 -> Key <$> get
2 -> pure Escaped
- 3 -> If <$> get <*> get <*> get
+ 3 -> If <$> get <*> get <*> get
+ 4 -> For <$> get <*> get <*> get
_ -> error $
"Hakyll.Web.Template.Internal: Error reading cached template"
diff --git a/src/Hakyll/Web/Template/Read.hs b/src/Hakyll/Web/Template/Read.hs
index 620ce14..2dd0fc5 100644
--- a/src/Hakyll/Web/Template/Read.hs
+++ b/src/Hakyll/Web/Template/Read.hs
@@ -26,7 +26,8 @@ readTemplate input = case parse template "" input of
--------------------------------------------------------------------------------
template :: Parser Template
-template = Template <$> (many1 $ chunk <|> escaped <|> conditional <|> key)
+template = Template <$>
+ (many1 $ chunk <|> escaped <|> conditional <|> for <|> key)
--------------------------------------------------------------------------------
@@ -52,6 +53,18 @@ conditional = try $ do
--------------------------------------------------------------------------------
+for :: Parser TemplateElement
+for = try $ do
+ void $ string "$for("
+ i <- metadataKey
+ void $ string ")$"
+ body <- template
+ sep <- optionMaybe $ try (string "$sep$") >> template
+ void $ string "$endfor$"
+ return $ For i body sep
+
+
+--------------------------------------------------------------------------------
key :: Parser TemplateElement
key = try $ do
void $ char '$'
diff --git a/tests/Hakyll/Web/Template/Context/Tests.hs b/tests/Hakyll/Web/Template/Context/Tests.hs
index 5533c71..627624f 100644
--- a/tests/Hakyll/Web/Template/Context/Tests.hs
+++ b/tests/Hakyll/Web/Template/Context/Tests.hs
@@ -51,4 +51,9 @@ testContextDone :: Store -> Provider -> Identifier -> String
testContextDone store provider identifier key context =
testCompilerDone store provider identifier $ do
item <- getResourceBody
- unContext context key item
+ cf <- unContext context key item
+ case cf of
+ StringField str -> return str
+ ListField _ _ -> error $
+ "Hakyll.Web.Template.Context.Tests.testContextDone: " ++
+ "Didn't expect ListField"
diff --git a/tests/Hakyll/Web/Template/Tests.hs b/tests/Hakyll/Web/Template/Tests.hs
index b96cfa5..1d80a06 100644
--- a/tests/Hakyll/Web/Template/Tests.hs
+++ b/tests/Hakyll/Web/Template/Tests.hs
@@ -13,6 +13,7 @@ import Test.HUnit (Assertion, (@=?), (@?=))
--------------------------------------------------------------------------------
+import Hakyll.Core.Compiler
import Hakyll.Core.Item
import Hakyll.Core.Provider
import Hakyll.Web.Pandoc
@@ -43,6 +44,8 @@ case01 = do
item <- testCompilerDone store provider "example.md" $
pandocCompiler >>= applyTemplate (itemBody tpl) testContext
+ writeFile "foo" (itemBody item)
+
out @=? itemBody item
cleanTestEnv
@@ -50,9 +53,13 @@ case01 = do
--------------------------------------------------------------------------------
testContext :: Context String
testContext = mconcat
- [ functionField "echo" (\args _ -> return $ unwords args)
- , defaultContext
+ [ defaultContext
+ , listField "authors" (bodyField "name") $ do
+ n1 <- makeItem "Jan"
+ n2 <- makeItem "Piet"
+ return [n1, n2]
]
+ where
--------------------------------------------------------------------------------
diff --git a/tests/data/template.html b/tests/data/template.html
index 6f668ee..22e5ddd 100644
--- a/tests/data/template.html
+++ b/tests/data/template.html
@@ -1,16 +1,27 @@
<div>
I'm so rich I have $$3.
- $echo test$
+
$if(body)$
I have body
$else$
or no
$endif$
+
$if(unbound)$
should not be printed
$endif$
+
$if(body)$
should be printed
$endif$
+
+ <ul>
+ $for(authors)$
+ <li>$name$</li>
+ $endfor$
+ </ul>
+
+ $for(authors)$$name$$sep$, $endfor$
+
$body$
</div>
diff --git a/tests/data/template.html.out b/tests/data/template.html.out
index 5c201e2..8047b0d 100644
--- a/tests/data/template.html.out
+++ b/tests/data/template.html.out
@@ -1,12 +1,25 @@
<div>
I'm so rich I have $3.
- test
+
I have body
+
+
should be printed
+
+ <ul>
+
+ <li>Jan</li>
+
+ <li>Piet</li>
+
+ </ul>
+
+ Jan, Piet
+
<p>This is an example.</p>
</div>