on master help sign in
Finish tutorial on snapshots/feeds
commit408a991b5eb2a766402b6c9b6caeada98665bfa4 authorJasper Van der Jeugt <m@jaspervdj.be> dateSun 13 Jan 2013, 11:20 (14 years ago) parentc59d113
1 file changed+89−1
web/tutorials/05-snapshots-feeds.markdown+89−1
web/tutorials/05-snapshots-feeds.markdown+89−1
@@ -22,7 +22,10 @@ myFeedConfiguration = FeedConfiguration
2222 }
2323```
2424
25−Now, we'll create the actual feed.
25+Simple feed rendering
26+---------------------
27+
28+Now, let's look at how we actually create a feed. Two functions are available:
2629
2730```haskell
2831renderAtom :: FeedConfiguration
@@ -37,3 +40,88 @@ renderRss :: FeedConfiguration
3740 -> [Item String]
3841 -> Compiler (Item String)
3942```
43+
44+As you can see, they have exactly the same signature: we're going to use
45+`renderAtom` in this tutorial, but it's trivial to change this to an RSS feed.
46+
47+```haskell
48+create ["atom.xml"] $ do
49+ route idRoute
50+ compile $ do
51+ let feedCtx = postCtx `mappend`
52+ constField "description" "This is the post description"
53+
54+ posts <- take 10 . recentFirst <$> loadAll "posts/*"
55+ renderAtom myFeedConfiguration feedCtx posts
56+```
57+
58+There we go! We simply take the 10 last posts and pass them to `renderAtom`,
59+with our configuration and a `Context`.
60+
61+It's a bit of a problem that we don't have a description for our posts, and the
62+Atom/RSS feed renderer requires this. One option is to add a `description: Foo`
63+header to our all posts. However, the description is the body text as it appears
64+in most RSS readers, so we would prefer to include the entire content of the
65+posts here.
66+
67+Snapshots
68+---------
69+
70+This poses a problem: if we just `load` all posts and take their content: we get
71+the *finished, processed* content. This means all templates have been applied at
72+that point (including `templates/default.html`). We don't want to include our
73+entire site with navigation in the RSS feed, but rather just the post HTML.
74+
75+Snapshots provide a solution to this problem. They allow you to save an `Item`
76+at any point during its compilation, so you can `load` it later. Let's apply
77+this to our concrete problem.
78+
79+```haskell
80+match "posts/*" $ do
81+ route $ setExtension "html"
82+ compile $ pandocCompiler
83+ >>= loadAndApplyTemplate "templates/post.html" postCtx
84+ >>= loadAndApplyTemplate "templates/default.html" postCtx
85+ >>= relativizeUrls
86+```
87+
88+now becomes:
89+
90+```haskell
91+match "posts/*" $ do
92+ route $ setExtension "html"
93+ compile $ pandocCompiler
94+ >>= loadAndApplyTemplate "templates/post.html" postCtx
95+ >>= saveSnapshot "content"
96+ >>= loadAndApplyTemplate "templates/default.html" postCtx
97+ >>= relativizeUrls
98+```
99+
100+The `saveSnapshot` function is really simple: it takes an item and returns the
101+same item, after saving it. This return value makes it easier to use
102+`saveSnapshot` in a chain of compilers as we did in the above example, but you
103+can discard it if you want.
104+
105+```haskell
106+type Snapshot = String
107+
108+saveSnapshot :: (Typeable a, Binary a)
109+ => Snapshot -> Item a -> Compiler (Item a)
110+```
111+
112+Including the post body
113+-----------------------
114+
115+With this modification, we can update our Atom code. Instead of loading the
116+compiled posts, we just load their content (i.e. the snapshot we just took).
117+
118+We update the `Context` to map `$description$` to the post body, and we're done!
119+
120+```haskell
121+create ["atom.xml"] $ do
122+ route idRoute
123+ compile $ do
124+ let feedCtx = postCtx `mappend` bodyField "description"
125+ posts <- take 10 . recentFirst <$> loadAllSnapshots "posts/*" "content"
126+ renderAtom myFeedConfiguration feedCtx posts
127+```