on b287c82 help sign in
hakyll/web/tutorials/04-compilers.markdown
178 lines 5.1 kB history raw
1---
2title: More on compilers: load, and templates
3author: Jasper Van der Jeugt
4---
6Loading items
7-------------
9The compiler Monad is a complex beast, but this is nicely hidden for the user of
10the Hakyll library.
12Suppose that you're generating `index.html` which shows your latest brilliant
13blogpost. This requires `posts/foo.markdown` to be generated *before*
14`index.html` (so we don't have to generate it twice). But you don't have to care
15about any of that: Hakyll will sort this out for you automatically!
17Let's see some quick examples. We can load a specific item:
19```haskell
20load "posts/foo.markdown" :: Compiler (Item String)
21```
23Or a whole bunch of them:
25```haskell
26loadAll "posts/*" :: Compiler [Item String]
27```
29Sometimes you just want the *contents* and not the `Item`:
31```haskell
32loadBody "posts/foo.markdown" :: Compiler String
33```
35This is all useful if we want to use Hakyll's templating system.
37Templates
38---------
40### Basic templates
42Let's have a look at a simple template:
44```html
45<h1>$title$</h1>
46<div class="info">Posted on $date$</div>
47$body$
48```
50As you can probably guess, template files just contain text and only the `$`
51character has special meaning: text between dollar signs ("fields") is replaced
52when the template is applied. If you want an actual dollar sign in the output,
53use `$$`.
55You usually compile the templates from disk using the aptly named
56`templateCompiler`:
58```haskell
59match "templates/*" $ compile templateCompiler
60```
62Notice the lack of `route` here: this is because we don't need to write the
63templates to your `_site` folder, we just want to use them elsewhere.
65### Templates: Context
67We can easily guess the meaning of `$title$`, `$date$`, and `$body$`, but these
68are not hard-coded fields: they belong to a certain [Context]. A `Context`
69determines how the fields are interpreted. It's a [Monoid] and therefore very
70composable.
72[Context]: /reference/Hakyll-Web-Template-Context.html
73[Monoid]: http://learnyouahaskell.com/functors-applicative-functors-and-monoids
75`field` allows us to create a `Context` for a single field:
77```haskell
78field :: String -> (Item a -> Compiler String) -> Context a
79```
81Let's try this out. Note that this is for illustration purposes only: you
82shouldn't have to write complicated fields often. We can implement the `$body$`
83field like this:
85```haskell
86field "body" $ \item -> return (itemBody item) :: Context String
87```
89And `$title$` like this:
91```haskell
92titleContext :: Context a
93titleContext = field "title" $ \item -> do
94 metadata <- getMetadata (itemIdentifier item)
95 return $ fromMaybe "No title" $ M.lookup "title" metadata
96```
98And compose them using the `Monoid` instance:
100```haskell
101context :: Context String
102context = mconcat
103 [ titleContext
104 , field "body" $ return . itemBody
105 ]
106```
108Obviously, it would be tedious to implement things like `titleContext` over and
109over again for different websites and different fields. This is why hakyll
110provides `defaultContext`. `defaultContext` is a composed `Context` and allows
111you to use:
113- `$body$` for the body of the page;
114- `$url$` for the destination URL of the page;
115- `$path$` for the original filepath of the page;
116- `$foo$` where foo is specified in the metadata.
118`$date$` is not provided by default, you can see how we add it in the definition
119of `postCtx` in `site.hs`:
121```haskell
122postCtx :: Context String
123postCtx =
124 dateField "date" "%B %e, %Y" `mappend`
125 defaultContext
126```
128### Loading and applying templates
130Now we know about templates, context and how to load arbitrary items. This gives
131us enough background information in order to understand you can apply a
132template:
134```haskell
135compile $ do
136 tpl <- loadBody "templates/post.html"
137 pandocCompiler >>= applyTemplate tpl postCtx
138```
140Loading and then immediately applying a template is so common there's a
141shorthand function:
143```haskell
144compile $
145 pandocCompiler >>= loadAndApplyTemplate "templates/post.html" postCtx
146```
148## Producing a list of items
150At this point, everything in the example site we generated should be clear to
151you, except for how we produce the list of posts in `archive.html` and
152`index.html`.
154However, this really isn't hard and just uses the things we saw before: loading
155other items and applying templates.
157We can reproduce a list of items in the archive using the following code:
159```haskell
160compile $ do
161 posts <- recentFirst =<< loadAll "posts/*"
162 itemTpl <- loadBody "templates/post-item.html"
163 list <- applyTemplateList itemTpl postCtx posts
164 makeItem list
165```
167`recentFirst` sorts items by date. This relies on the convention that posts are
168always named `YYYY-MM-DD-title.extension` in Hakyll -- or that the date must be
169present in the metadata.
171```haskell
172recentFirst :: [Item a] -> Compiler [Item a]
173```
175After loading and sorting the items, we load a template for the posts.
176`applyTemplateList` applies this template to every post and concatenates the
177result, which is a simple `String`. After that, we need `makeItem` to wrap the
178returned `String` to `Item String`.