on master help sign in
This might be "it"
commite6868dde0c51db61b41550d62c92f0798fc7f3e8 authorJasper Van der Jeugt <m@jaspervdj.be> dateMon 17 Dec 2012, 17:28 (14 years ago) parente4474c8
4 files changed+100−353
web/site.hs+1−1 web/tutorials/05-arrows.markdown−223 web/tutorials/06-guide.markdown−129 web/tutorials/guide.markdown+99
web/site.hs+1−1
@@ -67,5 +67,5 @@ config :: Configuration
6767config = defaultConfiguration
6868 { verbosity = Debug
6969 , deployCommand = "rsync --checksum -ave 'ssh -p 2222' \
70− \_site/* jaspervdj@jaspervdj.be:jaspervdj.be/hakyll"
70+ \_site/* jaspervdj@jaspervdj.be:jaspervdj.be/tmp/hakyll4"
7171 }
web/tutorials/05-arrows.markdowndeleted−223
@@ -1,223 +0,0 @@
1−---
2−title: Arrow Magic: Metadata Dependent Page Generation
3−author: Florian Hars
4−---
5−
6−## Supporting a "published: false" attribute on pages
7−
8−Many content management systems or blog platforms support
9−some kind of workflow that display articles differently or
10−not at all depending on which state the article is in, for
11−example whether it has a "published" attribute or not.
12−Hakyll has no built-in support for anything like this, but since
13−its compilers are just arrows, it is easy to implement arbitrary
14−metadata dependent behaviour for rendering pages.
15−
16−Let's start by adding support for a "published" attributes to the
17−`simpleblog` example. We want to consider a blog post published if it
18−has a `published` metadata element that does not have the value
19−`false`. A function to test for this is simple:
20−
21−~~~~~{.haskell}
22−isPublished :: Page a -> Bool
23−isPublished p =
24− let published = getField "published" p in
25− published /= "" && published /= "false"
26−~~~~~
27−
28−The next step is to write a function that tags a page with its
29−published status, which can be either unpublished or published, using
30−the standard `Either` datatype and then transform this function
31−into a `Compiler`. The latter can be done with the standard `arr`
32−function from `Control.Arrow`, which lifts a function into an arrow:
33−
34−~~~~~{.haskell}
35−isPagePublished :: Compiler (Page a) (Either (Page a) (Page a))
36−isPagePublished = arr (\p -> if isPublished p then Right p else Left p)
37−~~~~~
38−
39−For the next processing steps we now need a compiler that takes an
40−`Either (Page a) (Page a)` instead of the usual `Page a` as an
41−input. But the former can be built up from the latter using some
42−standard combinators from the `Control.Arrow` library. The simplest
43−one is `|||`, which takes two compilers (arrows) with the same output
44−type and returns a new compiler that takes an `Either` of the input
45−types of the Compilers as an input. Maybe we just want to render our
46−unpublished posts with a big warning that they are provisional, so we
47−just want to render the unpublished `Left` pages with another template
48−than the published `Right` pages:
49−
50−~~~~~{.haskell}
51− match "posts/*" $ do
52− route $ setExtension ".html"
53− compile $ pageCompiler
54− >>> isPagePublished
55− >>> (applyTemplateCompiler "templates/embargo-post.html"
56− ||| applyTemplateCompiler "templates/post.html")
57− >>> applyTemplateCompiler "templates/default.html"
58− >>> relativizeUrlsCompiler
59−~~~~~
60−
61−With the conditional rendering in place, the next step is to hide
62−the unpublished posts from the homepage and the list of posts.
63−Both lists are generated from the results of a requireAllA call.
64−The last argument of requireAllA is a Compiler, and requireAllA
65−passes a pair consisting of the currently rendered page and a list
66−of all the required pages. All we have to do to suppress the pages
67−is to write a Compiler that takes such a pair as input, leaves the
68−first element of the pair unchanged and filters out all the unpublished
69−pages from list in the second element of the pair and then pass the
70−output from this compiler to the existing compilers handling the
71−list generation for the `index` and `posts` pages.
72−
73−Again, we can use a function from `Control.Arrow` to build this
74−compiler from simpler ones, in this case it is `***`, which combines
75−two arrows to one arrow from pairs to pairs. For our purposes, we
76−combine the identity arrow, which leaves its input unchanged, and an
77−ordinary `filter` on a list lifted into the compiler arrow:
78−
79−~~~~~{.haskell}
80−filterPublished :: Compiler (Page a, [Page b]) (Page a, [Page b])
81−filterPublished = id *** arr (filter isPublished)
82−~~~~~
83−
84−All that remains to do is to chain this compiler in front of the existing
85−compilers passed to requireAllA in the code for `posts.html`
86−
87−~~~~~{.haskell}
88− >>> requireAllA "posts/*" (filterPublished >>> addPostList)
89−~~~~~
90−
91−and for `index.html`:
92−
93−~~~~~{.haskell}
94− >>> requireAllA "posts/*"
95− (filterPublished
96− >>> (id *** arr (take 3 . reverse . sortByBaseName))
97− >>> addPostList)
98−~~~~~
99−
100−You may have noticed that the code for the index page uses the same
101−`id *** something` construct to extract some elements from the list
102−of all posts.
103−
104−## Don't generate unpublished pages at all
105−
106−The above code will treat unpublished posts differently and hide them
107−from all lists of posts, but they will still be generated, and someone
108−who knows their URLs will still be able to access them. That may be
109−what you need, but sometimes you might want to suppress them
110−completely. The simplest way to do so is to leave the rendering
111−pipeline for `"posts/*"` unchanged and just add the `isPagePublished`
112−compiler at the end. This will not compile, since hakyll knows how to
113−write a `Page String`, but not how to write an `Either (Page String)
114−(Page String)`. But that can be amended by a simple type class
115−declaration:
116−
117−~~~~~{.haskell}
118−instance Writable b => Writable (Either a b) where
119− write p (Right b) = write p b
120− write _ _ = return ()
121−~~~~~
122−
123−Now hakyll will happily generate published pages and ignore
124−unpublished ones. This solution is of course slightly wasteful, as at
125−will apply all the templates to an unpublished page before finally
126−discarding it. You can avoid this by using the `+++` function, which
127−does for the sum datatype `Either` what `***` does for the product
128−type pair:
129−
130−~~~~~{.haskell}
131− match "posts/*" $ do
132− route $ setExtension ".html"
133− compile $ pageCompiler
134− >>> isPagePublished
135− >>> (id +++ (applyTemplateCompiler "templates/post.html"
136− >>> applyTemplateCompiler "templates/default.html"
137− >>> relativizeUrlsCompiler))
138−~~~~~
139−
140−The other problem with this solution is more severe: hakyll will no
141−longer generate the index and posts pages due to a rare problem in
142−haskell land: a runtime type error. Hakyll tries to be smart and reuse
143−the parsed pages from the `match "posts/*"` when processing the
144−`requireAllA "posts/*"` calls by caching them. But the compilers there
145−still expect a list of pages instead of a list of eithers, so we have
146−to replace `filterPublised` with something that works on the
147−latter. Luckily (or, probably, by design), `Data.Either` provides just
148−the function we need, so the new filtering compiler is actually
149−shorter that the original, even though it has a more intimidating
150−type:
151−
152−~~~~~{.haskell}
153−filterPublishedE :: Compiler (Page a, [Either (Page b) (Page b)]) (Page a, [Page b])
154−filterPublishedE = id *** arr rights
155−~~~~~
156−
157−## Timed releases
158−
159−Exploiting the fact that compilers are arrows, we can do more mixing
160−and matching of compilers to further refine how hakyll deals with page
161−attributes like `published`. Maybe you want `cron` to update your blog
162−while you are on vacation, so you want posts to be considered
163−published if the `published` field is either `true` or a time in the
164−past.
165−
166−If you happen to live in the UK in winter or enjoy to do time zone
167−calculation in your head, your new function to test if a page is
168−published and the compiler derived from it might then look like this
169−
170−~~~~~{.haskell}
171−isPublishedYet :: Page a -> UTCTime -> Bool
172−isPublishedYet page time =
173− let published = getField "published" page in
174− published == "true" || after published
175− where
176− after published =
177− let publishAt = parseTime defaultTimeLocale "%Y-%m-%d %H:%M" published in
178− fromMaybe False (fmap (\embargo -> embargo < time) publishAt)
179−
180−isPagePublishedYet :: Compiler (Page a, UTCTime) (Either (Page a) (Page a))
181−isPagePublishedYet = arr (\(p,t) -> if isPublishedYet p t then Right p else Left p)
182−~~~~~
183−
184−This compiler has a pair of a page and a time as its input, and we can
185−use yet another function from `Control.Arrow` to construct a compiler
186−that generates the input for it, the function `&&&`. It takes two
187−compilers (arrows) with the same input type and constructs a compiler
188−from that type to a pair of the output types of the two compilers.
189−For the first argument we take the `pageCompiler` which we already
190−call at the beginning of the page compilation. The second argument
191−should be a compiler with the same input type as `pageCompiler` that
192−returns the current time. But the current time lives in the IO monad
193−and does not at all depend on the resource the current page is
194−generated from, so we have to cheat a little bit by calling
195−`unsafeCompiler` with a function that ignores its argument and returns
196−an `IO UTCTime`, which `unsafeCompiler` will unwrap for us:
197−
198−~~~~~{.haskell}
199− match "posts/*" $ do
200− route $ setExtension ".html"
201− compile $ (pageCompiler &&& (unsafeCompiler (\_ -> getCurrentTime)))
202− >>> isPagePublishedYet
203− >>> (id +++ ( ... as above ...))
204−~~~~~
205−
206−This is all we have to change if we don't generate unpublished pages
207−at all. If we just hide them from the lists, the call to `|||`
208−discards the information that a page is not (yet) published that was
209−encoded in the `Either`. In that case we could use the `setField`
210−function from `Hakyll.Web.Page.Metadata` to rewrite the `published`
211−field of the left and right pages in `isPagePublished(Yet)` to
212−canonical values that the original `isPublished` function called from
213−`filterPublished` understands:
214−
215−~~~~~{.haskell}
216−isPagePublishedYet = arr (\(p,t) -> if isPublishedYet p t then pub p else unpub p)
217− where
218− pub p = Right $ setField "published" "true" p
219− unpub p = Left $ setField "published" "false" p
220−~~~~~
221−
222−The final version of this code can be found in the timedblog example,
223−together with the required `import` statements.
web/tutorials/06-guide.markdowndeleted−129
@@ -1,129 +0,0 @@
1−---
2−title: A Guide to the Hakyll Module Zoo
3−author: Brent Yorgey
4−---
5−
6−The hakyll package [contains a bewildering array](/reference/) of
7−modules, and it's hard to know where to look when you are just getting
8−started---especially since many of them are only used by hakyll
9−internally and not that useful to website authors. This guide
10−provides a quick reference to the contents of the most important and
11−useful modules.
12−
13−## Core modules
14−
15−These are the modules containing the fundamental tools and concepts
16−you need to get started building a site with hakyll.
17−
18−* [Hakyll.Core.Compiler](/reference/Hakyll-Core-Compiler.html)
19−
20− This is one of the modules you should look at first. It defines the
21− fundamental `Compiler` type and has a bunch of documentation explaining how to
22− use `Compiler`s and their `Arrow`-based interface.
23−
24− It also defines many primitive `Compiler`s as well as several
25− variants on `require`, which allow you to bring together multiple
26− resources to create a single output.
27−
28−* [Hakyll.Core.Routes](/reference/Hakyll-Core-Routes.html)
29−
30− Specify where compiled items should go in the output site.
31−
32−* [Hakyll.Core.Rules](/reference/Hakyll-Core-Rules.html)
33−
34− Specify which compilers and routes should be used on which
35− resources.
36−
37− Also has combinators for grouping (necessary if you want to use
38− the same resources for multiple purposes), creating outputs that
39− are not based on any resources, and even dynamically generating
40− compilers.
41−
42−* [Hakyll.Core.Identifier.Pattern](/reference/Hakyll-Core-Identifier-Pattern.html)
43−
44− Combinators for creating *patterns*, i.e. predicates that pick out
45− a set of resources: filesystem globs, arbitrary predicates,
46− explicit lists, regular expressions, and so on.
47−
48−* [Hakyll.Web.Page](/reference/Hakyll-Web-Page.html)
49−
50− A `Page`, consisting of some metadata and a body, is one of the
51− most fundamental structures used by hakyll. This module has some
52− documentation explaining how pages work, and defines a number of
53− compilers and utilities for working with them.
54−
55−* [Hakyll.Web.Page.Metadata](/reference/Hakyll-Web-Page-Metadata.html)
56−
57− Utilities for manipulating page metadata.
58−
59−* [Hakyll.Web.Template](/reference/Hakyll-Web-Template.html)
60−
61− Templates specify how to take the content of a `Page` and turn
62− it into HTML output.
63−
64−* [Hakyll.Main](/reference/Hakyll-Main.html)
65−
66− The main `hakyll` function that runs the whole show. There is
67− also a `hakyllWith` function which allows for a custom
68− configuration.
69−
70−## Pre-packaged solutions
71−
72−These modules contain some "pre-packaged" solutions for common
73−situations. They can be used as-is, or their source can be used as
74−inspiration for your own tools.
75−
76−* [Hakyll.Web.Page.List](/reference/Hakyll-Web-Page-List.html)
77−
78− Combine several pages into a list, such as a list of posts, images
79− in a gallery, etc.
80−
81−* [Hakyll.Web.Feed](/reference/Hakyll-Web-Feed.html)
82−
83− Create RSS feeds.
84−
85−* [Hakyll.Web.Tags](/reference/Hakyll-Web-Tags.html)
86−
87− Work with tags and categories.
88−
89−## Useful utilities
90−
91−* [Hakyll.Core.UnixFilter](/reference/Hakyll-Core-UnixFilter.html)
92−
93− Use any unix utility as a compiler.
94−
95−* [Hakyll.Core.Util.Arrow](/reference/Hakyll-Core-Util-Arrow.html)
96−
97− A few utilities for working with arrows, including a constant
98− arrow, unit arrow, and running an entire list of arrows on a
99− single input.
100−
101−* [Hakyll.Core.Util.File](/reference/Hakyll-Core-Util-File.html)
102−
103− Utility functions for working with the file system.
104−
105−* [Hakyll.Core.Util.String](/reference/Hakyll-Core-Util-String.html)
106−
107− A few utility functions for working with `String`s (trim spaces,
108− find and replace, split on regexp).
109−
110−* [Hakyll.Core.Writable.WritableTuple](/reference/Hakyll-Core-Writable-WritableTuple.html)
111−
112− A utility type covering the situation where you generate some
113− data, some of which you want to write to disk, and some of which
114− you don't want to write but will be needed in order to generate
115− something else later.
116−
117−## Advanced customization
118−
119−* [Hakyll.Core.Writable](/reference/Hakyll-Core-Writable.html)
120−
121− The `Writable` class is for resources which can be written to disk
122− as part of the output site. You can specify how to write your own
123− custom types to disk by giving them a `Writable` instance.
124−
125−* [Hakyll.Web.Pandoc](/reference/Hakyll-Web-Pandoc.html)
126−
127− Some compilers for running pandoc. Normally they are run
128− automatically as part of, for example, `pageCompiler`; but
129− sometimes it is useful to be able to run pandoc explicitly.
web/tutorials/guide.markdownnew file+99
@@ -0,0 +1,99 @@
1+---
2+title: A Guide to the Hakyll Module Zoo
3+author: Brent Yorgey
4+---
5+
6+The hakyll package [contains a bewildering array](/reference/) of modules, and
7+it's hard to know where to look when you are just getting started -- especially
8+since many of them are mostly just used by Hakyll internally and not that useful
9+to website authors. This guide provides a quick reference to the contents of the
10+most important and useful modules.
11+
12+## Core modules
13+
14+These are the modules containing the fundamental tools and concepts
15+you need to get started building a site with hakyll.
16+
17+* [Hakyll.Core.Compiler](/reference/Hakyll-Core-Compiler.html)
18+
19+ This is one of the modules you should look at first. It defines the
20+ fundamental `Compiler` type and primitive `Compiler`s as well as several
21+ variants on `load`, which allow you to bring together multiple resources to
22+ create a single output.
23+
24+* [Hakyll.Core.Routes](/reference/Hakyll-Core-Routes.html)
25+
26+ Specify where compiled items should go in the output site.
27+
28+* [Hakyll.Core.Rules](/reference/Hakyll-Core-Rules.html)
29+
30+ Specify which compilers and routes should be used on which resources. Also
31+ has combinators for versions (necessary if you want to use the same
32+ resources for multiple purposes).
33+
34+* [Hakyll.Core.Identifier.Pattern](/reference/Hakyll-Core-Identifier-Pattern.html)
35+
36+ Combinators for creating *patterns*, i.e. predicates that pick out a set of
37+ resources: filesystem globs, explicit lists, regular expressions, and so on.
38+
39+* [Hakyll.Web.Page](/reference/Hakyll-Web-Pandoc.html)
40+
41+ Provides functions for rendering your pages using pandoc, with a varying
42+ degree of high-levelness.
43+
44+* [Hakyll.Web.Template](/reference/Hakyll-Web-Template.html)
45+
46+ Templates specify how to take the content of an item and turn it into HTML
47+ output.
48+
49+* [Hakyll.Web.Template.Context](/reference/Hakyll-Web-Template-Context.html)
50+
51+ You can't do much with a `Template` if you don't have a `Context`: this
52+ module provides some predefined `Context`s and the tools to build your own.
53+
54+* [Hakyll.Main](/reference/Hakyll-Main.html)
55+
56+ The main `hakyll` function that runs the whole show. There is also a
57+ `hakyllWith` function which allows for a custom configuration.
58+
59+## Pre-packaged solutions
60+
61+These modules contain some "pre-packaged" solutions for common situations. They
62+can be used as-is, or their source can be used as inspiration for your own
63+tools.
64+
65+* [Hakyll.Web.Page.List](/reference/Hakyll-Web-Page-List.html)
66+
67+ Combine several pages into a list, such as a list of posts, images in a
68+ gallery, etc.
69+
70+* [Hakyll.Web.Feed](/reference/Hakyll-Web-Feed.html)
71+
72+ Create RSS feeds.
73+
74+* [Hakyll.Web.Tags](/reference/Hakyll-Web-Tags.html)
75+
76+ Work with tags and categories.
77+
78+## Useful utilities
79+
80+* [Hakyll.Core.UnixFilter](/reference/Hakyll-Core-UnixFilter.html)
81+
82+ Use any unix utility as a compiler.
83+
84+* [Hakyll.Core.Util.File](/reference/Hakyll-Core-Util-File.html)
85+
86+ Utility functions for working with the file system.
87+
88+* [Hakyll.Core.Util.String](/reference/Hakyll-Core-Util-String.html)
89+
90+ A few utility functions for working with `String`s (trim spaces, find and
91+ replace, split on regexp).
92+
93+## Advanced customization
94+
95+* [Hakyll.Core.Writable](/reference/Hakyll-Core-Writable.html)
96+
97+ The `Writable` class is for resources which can be written to disk as part
98+ of the output site. You can specify how to write your own custom types to
99+ disk by giving them a `Writable` instance.