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

标签 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 的所有 monadic 上下文。这是我到目前为止所拥有的:
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.

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

最佳答案

我已经修改了你的 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,每个帖子上的标签在默认情况下不会正确呈现 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 field 。对于 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正在做?好吧,它以 tagsMap tags 开头,就像你的例子一样。但是 Hakyll 希望结果是 Item ,所以我们必须使用 mkItem 把它包起来. Item里面有什么|除了 body ?只是一个 Identifier ,以及 Tags object 恰好包含一个字段,告诉我们如何获取它!所以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 ,但正文本Item不是一个字符串(而是一个对我们来说更有用的表示标签的 Haskell 结构)。有趣的一点是添加了 posts 的行领域,这应该看起来有点熟悉。最大的不同在于它使用 listFieldWith而不是 listField ,这基本上意味着 getPosts得到一个额外的参数,它是 Item 的主体该字段已启用。在这种情况下,这是来自 tagsMap 的标签记录.
          where getPosts :: Item (String, [Identifier])
                         -> Compiler [Item String]
                getPosts (itemBody -> (_, is)) = mapM load is
getPosts大多只是使用 load获取 Item 的函数对于每个帖子给出的 Identifier ---它很像loadAll您这样做是为了获取索引页面上的所有帖子,但它只会给您一个帖子。左边看起来很奇怪的模式匹配是 ViewPatterns实际操作:基本上是说要匹配此模式,-> 右侧的模式(即 (_, is) )应该匹配将左边的函数(即 itemBody )应用于参数的结果。
                postsCtx :: Context String
                postsCtx = postCtxWithTags tags
postsCtx很简单:就是一样postCtxWithTags在我们渲染帖子的其他地方使用。

这是获得 Context 所需的一切拥有你想要的一切;剩下的就是实际制作一个模板来渲染它!
tagListTemplateRaw :: Html
tagListTemplateRaw =
  ul $ do
    "$for(tags)$"
    li ! A.class_ "" $ do
      a ! href "$url$" $ "$title$"
      ul $ do
        "$for(posts)$"
        li ! A.class_ "" $ do
          a ! href "$url$" $ "$title$"
        "$endfor$"
    "$endfor$"

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

我已经对你的 repo 做了 PR,以便你可以在上下文中看到这些变化 here .

关于haskell - 在 Hakyll 中,如何生成标签页面?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52805193/

相关文章:

'selective' map 的 Haskell 习语

Haskell SDL 绑定(bind) : How to blit sprites with transparent color?

xml - 使用 xml-conduit 的脆弱和冗长的代码

haskell - 从页面中删除预告片

haskell - 哈基尔说 "Dependency cycle detected: ..."

haskell - 使用 haskell turtle 库运行交互式命令

inheritance - Haskell 类型类继承和参数类型类

haskell - 按自定义字段对 Hakyll 项目列表进行排序

haskell - 堆栈 ghc 错误 "Invalid Option ` --make`

haskell - 如何在 Hakyll 中使用 pandoc-crossref