on master help sign in
Update hakyll site
commit6950ab16f89236da2e00a812beb8d599d736e911 authorJasper Van der Jeugt <jaspervdj@gmail.com> dateMon 28 Feb 2011, 11:18 (16 years ago) parent0f51cf0
13 files changed−1223
examples/hakyll/examples/brochure.zip examples/hakyll/images/arrow-composition.png examples/hakyll/images/tutorial8-categories.png examples/hakyll/images/tutorial8-tags.png examples/hakyll/tutorials/part01.markdown−235 examples/hakyll/tutorials/part02.markdown−91 examples/hakyll/tutorials/part03.markdown−174 examples/hakyll/tutorials/part04.markdown−60 examples/hakyll/tutorials/part05.markdown−157 examples/hakyll/tutorials/part06.markdown−84 examples/hakyll/tutorials/part07.markdown−221 examples/hakyll/tutorials/part08.markdown−97 examples/hakyll/tutorials/part09.markdown−104
examples/hakyll/examples/brochure.zip

Binary file not shown.

examples/hakyll/images/arrow-composition.pngdeleted

Binary file not shown.

examples/hakyll/images/tutorial8-categories.pngdeleted

Binary file not shown.

examples/hakyll/images/tutorial8-tags.pngdeleted

Binary file not shown.

examples/hakyll/tutorials/part01.markdowndeleted−235
@@ -1,235 +0,0 @@
1−---
2−title: Quickstart
3−what: explains how to create a simple brochure site
4−---
5−
6−## Getting started
7−
8−First, make sure you have Hakyll installed. The recommended way to do this is
9−through [hackage] using [cabal-install]. This tutorial also assumes you have a
10−basic knowledge of Haskell.
11−
12−[hackage]: http://hackage.haskell.org/
13−[cabal-install]: http://www.haskell.org/haskellwiki/Cabal-Install
14−
15−~~~~~
16−[jasper@alice ~]$ cabal install hakyll
17−~~~~~
18−
19−## Building a simple static site
20−
21−As an example to get started with, we're going to develop a so called
22−"Brochure Site" for an imaginary company. The first step is to create a
23−directory for our new site.
24−
25−~~~~~
26−[jasper@alice Sites]$ mkdir brochure
27−[jasper@alice Sites]$ cd brochure/
28−[jasper@alice brochure]$
29−~~~~~
30−
31−I have a [zip file] with the files we need for this
32−tutorial available. Please unzip it in the brochure directory we just created.
33−We'll first have a look at what we're going to create (because we're curious
34−and all that).
35−
36−[zip file]: $root/examples/brochure.zip
37−
38−~~~~~
39−[jasper@alice brochure]$ ghc --make hakyll.hs
40−[1 of 1] Compiling Main ( hakyll.hs, hakyll.o )
41−Linking hakyll ...
42−[jasper@alice brochure]$ ./hakyll preview
43−Starting hakyll server on port 8000...
44−~~~~~
45−
46−If you now point your browser at [localhost:8000] you should see our simple
47−brochure site.
48−
49−[localhost:8000]: http://localhost:8000/
50−
51−## hakyll.hs
52−
53−The main configuration file of a Hakyll site is traditionally called
54−`hakyll.hs`. It is nothing special, just a small Haskell program. There is no
55−magic going on.
56−
57−~~~~~{.haskell}
58−import Text.Hakyll (hakyll)
59−import Control.Monad.Trans (liftIO)
60−main = hakyll "http://example.com" $ do
61− liftIO $ putStrLn "I'm in your computer, generating your site!"
62−~~~~~
63−
64−Note how we wrap everything in the `hakyll` function. This is useful because
65−it will generate a very nice main function. We also pass the full site URL to
66−the `hakyll` function. If you don't have an URL for your site yet, it doesn't
67−really matter for now; just fill in anything then. The URL is only used for
68−certain specific purposes where a full URL is needed, such as rendering RSS
69−feeds.
70−
71−## Context
72−
73−Let's look at one of the most important types in Hakyll.
74−
75−~~~~~{.haskell}
76−type Context = Map String String
77−~~~~~
78−
79−A `Context` is a key-value mapping, used to represent pieces of information.
80−One way to write such a `Context`, is a page.
81−
82−## Pages
83−
84−Another important concept in Hakyll is pages. Pages are text files that can be
85−written in markdown, html, rst... basically anything Pandoc supports.
86−Furthermore, they can also contain some metadata. The metadata is placed in the
87−file header and surrounded by `---` lines. Each line should contain a
88−`key: value` pair. Let's have a look at the `index.markdown` page.
89−
90− ---
91− title: About
92− ---
93− Nullam imperdiet sodales orci vitae molestie.
94− Nunc quam orci, pharetra a rhoncus vitae,
95− eleifend id felis. Suspendisse potenti...
96−
97−This contains one `key: value` pair, namely `title: About`. The rest of the
98−file is treated as markdown by pandoc. If you want to know more about
99−markdown, I think [this](http://daringfireball.net/projects/markdown/syntax)
100−is a pretty good page.
101−
102−## Templates
103−
104−Another concept are the so-called templates. Templates are text files (usually
105−html files) containing a number of keys. The syntax for these keys is
106−`$identifier`. Our example site contains one template, namely
107−`templates/default.html`. Let's have a better look at that.
108−
109−~~~~~{.html}
110−<html>
111− <head>
112− <title>MyAweSomeCompany - $title</title>
113− <link rel="stylesheet" type="text/css"
114− href="$$root/css/default.css" />
115− <link rel="stylesheet" type="text/css"
116− href="$$root/css/syntax.css" />
117− </head>
118− <body>
119− <h1>MyAweSomeCompany - $title</h1>
120− <div id="navigation">
121− <a href="$$root/index.html">Home</a>
122− <a href="$$root/about.html">About</a>
123− <a href="$$root/code.html">Code</a>
124− </div>
125−
126− $body
127− </body>
128−</html>
129−~~~~~
130−
131−We can see how our `Page` would fit in. When we render the page we saw using
132−this template, `$title` would be replaced by `About`, and `$body` would be
133−replaced by the body of the about page. `body` is the traditional name for the
134−body of any page - that is the convention in Hakyll. Also note that in this
135−case, `$body` would be replaced by a chunk of html - the result of the
136−markdown-to-html conversion.
137−
138−## The $$root key
139−
140−There are a few "special" keys in Hakyll: one of them is the $$root key. What
141−is so special about it? Well, internally, it is treated differently - but this
142−should not concern you. The thing is that it is the only key you can also use
143−in __Pages__.
144−
145−It will be substituted by a relative url part (like `..` or `../..`) so it
146−points to the root directory of your site. It is recommended to use this
147−whenever you need it, it can save you some time from messing with absolute
148−and relative URL's.
149−
150−## Putting it all together
151−
152−Now, we'll render the page using the `renderChain` function. This function
153−takes a list of templates and a `Context`. In our case, we only have one
154−template, and our `Context` is the about page we just saw - we can load that
155−using the `createPage` function.
156−
157−~~~~~{.haskell}
158−import Text.Hakyll (hakyll)
159−import Text.Hakyll.Render (renderChain)
160−import Text.Hakyll.CreateContext (createPage)
161−main = hakyll "http://example.com" $ do
162− renderChain ["templates/default.html"]
163− (createPage "index.markdown")
164−~~~~~
165−
166−Or, to render all our three pages:
167−
168−~~~~~{.haskell}
169−import Text.Hakyll (hakyll)
170−import Text.Hakyll.Render (renderChain)
171−import Text.Hakyll.CreateContext (createPage)
172−main = hakyll "http://example.com" $ do
173− render "about.rst"
174− render "index.markdown"
175− render "code.lhs"
176− where render = renderChain ["templates/default.html"]
177− . createPage
178−~~~~~
179−
180−As you can see, we can render a variety of formats. This will create the
181−following files:
182−
183−~~~~~
184−_site/about.html
185−_site/index.html
186−_site/code.html
187−~~~~~
188−
189−## CSS, images and other static files
190−
191−Now, we also have a css file we would like to have in the `_site` directory.
192−Static files can be rendered using the `static` function in Hakyll. We could
193−use:
194−
195−~~~~~{.haskell}
196−import Text.Hakyll (hakyll)
197−import Text.Hakyll.Render (static)
198−main = hakyll "http://example.com" $ do
199− static "css/default.css"
200−~~~~~
201−
202−This would work, but let's not forget that Hakyll also has css compression. If
203−we want to use that, we would use `css` instead of `static`.
204−
205−~~~~~{.haskell}
206−import Text.Hakyll (hakyll)
207−import Text.Hakyll.Render (css)
208−main = hakyll "http://example.com" $ do
209− css "css/default.css"
210−~~~~~
211−
212−If we were to create another css file, we would have to add a line to our
213−`hakyll.hs` configuration file. This is pretty stupid, because the whole
214−directory `css` contains only css files. That's why Hakyll has a `directory`
215−function, which will execute a given function on an entire directory. So,
216−our example would become:
217−
218−~~~~~{.haskell}
219−import Text.Hakyll (hakyll)
220−import Text.Hakyll.Render (css)
221−import Text.Hakyll.File (directory)
222−main = hakyll "http://example.com" $ do
223− directory css "css"
224−~~~~~
225−
226−## Deploying
227−
228−To setup your site, simply copy the contents of `_site` to your hosting provider
229−using your favorite piece of software.
230−
231−## The gist of it
232−
233−- You render "pages" with "templates".
234−- The most common render function is `renderChain`.
235−- Hakyll also deals with static files and css.
examples/hakyll/tutorials/part02.markdowndeleted−91
@@ -1,91 +0,0 @@
1−---
2−title: Arrows: A Crash Course
3−what: illustrates how Arrows are used in hakyll
4−---
5−
6−## Do I really need to know this stuff?
7−
8−Maybe. You don't need it when you only use the basic Hakyll functions, but
9−Arrows are used a lot in the Hakyll code, and a lot of the more advanced
10−features make use of Arrows. Besides, it's a pretty interesting subject.
11−
12−## What is an "Arrow"
13−
14−Arrows are comparable with monads. In fact, monads are a subset of arrows.
15−Arrows allow you to represent a "computation". This is all pretty vague, so
16−let's skip directly to the Arrows used in Hakyll.
17−
18−## HakyllAction
19−
20−The Arrow used throughout Hakyll is called `HakyllAction`. Arrows have two
21−type parameters, so it's actually `HakyllAction a b`. You can think of `a`
22−as the input for our action, and `b` is the corresponding output. Let's look
23−at the type of `createPage`:
24−
25−~~~~~{.haskell}
26−createPage :: FilePath -> HakyllAction () Context
27−~~~~~
28−
29−So, you give `createPage` a `FilePath`, and it creates a `HakyllAction` that
30−produces a `Context` out of thin air. Now, we want to render the `Context` we
31−just loaded with a template. The type of the `render` function is:
32−
33−~~~~~{.haskell}
34−render :: FilePath -> HakyllAction Context Context
35−~~~~~
36−
37−We pass the file name of a template to the `render` function, and we get a
38−`HakyllAction` that creates a `Context` from another `Context`. The result
39−of the `render` operation (so basically the rendered template) will be placed
40−in the `$body` field of the new `Context`. But we still haven't saved our
41−result, so let's do that using the `writePage` function.
42−
43−~~~~~{.haskell}
44−writePage :: HakyllAction Context ()
45−~~~~~
46−
47−This function writes our result and returns nothing.
48−
49−## Composition
50−
51−Now, let's look at the big picture.
52−
53−![Arrow illustration]($root/images/arrow-composition.png)
54−
55−If these were regular functions, we could've composed them using the `.`
56−operator. Since they're arrows, we'll have to use the `>>>` operator.
57−
58−~~~~~{.haskell}
59−test :: HakyllAction () ()
60−test = createPage "test.markdown"
61− >>> render "template.html"
62− >>> writePage
63−~~~~~
64−
65−Now, we only have to execute our test.
66−
67−~~~~~{.haskell}
68−runHakyllActionIfNeeded test
69−~~~~~
70−
71−## Aso, the point emerges
72−
73−The `runHakyllActionIfNeeded` suggests why we use arrows. `HakyllAction` is more
74−than just a function, it also tracks dependencies. This causes Hakyll to only
75−execute our functions when it is really needed. In this particular case, `test`
76−would only be executed if either `test.markdown` or `template.html` were
77−recently changed.
78−
79−## So what's renderChain then?
80−
81−Now, we have done pretty much the same as we did with the `renderChain` function
82−in the first part. That's right, the `renderChain` is more or less implemented
83−like this. So, you will probably use `renderChain` in most cases, but it's
84−handy if you know how it works.
85−
86−## The gist of it
87−
88−- Arrows really aren't complicated.
89−- Compose them using `>>>`.
90−- `HakyllAction` tracks dependencies for you. Use it.
91−- In most cases, you will just use `renderChain`.
examples/hakyll/tutorials/part03.markdowndeleted−174
@@ -1,174 +0,0 @@
1−---
2−title: How to write pages
3−what: elaborates a little on writing pages
4−---
5−
6−## The structure of a Page
7−
8−The most important thing to realize is that a page is reduced to a `Context`,
9−and therefore is just a key-value mapping. Another example:
10−
11− ---
12− title: About
13− author: Mia Wallace
14− ---
15− Hello there! This is
16− a simple about page.
17−
18−This will produce the following mapping:
19−
20−- `$title`: About
21−- `$author`: Mia Wallace
22−- `$body`: Hello there! This is a simple about page.
23−
24−`$body` is the traditional name for the main body part of a page. If the page
25−has a `.markdown` extension for example, this would also be rendered by pandoc.
26−But pages are more flexible. The following is also a valid page:
27−
28− Hello there! This is
29− a simple about page.
30−
31−This will produce one key-value pair:
32−
33−- `$body`: Hello there! This is a simple about page.
34−
35−But Hakyll can do more than this. You can add extra sections, apart from the
36−body, and even leave out the body.
37−
38− ---
39− author: Vincent Vega
40−
41− --- prelude
42− A small introduction goes here. I can write *markdown*
43− here, by the way. Well, assuming this page has a
44− `.markdown` extension.
45−
46− --- main
47− I can write some more things here.
48−
49− ---
50− The body comes last, and is optional.
51−
52−This will produce the following:
53−
54−- `$author`: Vincent Vega
55−- `$prelude`: A small introduction goes here. I can write *markdown* here, by the
56− way. Well, assuming this page has a `.markdown` extension.
57−- `$main`: I can write some more things here.
58−- `$body`: The body comes last, and is optional.
59−
60−The example from this tutorial (we will see later) uses this to build a
61−three-column system for the website, separating content from layout.
62−
63−## Combining Contexts
64−
65−Now you know that pages, and `Context`s in general, are basically nothing more
66−than key-values mappings, it is time to abuse this fact. There is another
67−way to create a `Context`, called `combine`.
68−
69−The type signature of the `combine` function does a pretty good job at
70−explaining it:
71−
72−~~~~~{.haskell}
73−combine :: HakyllAction () Context
74− -> HakyllAction () Context
75− -> HakyllAction () Context
76−~~~~~
77−
78−This means we can take two `Context`s values and combine them. This is
79−basically a `Map.union`: The result will contain all keys from both `Context`s,
80−with there corresponding values. If a key is present in both `Context`s, the
81−value from the first argument will be chosen. This is, for example, almost
82−always the case with the `$url` field (since almost all `Context`s have an url
83−in Hakyll).
84−
85−Combining two `Context`s, but overriding the `$url` is quite common, so there is
86−another function that helps us here:
87−
88−~~~~~{.haskell}
89−combineWithUrl :: FilePath
90− -> HakyllAction () Context
91− -> HakyllAction () Context
92− -> HakyllAction () Context
93−~~~~~
94−
95−## The example
96−
97−Now that we have the tools, we'll get on to the example. This time, we'll
98−be making a more advanced brochure site. Here [is a zip file] containing the
99−source code for the tutorial.
100−
101−[is a zip file]: $root/examples/morepages.zip
102−
103−Every page consists of three sections, originally named `section1`, `section2`
104−and `section3`. So our pages look more or less like this:
105−
106− ---
107− title: About
108−
109− --- section1
110− ## Mattis
111− Nullam imperdiet sodales orci vitae molestie. Nunc...
112−
113− --- section2
114− ## Orci
115− Vivamus eget mauris sit amet nulla laoreet lobortis.
116− Nulla in...
117−
118− --- section3
119− ## Augue
120− In urna ante, pulvinar et imperdiet nec, fermentum ac...
121−
122−The cool thing is we do not have to specify how these will be layed out. In our
123−template, we decide to use a simple three column system:
124−
125−~~~~~{.html}
126−<div class="column"> $section1 </div>
127−<div class="column"> $section2 </div>
128−<div class="column"> $section3 </div>
129−~~~~~
130−
131−The columns are then floated using css. So far so good, but what if we wanted
132−an additional text block on every page? An easy solution would be to add this
133−to the template, but then our layout-content separation idea will be broken
134−again. So we simply add to the template:
135−
136−~~~~~{.html}
137−<div class="footer"> $footer </div>
138−~~~~~
139−
140−And now we will use `combine` to put the footer on every page - so we need to
141−add the footer page to every `Context`. We write a small auxiliary function
142−that combines a given `Context` with the footer:
143−
144−~~~~~{.haskell}
145−withFooter = flip combine $ createPage "footer.markdown"
146−~~~~~
147−
148−Note that we use `flip` here - we want `footer.markdown` to be our second
149−argument. That is because Hakyll will take the `$url` from the first `Context`,
150−so all pages would be rendered to `footer.html` - obviously not what we want.
151−Now, were we previously wrote:
152−
153−~~~~~{.haskell}
154−render "about.markdown"
155−where render = renderChain ["templates/default.html"]
156− . createPage
157−~~~~~
158−
159−We simply have to add our footer:
160−
161−~~~~~{.haskell}
162−render "about.markdown"
163−where render = renderChain ["templates/default.html"]
164− . withFooter
165− . createPage
166−~~~~~
167−
168−And now every page will include the footer.
169−
170−## The gist of it
171−
172−- Pages are just key-value mappings.
173−- You can have multiple sections in every page.
174−- Combine pages using the `combine` function.
examples/hakyll/tutorials/part04.markdowndeleted−60
@@ -1,60 +0,0 @@
1−---
2−title: How to write templates
3−what: more information on template writing
4−---
5−
6−## Simple templates
7−
8−Simple templates are simply HTML files, with `$identifiers`. An example:
9−
10−~~~~~{.html}
11−<html>
12− <head>
13− <title>$title</title>
14− </head>
15− <body>
16− $body
17− </body>
18−</html>
19−~~~~~
20−
21−## Markup in templates
22−
23−Most of the examples in these tutorials use HTML for templates. However, since
24−Hakyll 2.2, it is possible use other markup languages in your templates. Simply
25−use an appropriate extension, and Hakyll will pick it up. For example, you could
26−write your `templates/post.markdown` template as:
27−
28− # $title
29−
30− _On $date_
31−
32− $body
33−
34−__Warning__: you shouldn't use markdown for your "root" template, as these
35−templates will never insert things like the doctype for you -- so you always
36−need at least one top-level HTML template.
37−
38−## Hamlet templates
39−
40−From Hakyll 2.3 onwards, it is possible to use [hamlet] templates. You can find
41−more information about hamlet on that website. Usage is fairly simple -- since
42−pages are strictly key-value mappings, only `$variable$` control is supported in
43−hamlet templates. As an example, here is the template that can be used for the
44−brochure site, but in hamlet:
45−
46− !!!
47− %html
48− %head
49− %title MyAweSomeCompany - $$title$
50− %body
51− %h1 MyAweSomeCompany - $$title$
52− #navigation
53− %a!href="$$root$/index.html" Home
54− %a!href="$$root$/about.html" About
55− %a!href="$$root$/code.html" Code
56− $body$
57−
58−Hakyll will recognise hamlet templates automatically by the `.hamlet` extension.
59−
60−[hamlet]: http://docs.yesodweb.com/hamlet/
examples/hakyll/tutorials/part05.markdowndeleted−157
@@ -1,157 +0,0 @@
1−---
2−title: Creating a Blog
3−what: creates a simple blog
4−---
5−
6−## Creating a simple blog with Hakyll
7−
8−After we created a simple brochure site, we're going to try something more
9−advanced: we are going to create a simple blog system.
10−
11−A [zip file] containing the source for this tutorial is also available.
12−
13−[zip file]: $root/examples/simpleblog.zip
14−
15−Blogs, as you probably know, are composed of posts. In Hakyll, we're going
16−to use simple pages for posts. All posts are located in the `posts`
17−directory. But we're not going to use the `directory` command here - you will
18−see why later. First, some trivial things like css.
19−
20−~~~~~{.haskell}
21−main = hakyll "http://example.com" $ do
22− directory css "css"
23−~~~~~
24−
25−## Finding the posts
26−
27−`Text.Hakyll.File` contains a handy function `getRecursiveContents`, which will
28−provide us with all the blog posts. The blog posts have a
29−`yyyy-mm-dd-title.extension` naming scheme. This is just a simple trick so we
30−can sort them easily (sorting on filename implies sorting on date). You could of
31−course name them whatever you want, but it's always a good idea to stick to the
32−conventions. They contain some metadata, too:
33−
34− title: A first post
35− author: Julius Caesar
36− date: November 5, 2009
37− ---
38− Lorem ipsum dolor sit amet, consectetur adipiscing elit.
39− Vivamus pretium leo adipiscing lectus iaculis lobortis.
40− Vivamus scelerisque velit dignissim metus...
41−
42−Now, we find the posts and sort them reversed, so the most recent post will
43−become the first item in the list:
44−
45−~~~~~{.haskell}
46−postPaths <- liftM (reverse . sort) $ getRecursiveContents "posts"
47−~~~~~
48−
49−Our `postPaths` value is now of the type `[FilePath]`. We want to be able to
50−render all posts, so we pass them to the `createPage` function.
51−
52−~~~~~{.haskell}
53−let postPages = map createPage postPaths
54−~~~~~
55−
56−We have two templates we want to render our posts with: first we would like to
57−render them using `templates/post.html`, and we want to render the result
58−using `templates/default.html`. This can be done with the `renderChain`
59−function:
60−
61−~~~~~{.haskell}
62−mapM_ (renderChain [ "templates/post.html"
63− , "templates/default.html"
64− ]) postPages
65−~~~~~
66−
67−Remember that the `renderChain` works by rendering the item using the first
68−template, creating a new page with the render result in the `$body` field, and
69−so on until it has been rendered with all templates.
70−
71−Now, we have the posts rendered. What is left is to generate some kind of index
72−page with links to those posts. We want one general list showing all posts, and
73−we want to show a few recent posts on the index page.
74−
75−## Creating listings.
76−
77−`createPage` is the easiest way of reading a `Context`. But in this case, we
78−want something more custom, so we'll use the `createCustomPage` function. This
79−allows us to create a more specific `Context`.
80−
81−~~~~~{.haskell}
82−createCustomPage :: FilePath
83− -> [(String, Either String (HakyllAction () String))]
84− -> HakyllAction () Context
85−~~~~~
86−
87−The first argument is the `url` of the page to generate. For our index page,
88−this will be, `index.html`. The second argument is obviously our `key: value`
89−mapping. But why the `Either`? This, once again, is about dependency handling.
90−The idea is that you can choose which type to use for the value:
91−
92−- `String`: Simply a `String`.
93−- `HakyllAction () String`: Here, you can give an `HakyllAction` Arrow action
94− that can produce a String. However - this action _will not be executed_ when
95− the file in `_site` is up-to-date.
96−
97−However, in this specific case - a list of posts - there is an easier, and more
98−high-level approach than `createCustomPage`[^1]. Let's look at the type
99−signature of `createListing`:
100−
101−~~~~~{.haskell}
102−createListing :: FilePath
103− -> [FilePath]
104− -> [HakyllAction () Context]
105− -> [(String, Either String (HakyllAction () String))]
106− -> HakyllAction () Context
107−~~~~~
108−
109−[^1]: Since Hakyll-1.3 onwards.
110−
111−The first argument is the destination url. For our blog, this is of course
112−`index.html`. The second argument is a list templates to render _each_ `Context`
113−with. We use only `templates/postitem.html` here. This is, as you can see, a
114−simple template:
115−
116−~~~~~{.html}
117−<li>
118− <a href="$$root/$url">$title</a>
119− - <em>$date</em> - by <em>$author</em>
120−</li>
121−~~~~~
122−
123−We then give a list of `Context`s to render. For our index, these are the 3 last
124−posts. The last argument of the `createListing` functions lets you specify
125−additional key-value pairs, like in `createCustomPage`. We use this to set the
126−title of our page. So, we create our index page using:
127−
128−~~~~~{.haskell}
129−let index = createListing "index.html"
130− ["templates/postitem.html"]
131− (take 3 postPages)
132− [("title", Left "Home")]
133−~~~~~
134−
135−The result of this will be a `HakyllAction () Context`. This `Context`'s `$body`
136−will contain a concatenation of all the 3 posts, rendered with the
137−`templates/postitem.html` template.
138−
139−Now, we only have to render it: first using the `index.html` template - which
140−adds some more information to our index - then using the
141−`templates/default.html` template.
142−
143−~~~~~{.haskell}
144−renderChain ["index.html", "templates/default.html"] index
145−~~~~~
146−
147−Note that the `index.html` in the `renderChain` list is also a template. Now,
148−you might want to take your time to read the `index.html` template and the other
149−files in the zip so you understand what is going on here.
150−
151−## The gist of it
152−
153−- You can find blogposts using `getRecursiveContents`.
154−- The convention is to call them `yyyy-mm-dd-rest-of-title.extension`. This
155− allows us to sort them easily.
156−- You can use `createCustomPage` or `createListing` to create custom pages and
157− simple listings.
examples/hakyll/tutorials/part06.markdowndeleted−84
@@ -1,84 +0,0 @@
1−---
2−title: Creating feeds
3−what: adds an rss feed to the simple blog
4−---
5−
6−## Adding Feeds
7−
8−In this tutorial, we're going to add an RSS feed to the blog we wrote in the
9−previous part. Here is a [zip file] containing the source.
10−
11−[zip file]: $root/examples/feedblog.zip
12−
13−You will be glad to hear that Hakyll has native support for RSS as well as Atom
14−feeds[^1]. This simplifies our object a lot.
15−
16−[^1]: Since Hakyll-2.0
17−
18−This is the first time that the absolute URL of your site you have to give to
19−the `hakyll` function actually matters. That's because the specifications of
20−those feed formats dictate you need the full URL of them.
21−
22−## Creating a configuration
23−
24−The first thing to do is creating a configuration for your feed. You could
25−place this code outside of your main function, as is done in the example.
26−
27−~~~~~{.haskell}
28−myFeedConfiguration = FeedConfiguration
29− { feedUrl = "rss.xml"
30− , feedTitle = "SimpleBlog RSS feed."
31− , feedDescription = "Simple demo of a feed created with Hakyll."
32− , feedAuthorName = "Jasper Van der Jeugt"
33− }
34−~~~~~
35−
36−Note that we enter the url of the feed in our configuration. So the function
37−to render our feed only takes two arguments, the configuration and a list of
38−items to put in the feed. Let's put the three most recent posts in our feed.
39−
40−~~~~~{.haskell}
41−renderRss myFeedConfiguration (take 3 postPages)
42−~~~~~
43−
44−## But it's not that easy
45−
46−If you look at our generated RSS feed (build the site), you will see
47−`$description` tags appearing in our final render. We don't want that! How
48−did they get there in the first place?
49−
50−To render feeds, Hakyll expects a number of fields in the renderables you put
51−in the feed. They are:
52−
53−- `$title`: Title of the item. This is set in our posts, since we use a `title`
54− metadata field.
55−- `$url`: Url of the item. This is automatically set by Hakyll, so you shouldn't
56− worry about it.
57−- `$description`: A description of our item to appear in the feed reader.
58−
59−The latter is obviously the problem: we don't have a description in our posts.
60−In fact, we would like to copy the `$body` key to the `$description` key, so
61−people can read the full post in their feed readers.
62−
63−## Where arrows come in
64−
65−The `Text.Hakyll.ContextManipulations` module contains a number of simple
66−functions that create Arrows for us. One of these functions is `copyValue`,
67−which takes a source and a destination key. So, we need to pass our
68−items through this Arrow first.
69−
70−~~~~~{.haskell}
71−renderRss myFeedConfiguration $
72− map (>>> copyValue "body" "description") (take 3 postPages)
73−~~~~~
74−
75−And that's that, now our feed gets rendered properly. Exercise for the reader
76−is to add a Atom feed[^2].
77−
78−[^2]: Hint: look around in the [reference]($root/reference.html).
79−
80−## The gist of it
81−
82−- Hakyll has native support for RSS and Atom feeds.
83−- The items must contain `$title` and `$description` fields.
84−- Arrows can be used to copy values in a `Context`.
examples/hakyll/tutorials/part07.markdowndeleted−221
@@ -1,221 +0,0 @@
1−---
2−title: Tags and manipulations
3−what: enhances our blog with tags and explains context manipulations.
4−---
5−
6−## Context manipulations
7−
8−Here, have [a zip file]($root/examples/tagblog.zip) for this tutorial.
9−
10−You probably remember that `Context` objects are just key-value mappings. We can
11−render those with templates, and then the `$key`'s in the template get
12−substituted by the appropriate values. This is a rather flexible system, but
13−there are limitations. Some of these limitations can be solved using
14−_context manipulations_.
15−
16−Like rendering actions, _context manipulations_ are also simply
17−`HakyllAction Context Context` arrows. The `Text.Hakyll.ContextManipulations`
18−contains some functions to easily construct easy variants.
19−
20−One of the most general functions is the `renderValue` function. Let's have a
21−look at it's type.
22−
23−~~~~~{.haskell}
24−renderValue :: String
25− -> String
26− -> (String -> String)
27− -> HakyllAction Context Context
28−~~~~~
29−
30−This is the preferred way of creating context manipulations. The first argument
31−is the `key` to manipulate. The second argument is the `key` where the new value
32−should be placed. If this is the same as the first argument, it will be
33−replaced. The third argument is the function to manipulate the `value` with.
34−
35−As a simple example, let's write a function that puts the `$title` in uppercase.
36−
37−~~~~~{.haskell}
38−import Data.Char (toUpper)
39−
40−titleUpper :: HakyllAction Context Context
41−titleUpper = renderValue "title" "title" $ map toUpper
42−~~~~~
43−
44−Because the destination `key` is the same as the source `key`, we can also use
45−the `changeValue` function here.
46−
47−~~~~~{.haskell}
48−titleUpper = changeValue "title" $ map toUpper
49−~~~~~
50−
51−For further reading, refer to the `Text.Hakyll.ContextManipulations`
52−documentation.
53−
54−## Applying Context Manipulations
55−
56−Because we're dealing with Arrows again, we can use `>>>` to apply our
57−manipulations. For example, we could use or title manipulation like this:
58−
59−~~~~~{.haskell}
60−renderChain ["templates/default.html"]
61− (createPage "index.markdown" >>> titleUpper)
62−~~~~~
63−
64−## Rendering dates
65−
66−As you remember, in our previous blog, all posts had a file name like
67−`posts/yyyy-mm-dd-title.extension`, as is the Hakyll convention. But they also
68−had a metadata field `date`, containing a human-readable date. This is not very
69−D.R.Y., of course! Hakyll has a specialized `renderValue` function to deal with
70−dates encoded in paths: `renderDate`.
71−
72−~~~~~{.haskell}
73−postManipulation :: HakyllAction Context Context
74−postManipulation = renderDate "date" "%B %e, %Y" "Unknown date"
75−~~~~~
76−
77−That manipulation will:
78−- Read the date from the file name the post was loaded from.
79−- Parse the date and render it in a `%B %e, %Y` format. This is a
80− `Month day, Year` format.
81−- Put the result in the `date` metadata field.
82−- If the date could not be parsed, it will put `"Unknown date"` in the `date`
83− metadata field.
84−
85−So, we can throw away our `date: ` lines from our posts, and still use `$date`
86−in our templates.
87−
88−## Abstracting the post list
89−
90−Now, we're going to render tags. This is also done using context manipulations.
91−Hakyll has a specialized module to deal with tags, provided by
92−`Text.Hakyll.Tags`. This module assumes tags are comma separated, and placed in
93−the `tags` metadata field.
94−
95− ---
96− title: A third post
97− author: Publius Ovidius Naso
98− tags: epic fail, ovidius
99− ---
100− Pellentesque tempor blandit elit, vel...
101−
102−But first things first. We need to render a post list for every tag. We already
103−had some code to render a list of all posts. We're just going to abstract this
104−code into a more general function:
105−
106−~~~~{.haskell}
107−renderPostList url title posts = do
108− let list = createListingWith url ["templates/postitem.html"]
109− posts [("title", Left title)]
110− renderChain ["posts.html", "templates/default.html"] list
111−~~~~~
112−
113−Our "render all posts" action can now be written as:
114−
115−~~~~~{.haskell}
116−renderPostList "posts.html" "All posts" renderablePosts
117−~~~~~
118−
119−## Tag links
120−
121−We want to display the tags for our post under the title. But if we use the
122−`$tags` key in a template, we will just have the plain tags - they will not be
123−clickable. We can again solve this with a `ContextManipulation`. We have a
124−function that produces an url for a given tag:
125−
126−~~~~~{.haskell}
127−tagToUrl tag = "$root/tags/" ++ removeSpaces tag ++ ".html"
128−~~~~~
129−
130−`removeSpaces` is an auxiliary function from `Text.Hakyll.File`. Now, there is
131−a specialized `renderValue` function for creating linked tags called
132−`renderTagLinks`. This function simply takes a function that produces an url
133−for a given tag - the function we just wrote. Let's extend our
134−`postManipulation`.
135−
136−~~~~~{.haskell}
137−postManipulation :: HakyllAction Context Context
138−postManipulation = renderDate "date" "%B %e, %Y" "Unknown date"
139− >>> renderTagLinks tagToUrl
140−~~~~~
141−
142−We apply this manipulation when we load the tags.
143−
144−~~~~~{.haskell}
145−let renderablePosts =
146− map ((>>> postManipulation) . createPage) postPaths
147−~~~~~
148−
149−So, the `renderTagLinks` function replaces the `$tags` value from
150−`epic fail, random` to `<a href="$root/tags/epic-fail.html">epic fail</a>, ...`.
151−If we click a tag, we get a `404`. That's because we haven't generated the
152−post lists for every tag.
153−
154−## The Tag Map
155−
156−Hakyll provides a function called `readTagMap`. Let's inspect it's type.
157−
158−~~~~~{.haskell}
159−type TagMap = Map String [HakyllAction () Context]
160−readTagMap String [FilePath] -> HakyllAction () TagMap
161−~~~~~
162−
163−You give it a list of paths, and it creates a map that, for every tag, holds
164−a number of posts. We can easily use this to render a post list for every tag.
165−The first argument given is an "identifier", unique to this tag map. Hakyll
166−needs this so it can cache the tags.
167−
168−~~~~~{.haskell}
169−let tagMap = readTagMap "postTags" postPaths
170−~~~~~
171−
172−When we have the `TagMap`, we can need to render a post list for every tag.
173−There is a function in Hakyll designed more or less for this purpose:
174−`withTagMap`. This takes a `TagMap` and an action to execute for every tag and
175−it's associated posts. We pass a small function to it we create ourselves[^1]:
176−
177−[^1]: Exercise for the reader: why do we use `>>> postManipulation` again here?
178−
179−~~~~~{.haskell}
180−let renderListForTag tag posts =
181− renderPostList (tagToUrl tag)
182− ("Posts tagged " ++ tag)
183− (map (>>> postManipulation) posts)
184−withTagMap tagMap renderPostList
185−~~~~~
186−
187−There we go. We now have clickable tags, and a post list for every tag.
188−
189−## A Tag Cloud
190−
191−A tag cloud is a commonly found thing on blogs. Hakyll also provides code to
192−generate a tag cloud. Let's have a look at the `renderTagCloud` function.
193−
194−~~~~~{.haskell}
195−renderTagCloud :: (String -> String)
196− -> Float
197− -> Float
198− -> HakyllAction TagMap String
199−~~~~~
200−
201−The first argument is, once again, a function to create an url for a given tag.
202−Then, we give a minimum and a maximum font size in percent, and we get a tag
203−cloud Arrow back. We can add this to our index:
204−
205−~~~~~{.haskell}
206−let tagCloud = tagMap >>> renderTagCloud tagToUrl 100 200
207− index = createListing "index.html"
208− ["templates/postitem.html"]
209− (take 3 renderablePosts)
210− [ ("title", Left "Home")
211− , ("tagcloud", Right tagCloud)
212− ]
213−renderChain ["index.html", "templates/default.html"] index
214−~~~~~
215−
216−## The gist of it
217−
218−- There's some handy, simple functions in `Text.Hakyll.ContextManipulations`.
219−- Seperate tags by commas and put them in the `$tags` field.
220−- Use `withTagMap` to render a list for every tag.
221−- Hakyll can also create tag clouds.
examples/hakyll/tutorials/part08.markdowndeleted−97
@@ -1,97 +0,0 @@
1−---
2−title: Interlude
3−what: gives some various tips and tricks about Hakyll (quite handy, read this!)
4−---
5−
6−## Syntax-highlighting
7−
8−Pandoc (which Hakyll uses as a backend) offers powerful syntax highlighting.
9−To enable this, Pandoc needs to be compiled with highlighting support. If this
10−is not the case, you can fix this using:
11−
12−~~~~~
13−[jasper@alice ~]$ cabal install --reinstall -fhighlighting pandoc
14−~~~~~
15−
16−## Auto-compilation
17−
18−Hakyll features a simple _auto-compilation_ mode. This is invoked by running
19−
20−~~~~~
21−[jasper@alice ~]$ ./hakyll preview
22−Starting hakyll server on port 8000...
23−~~~~~
24−
25−Now, Hakyll will recompile your site when you refresh in your browser. This will
26−not update your site automatically when `hakyll.hs` changes. So if you make any
27−changes to the configuration file, you'll have to compile it again, and then you
28−can enter `preview` mode again.
29−
30−If you use a custom `HakyllConfiguration`, you can select your custom
31−`PreviewMode`:
32−
33−- `BuildOnRequest`: rebuild site when the preview server receives a request
34− (default).
35−- `BuildOnInterval`: build when you change files.
36−
37−## When to rebuild
38−
39−If you execute a `./hakyll build`, Hakyll will build your site incrementally.
40−This means it will be very fast, but it will not pick up _all_ changes.
41−
42−- In case you edited `hakyll.hs`, you first want to compile it again.
43−- It is generally recommended to do a `./hakyll rebuild` before you deploy your
44− site.
45−
46−After rebuilding your site, all files will look as "modified" to the filesystem.
47−This means that when you upload your site, it will usually transfer all files --
48−this can generate more traffic than necessary, since it is possible that some
49−files were not actually modified. If you use `rsync`, you can counter this using
50−the `--checksum` option.
51−
52−## Pretty URL's
53−
54−There is an option in Hakyll to produce pretty URL's, which is disabled by
55−default because it can be confusing when you're first introduced to Hakyll.
56−
57−It can be enabled this way:
58−
59−~~~~~{.haskell}
60−import Text.Hakyll
61−import Text.Hakyll.HakyllMonad
62−
63−myConfig :: HakyllConfiguration
64−myConfig = (defaultHakyllConfiguration "http://jaspervdj.be")
65− { enableIndexUrl = True
66− }
67−
68−main = hakyllWithConfiguration myConfig $ do
69− -- Further code here
70−~~~~~
71−
72−The effect will be that the internal `toUrl` function will behave differently.
73−A few examples:
74−
75−- `about.html` will be rendered to `about/index.html`.
76−- `posts/2010-02-16-a-post.markdown` will be rendered to
77− `posts/2010-02-16-a-post/index.html`.
78−- However, `index.markdown` will still be rendered to `index.html`. Likewise,
79− `posts/index.html` would be rendered to `posts.index.html`.
80−
81−The benefit of this is simply prettier URL's. That is, if you consider
82−`example.com/about` prettier than `example.com/about.html`.
83−
84−## Default values
85−
86−At some point, you might want to use a number of global key-value pairs, for
87−example, `$author`. There are two possible ways to achieve this.
88−
89−- There is an option in `HakyllConfiguration` supporting this, called
90− `additionalContext`. For an example on how to use `HakyllConfiguration`, see
91− the pretty URL's section above.
92−
93−- Another option is to use a `defaults.markdown` file, simply containing some
94− metadata, and then `combine` this file with other pages. The advantage is
95− that autocompilation mode will pick up changes in this file[^1].
96−
97−[^1]: Original idea by zenzike.
examples/hakyll/tutorials/part09.markdowndeleted−104
@@ -1,104 +0,0 @@
1−---
2−title: CategoryBlog
3−what: explains how to use categories instead of tags
4−---
5−
6−## Categories
7−
8−Most people familiar with "tags" will also know the concept "categories".
9−
10−![Tags illustration]($root/images/tutorial8-tags.png)
11−
12−In fact, tags are harder to implement because they have to be represented as a
13−many-to-many relation, and categories are a simple 1-to-many relation.
14−
15−![Tags illustration]($root/images/tutorial8-categories.png)
16−
17−This is also the reason you can "simulate" categories using tags. In this
18−tutorial we will adapt the blog to use categories instead of tags. Here is
19−[a zip file]($root/examples/categoryblog.zip) containing the files used in this
20−tutorial.
21−
22−## About category support
23−
24−Categories are simpler, but they are usually used in custom ways. That's why
25−Hakyll provides less "standard" functions to deal with them. But this gives us
26−another chance to learn some of the things we can do with Hakyll.
27−
28−## Reading Categories
29−
30−Tags are located in the `tags` metadata field. Since one post can only belong
31−in one category, a different approach was chosen here. The category of a post
32−is determined by the subfolder it is in. Here you see the directory layout for
33−our posts using categories:
34−
35− posts
36− |-- coding
37− | |-- 2009-11-05-a-first-post.markdown
38− | |-- 2009-11-28-a-third-post.markdown
39− | `-- 2009-12-04-this-blog-aint-dead.markdown
40− `-- random
41− |-- 2009-11-10-another-post.markdown
42− `-- 2009-12-23-almost-christmas.markdown
43−
44−Because we find all our posts in different subdirectories, sorting them is a
45−little harder: we still want them sorted by date, so it boils down to sorting
46−them by "base name". I hope it does not surprise you Hakyll provides a function
47−for that:
48−
49−~~~~~{.haskell}
50−postPaths <- liftM (reverse . sortByBaseName)
51− (getRecursiveContents "posts")
52−~~~~~
53−
54−We reverse them again, because we want the most recent posts first. Now, we can
55−use the `readCategoryMap` function instead of `readTagMap`, which has the same
56−signature, but assigns categories based on the folders the posts are in.
57−
58−~~~~~{.haskell}
59−categoryMap <- readCategoryMap "categoryMap" renderablePosts
60−~~~~~
61−
62−The rest of the `hakyll.hs` is very similar to the one in the previous
63−tutorial, except we want to render a category list instead of a tag cloud.
64−
65−## Rendering a category list
66−
67−Because rendering a category list is quite easy, and it would be hard to
68−write a "general" function for this, hakyll does not provide such a function --
69−but it is not hard to write. First, we write an auxiliary function that produces
70−a list item for one category:
71−
72−~~~~~{.haskell}
73−categoryListItem category posts =
74− "<li>" ++ link category (categoryToUrl category)
75− ++ " - " ++ show (length posts) ++ " items.</li>"
76−~~~~~
77−
78−This is nothing more that some basic string concatenation to create a `li` HTML
79−element. The function that applies this on every element in the `TagMap` is more
80−interesting:
81−
82−~~~~~{.haskell}
83−categoryList :: HakyllAction TagMap String
84−categoryList = arr $ uncurry categoryListItem <=< toList
85−~~~~~
86−
87−This function might seem a little harder to understand if you are not familiar
88−with the `<=<` operator -- but it's just right-to-left monad composition in the
89−list monad. `uncurry categoryListItem <=< toList` is a pure function we want to
90−execute on the `TagMap`. But this is not possible in Hakyll[^1]. We need to make
91−an arrow of this function. The `arr` function solves this problem easily.
92−
93−[^1]: This is a feature, not a bug. It helps dependency handling.
94−
95−We then add this to our index page, and we are done. Feel free to hack around
96−with the source code. If you still have questions, feel free to ask them at the
97−[google discussion group](http://groups.google.com/group/hakyll).
98−
99−## The gist of it
100−
101−- Hakyll supports categories as well as tags.
102−- Tags are actually a generalization of categories.
103−- Use `readCategoryMap` to read categories.
104−- You need to write some custom functions to render category lists etc.