【问题标题】:Creating a list field from metadata in Hakyll从 Hakyll 中的元数据创建列表字段
【发布时间】:2019-08-11 09:33:10
【问题描述】:

我正在尝试为 Hakyll 中元数据中有 versions 条目的帖子生成 HTML。例如,一个帖子可能有versions: Python 3.4, pytest 1.5.2,它会在帖子底部很好地格式化。

为了实现这一点,我想创建一个上下文来加载元数据并创建一个ListField。类似于以下存根:

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
                }

在我的post.html 模板中,我有:

...
    <section>
        $body$

        $if(versions)$
        <hr />
        <ul>
            $for(versions)$
                <li>$version$</li>
            $endfor$
        </ul>
        $else$
            <p>Fail...</p>
        $endif$
    </section>
...

然而,我尝试了许多不同的versionsCtx 定义,并在网上找到了类似的尝试。似乎没有任何工作,并且帖子总是以“失败......”呈现。我做错了什么?

编辑:更新了问题,提出了建议和说明。

【问题讨论】:

    标签: metadata listfield hakyll


    【解决方案1】:

    您的代码存在多个问题:

    1. getMetadataField 提供了一个 Maybe 类型,在 Haskell 中有数据构造函数 JustNothing,而不是 SomeNone
    2. makeItem 函数创建了一个已包装在 Compiler 中的 Item,导致以下错误:
    • Couldn't match type ‘Compiler (Item String)’ with ‘Item String’
      Expected type: Compiler [Item String]
        Actual type: Compiler [Compiler (Item String)]
    

    虽然您可以尝试从中提取项目,但使用以下方法从头开始创建项目可能更简洁:

    mkVersionItem version = Item {
        itemIdentifier = fromString ("version/" ++ version),
        itemBody = version
    }
    
    1. 我没有看到您将新创建的上下文添加到帖子上下文中。你这样做了吗?
    2. 正如docs 中所述,附加Contexts 的顺序很重要。从您的问题中看不出,但您可能正在使用defaultContext,其中包括metadataField。您在帖子的元数据块中有versions 字段,因此当defaultContext 获胜时,它将使versions 在模板中作为字符串字段可用。当versions 是一个字符串字段时,$if(versions)$ 出于某种原因跳转到else 分支,这解释了为什么显示“失败”。当您将 for 循环移到条件块之外时,您可以在控制台中看到更多信息错误:
    [ERROR] Hakyll.Web.Template.applyTemplateWith: expected ListField but got StringField for expr versions
    

    完整的代码可能如下所示:

    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
        }
    

    【讨论】:

    • 感谢您的建议。我更新了我的问题,以更好地反映我的 versionsCtx 是一个存根。我将versionsCtx 添加到match "posts/*" 规则的上下文中。我通过添加versionsCtx before postCtx 而不是 after 来让它工作。文档说Context monoid 的组合顺序很重要,因为字段可以被覆盖,但在这种情况下,我的postCtx 不会覆盖任何内容(包含日期、标签等),所以我不确定为什么在这里订购很重要。
    • 啊哈!这就解释了。 metadataField 的源代码显示了默认情况下如何使用 maybe 创建字符串字段。非常感谢!我在site.hs 中添加了一条评论,以确保我以后记得。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-13
    • 2014-02-10
    • 2018-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多