I'm trying to generate HTML for posts in Hakyll that have a versions
entry in their metadata. For example, a post may have versions: Python 3.4, pytest 1.5.2
which would be formatted nicely at the bottom of the post.
To achieve this, I want to create a context which loads the metadata and creates a ListField
. Something like the following stub:
versionsCtx :: Context String
versionsCtx = listFieldWith "versions" ctx (\item -> do
versions <- getMetadataField (itemIdentifier item) "versions"
return $ case versions of
Just lst -> map (mkVersinoItem . trim) $ splitAll "," lst
Nothing -> [])
where ctx = field "version" (return . itemBody)
mkVersionItem version = Item {
itemIdentifier = fromString ("version/" ++ version),
itemBody = version
}
In my post.html
template, I have:
...
<section>
$body$
$if(versions)$
<hr />
<ul>
$for(versions)$
<li>$version$</li>
$endfor$
</ul>
$else$
<p>Fail...</p>
$endif$
</section>
...
Yet I have tried many different definitions of versionsCtx
and found similar attempts online. None seem to work and the post is always rendered with "Fail...". What am I doing wrong?
EDIT: Updated question with suggestions and clarifications.
There are multiple issues with your code:
getMetadataField
provides a Maybe
type, which in Haskell has data constructors Just
and Nothing
, not Some
and None
.makeItem
function creates an Item
already wrapped in a Compiler
, resulting in the following error:• Couldn't match type ‘Compiler (Item String)’ with ‘Item String’
Expected type: Compiler [Item String]
Actual type: Compiler [Compiler (Item String)]
While you could try to extract the item from it, it is probably cleaner to create an item from scratch using something like this:
mkVersionItem version = Item {
itemIdentifier = fromString ("version/" ++ version),
itemBody = version
}
Context
s are being appended is important. It is not evident from your question, but you are probably using defaultContext
, which includes metadataField
. You have versions
field in the metadata block of your posts so when the defaultContext
wins, it will make versions
available as a string field in the template. $if(versions)$
for some reason jumps to the else
branch when versions
is a string field, which explains why “Fail” is shown. You can see a more informative error in the console when you move the for
loop outside the conditional block:[ERROR] Hakyll.Web.Template.applyTemplateWith: expected ListField but got StringField for expr versions
In full the code could look something like this:
import Data.String (fromString)
postCtx :: Context String
postCtx =
versionsCtx `mappend`
dateField "date" "%B %e, %Y" `mappend`
defaultContext
versionsCtx :: Context String
versionsCtx = listFieldWith "versions" ctx (\item -> do
versions <- getMetadataField (itemIdentifier item) "versions"
return $ case versions of
Just lst -> map (mkVersionItem . trim) $ splitAll "," lst
Nothing -> []
)
where
ctx = field "version" (return . itemBody)
mkVersionItem version = Item {
itemIdentifier = fromString ("version/" ++ version),
itemBody = version
}