on master help sign in
Started writing Hakyll 2.x tutorials. Updated brochure example.
commitfa88a0e9228a831f2808cda5c859e882b6579ccd authorJasper Van der Jeugt <jaspervdj@gmail.com> dateThu 11 Mar 2010, 18:03 (17 years ago) parent01d8c4b
8 files changed+333−20
examples/brochure/hakyll.hs+4−3 examples/hakyll/hakyll.hs+8−8 examples/hakyll/images/arrow-composition.png examples/hakyll/sidebar.markdown+6−6 examples/hakyll/templates/default.html+3−3 examples/hakyll/tutorials/part01.markdown+228 examples/hakyll/tutorials/part02.markdown+83 src/Text/Hakyll/Render.hs+1
examples/brochure/hakyll.hs+4−3
@@ -1,11 +1,12 @@
11import Text.Hakyll (hakyll)
22import Text.Hakyll.File (directory)
33import Text.Hakyll.Render (css, static, renderChain)
4−import Text.Hakyll.Renderables (createPagePath)
4+import Text.Hakyll.CreateContext (createPage)
55
6−main = hakyll $ do
6+main = hakyll "http://example.com" $ do
77 directory css "css"
88 render "about.rst"
99 render "index.markdown"
1010 render "code.lhs"
11− where render = renderChain ["templates/default.html"] . createPagePath
11+ where render = renderChain ["templates/default.html"]
12+ . createPage
examples/hakyll/hakyll.hs+8−8
@@ -1,6 +1,6 @@
11import Text.Hakyll
22import Text.Hakyll.Render
3−import Text.Hakyll.Renderables
3+import Text.Hakyll.CreateContext
44import Text.Hakyll.File
55import Text.Hakyll.Regex
66import Control.Monad.Reader (liftIO)
@@ -8,17 +8,17 @@ import System.Directory
88import Control.Monad (mapM_, liftM)
99import Data.List (sort)
1010
11−main = hakyll $ do
11+main = hakyll "http://jaspervdj.be/hakyll" $ do
1212 directory css "css"
1313 directory static "images"
1414 directory static "examples"
1515 directory static "reference"
1616
17− tutorials <- liftIO $ liftM (sort . filter (`matchesRegex` "^tutorial[0-9]*.markdown$")) $ getDirectoryContents "."
17+ tutorials <- liftM sort $ getRecursiveContents "tutorials"
1818 let tutorialPage = createListing "tutorials.html"
19− "templates/tutorialitem.html"
20− (map createPagePath tutorials)
21− [("title", "Tutorials")]
19+ ["templates/tutorialitem.html"]
20+ (map createPage tutorials)
21+ [("title", Left "Tutorials")]
2222 renderChain ["templates/tutorials.html", "templates/default.html"] $ withSidebar tutorialPage
2323
2424 mapM_ render' $ [ "about.markdown"
@@ -29,6 +29,6 @@ main = hakyll $ do
2929 ] ++ tutorials
3030
3131 where
32− render' = renderChain ["templates/default.html"] . withSidebar . createPagePath
33− withSidebar a = a `combine` createPagePath "sidebar.markdown"
32+ render' = renderChain ["templates/default.html"] . withSidebar . createPage
33+ withSidebar a = a `combine` createPage "sidebar.markdown"
3434
examples/hakyll/images/arrow-composition.pngnew file

Binary file not shown.

examples/hakyll/sidebar.markdown+6−6
@@ -1,9 +1,9 @@
11--- sidebar
22## Navigation
33
4−[home](index.html)
5−[philosophy](philosophy.html)
6−[about](about.html)
7−[tutorials](tutorials.html)
8−[reference](reference.html)
9−[changelog](changelog.html)
4+[home]($root/index.html)
5+[philosophy]($root/philosophy.html)
6+[about]($root/about.html)
7+[tutorials]($root/tutorials.html)
8+[reference]($root/reference.html)
9+[changelog]($root/changelog.html)
examples/hakyll/templates/default.html+3−3
@@ -7,8 +7,8 @@
77 <title>Hakyll - $title</title>
88
99 <!-- Stylesheets. -->
10− <link rel="stylesheet" type="text/css" href="css/default.css" />
11− <link rel="stylesheet" type="text/css" href="css/syntax.css" />
10+ <link rel="stylesheet" type="text/css" href="$root/css/default.css" />
11+ <link rel="stylesheet" type="text/css" href="$root/css/syntax.css" />
1212
1313 <!-- Metadata. -->
1414 <meta name="keywords" content="hakyll,static site generator,static,site,generator,haskell,blog"/>
@@ -17,7 +17,7 @@
1717 <body>
1818 <div id="main">
1919 <div id="header">
20− <img src="images/lambda.png" alt="lambda" />
20+ <img src="$root/images/lambda.png" alt="lambda" />
2121 <h1>Hakyll - $title</h1>
2222 </div>
2323 <!-- Sidebar. -->
examples/hakyll/tutorials/part01.markdownnew file+228
@@ -0,0 +1,228 @@
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]: 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+main = hakyll "http://example.com" $ do
60+ liftIO $ putStrLn "I'm in your computer, generating your site!"
61+~~~~~
62+
63+Note how we wrap everyting in the `hakyll` function. This is useful because
64+it will generate a very nice main function. We also pass the full site URL to
65+the `hakyll` function. If you don't have an URL for your site yet, it doesn't
66+really matter for now; just fill in anything then. The URL is only used for
67+certain specific purposes where a full URL is needed, such as rendering RSS
68+feeds.
69+
70+## Context
71+
72+Let's look at one of the most important types in Hakyll.
73+
74+~~~~~{.haskell}
75+type Context = Map String String
76+~~~~~
77+
78+A `Context` is a key-value mapping, used to represent pieces of information.
79+One way to write such a `Context`, is a page.
80+
81+## Pages
82+
83+Another important concept in Hakyll is pages. Pages are text files that can be
84+written in markdown, html, rst... basically anything Pandoc supports.
85+Furthermore, they can also contain some metadata. The metadata is placed in the
86+file header and surrouded by `---` lines. Each line should contain a
87+`key: value` pair. Let's have a look at the `index.markdown` page.
88+
89+ ---
90+ title: About
91+ ---
92+ Nullam imperdiet sodales orci vitae molestie.
93+ Nunc quam orci, pharetra a rhoncus vitae,
94+ eleifend id felis. Suspendisse potenti...
95+
96+This contains one `key: value` pair, namely `title: About`. The rest of the
97+file is treated as markdown by pandoc. If you want to know more about
98+markdown, I think [this](http://daringfireball.net/projects/markdown/syntax)
99+is a pretty good page.
100+
101+## Templates
102+
103+Another concept are the so-called templates. Templates are text files (usually
104+html files) containing a number of keys. The syntax for these keys is
105+`$identifier`. Our example site contains one template, namely
106+`templates/default.html`. Let's have a better look at that.
107+
108+~~~~~{.html}
109+<html>
110+ <head>
111+ <title>MyAweSomeCompany - $title</title>
112+ <link rel="stylesheet" type="text/css"
113+ href="$$root/css/default.css" />
114+ <link rel="stylesheet" type="text/css"
115+ href="$$root/css/syntax.css" />
116+ </head>
117+ <body>
118+ <h1>MyAweSomeCompany - $title</h1>
119+ <div id="navigation">
120+ <a href="$$root/index.html">Home</a>
121+ <a href="$$root/about.html">About</a>
122+ <a href="$$root/code.html">Code</a>
123+ </div>
124+
125+ $body
126+ </body>
127+</html>
128+~~~~~
129+
130+We can see how our `Page` would fit in. When we render the page we saw using
131+this template, `$title` would be replaced by `About`, and `$body` would be
132+replaced by the body of the about page. `body` is the traditional name for the
133+body of any page - that is the convention in Hakyll. Also note that in this
134+case, `$body` would be replaced by a chunk of html - the result of the
135+markdown-to-html conversion.
136+
137+## The $$root key
138+
139+There are a few "special" keys in Hakyll: one of them is the $$root key. What
140+is so special about it? Well, internally, it is treated differently - but this
141+should not concern you. The thing is that it is the only key you can also use
142+in __Pages__.
143+
144+It will be substituted by a relative url part (like `..` or `../..`) so it
145+points to the root directory of your site. It is recommended to use this
146+whenever you need it, it can save you some time from messing with absolute
147+and relative URL's.
148+
149+## Putting it all together
150+
151+Now, we'll render the page using the `renderChain` function. This function
152+takes a list of templates and a `Context`. In our case, we only have one
153+template, and our `Context` is the about page we just saw - we can load that
154+using the `createPage` function.
155+
156+~~~~~{.haskell}
157+import Text.Hakyll (hakyll)
158+import Text.Hakyll.Render (renderChain)
159+import Text.Hakyll.CreateContext (createPage)
160+main = hakyll "http://example.com" $ do
161+ renderChain ["templates/default.html"]
162+ (createPage "index.markdown")
163+~~~~~
164+
165+Or, to render all our three pages:
166+
167+~~~~~{.haskell}
168+import Text.Hakyll (hakyll)
169+import Text.Hakyll.Render (renderChain)
170+import Text.Hakyll.CreateContext (createPage)
171+main = hakyll "http://example.com" $ do
172+ render "about.rst"
173+ render "index.markdown"
174+ render "code.lhs"
175+ where render = renderChain ["templates/default.html"]
176+ . createPage
177+~~~~~
178+
179+As you can see, we can render a variety of formats. This will create the
180+following files:
181+
182+~~~~~
183+_site/about.html
184+_site/index.html
185+_site/code.html
186+~~~~~
187+
188+## CSS, images and other static files
189+
190+Now, we also have a css file we would like to have in the `_site` directory.
191+Static files can be rendered using the `static` function in Hakyll. We could
192+use:
193+
194+~~~~~{.haskell}
195+import Text.Hakyll (hakyll)
196+import Text.Hakyll.Render (static)
197+main = hakyll "http://example.com" $ do
198+ static "css/default.css"
199+~~~~~
200+
201+This would work, but let's not forget that Hakyll also has css compression. If
202+we want to use that, we would use `css` instead of `static`.
203+
204+~~~~~{.haskell}
205+import Text.Hakyll (hakyll)
206+import Text.Hakyll.Render (css)
207+main = hakyll "http://example.com" $ do
208+ css "css/default.css"
209+~~~~~
210+
211+If we were to create another css file, we would have to add a line to our
212+`hakyll.hs` configuration file. This is pretty stupid, because the whole
213+directory `css` contains only css files. That's why Hakyll has a `directory`
214+function, which will execute a given function on an entire directory. So,
215+our example would become:
216+
217+~~~~~{.haskell}
218+import Text.Hakyll (hakyll)
219+import Text.Hakyll.Render (css)
220+import Text.File (directory)
221+main = hakyll "http://example.com" $ do
222+ directory css "css"
223+~~~~~
224+
225+## Deploying
226+
227+To setup your site, simply copy the contents of `_site` to your hosting provider
228+using your favorite piece of software.
examples/hakyll/tutorials/part02.markdownnew file+83
@@ -0,0 +1,83 @@
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 Hakyll to only execute
75+our functions when it is really needed. In this particular case, `test` would
76+only be executed if either `test.markdown` or `template.html` were recently
77+changed.
78+
79+## The gist of it
80+
81+- Arrows really aren't complicated.
82+- Compose them using `>>>`.
83+- `HakyllAction` tracks dependencies for you. Use it.
src/Text/Hakyll/Render.hs+1
@@ -6,6 +6,7 @@ module Text.Hakyll.Render
66 , renderChain
77 , static
88 , css
9+ , writePage
910 ) where
1011
1112import Control.Arrow ((>>>))