summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJasper Van der Jeugt <jaspervdj@gmail.com>2010-03-11 18:03:48 +0100
committerJasper Van der Jeugt <jaspervdj@gmail.com>2010-03-11 18:03:48 +0100
commitfa88a0e9228a831f2808cda5c859e882b6579ccd (patch)
tree92a533a2c7f627ff3308f6f182829858afe8f3e9
parent01d8c4b330ecbc8ca52bfbe659bdecad475bd1ab (diff)
downloadhakyll-fa88a0e9228a831f2808cda5c859e882b6579ccd.tar.gz
Started writing Hakyll 2.x tutorials. Updated brochure example.
-rw-r--r--examples/brochure/hakyll.hs7
-rw-r--r--examples/hakyll/hakyll.hs16
-rw-r--r--examples/hakyll/images/arrow-composition.pngbin0 -> 16066 bytes
-rw-r--r--examples/hakyll/sidebar.markdown12
-rw-r--r--examples/hakyll/templates/default.html6
-rw-r--r--examples/hakyll/tutorials/part01.markdown228
-rw-r--r--examples/hakyll/tutorials/part02.markdown83
-rw-r--r--src/Text/Hakyll/Render.hs1
8 files changed, 333 insertions, 20 deletions
diff --git a/examples/brochure/hakyll.hs b/examples/brochure/hakyll.hs
index c550202..93a2393 100644
--- a/examples/brochure/hakyll.hs
+++ b/examples/brochure/hakyll.hs
@@ -1,11 +1,12 @@
import Text.Hakyll (hakyll)
import Text.Hakyll.File (directory)
import Text.Hakyll.Render (css, static, renderChain)
-import Text.Hakyll.Renderables (createPagePath)
+import Text.Hakyll.CreateContext (createPage)
-main = hakyll $ do
+main = hakyll "http://example.com" $ do
directory css "css"
render "about.rst"
render "index.markdown"
render "code.lhs"
- where render = renderChain ["templates/default.html"] . createPagePath
+ where render = renderChain ["templates/default.html"]
+ . createPage
diff --git a/examples/hakyll/hakyll.hs b/examples/hakyll/hakyll.hs
index ca45175..53b9a4b 100644
--- a/examples/hakyll/hakyll.hs
+++ b/examples/hakyll/hakyll.hs
@@ -1,6 +1,6 @@
import Text.Hakyll
import Text.Hakyll.Render
-import Text.Hakyll.Renderables
+import Text.Hakyll.CreateContext
import Text.Hakyll.File
import Text.Hakyll.Regex
import Control.Monad.Reader (liftIO)
@@ -8,17 +8,17 @@ import System.Directory
import Control.Monad (mapM_, liftM)
import Data.List (sort)
-main = hakyll $ do
+main = hakyll "http://jaspervdj.be/hakyll" $ do
directory css "css"
directory static "images"
directory static "examples"
directory static "reference"
- tutorials <- liftIO $ liftM (sort . filter (`matchesRegex` "^tutorial[0-9]*.markdown$")) $ getDirectoryContents "."
+ tutorials <- liftM sort $ getRecursiveContents "tutorials"
let tutorialPage = createListing "tutorials.html"
- "templates/tutorialitem.html"
- (map createPagePath tutorials)
- [("title", "Tutorials")]
+ ["templates/tutorialitem.html"]
+ (map createPage tutorials)
+ [("title", Left "Tutorials")]
renderChain ["templates/tutorials.html", "templates/default.html"] $ withSidebar tutorialPage
mapM_ render' $ [ "about.markdown"
@@ -29,6 +29,6 @@ main = hakyll $ do
] ++ tutorials
where
- render' = renderChain ["templates/default.html"] . withSidebar . createPagePath
- withSidebar a = a `combine` createPagePath "sidebar.markdown"
+ render' = renderChain ["templates/default.html"] . withSidebar . createPage
+ withSidebar a = a `combine` createPage "sidebar.markdown"
diff --git a/examples/hakyll/images/arrow-composition.png b/examples/hakyll/images/arrow-composition.png
new file mode 100644
index 0000000..947561a
--- /dev/null
+++ b/examples/hakyll/images/arrow-composition.png
Binary files differ
diff --git a/examples/hakyll/sidebar.markdown b/examples/hakyll/sidebar.markdown
index 3212d61..9444c58 100644
--- a/examples/hakyll/sidebar.markdown
+++ b/examples/hakyll/sidebar.markdown
@@ -1,9 +1,9 @@
--- sidebar
## Navigation
-[home](index.html)
-[philosophy](philosophy.html)
-[about](about.html)
-[tutorials](tutorials.html)
-[reference](reference.html)
-[changelog](changelog.html)
+[home]($root/index.html)
+[philosophy]($root/philosophy.html)
+[about]($root/about.html)
+[tutorials]($root/tutorials.html)
+[reference]($root/reference.html)
+[changelog]($root/changelog.html)
diff --git a/examples/hakyll/templates/default.html b/examples/hakyll/templates/default.html
index 9bcd33a..2e94f72 100644
--- a/examples/hakyll/templates/default.html
+++ b/examples/hakyll/templates/default.html
@@ -7,8 +7,8 @@
<title>Hakyll - $title</title>
<!-- Stylesheets. -->
- <link rel="stylesheet" type="text/css" href="css/default.css" />
- <link rel="stylesheet" type="text/css" href="css/syntax.css" />
+ <link rel="stylesheet" type="text/css" href="$root/css/default.css" />
+ <link rel="stylesheet" type="text/css" href="$root/css/syntax.css" />
<!-- Metadata. -->
<meta name="keywords" content="hakyll,static site generator,static,site,generator,haskell,blog"/>
@@ -17,7 +17,7 @@
<body>
<div id="main">
<div id="header">
- <img src="images/lambda.png" alt="lambda" />
+ <img src="$root/images/lambda.png" alt="lambda" />
<h1>Hakyll - $title</h1>
</div>
<!-- Sidebar. -->
diff --git a/examples/hakyll/tutorials/part01.markdown b/examples/hakyll/tutorials/part01.markdown
new file mode 100644
index 0000000..9647248
--- /dev/null
+++ b/examples/hakyll/tutorials/part01.markdown
@@ -0,0 +1,228 @@
+---
+title: Quickstart
+what: explains how to create a simple brochure site
+---
+
+## Getting started
+
+First, make sure you have Hakyll installed. The recommended way to do this is
+through [hackage] using [cabal-install]. This tutorial also assumes you have a
+basic knowledge of Haskell.
+
+[hackage]: http://hackage.haskell.org/
+[cabal-install]: http://www.haskell.org/haskellwiki/Cabal-Install
+
+~~~~~
+[jasper@alice ~]$ cabal install hakyll
+~~~~~
+
+## Building a simple static site
+
+As an example to get started with, we're going to develop a so called
+"Brochure Site" for an imaginary company. The first step is to create a
+directory for our new site.
+
+~~~~~
+[jasper@alice Sites]$ mkdir brochure
+[jasper@alice Sites]$ cd brochure/
+[jasper@alice brochure]$
+~~~~~
+
+I have a [zip file] with the files we need for this
+tutorial available. Please unzip it in the brochure directory we just created.
+We'll first have a look at what we're going to create (because we're curious
+and all that).
+
+[zip file]: examples/brochure.zip
+
+~~~~~
+[jasper@alice brochure]$ ghc --make hakyll.hs
+[1 of 1] Compiling Main ( hakyll.hs, hakyll.o )
+Linking hakyll ...
+[jasper@alice brochure]$ ./hakyll preview
+Starting hakyll server on port 8000...
+~~~~~
+
+If you now point your browser at [localhost:8000] you should see our simple
+brochure site.
+
+[localhost:8000]: http://localhost:8000/
+
+## hakyll.hs
+
+The main configuration file of a Hakyll site is traditionally called
+`hakyll.hs`. It is nothing special, just a small Haskell program. There is no
+magic going on.
+
+~~~~~{.haskell}
+import Text.Hakyll (hakyll)
+main = hakyll "http://example.com" $ do
+ liftIO $ putStrLn "I'm in your computer, generating your site!"
+~~~~~
+
+Note how we wrap everyting in the `hakyll` function. This is useful because
+it will generate a very nice main function. We also pass the full site URL to
+the `hakyll` function. If you don't have an URL for your site yet, it doesn't
+really matter for now; just fill in anything then. The URL is only used for
+certain specific purposes where a full URL is needed, such as rendering RSS
+feeds.
+
+## Context
+
+Let's look at one of the most important types in Hakyll.
+
+~~~~~{.haskell}
+type Context = Map String String
+~~~~~
+
+A `Context` is a key-value mapping, used to represent pieces of information.
+One way to write such a `Context`, is a page.
+
+## Pages
+
+Another important concept in Hakyll is pages. Pages are text files that can be
+written in markdown, html, rst... basically anything Pandoc supports.
+Furthermore, they can also contain some metadata. The metadata is placed in the
+file header and surrouded by `---` lines. Each line should contain a
+`key: value` pair. Let's have a look at the `index.markdown` page.
+
+ ---
+ title: About
+ ---
+ Nullam imperdiet sodales orci vitae molestie.
+ Nunc quam orci, pharetra a rhoncus vitae,
+ eleifend id felis. Suspendisse potenti...
+
+This contains one `key: value` pair, namely `title: About`. The rest of the
+file is treated as markdown by pandoc. If you want to know more about
+markdown, I think [this](http://daringfireball.net/projects/markdown/syntax)
+is a pretty good page.
+
+## Templates
+
+Another concept are the so-called templates. Templates are text files (usually
+html files) containing a number of keys. The syntax for these keys is
+`$identifier`. Our example site contains one template, namely
+`templates/default.html`. Let's have a better look at that.
+
+~~~~~{.html}
+<html>
+ <head>
+ <title>MyAweSomeCompany - $title</title>
+ <link rel="stylesheet" type="text/css"
+ href="$$root/css/default.css" />
+ <link rel="stylesheet" type="text/css"
+ href="$$root/css/syntax.css" />
+ </head>
+ <body>
+ <h1>MyAweSomeCompany - $title</h1>
+ <div id="navigation">
+ <a href="$$root/index.html">Home</a>
+ <a href="$$root/about.html">About</a>
+ <a href="$$root/code.html">Code</a>
+ </div>
+
+ $body
+ </body>
+</html>
+~~~~~
+
+We can see how our `Page` would fit in. When we render the page we saw using
+this template, `$title` would be replaced by `About`, and `$body` would be
+replaced by the body of the about page. `body` is the traditional name for the
+body of any page - that is the convention in Hakyll. Also note that in this
+case, `$body` would be replaced by a chunk of html - the result of the
+markdown-to-html conversion.
+
+## The $$root key
+
+There are a few "special" keys in Hakyll: one of them is the $$root key. What
+is so special about it? Well, internally, it is treated differently - but this
+should not concern you. The thing is that it is the only key you can also use
+in __Pages__.
+
+It will be substituted by a relative url part (like `..` or `../..`) so it
+points to the root directory of your site. It is recommended to use this
+whenever you need it, it can save you some time from messing with absolute
+and relative URL's.
+
+## Putting it all together
+
+Now, we'll render the page using the `renderChain` function. This function
+takes a list of templates and a `Context`. In our case, we only have one
+template, and our `Context` is the about page we just saw - we can load that
+using the `createPage` function.
+
+~~~~~{.haskell}
+import Text.Hakyll (hakyll)
+import Text.Hakyll.Render (renderChain)
+import Text.Hakyll.CreateContext (createPage)
+main = hakyll "http://example.com" $ do
+ renderChain ["templates/default.html"]
+ (createPage "index.markdown")
+~~~~~
+
+Or, to render all our three pages:
+
+~~~~~{.haskell}
+import Text.Hakyll (hakyll)
+import Text.Hakyll.Render (renderChain)
+import Text.Hakyll.CreateContext (createPage)
+main = hakyll "http://example.com" $ do
+ render "about.rst"
+ render "index.markdown"
+ render "code.lhs"
+ where render = renderChain ["templates/default.html"]
+ . createPage
+~~~~~
+
+As you can see, we can render a variety of formats. This will create the
+following files:
+
+~~~~~
+_site/about.html
+_site/index.html
+_site/code.html
+~~~~~
+
+## CSS, images and other static files
+
+Now, we also have a css file we would like to have in the `_site` directory.
+Static files can be rendered using the `static` function in Hakyll. We could
+use:
+
+~~~~~{.haskell}
+import Text.Hakyll (hakyll)
+import Text.Hakyll.Render (static)
+main = hakyll "http://example.com" $ do
+ static "css/default.css"
+~~~~~
+
+This would work, but let's not forget that Hakyll also has css compression. If
+we want to use that, we would use `css` instead of `static`.
+
+~~~~~{.haskell}
+import Text.Hakyll (hakyll)
+import Text.Hakyll.Render (css)
+main = hakyll "http://example.com" $ do
+ css "css/default.css"
+~~~~~
+
+If we were to create another css file, we would have to add a line to our
+`hakyll.hs` configuration file. This is pretty stupid, because the whole
+directory `css` contains only css files. That's why Hakyll has a `directory`
+function, which will execute a given function on an entire directory. So,
+our example would become:
+
+~~~~~{.haskell}
+import Text.Hakyll (hakyll)
+import Text.Hakyll.Render (css)
+import Text.File (directory)
+main = hakyll "http://example.com" $ do
+ directory css "css"
+~~~~~
+
+## Deploying
+
+To setup your site, simply copy the contents of `_site` to your hosting provider
+using your favorite piece of software.
diff --git a/examples/hakyll/tutorials/part02.markdown b/examples/hakyll/tutorials/part02.markdown
new file mode 100644
index 0000000..8fdbc78
--- /dev/null
+++ b/examples/hakyll/tutorials/part02.markdown
@@ -0,0 +1,83 @@
+---
+title: Arrows: A Crash Course
+what: illustrates how Arrows are used in hakyll
+---
+
+## Do I really need to know this stuff?
+
+Maybe. You don't need it when you only use the basic Hakyll functions, but
+Arrows are used a lot in the Hakyll code, and a lot of the more advanced
+features make use of Arrows. Besides, it's a pretty interesting subject.
+
+## What is an "Arrow"
+
+Arrows are comparable with monads. In fact, monads are a subset of arrows.
+Arrows allow you to represent a "computation". This is all pretty vague, so
+let's skip directly to the Arrows used in Hakyll.
+
+## HakyllAction
+
+The Arrow used throughout Hakyll is called `HakyllAction`. Arrows have two
+type parameters, so it's actually `HakyllAction a b`. You can think of `a`
+as the input for our action, and `b` is the corresponding output. Let's look
+at the type of `createPage`:
+
+~~~~~{.haskell}
+createPage :: FilePath -> HakyllAction () Context
+~~~~~
+
+So, you give `createPage` a `FilePath`, and it creates a `HakyllAction` that
+produces a `Context` out of thin air. Now, we want to render the `Context` we
+just loaded with a template. The type of the `render` function is:
+
+~~~~~{.haskell}
+render :: FilePath -> HakyllAction Context Context
+~~~~~
+
+We pass the file name of a template to the `render` function, and we get a
+`HakyllAction` that creates a `Context` from another `Context`. The result
+of the `render` operation (so basically the rendered template) will be placed
+in the `$body` field of the new `Context`. But we still haven't saved our
+result, so let's do that using the `writePage` function.
+
+~~~~~{.haskell}
+writePage :: HakyllAction Context ()
+~~~~~
+
+This function writes our result and returns nothing.
+
+## Composition
+
+Now, let's look at the big picture.
+
+![Arrow illustration]($root/images/arrow-composition.png)
+
+If these were regular functions, we could've composed them using the `.`
+operator. Since they're arrows, we'll have to use the `>>>` operator.
+
+~~~~~{.haskell}
+test :: HakyllAction () ()
+test = createPage "test.markdown"
+ >>> render "template.html"
+ >>> writePage
+~~~~~
+
+Now, we only have to execute our test.
+
+~~~~~{.haskell}
+runHakyllActionIfNeeded test
+~~~~~
+
+## Aso, the point emerges
+
+The `runHakyllActionIfNeeded` suggests why we use arrows. `HakyllAction` is more
+than just a function, it also tracks dependencies. This Hakyll to only execute
+our functions when it is really needed. In this particular case, `test` would
+only be executed if either `test.markdown` or `template.html` were recently
+changed.
+
+## The gist of it
+
+- Arrows really aren't complicated.
+- Compose them using `>>>`.
+- `HakyllAction` tracks dependencies for you. Use it.
diff --git a/src/Text/Hakyll/Render.hs b/src/Text/Hakyll/Render.hs
index 6f06953..d0bd138 100644
--- a/src/Text/Hakyll/Render.hs
+++ b/src/Text/Hakyll/Render.hs
@@ -6,6 +6,7 @@ module Text.Hakyll.Render
, renderChain
, static
, css
+ , writePage
) where
import Control.Arrow ((>>>))