在Hakyll中,如何生成标签页面?

时间:2018-10-14 17:22:43

标签: haskell hakyll

我正在尝试做类似what's described in this tutorial的操作,即,将标签添加到我的Hakyll博客中,但是与其为每个标签生成一个页面,还没有一个页面列出所有标签及其帖子。因此,给定一个Post1标记为Tag1,一个Post2标记为Tag1, Tag2和一个Post3标记为Tag2的情况,我的tags.html将会看起来像这样:

 Tag1: 
  - Post1
  - Post2
 Tag2: 
  - Post2
  - Post3

但是我是Haskell的初学者,并且我不完全了解Hakyll的所有单子语境。这是我到目前为止的内容:

create ["tags.html"] $ do
    route idRoute
    tags <- buildTags "posts/*" (fromCapture "tags.html")
    compile $
        makeItem ""
            >>= applyTemplate tagListTemplate defaultContext
            >>= applyTemplate defaultTemplate defaultContext
            >>= relativizeUrls
            >>= cleanIndexUrls

问题是,在我的博客环境中,我真的不知道Tags是什么。我似乎无法将它们打印出来进行调试。 (我尝试添加print tags,但没有用。)因此,我很难思考如何进行此操作。

The complete file is here on GitHub.

非常感谢您的帮助。

更新:我距离解决这个问题还差得很远。这是我现在正在尝试的:

create ["tags.html"] $ do
        route idRoute
        tags <- buildTags "posts/*" (fromCapture "tags.html#")
        let tagList = tagsMap tags
        compile $ do
            makeItem ""
              >>= applyTemplate tagListTemplate (defaultCtxWithTags tags)

以及:

-- Add tags to default context, for tag listing
defaultCtxWithTags :: Tags -> Context String
defaultCtxWithTags tags = listField "tags" defaultContext (return (tagsMap tags)) `mappend` defaultContext

The full code, as it currently stands, is up here.

任何对此的帮助将不胜感激。我知道所有文档,但似乎无法将其转换为工作代码。

2 个答案:

答案 0 :(得分:3)

我已经修改了您的site.hs,以创建一个基本的标签列表页面,我认为该页面具有所需的结构:标签列表,每个标签列表都包含带有该标签的帖子。

以下是我要使其正常工作必须做的每件事的摘要:

{-# LANGUAGE ViewPatterns #-}

不是严格必需的,但是我曾经使用过一个不错的语言扩展。我以为我会使用/提及它,因为您提到您是Haskell的初学者,很高兴知道。

tags <- buildTags "posts/*" (fromCapture "tags/*.html")

与初始buildTags中的site.hs相比,此行需要进行两项更改。一种是它可能应该从单个match子句中移出到顶级Rules monad中,以便我们可以根据需要创建单个标记页。另一个是捕获类似地从"tags.html#"更改为"tags/*.html"。这很重要,因为Hakyll希望每个Item都具有唯一的Identifier,并且并非每个标签页面都是相同的。

具有唯一标识符的各个标签页可能不是严格必要的,但是由于许多Hakyll机器都假定它们存在,因此简化了其余设置。特别是,各个帖子描述中的Tags:行以前也没有正确显示。

出于相同的原因,最好使这些单独的标签页可路由:不使用顶级Rules monad中的该节,则每个帖子上的标签都无法使用默认的{您使用的{1}},因为他们不知道如何链接到单个标签页:

tagsField

好的,那是预备赛。现在进入主要景点:

tagsRules tags $ \tag pat -> do
    route idRoute
    compile $ do
        posts <- recentFirst =<< loadAll pat
        let postCtx = postCtxWithTags tags
            postsField = listField "posts" postCtx (pure posts)
            titleField = constField "title" ("Posts tagged \""++tag++"\"")
            indexCtx = postsField <> titleField <> defaultContext
        makeItem "" >>= applyTemplate postListTemplate indexCtx
                    >>= applyTemplate defaultTemplate defaultContext
                    >>= relativizeUrls
                    >>= cleanIndexUrls

好的,这里添加的重要内容是一个defaultCtxWithTags tags = listField "tags" tagsCtx getAllTags `mappend` defaultContext 字段。 tags返回的每项将包含一个项目,getAllTags将给出每个项目的字段。

tagsCtx

where getAllTags :: Compiler [Item (String, [Identifier])] getAllTags = pure . map mkItem $ tagsMap tags where mkItem :: (String, [Identifier]) -> Item (String, [Identifier]) mkItem x@(t, _) = Item (tagsMakeId tags t) x 在做什么?好吧,它以getAllTags开头,就像您的示例一样。但是Hakyll希望结果为tagsMap tags,因此我们必须使用Item将其包装起来。 mkItem除了身体之外还有什么?只是一个Item,而Identifier对象恰好包含一个告诉我们如何获得此值的字段!因此,Tags仅使用mkItem来获取标识符,并使用该标识符包装给定的正文。

tagsMakeId

tagsCtx?

tagsCtx :: Context (String, [Identifier]) tagsCtx = listFieldWith "posts" postsCtx getPosts `mappend` metadataField `mappend` urlField "url" `mappend` pathField "path" `mappend` titleField "title" `mappend` missingField 开头的所有内容都是我们期望从metadataField获得的常见内容;我们无法在此处使用defaultContext,因为它想添加一个defaultContext,但是此bodyField的主体不是字符串(而是对我们的Haskell结构表示有用,标签)。有趣的是,这行添加了Item字段,这看起来有些熟悉。最大的区别是它使用posts而不是listFieldWith,这基本上意味着listField得到了一个额外的参数,它是该字段所在的getPosts的主体。在这种情况下,这就是Item的标签记录。

tagsMap

where getPosts :: Item (String, [Identifier]) -> Compiler [Item String] getPosts (itemBody -> (_, is)) = mapM load is 通常仅使用getPosts函数来获取给定load的每个帖子的Item,这与{{1} },您可以在索引页面上获取所有帖子,但这只为您提供了一篇帖子。左边的模式匹配看起来很奇怪,实际上是Identifier:基本上是说,要匹配此模式,loadAll(即ViewPatterns)右边的模式应该将应用左侧函数(即->)的结果与参数匹配。

(_, is)

itemBody非常简单:就像我们在其他地方渲染帖子一样使用的 postsCtx :: Context String postsCtx = postCtxWithTags tags

这是使postsCtx与您想要的所有内容所必需的一切;剩下的就是实际制作一个模板来渲染它!

postCtxWithTags

这只是一个非常简单的模板,用于呈现嵌套列表;当然,您可以做各种事情使它看起来更漂亮/更黑。

我已对您的仓库进行了PR,以便您可以在上下文here中看到这些更改。

答案 1 :(得分:1)

这是我们为实现此行为而在网页上所做的事情:

Kowainik webpage tags building

以及标记页的示例:

https://kowainik.github.io/tags/haskell

您可以询问有关代码的任何问题:)