在Hakyll,我如何生成标签页?

Jon*_*han 11 haskell hakyll

我正在尝试做类似本教程中描述的内容,即向我的Hakyll博客添加标签,但不是为每个标签生成一个页面,只需要一个页面列出所有标签及其帖子.所以给定一个Post1标记Tag1,Post2标记Tag1, Tag2Post3标记Tag2,我tags.html会看起来像这样:

 Tag1: 
  - Post1
  - Post2
 Tag2: 
  - Post2
  - Post3
Run Code Online (Sandbox Code Playgroud)

但我是哈斯克尔的初学者,我并不完全理解哈基尔的所有monadic环境.这是我到目前为止所拥有的:

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

问题是,Tags在我的博客中,我真的不知道是什么.我似乎无法将它们打印出来进行调试.(我尝试添加print tags,但它不起作用.)所以我很难想到如何继续这个.

完整的文件在GitHub上.

任何帮助深表感谢.

更新:我仍然没有更接近解决这个问题.这是我现在正在尝试的内容:

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

随着:

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

目前的完整代码就在这里.

任何有关这方面的帮助将非常感激.我知道所有文档,但我似乎无法将其转换为工作代码.

Pet*_*don 6

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

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

{-# LANGUAGE ViewPatterns #-}
Run Code Online (Sandbox Code Playgroud)

并非绝对必要,但我曾经使用过一种不错的语言扩展。我以为我会使用/提及它,因为您提到您是Haskell的初学者,并且很高兴知道。

tags <- buildTags "posts/*" (fromCapture "tags/*.html")
Run Code Online (Sandbox Code Playgroud)

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

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

出于同样的原因,最好使这些单独的标签页可路由:一个好主意:如果顶级Rulesmonad中没有此节,则每个帖子上的标签都无法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
Run Code Online (Sandbox Code Playgroud)

好吧,这是预备赛。现在到主要景点:

defaultCtxWithTags tags = listField "tags" tagsCtx getAllTags         `mappend`
                          defaultContext
Run Code Online (Sandbox Code Playgroud)

好吧,这里添加的重要内容是某个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
Run Code Online (Sandbox Code Playgroud)

什么是getAllTags做什么?嗯,tagsMap tags就像您的示例一样,它以开头。但是Hakyll希望结果为an 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
Run Code Online (Sandbox Code Playgroud)

一切都是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
Run Code Online (Sandbox Code Playgroud)

getPosts给定它的每个帖子,大多数情况下只是使用该load函数来获取Item它们的Identifier---就像loadAll您在索引页面上获取所有帖子一样,但是它只给您一个帖子。左侧的模式匹配看起来很奇怪ViewPatterns:实际上是在说要使该模式匹配,->(_, is))右侧的模式应与将左侧(即itemBody)函数应用于该模式的结果相匹配。论点。

                postsCtx :: Context String
                postsCtx = postCtxWithTags tags
Run Code Online (Sandbox Code Playgroud)

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$"
Run Code Online (Sandbox Code Playgroud)

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

我已经对您的仓库进行了公关,以便您可以在此处查看这些更改。


vro*_*911 1

以下是我们为在网页上实现此行为所做的操作:

Kowainik 网页标签构建

以及标签页的示例:

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

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