How do I avoid a dependency cycle when generating a list of recent posts on posts?

我是研究僧i 提交于 2020-01-03 09:45:34

问题


So this works:

create ["archive.html"] $ do
    route idRoute
    compile $ do
        posts <- (myRecentFirst gitTimes) =<< loadAll "posts/**"
        let archiveCtx =
                listField "posts" (postCtx allTags allCategories gitTimes) (return posts) `mappend`
                constField "title" "Archives"            `mappend`
                (postCtx allTags allCategories gitTimes)

        makeItem ""
            >>= loadAndApplyTemplate "templates/archive.html" archiveCtx
            >>= loadAndApplyTemplate "templates/default.html" archiveCtx
            >>= relativizeUrls

to create a list of recent posts in archive.html ; this is bog-standard, it came from one of the tutorials I think. Except for postsCtx, which is a tad complicated, but shouldn't be relevant here.

However, I want to put a list of a few recent posts in the sidebar of normal posts. The problem becomes that the recent posts end up depending on themselves. I tried excluding the post itself from its own generated list, but I couldn't find a good place to do that. Here's what I've got so far:

match "posts/**" $ do
    route $ (gsubRoute "posts/" (const "")) `composeRoutes` setExtension "html"
    compile $ do
        recents <- (myRecentFirst gitTimes) =<< loadAll "posts/**"
        let postsContext = postCtx allTags allCategories gitTimes `mappend`
                           -- Show recent posts
                           recentsNotSelfField "recents" (postCtx allTags allCategories gitTimes) (return $ take 3 recents)

        pandocCompilerWithTransform hblogPandocReaderOptions hblogPandocWriterOptions (titleFixer titles)
            >>= loadAndApplyTemplate "templates/post.html"    postsContext
            >>= loadAndApplyTemplate "templates/default.html" postsContext
            >>= relativizeUrls

recentsNotSelfField :: String -> Context a -> Compiler [Item a] -> Context b
recentsNotSelfField key context items = Context $ \k _ i ->·
    if k == key then do
        let myId = itemIdentifier i
        strippedItems <- items
        let remains = filter (\x -> (itemIdentifier x) /= myId) strippedItems
        return $ ListField context remains
    else
        CA.empty

recentsNotSelfField should produce a field with all the recents except itself, but it doesn't seem to be working or it's the wrong place to do that, because:

Initialising...
  Creating store...
  Creating provider...
  Running rules...
Checking for out-of-date items
Compiling
  [ERROR] Hakyll.Core.Runtime.chase: Dependency cycle detected: posts/computing/contact.md depends on posts/computing/contact.md

I'm stuck so far.

EDIT:

I saw Hakyll says "Dependency cycle detected: ..." , that it's the loadPosts that does it, so I tried this:

match "posts/**" $ do
    route $ (gsubRoute "posts/" (const "")) `composeRoutes` setExtension "html"
    compile $ do
        myId <- getUnderlying
        recents <- (myRecentFirst gitTimes) =<< loadAll ("posts/**" .&&. complement (fromList [myId]))
        let postsContext = postCtx allTags allCategories gitTimes `mappend`
                           -- Show recent posts
                           listField "recents" (postCtx allTags allCategories gitTimes) (return $ take 3 recents)

        pandocCompilerWithTransform hblogPandocReaderOptions hblogPandocWriterOptions (titleFixer titles)
            >>= loadAndApplyTemplate "templates/post.html"    postsContext
            >>= loadAndApplyTemplate "templates/default.html" postsContext
            >>= relativizeUrls

but that just gets me:

[ERROR] Hakyll.Core.Runtime.chase: Dependency cycle detected: posts/computing/contact.md depends on posts/computing/general/ched.md depends on posts/computing/contact.md

, in other words I end up with the two most recent cycling around each other.


回答1:


Turns out the technique at https://stackoverflow.com/a/35651294/1461430 works for me, and in fact it works better for me than the author said it would. Here's the relevant bits of my code now:

match "posts/**" $ version "recents" $ do
    route $ (gsubRoute "posts/" (const "")) `composeRoutes` setExtension "html"
    compile $ do
        pandocCompilerWithTransform hblogPandocReaderOptions hblogPandocWriterOptions (titleFixer titles)
            >>= loadAndApplyTemplate "templates/post.html"    (postCtx allTags allCategories gitTimes)
            >>= relativizeUrls

match "posts/**" $ do
    route $ (gsubRoute "posts/" (const "")) `composeRoutes` setExtension "html"
    compile $ do
        myId <- getUnderlying
        recents <- (myRecentFirst gitTimes) =<< loadAll ("posts/**" .&&. hasVersion "recents")
        let postsContext = postCtx allTags allCategories gitTimes `mappend`
                           -- Show recent posts
                           listField "recents" (postCtx allTags allCategories gitTimes) (return $ take 3 recents)

        pandocCompilerWithTransform hblogPandocReaderOptions hblogPandocWriterOptions (titleFixer titles)
            >>= loadAndApplyTemplate "templates/post.html"    postsContext
            >>= loadAndApplyTemplate "templates/default.html" postsContext
            >>= relativizeUrls

I had to add the listField and recents to several other places that used templates/default.html, but that was straightforward.

I also had to modify a function that used Identifier to look up a list of times pulled from git:

-- Pull a file's time out of the list
getGitTimeUTC :: Identifier -> [GitTimes] -> (GitTimes -> UTCTime) -> UTCTime
getGitTimeUTC ident times typeF =
  -- The identifier for the things compiled with the "recents"
  -- version has the identifierVersion "recents", but we don't care
  -- about that since the only reason that exists is to avoid loops,
  -- so we strip it here for our lookup.
  let fixedIdent = ident { identifierVersion = Nothing }
      timeList = filter (\x -> fixedIdent == (gtid x)) times in
    if length timeList /= 1 then
      -- It's not obvious to me how this could occur even in theory; I'd expect it to error out during getGitTimes
      error $ "getGitTimeUTC: Couldn't find the time for " ++ (show fixedIdent) ++ " in GitTimes list " ++ (show times)
    else
      typeF $ head timeList

I have no duplication of files as a result, and had no problem making links to the recents files; everything just works.



来源:https://stackoverflow.com/questions/47067851/how-do-i-avoid-a-dependency-cycle-when-generating-a-list-of-recent-posts-on-post

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!