on master help sign in
Some minor updates to hakyll site.

- Hide image captions. - Remove old tutorials. - Some minor changes to the tutorials.

commit8a27b9af12e82549cd7f141495a4e5757398857c authorJasper Van der Jeugt <jaspervdj@gmail.com> dateWed 31 Mar 2010, 14:19 (17 years ago) parent193dc6d
10 files changed+12−1009
examples/hakyll/css/default.css+4 examples/hakyll/tutorial1.markdown−215 examples/hakyll/tutorial2.markdown−161 examples/hakyll/tutorial3.markdown−170 examples/hakyll/tutorial4.markdown−114 examples/hakyll/tutorial5.markdown−209 examples/hakyll/tutorial6.markdown−88 examples/hakyll/tutorial7.markdown−51 examples/hakyll/tutorials/part07.markdown+1−1 examples/hakyll/tutorials/part08.markdown+7
examples/hakyll/css/default.css+4
@@ -108,3 +108,7 @@ pre code {
108108 padding: 8px;
109109 margin-bottom: 2em;
110110}
111+
112+p.caption {
113+ display: none;
114+}
examples/hakyll/tutorial1.markdowndeleted−215
@@ -1,215 +0,0 @@
1−---
2−title: Brochure
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](http://hackage.haskell.org/) using
10−[cabal-install](http://www.haskell.org/haskellwiki/Cabal-Install). This
11−tutorial also assumes you have a basic knowledge of Haskell.
12−
13−~~~~~
14−[jasper@alice ~]$ cabal install hakyll
15−~~~~~
16−
17−## Building a simple static site
18−
19−As an example to get started with, we're going to develop a so called
20−"Brochure Site" for an imaginary company. The first step is to create
21−a directory for our new site.
22−
23−~~~~~
24−[jasper@alice Sites]$ mkdir brochure
25−[jasper@alice Sites]$ cd brochure/
26−[jasper@alice brochure]$
27−~~~~~
28−
29−I have a [zip file](examples/brochure.zip) with the files we need for this
30−tutorial available. Please unzip it in the brochure directory we just created.
31−We'll first have a look at what we're going to create (because we're curious
32−and all that).
33−
34−~~~~~
35−[jasper@alice brochure]$ ghc --make hakyll.hs
36−[1 of 1] Compiling Main ( hakyll.hs, hakyll.o )
37−Linking hakyll ...
38−[jasper@alice brochure]$ ./hakyll preview
39−Generating...
40−Starting hakyll server on port 8000...
41−~~~~~
42−
43−If you now point your browser at [localhost:8000](http://localhost:8000/) you
44−should see our simple brochure site.
45−
46−## hakyll.hs
47−
48−The main configuration file of a Hakyll site is traditionally called
49−`hakyll.hs`. It is nothing special, just a small Haskell program. There is no
50−magic going on.
51−
52−~~~~~{.haskell}
53−import Text.Hakyll (hakyll)
54−main = hakyll $ do
55− putStrLn "I'm in your computer, generating your site!"
56−~~~~~
57−
58−Note how we wrap everyting in the `hakyll` function. This is useful because
59−it will generate a very nice main function.
60−
61−## Pages
62−
63−An important concept in Hakyll is Pages. Pages are text files that can be
64−written in markdown, html, rst... basically anything Pandoc supports.
65−Furthermore, they can also contain some metadata. The metadata is placed in the
66−file header and surrouded by `---` lines. Each line should contain a
67−`key: value` pair. Let's have a look at the `index.markdown` page.
68−
69− ---
70− title: About
71− ---
72− Nullam imperdiet sodales orci vitae molestie.
73− Nunc quam orci, pharetra a rhoncus vitae,
74− eleifend id felis. Suspendisse potenti...
75−
76−This contains one `key: value` pair, namely `title: About`. The rest of the
77−file is treated as markdown by pandoc. If you want to know more about
78−markdown, I think [this](http://daringfireball.net/projects/markdown/syntax)
79−is a pretty good page.
80−
81−## Templates
82−
83−Another concept are the so-called templates. Templates are text files (usually
84−html files) containing a number of keys. The syntax for these keys is
85−`$identifier`. Our example site contains one template, namely
86−`templates/default.html`. Let's have a better look at that.
87−
88−~~~~~{.html}
89−<html>
90− <head>
91− <title>MyAweSomeCompany - $title</title>
92− <link rel="stylesheet" type="text/css"
93− href="$$root/css/default.css" />
94− <link rel="stylesheet" type="text/css"
95− href="$$root/css/syntax.css" />
96− </head>
97− <body>
98− <h1>MyAweSomeCompany - $title</h1>
99− <div id="navigation">
100− <a href="$$root/index.html">Home</a>
101− <a href="$$root/about.html">About</a>
102− <a href="$$root/code.html">Code</a>
103− </div>
104−
105− $body
106− </body>
107−</html>
108−~~~~~
109−
110−We can see how our `Page` would fit in. When we render the page we saw using
111−this template, `$title` would be replaced by `About`, and `$body` would be
112−replaced by the body of the about page. `body` is the traditional name for the
113−body of any page - that is the convention in Hakyll. Also note that in this
114−case, `$body` would be replaced by a chunk of html - the result of the
115−markdown-to-html conversion.
116−
117−## The $$root key
118−
119−There is one "special" key in Hakyll: the $$root key. What is so special about
120−it? Well, internally, it is treated differently - but this should not concern
121−you. The thing is that it is the only key you can also use in **Pages**.
122−
123−It will be substituted by a relative url part (like `..` or `../..`) so it
124−points to the root directory of your site. It is recommended to use this
125−whenever you need it, it can save you some time from messing with absolute
126−and relative URL's.
127−
128−## Putting it all together
129−
130−Now, we'll render the page using the `renderChain` function. This function
131−takes a list of templates and a renderable object. In our case, we only have
132−one template, and our renderable object is simply a `PagePath`.
133−
134−~~~~~{.haskell}
135−import Text.Hakyll (hakyll)
136−import Text.Hakyll.Render (renderChain)
137−import Text.Hakyll.Renderables (createPagePath)
138−main = hakyll $ do
139− renderChain ["templates/default.html"]
140− (createPagePath "index.markdown")
141−~~~~~
142−
143−Or, to render all our three pages:
144−
145−~~~~~{.haskell}
146−import Text.Hakyll (hakyll)
147−import Text.Hakyll.Render (renderChain)
148−import Text.Hakyll.Renderables (createPagePath)
149−main = hakyll $ do
150− render "about.rst"
151− render "index.markdown"
152− render "code.lhs"
153− where render = renderChain ["templates/default.html"]
154− . createPagePath
155−~~~~~
156−
157−As you can see, we can render a variety of formats. This will create the
158−following files:
159−
160−~~~~~
161−_site/about.html
162−_site/index.html
163−_site/code.html
164−~~~~~
165−
166−## CSS, images and other static files
167−
168−Now, we also have a css file we would like to have in the `_site` directory.
169−Static files can be rendered using the `static` function in Hakyll. We could
170−use:
171−
172−~~~~~{.haskell}
173−import Text.Hakyll (hakyll)
174−import Text.Hakyll.Render (static)
175−main = hakyll $ do
176− static "css/default.css"
177−~~~~~
178−
179−This would work, but let's not forget that Hakyll also has css compression. If
180−we want to use that, we would use `css` instead of `static`.
181−
182−~~~~~{.haskell}
183−import Text.Hakyll (hakyll)
184−import Text.Hakyll.Render (css)
185−main = hakyll $ do
186− css "css/default.css"
187−~~~~~
188−
189−If we were to create another css file, we would have to add a line to our
190−`hakyll.hs` configuration file. This is pretty stupid, because the whole
191−directory `css` contains only css files. That's why Hakyll has a `directory`
192−function, which will execute a given function on an entire directory. So,
193−our example would become:
194−
195−~~~~~{.haskell}
196−import Text.Hakyll (hakyll)
197−import Text.Hakyll.Render (css)
198−import Text.Hakyll.File (directory)
199−main = hakyll $ do
200− directory css "css"
201−~~~~~
202−
203−## Deploying
204−
205−To setup your site, simply copy the contents of `_site` to your hosting provider
206−using your favorite piece of software.
207−
208−## That's it!
209−
210−Now you should fully understand our brochure site example. If you still have
211−questions, feel free to ask them on the
212−[google discussion group](http://groups.google.com/group/hakyll).
213−
214−If you feel comfortable with the basics, here is a next tutorial:
215−[more about writing pages](tutorial2.html).
examples/hakyll/tutorial2.markdowndeleted−161
@@ -1,161 +0,0 @@
1−---
2−title: MorePages
3−what: elaborates a little on writing pages and templates
4−---
5−
6−## The structure of a Page
7−
8−The most important thing to realize is that a `Page` is just a key-value
9−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 has
25−a `.markdown` extension for example, this would also be rendered by pandoc. But
26−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 the `Page` parser can do more than this. You can add extra sections, apart
36−from the 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−This will produce the following:
50−
51−- `author`: Vincent Vega
52−- `prelude`: A small introduction goes here. I can write *markdown* here, by the
53− way. Well, assuming this page has a `.markdown` extension.
54−- `main`: I can write some more things here.
55−
56−The example from this tutorial (we will see later) uses this to build a
57−three-column system for the website, separating content from layout.
58−
59−## Combining pages
60−
61−Now you know that `Page`s, and `Renderable`s in general, are basically nothing
62−more than key-values mappings, it is time to abuse this fact. There is another
63−`Renderable` type we haven't talked about before: a `CombinedRenderable`.
64−
65−The type signature of the `combine` function does a pretty good job at
66−explaining it:
67−
68−~~~~~{.haskell}
69−combine :: (Renderable a, Renderable b)
70− => a -> b -> CombinedRenderable a b
71−~~~~~
72−
73−This means we can take two `Renderable` values and combine them. This is
74−basically a `Map.union`: The result will contain all keys from `a`, and all
75−keys from `b`. If a key is present in both `Renderable`s, the value from `a`
76−will be chosen. This is, for example, always the case with an `url` (since
77−all `Renderable` types always have an url).
78−
79−Combining two `Renderable`s, but setting a different `url` is quite common, so
80−there is another function that helps us here:
81−
82−~~~~~{.haskell}
83−combineWithUrl :: (Renderable a, Renderable b)
84− => FilePath -> a -> b -> CombinedRenderable a b
85−~~~~~
86−
87−## The example
88−
89−Now that we have the tools, we'll get on to the example. This time, we'll
90−be making a more advanced brochure site. Here
91−[is a zip file](examples/morepages.zip) containing the source code for the
92−tutorial.
93−
94−Every page consists of three sections, originally named `section1`, `section2`
95−and `section3`. So our pages look more or less like this:
96−
97− ---
98− title: About
99−
100− --- section1
101− ## Mattis
102− Nullam imperdiet sodales orci vitae molestie. Nunc...
103−
104− --- section2
105− ## Orci
106− Vivamus eget mauris sit amet nulla laoreet lobortis.
107− Nulla in...
108−
109− --- section3
110− ## Augue
111− In urna ante, pulvinar et imperdiet nec, fermentum ac...
112−
113−The cool thing is we do not have to specify how these will be layed out. In our
114−template, we decide to use a simple three column system:
115−
116−~~~~~{.html}
117−<div class="column"> $section1 </div>
118−<div class="column"> $section2 </div>
119−<div class="column"> $section3 </div>
120−~~~~~
121−
122−The columns are then floated using css. So far so good, but what if we wanted
123−an additional text block on every page? An easy solution would be to add this
124−to the template, but then our layout-content separation idea will be broken
125−again. So we simply add to the template:
126−
127−~~~~~{.html}
128−<div class="footer"> $footer </div>
129−~~~~~
130−
131−And now we will use `combine` to put the footer on every page. We write a small
132−auxiliary function that combines a given `Renderable` with the footer:
133−
134−~~~~~{.haskell}
135−withFooter a = a `combine` createPagePath "footer.markdown"
136−~~~~~
137−
138−Now, were we previously wrote:
139−
140−~~~~~{.haskell}
141−render "about.markdown"
142−where render = renderChain ["templates/default.html"]
143− . createPagePath
144−~~~~~
145−
146−We simply have to add our footer:
147−
148−~~~~~{.haskell}
149−render "about.markdown"
150−where render = renderChain ["templates/default.html"]
151− . withFooter
152− . createPagePath
153−~~~~~
154−
155−And now every page will include the footer.
156−
157−## That's all folks
158−
159−I hope this tutorial was clear enough to teach you the concepts of pages and
160−combining renderables. As always, questions and feedback are welcome at the
161−[google discussion group](http://groups.google.com/group/hakyll).
examples/hakyll/tutorial3.markdowndeleted−170
@@ -1,170 +0,0 @@
1−---
2−title: SimpleBlog
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](examples/simpleblog.zip) for this
12−tutorial is also available.
13−
14−Blogs, as you probably know, are composed of posts. In Hakyll, we're going
15−to use simple pages for posts. All posts are located in the `posts`
16−directory. But we're not going to use the `directory` command here - you will
17−see why later. First, some trivial things like css.
18−
19−~~~~~{.haskell}
20−main = hakyll $ do
21− directory css "css"
22−~~~~~
23−
24−## Finding the posts, and a bit about renderables
25−
26−`Text.Hakyll.File` contains a handy function `getRecursiveContents`, which will
27−provide us with all the blog posts. The blog posts have a
28−`yyyy-mm-dd-title.extension` naming scheme. This is just a simple trick so we
29−can sort them easily, you could of course name them whatever you want. They
30−contain some metadata, too:
31−
32− title: A first post
33− author: Julius Caesar
34− date: November 5, 2009
35− ---
36− Lorem ipsum dolor sit amet, consectetur adipiscing elit.
37− Vivamus pretium leo adipiscing lectus iaculis lobortis.
38− Vivamus scelerisque velit dignissim metus...
39−
40−Now, we find the posts and sort them reversed:
41−
42−~~~~~{.haskell}
43−-- Find all post paths.
44−postPaths <- liftM (reverse . sort) $ getRecursiveContents "posts"
45−~~~~~
46−
47−Our `postPaths` value is now of the type `[FilePath]`. `FilePath` is no
48−instance of `Renderable`, but `PagePath` is:
49−
50−~~~~~{.haskell}
51−let renderablePosts = map createPagePath postPaths
52−~~~~~
53−
54−We have two templates we want to render our posts with: first we would like to
55−render them using `templates/post.html`, and we want to render the result
56−using `templates/default.html`. This can be done with the `renderChain`
57−function:
58−
59−~~~~~{.haskell}
60−mapM_ (renderChain [ "templates/post.html"
61− , "templates/default.html"
62− ]) renderablePosts
63−~~~~~
64−
65−Remember that the `renderChain` works by rendering the datatype using the first
66−template, creating a new page with the render result in the `body` field, and so
67−on until it has been rendered with all templates.
68−
69−Now, we have the posts rendered. What is left is to generate some kind of index
70−page with links to those posts. We want one general list showing all posts, and
71−we want to show a few recent posts on the index page.
72−
73−## Custom Pages
74−
75−Currently, there are 4 renderable datatypes in Hakyll:
76−
77−- `Page`: The result of any rendering action. It is generally not recommended
78− to use pages a lot, because they cannot check dependencies (and therefore,
79− you would always regenerate your entire site if you use pages the wrong way).
80−- `PagePath`: Basically just a `FilePath` in a box. Internally, this will use
81− a `Page` for rendering, but `PagePath` provides better dependency checking
82− and works on a higher level.
83−- `CustomPage`: Basically the name says it - the preferred way of creating
84− custom pages in Hakyll.
85−- `CombinedRenderable`: A way of combining two other `Renderable`s - this is
86− explained in tutorial 2.
87−
88−We will use a `CustomPage` here. Basically, all `Renderable` datatypes are in
89−the end just `key: value` mappings. A CustomPage is created using the
90−`createCustomPage` function, which has the following type signature:
91−
92−~~~~~{.haskell}
93−createCustomPage :: FilePath
94− -> [FilePath]
95− -> [(String, Either String (Hakyll String)]
96−~~~~~
97−
98−The first argument is the `url` of the page to generate. For our index page,
99−this will be, `index.html`. The second argument is _a list of dependencies_.
100−Basically, you should here give a list of files on which your custom page
101−depends.
102−
103−The last argument is obviously our `key: value` mapping. But why the `Either`?
104−This, once again, is about dependency handling. The idea is that you can choose
105−which type to use for the value:
106−
107−- `String`: Simply a `String`.
108−- `Hakyll String`: Here, you can give an arbitrary `Hakyll` action that will
109− result in a String. However - this action _will not be executed_ when the file
110− in `_site` is up-to-date.
111−
112−However, in this specific case - a list of posts - there is an easier, and more
113−high-level approach[^1]. Let's look at the signature of `createListing`:
114−
115−~~~~~{.haskell}
116−createListing :: (Renderable a)
117− -> String
118− -> FilePath
119− -> [a]
120− -> [(String, String)]
121−~~~~~
122−
123−[^1]: Since Hakyll-1.3 onwards.
124−
125−The first argument is the destination url. For our blog, this is of course
126−`index.html`. The second argument is a template to render _each renderable_
127−with. We use `templates/postitem.html` here. This is, as you can see, a simple
128−template:
129−
130−~~~~~{.html}
131−<li>
132− <a href="$$root/$url">$title</a>
133− - <em>$date</em> - by <em>$author</em>
134−</li>
135−~~~~~
136−
137−We then give a list of renderables. For our index, these are the 3 last posts.
138−The last argument of the `createListing` functions lets you specify additional
139−key-value pairs. We use this to set the title of our page. So, we create our
140−index page using:
141−
142−~~~~~{.haskell}
143−let index = createListing "index.html"
144− "templates/postitem.html"
145− (take 3 renderablePosts)
146− [("title", "Home")]
147−~~~~~
148−
149−The result of this will be a `CustomPage`. The body of this page will contain
150−a concatenation of all the renderables, rendered with the
151−`templates/postitem.html` template.
152−
153−Now, we only have to render it: first using the `index.html` template, then
154−using the `templates/default.html` template.
155−
156−~~~~~{.haskell}
157−renderChain ["index.html", "templates/default.html"] index
158−~~~~~
159−
160−Note that the `index.html` in the `renderChain` list is also a template. Now,
161−take your time to read the `index.html` template and the other files in the zip
162−so you understand what is going on here.
163−
164−## That's that
165−
166−If you have any more questions, feel free to ask them on the
167−[google discussion group](http://groups.google.com/group/hakyll).
168−
169−There is a [next tutorial](tutorial4.html), explaining how to add an RSS feed
170−to our sample blog.
examples/hakyll/tutorial4.markdowndeleted−114
@@ -1,114 +0,0 @@
1−---
2−title: RSSBlog
3−what: adds an RSS feed to the blog from the previous tutorial
4−---
5−
6−## Adding RSS to our simple blog
7−
8−In this tutorial, we're going to add an RSS feed to the blog we wrote in
9−[the previous tutorial](tutorial2.html). Here is a
10−[zip file containing the source](examples/rssblog.zip).
11−
12−An RSS feed looks like this:
13−
14−~~~~~{.xml}
15−<?xml version="1.0" ?>
16−<rss version="2.0">
17− <channel>
18− <title>The SimpleBlog</title>
19− <link>http://example.com/</link>
20− <description>Simple blog in hakyll</description>
21− <item>
22− <title>Title goes here</title>
23− <link>http://example.com/post.html</link>
24− <description>
25− A description is optional.
26− </description>
27− </item>
28− </channel>
29−</rss>
30−~~~~~
31−
32−Note that, obviously, there can be more than one item. We're going to use a
33−template to render this. This is where `templates/rss.xml` comes in:
34−
35−~~~~~{.xml}
36−<?xml version="1.0" ?>
37−<rss version="2.0">
38− <channel>
39− <title>The SimpleBlog</title>
40− <link>http://jaspervdj.be/</link>
41− <description>Simple blog in hakyll</description>
42− $body
43− </channel>
44−</rss>
45−~~~~~
46−
47−We thus render our feed with the following code (no, I didn't define `rss`
48−yet - I'm going to work from bottom to top here, it's easier to explain it
49−that way).
50−
51−~~~~~{.haskell}
52−renderChain ["templates/rss.xml"] rss
53−~~~~~
54−
55−This, as you can see, is a regular render chain, once again. We need make a
56−`Renderable` that "fills in" the `$items` identifier. We're going to do this
57−using a [custom page](tutorial2.html#custom-pages).
58−
59−## Custom pages again
60−
61−Note that we do not have to include all posts in the rss feed - only a few
62−recent ones. We'll settle on the latest three here.
63−
64−We want to render every post using the following template,
65−`templates/rssitem.xml`:
66−
67−~~~~~{.haskell}
68−<item>
69− <title>$title</title>
70− <link>http://example.com/$url</link>
71− <description>$title by $author</description>
72−</item>
73−~~~~~
74−
75−Since we build on the previous example, we still have our `renderablePosts`
76−list. We'll be using it again to create a listing:
77−
78−~~~~~{.haskell}
79−let rss = createListing "rss.xml" "templates/rssitem.xml"
80− (take 3 renderablePosts) []
81−~~~~~
82−
83−Note that we give `rss.xml` as destination url, because that is where we want
84−our `CustomPage`. We give a template to render every post with
85−(`"templates/rssitem.xml"`) and no additional context (so we use the empty list
86−`[]`).
87−
88−## Adding a link to the feed
89−
90−According to the w3 organization,
91−[you should add \<link\> tags](http://www.w3.org/QA/Tips/use-links) to your
92−documents, so we'll do this. In `templates/default.html`:
93−
94−~~~~~~{.html}
95−<head>
96− <title>SimpleBlog - $title</title>
97− <link rel="stylesheet" type="text/css" href="$$root/css/default.css" />
98− <link rel="alternate"
99− type="application/rss+xml"
100− title="SimpleBlog"
101− href="$root/rss.xml" />
102−</head>
103−~~~~~~
104−
105−This makes most browsers see the rss feed, and show it in the address bar.
106−If you want, you can also add a pretty button to your blog linking to
107−`rss.xml`.
108−
109−## That's it!
110−
111−Yep, that's it. Feel free to play with the source code in
112−[the zip file](examples/rssblog.zip) and extend the blog further. As always,
113−all questions are welcome on the
114−[google discussion group](http://groups.google.com/group/hakyll).
examples/hakyll/tutorial5.markdowndeleted−209
@@ -1,209 +0,0 @@
1−---
2−title: TagBlog
3−what: adds tags and context manipulations to our blog
4−---
5−
6−Here, have [a zip file](examples/tagblog.zip) for this tutorial.
7−
8−## Context manipulations
9−
10−As you might remember, `Renderable` objects are usually just key-value mappings.
11−We can 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−In `Text.Hakyll.Context`, we see the type of `ContextManipulation`:
17−
18−~~~~~{.haskell}
19−type ContextManipulation = Context -> Context
20−~~~~~
21−
22−Where `Context` is a key-value mapping. Usually, you don't create
23−`ContextManipulation`'s by hand, but you use the `renderValue` function. Let's
24−have a look at it's type.
25−
26−~~~~~{.haskell}
27−renderValue :: String -> String
28− -> (String -> String)
29− -> ContextManipulation
30−~~~~~
31−
32−This is the preferred way of creating context manipulations. The first argument
33−is the `key` to manipulate. The second argument is the `key` where the new value
34−should be placed. If this is the same as the first argument, it will be
35−replaced. The third argument is the function to manipulate the `value` with.
36−
37−As a simple example, let's write a function that puts the `$title` in uppercase.
38−
39−~~~~~{.haskell}
40−import Data.Char (toUpper)
41−
42−titleUpper :: ContextManipulation
43−titleUpper = renderValue "title" "title" $ map toUpper
44−~~~~~
45−
46−## Applying Context Manipulations
47−
48−Now, the question is how to apply these `ContextManipulation`'s. The answer is
49−simple. For every important render function (`render`, `renderChain`,
50−`renderAndConcat`), there is a variant that takes a `ContextManipulation` as a
51−first argument. These functions are thus: `renderWith`, `renderChainWith`,
52−`renderAndConcatWith`. In fact, the following holds true:
53−
54−~~~~~{.haskell}
55−render == renderWith id
56−renderChain == renderChainWith id
57−renderAndConcat == renderAndConcatWith id
58−~~~~~
59−
60−So we could use or title manipulation like this:
61−
62−~~~~~{.haskell}
63−renderChainWith titleUpper ["templates/default.html"]
64− (createPagePath "index.markdown")
65−~~~~~
66−
67−## Rendering dates
68−
69−As you remember, in our previous blog, all posts had a file name like
70−`posts/yyyy-mm-dd-title.extension`, as is the Hakyll convention. But they also
71−had a metadata field `date`, containing a human-readable date. This is not very
72−D.R.Y., of course! Hakyll has a specialized `renderValue` function to deal with
73−dates encoded in paths: `renderDate`.
74−
75−~~~~~{.haskell}
76−postManipulation :: ContextManipulation
77−postManipulation = renderDate "date" "%B %e, %Y" "Unknown date"
78−~~~~~
79−
80−That manipulation will:
81−- Read the date from the file name the post was loaded from.
82−- Parse the date and render it in a `%B %e, %Y` format. This is a
83− `Month day, Year` format.
84−- Put the result in the `date` metadata field.
85−- If the date could not be parsed, it will put `"Unknown date"` in the `date`
86− metadata field.
87−
88−So, we can throw away our `date: ` lines from our posts, and still use `$date`
89−in our templates.
90−
91−## Abstracting the post list
92−
93−Now, we're going to render tags. This is also done using context manipulations.
94−Hakyll has a specialized module to deal with tags, provided by
95−`Text.Hakyll.Tags`. This module assumes tags are comma separated, and placed in
96−the `tags` metadata field.
97−
98− ---
99− title: A third post
100− author: Publius Ovidius Naso
101− tags: epic fail, ovidius
102− ---
103− Pellentesque tempor blandit elit, vel...
104−
105−But first things first. We need to render a post list for every tag. We already
106−had some code to render a list of all posts. We're just going to abstract this
107−code into a more general function:
108−
109−~~~~{.haskell}
110−renderPostList url title posts = do
111− let list = createListingWith postManipulation
112− url "templates/postitem.html"
113− posts [("title", title)]
114− renderChain ["posts.html", "templates/default.html"] list
115−~~~~~
116−
117−As you can see, `createListing` also has a `xxxWith` variant that lets you
118−specify a `ContextManipulation` to apply on every `Renderable`. Our "render all
119−posts" action can now be written as:
120−
121−~~~~~{.haskell}
122−renderPostList "posts.html" "All posts" renderablePosts
123−~~~~~
124−
125−## Tag links
126−
127−We want to display the tags for our post under the title. But if we use the
128−`$tags` key in a template, we will just have the plain tags - they will not be
129−clickable. We can again solve this with a `ContextManipulation`. We have a
130−function that produces an url for a given tag:
131−
132−~~~~~{.haskell}
133−tagToUrl tag = "$root/tags/" ++ removeSpaces tag ++ ".html"
134−~~~~~
135−
136−`removeSpaces` is an auxiliary function from `Text.Hakyll.File`. Now, there is
137−a specialized `renderValue` function for creating linked tags called
138−`renderTagLinks`. This function simply takes a function that produces an url
139−for a given tag - the function we just wrote. Let's extend our
140−`postManipulation`.
141−
142−~~~~~{.haskell}
143−postManipulation :: ContextManipulation
144−postManipulation = renderDate "date" "%B %e, %Y" "Unknown date"
145− . renderTagLinks tagToUrl
146−~~~~~
147−
148−So, the `renderTagLinks` function replaces the `$tags` value from
149−`epic fail, random` to `<a href="$root/tags/epic-fail.html">epic fail</a>, ...`.
150−If we click a tag, we get a `404`. That's because we haven't generated the
151−post lists for every tag.
152−
153−## The Tag Map
154−
155−Hakyll provides a function called `readTagMap`. Let's inspect it's type.
156−
157−~~~~~{.haskell}
158−type TagMap = Map String [FilePath]
159−readTagMap String [FilePath] -> Hakyll TagMap
160−~~~~~
161−
162−You give it a list of paths, and it creates a map that, for every tag, holds
163−a number of posts. We can easily use this to render a post list for every tag.
164−The first argument given is an "identifier", unique to this tag map. Hakyll
165−needs this so it can cache the tags.
166−
167−~~~~~{.haskell}
168−tagMap <- readTagMap "postTags" postPaths
169−let renderListForTag (tag, posts) =
170− renderPostList (tagToUrl tag)
171− ("Posts tagged " ++ tag)
172−mapM_ renderListForTag (toList tagMap)
173−~~~~~
174−
175−There we go. We now have clickable tags, and a post list for every tag.
176−
177−## A Tag Cloud
178−
179−A tag cloud is a commonly found thing on blogs. Hakyll also provides code to
180−generate a tag cloud. Let's have a look at the `renderTagCloud` function.
181−
182−~~~~~{.haskell}
183−renderTagCloud :: TagMap
184− -> (String -> String)
185− -> Float
186− -> Float
187− -> String
188−~~~~~
189−
190−The first argument is obviously the result of the `readTagMap` function. The
191−second argument is, once again, a function to create an url for a given tag.
192−Then, we give a minimum and a maximum font size in percent, and we get the
193−tag cloud back. We can add this to our index:
194−
195−~~~~~{.haskell}
196−let tagCloud = renderTagCloud tagMap tagToUrl 100 200
197− index = createListingWith postManipulation "index.html"
198− "templates/postitem.html"
199− (take 3 renderablePosts)
200− [ ("title", "Home")
201− , ("tagcloud", tagCloud)
202− ]
203−~~~~~
204−
205−## That's it
206−
207−Feel free to hack around with the code from the zip file. As always, if you
208−still have questions, ask them at the
209−[google discussion group](http://groups.google.com/group/hakyll).
examples/hakyll/tutorial6.markdowndeleted−88
@@ -1,88 +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/tutorial6-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/tutorial6-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](examples/categoryblog.zip) containing the files used in this
20−tutorial.
21−
22−## Reading Categories
23−
24−Tags are located in the `tags` metadata field. Since one post can only belong
25−in one category, a different approach was chosen here. The category of a post
26−is determined by the subfolder it is in. Here you see the directory layout for
27−our posts using categories:
28−
29− posts
30− |-- coding
31− | |-- 2009-11-05-a-first-post.markdown
32− | |-- 2009-11-28-a-third-post.markdown
33− | `-- 2009-12-04-this-blog-aint-dead.markdown
34− `-- random
35− |-- 2009-11-10-another-post.markdown
36− `-- 2009-12-23-almost-christmas.markdown
37−
38−Because we find all our posts in different subdirectories, sorting them is a
39−little harder: we still want them sorted by date, so it boils down to sorting
40−them by "base name". I hope it does not surprise you Hakyll provides a function
41−for that:
42−
43−~~~~~{.haskell}
44−postPaths <- liftM (reverse . sortByBaseName)
45− (getRecursiveContents "posts")
46−~~~~~
47−
48−Now, we can use the `readCategoryMap` function instead of `readTagMap`, which
49−has the same signature, but assigns categories based on the folders the posts
50−are in.
51−
52−~~~~~{.haskell}
53−categoryMap <- readCategoryMap "categoryMap" renderablePosts
54−~~~~~
55−
56−The rest of the `hakyll.hs` is very similar to the one in the previous
57−tutorial, except we want to render a category list instead of a tag cloud.
58−
59−## Rendering a category list
60−
61−Because rendering a category list is quite easy, and it would be hard to
62−write a "general" function for this, hakyll does not provide such a function -
63−but it is not hard to write. First, we write an auxiliary function that produces
64−a list item for one category:
65−
66−~~~~~{.haskell}
67−categoryListItem category posts =
68− "<li>" ++ link category (categoryToUrl category)
69− ++ " - " ++ show (length posts) ++ " items.</li>"
70−~~~~~
71−
72−This is nothing more that some string concatenation. The function that applies
73−this on every element in the `TagMap` is more interesting:
74−
75−~~~~~{.haskell}
76−categoryList :: TagMap -> String
77−categoryList = uncurry categoryListItem <=< toList
78−~~~~~
79−
80−This function might seem a little harder to understand if you are not familiar
81−with the `<=<` operator. Figuring out how it works is left as an exercise for
82−the reader[^1].
83−
84−[^1]: Hint: `>>=` is `concatMap` in the list monad.
85−
86−We then add this to our index page, and we are done. Feel free to hack around
87−with the source code. If you still have questions, feel free to ask them at the
88−[google discussion group](http://groups.google.com/group/hakyll).
examples/hakyll/tutorial7.markdowndeleted−51
@@ -1,51 +0,0 @@
1−---
2−title: Tips and Tricks
3−what: some various tips and tricks about Hakyll
4−---
5−
6−## Auto-compilation
7−
8−Hakyll features a simple _auto-compilation_ mode. This is invoked by running
9−
10−~~~~~
11−[jasper@alice ~]$ ./hakyll preview
12−Starting hakyll server on port 8000...
13−~~~~~
14−
15−Now, Hakyll will recompile your site when you change files, so you can just
16−refresh in your browser. There is one more thing to note: this will not update
17−your site automatically when `hakyll.hs` changes. So if you make any changes to
18−the configuration file, you'll have to compile it again, and then you can enter
19−`preview` mode again.
20−
21−## Pretty URL's
22−
23−There is an option in Hakyll to produce pretty URL's, which is disabled by
24−default because it can be confusing when you're first introduced to Hakyll.
25−
26−It can be enabled this way:
27−
28−~~~~~{.haskell}
29−import Text.Hakyll
30−import Text.Hakyll.Hakyll
31−
32−myHakyllConfiguration :: HakyllConfiguration
33−myHakyllConfiguration = defaultHakyllConfiguration
34− { enableIndexUrl = True
35− }
36−
37−main = hakyllWithConfiguration HakyllConfiguration $ do
38− -- Further code here
39−~~~~~
40−
41−The effect will be that the internal `toUrl` function will behave differently.
42−A few examples:
43−
44−- `about.html` will be rendered to `about/index.html`.
45−- `posts/2010-02-16-a-post.markdown` will be rendered to
46− `posts/2010-02-16-a-post/index.html`.
47−- However, `index.markdown` will still be rendered to `index.html`. Likewise,
48− `posts/index.html` would be rendered to `posts.index.html`.
49−
50−The benefit of this is simply prettier URL's. That is, if you consider
51−`example.com/about` prettier than `example.com/about.html`.
examples/hakyll/tutorials/part07.markdown+1−1
@@ -1,6 +1,6 @@
11---
22title: Interlude
3−what: some various tips and tricks about Hakyll
3+what: gives some various tips and tricks about Hakyll
44---
55
66## Auto-compilation
examples/hakyll/tutorials/part08.markdown+7
@@ -95,3 +95,10 @@ an arrow of this function. The `arr` function solves this problem easily.
9595We then add this to our index page, and we are done. Feel free to hack around
9696with the source code. If you still have questions, feel free to ask them at the
9797[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.