2021-04-18 10:23:24 +00:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< title > YBlog - Hakyll setup< / title >
< meta name = "keywords" content = "programming, hakyll, Haskell, nanoc" / >
< link rel = "shortcut icon" type = "image/x-icon" href = "../../../../Scratch/img/favicon.ico" / >
2021-05-25 20:25:47 +00:00
< link rel = "stylesheet" type = "text/css" href = "/css/y.css" / >
< link rel = "stylesheet" type = "text/css" href = "/css/legacy.css" / >
< link rel = "alternate" type = "application/rss+xml" title = "RSS" href = "/rss.xml" / >
2021-04-18 10:23:24 +00:00
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< link rel = "apple-touch-icon" href = "../../../../Scratch/img/about/FlatAvatar@2x.png" / >
<!-- [if lt IE 9]>
< script src = "http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js" > < / script >
<![endif]-->
<!-- IndieAuth -->
< link href = "https://twitter.com/yogsototh" rel = "me" >
< link href = "https://github.com/yogsototh" rel = "me" >
< link href = "mailto:yann.esposito@gmail.com" rel = "me" >
< link rel = "pgpkey" href = "../../../../pubkey.txt" >
< / head >
< body lang = "en" class = "article" >
< div id = "content" >
< div id = "header" >
< div id = "choix" >
< span id = "choixlang" >
< a href = "../../../../Scratch/fr/blog/Hakyll-setup/" > French< / a >
< / span >
< span class = "tomenu" > < a href = "#navigation" > ↓ Menu ↓< / a > < / span >
< span class = "flush" > < / span >
< / div >
< / div >
< div id = "titre" >
< h1 > Hakyll setup< / h1 >
< / div >
< div class = "flush" > < / div >
< div id = "afterheader" class = "article" >
< div class = "corps" >
< div >
< img src = "../../../../Scratch/img/blog/Hakyll-setup/main.png" alt = "Main image" / >
< / div >
< div class = "intro" >
< p > < span class = "sc" > < abbr title = "Too long; didn't read" > tl;dr< / abbr > : < / span > How I use < a href = "http://jaspervdj.be/hakyll" > hakyll< / a > . Abbreviations, typography corrections, multi-language, use < code > index.html< / code > , etc…< / p >
< / div >
< p > This website is done with < a href = "http://jaspervdj.be/hakyll" > Hakyll< / a > .< / p >
< p > < a href = "http://jaspervdj.be/hakyll" > Hakyll< / a > can be considered as a minimal < span class = "sc" > < abbr title = "Content Management System" > cms< / abbr > < / span > . But more generally it is a library helping file generation. We can view it as an advanced build system (like < code > make< / code > ).< / p >
< p > From the user perspective I blog this way:< / p >
< ol type = "1" >
< li > I open an editor (vim in my case) and edit a markdown file. It looks like this< / li >
< / ol >
< div class = "sourceCode" id = "cb1" > < pre class = "sourceCode markdown" > < code class = "sourceCode markdown" > < a class = "sourceLine" id = "cb1-1" title = "1" > A First Level Header< / a >
< a class = "sourceLine" id = "cb1-2" title = "2" > ====================< / a >
< a class = "sourceLine" id = "cb1-3" title = "3" > < / a >
< a class = "sourceLine" id = "cb1-4" title = "4" > A Second Level Header< / a >
< a class = "sourceLine" id = "cb1-5" title = "5" > ---------------------< / a >
< a class = "sourceLine" id = "cb1-6" title = "6" > < / a >
< a class = "sourceLine" id = "cb1-7" title = "7" > Who would cross the Bridge of Death must answer me< / a >
< a class = "sourceLine" id = "cb1-8" title = "8" > these questions three, ere the other side he see.< / a >
< a class = "sourceLine" id = "cb1-9" title = "9" > This is just a regular paragraph.< / a >
< a class = "sourceLine" id = "cb1-10" title = "10" > < / a >
< a class = "sourceLine" id = "cb1-11" title = "11" > Ask me the questions, bridgekeeper. I am not afraid.< / a >
< a class = "sourceLine" id = "cb1-12" title = "12" > < / a >
< a class = "sourceLine" id = "cb1-13" title = "13" > < span class = "fu" > ### Header 3< / span > < / a >
< a class = "sourceLine" id = "cb1-14" title = "14" > < / a >
< a class = "sourceLine" id = "cb1-15" title = "15" > > < span class = "dt" > This is a blockquote.< / span > < / a >
< a class = "sourceLine" id = "cb1-16" title = "16" > < span class = "dt" > > < / span > < / a >
< a class = "sourceLine" id = "cb1-17" title = "17" > < span class = "dt" > > This is the second paragraph in the blockquote.< / span > < / a >
< a class = "sourceLine" id = "cb1-18" title = "18" > < span class = "dt" > > < / span > < / a >
< a class = "sourceLine" id = "cb1-19" title = "19" > < span class = "dt" > > ## This is an H2 in a blockquote< / span > < / a > < / code > < / pre > < / div >
< ol start = "2" type = "1" >
< li > I open a browser and reload time to time to see the change.< / li >
< li > Once I finished I’ ve written a very minimal script which mainly do a < code > git push< / code > . My blog is hosted on < a href = "http://github.com" > github< / a > .< / li >
< / ol >
< p > Being short sighted one could reduce the role of Hakyll to:< / p >
< blockquote >
< p > create (resp. update) < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > file when I create (resp. change) a markdown file.< / p >
< / blockquote >
< p > While it sounds easy, there are a lot of hidden details:< / p >
< ul >
< li > Add metadatas like keywords.< / li >
< li > Create an archive page containing a list of all the posts.< / li >
< li > Deal with static files.< / li >
< li > Creating an < span class = "sc" > < abbr title = "Rich Site Summary" > rss< / abbr > < / span > feed.< / li >
< li > Filter the content with some function.< / li >
< li > Dealing with dependencies.< / li >
< / ul >
< p > The work of Hakyll is to help you with these. But let’ s start with the basic concepts.< / p >
< h2 id = "the-concepts-and-syntax" > The concepts and syntax< / h2 >
< div >
< img src = "../../../../Scratch/img/blog/Hakyll-setup/overview.png" alt = "Overview" / >
< / div >
< p > For each file you create, you have to provide:< / p >
< ul >
< li > a destination path< / li >
< li > a list of content filters.< / li >
< / ul >
< p > First, let’ s start with the simplest case: static files (images, fonts, etc…). Generally, you have a source directory (here is the current directory) and a destination directory < code > _site< / code > .< / p >
< p > The Hakyll code is:< / p >
< div class = "sourceCode" id = "cb2" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb2-1" title = "1" > < span class = "co" > -- for each file in the static directory< / span > < / a >
< a class = "sourceLine" id = "cb2-2" title = "2" > match < span class = "st" > " static/*" < / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb2-3" title = "3" > < span class = "co" > -- don't change its name nor directory< / span > < / a >
< a class = "sourceLine" id = "cb2-4" title = "4" > route idRoute< / a >
< a class = "sourceLine" id = "cb2-5" title = "5" > < span class = "co" > -- don't change its content< / span > < / a >
< a class = "sourceLine" id = "cb2-6" title = "6" > compile copyFileCompiler< / a > < / code > < / pre > < / div >
< p > This program will copy < code > static/foo.jpg< / code > to < code > _site/static/foo.jpg< / code > . I concede this is a bit overkill for a simple < code > cp< / code > . Now how to write a markdown file and generate an < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > one?< / p >
< div class = "sourceCode" id = "cb3" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb3-1" title = "1" > < span class = "co" > -- for each file with md extension in the " posts/" directory< / span > < / a >
< a class = "sourceLine" id = "cb3-2" title = "2" > match < span class = "st" > " posts/*.md" < / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb3-3" title = "3" > < span class = "co" > -- change its extension to html< / span > < / a >
< a class = "sourceLine" id = "cb3-4" title = "4" > route < span class = "op" > $< / span > setExtension < span class = "st" > " html" < / span > < / a >
< a class = "sourceLine" id = "cb3-5" title = "5" > < span class = "co" > -- use pandoc library to compile the markdown content into html< / span > < / a >
< a class = "sourceLine" id = "cb3-6" title = "6" > compile < span class = "op" > $< / span > pandocCompiler< / a > < / code > < / pre > < / div >
< p > If you create a file < code > posts/foo.md< / code > , it will create a file < code > _site/posts/foo.html< / code > .< / p >
< p > If the file < code > posts/foo.md< / code > contains< / p >
< div class = "sourceCode" id = "cb4" > < pre class = "sourceCode markdown" > < code class = "sourceCode markdown" > < a class = "sourceLine" id = "cb4-1" title = "1" > < span class = "fu" > # Cthulhu< / span > < / a >
< a class = "sourceLine" id = "cb4-2" title = "2" > < / a >
< a class = "sourceLine" id = "cb4-3" title = "3" > ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn< / a > < / code > < / pre > < / div >
< p > the file < code > _site/posts/foo.html< / code > , will contain< / p >
< div class = "sourceCode" id = "cb5" > < pre class = "sourceCode html" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb5-1" title = "1" > < span class = "kw" > < h1> < / span > Cthulhu< span class = "kw" > < /h1> < / span > < / a >
< a class = "sourceLine" id = "cb5-2" title = "2" > < span class = "kw" > < p> < / span > ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn< span class = "kw" > < /p> < / span > < / a > < / code > < / pre > < / div >
< p > But horror! < code > _site/posts/cthulhu.html< / code > is not a complete < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > file. It doesn’ t have any header nor footer, etc… This is where you use templates. I simply add a new directive in the compile block.< / p >
< div class = "sourceCode" id = "cb6" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb6-1" title = "1" > match < span class = "st" > " posts/*.md" < / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb6-2" title = "2" > route < span class = "op" > $< / span > setExtension < span class = "st" > " html" < / span > < / a >
< a class = "sourceLine" id = "cb6-3" title = "3" > compile < span class = "op" > $< / span > pandocCompiler< / a >
< a class = "sourceLine" id = "cb6-4" title = "4" > < span class = "co" > -- use the template with the current content< / span > < / a >
< a class = "sourceLine" id = "cb6-5" title = "5" > < span class = "highlight" > < span class = "op" > > > =< / span > loadAndApplyTemplate < span class = "st" > " templates/post.html" < / span > defaultContext< / span > < / a > < / code > < / pre > < / div >
< p > Now if < code > templates/posts.html< / code > contains:< / p >
< div class = "sourceCode" id = "cb7" > < pre class = "sourceCode html" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb7-1" title = "1" > < span class = "kw" > < html> < / span > < / a >
< a class = "sourceLine" id = "cb7-2" title = "2" > < span class = "kw" > < head> < / span > < / a >
< a class = "sourceLine" id = "cb7-3" title = "3" > < span class = "kw" > < title> < / span > How could I get the title?< span class = "kw" > < /title> < / span > < / a >
< a class = "sourceLine" id = "cb7-4" title = "4" > < span class = "kw" > < /head> < / span > < / a >
< a class = "sourceLine" id = "cb7-5" title = "5" > < span class = "kw" > < body> < / span > < / a >
< a class = "sourceLine" id = "cb7-6" title = "6" > < span class = "highlight" > $body$< / span > < / a >
< a class = "sourceLine" id = "cb7-7" title = "7" > < span class = "kw" > < /body> < / span > < / a >
< a class = "sourceLine" id = "cb7-8" title = "8" > < span class = "kw" > < /html> < / span > < / a > < / code > < / pre > < / div >
< p > our < code > cthulhu.html< / code > contains (indentation added for readability):< / p >
< div class = "sourceCode" id = "cb8" > < pre class = "sourceCode html" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb8-1" title = "1" > < span class = "kw" > < html> < / span > < / a >
< a class = "sourceLine" id = "cb8-2" title = "2" > < span class = "kw" > < head> < / span > < / a >
< a class = "sourceLine" id = "cb8-3" title = "3" > < span class = "kw" > < title> < / span > How could I get the title?< span class = "kw" > < /title> < / span > < / a >
< a class = "sourceLine" id = "cb8-4" title = "4" > < span class = "kw" > < /head> < / span > < / a >
< a class = "sourceLine" id = "cb8-5" title = "5" > < span class = "kw" > < body> < / span > < / a >
< a class = "sourceLine" id = "cb8-6" title = "6" > < span class = "highlight" > < span class = "kw" > < h1> < / span > Cthulhu< span class = "kw" > < /h1> < / span > < / span > < / a >
< a class = "sourceLine" id = "cb8-7" title = "7" > < span class = "highlight" > < span class = "kw" > < p> < / span > ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn< span class = "kw" > < /p> < / span > < / span > < / a >
< a class = "sourceLine" id = "cb8-8" title = "8" > < span class = "kw" > < /body> < / span > < / a >
< a class = "sourceLine" id = "cb8-9" title = "9" > < span class = "kw" > < /html> < / span > < / a > < / code > < / pre > < / div >
< p > See, it’ s easy But we have a problem. How could we change the title or add keywords?< / p >
< p > The solution is to use < code > Context< / code > s. For this, we first need to add some < em > metadatas< / em > to our markdown< a href = "#fn1" class = "footnote-ref" id = "fnref1" > < sup > 1< / sup > < / a > .< / p >
< div class = "sourceCode" id = "cb9" > < pre class = "sourceCode markdown" > < code class = "sourceCode markdown" > < a class = "sourceLine" id = "cb9-1" title = "1" > < span class = "highlight" > --- < / span > < / a >
< a class = "sourceLine" id = "cb9-2" title = "2" > < span class = "highlight" > title: Cthulhu< / span > < / a >
< a class = "sourceLine" id = "cb9-3" title = "3" > < span class = "highlight" > --- < / span > < / a >
< a class = "sourceLine" id = "cb9-4" title = "4" > < span class = "fu" > # Cthulhu< / span > < / a >
< a class = "sourceLine" id = "cb9-5" title = "5" > < / a >
< a class = "sourceLine" id = "cb9-6" title = "6" > ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn< / a > < / code > < / pre > < / div >
< p > And modify slightly our template:< / p >
< div class = "sourceCode" id = "cb10" > < pre class = "sourceCode html" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb10-1" title = "1" > < span class = "kw" > < html> < / span > < / a >
< a class = "sourceLine" id = "cb10-2" title = "2" > < span class = "kw" > < head> < / span > < / a >
< a class = "sourceLine" id = "cb10-3" title = "3" > < span class = "kw" > < title> < / span > < span class = "highlight" > $title$< / span > < span class = "kw" > < /title> < / span > < / a >
< a class = "sourceLine" id = "cb10-4" title = "4" > < span class = "kw" > < /head> < / span > < / a >
< a class = "sourceLine" id = "cb10-5" title = "5" > < span class = "kw" > < body> < / span > < / a >
< a class = "sourceLine" id = "cb10-6" title = "6" > $body$< / a >
< a class = "sourceLine" id = "cb10-7" title = "7" > < span class = "kw" > < /body> < / span > < / a >
< a class = "sourceLine" id = "cb10-8" title = "8" > < span class = "kw" > < /html> < / span > < / a > < / code > < / pre > < / div >
< p > As Sir Robin said just before dying before the Bridge of Death:< / p >
< blockquote >
< p > < strong > “That’ s EASY!”< / strong > < / p >
< p > – < cite > Sir Robin, the Not-Quite-So-Brave-As-Sir-Lancelot< / cite > < / p >
< / blockquote >
< h2 id = "real-customization" > Real customization< / h2 >
< p > Now that we understand the basic functionality. How to:< / p >
< ul >
< li > use SASS?< / li >
< li > add keywords?< / li >
< li > simplify < span class = "sc" > < abbr title = "Uniform Ressource Locator" > url< / abbr > < / span > ?< / li >
< li > create an archive page?< / li >
< li > create an < span class = "sc" > < abbr title = "Rich Site Summary" > rss< / abbr > < / span > feed?< / li >
< li > filter the content?< / li >
< li > add abbreviations support?< / li >
< li > manage two languages?< / li >
< / ul >
< h3 id = "use-sass" > Use SASS< / h3 >
< p > That’ s easy. Simply call the executable using < code > unixFilter< / code > . Of course you’ ll have to install SASS (< code > gem install sass< / code > ). And we also use compressCss to gain some space.< / p >
< div class = "sourceCode" id = "cb11" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb11-1" title = "1" > match < span class = "st" > " css/*" < / span > < span class = "op" > $< / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb11-2" title = "2" > route < span class = "op" > $< / span > setExtension < span class = "st" > " css" < / span > < / a >
< a class = "sourceLine" id = "cb11-3" title = "3" > compile < span class = "op" > $< / span > getResourceString < span class = "op" > > > =< / span > < / a >
< a class = "sourceLine" id = "cb11-4" title = "4" > withItemBody (unixFilter < span class = "st" > " sass" < / span > [< span class = "st" > " --trace" < / span > ]) < span class = "op" > > > =< / span > < / a >
< a class = "sourceLine" id = "cb11-5" title = "5" > < span class = "fu" > return< / span > < span class = "op" > .< / span > < span class = "fu" > fmap< / span > compressCss< / a > < / code > < / pre > < / div >
< h3 id = "add-keywords" > Add keywords< / h3 >
< p > In order to help to reference your website on the web, it is nice to add some keywords as meta datas to your < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > page.< / p >
< div class = "sourceCode" id = "cb12" > < pre class = "sourceCode html" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb12-1" title = "1" > < span class = "kw" > < meta< / span > < span class = "ot" > name=< / span > < span class = "st" > " keywords" < / span > < / a >
< a class = "sourceLine" id = "cb12-2" title = "2" > < span class = "ot" > content=< / span > < span class = "st" > " Cthulhu, Yog-Sothoth, Shub-Niggurath" < / span > < span class = "kw" > > < / span > < / a > < / code > < / pre > < / div >
< p > In order to add keywords, we could not directly use the markdown metadatas. Because, without any, there should be any meta tag in the < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > .< / p >
< p > An easy answer is to create a < code > Context< / code > that will contains the meta tag.< / p >
< div class = "sourceCode" id = "cb13" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb13-1" title = "1" > < span class = "co" > -- metaKeywordContext will return a Context containing a String< / span > < / a >
< a class = "sourceLine" id = "cb13-2" title = "2" > < span class = "ot" > metaKeywordContext ::< / span > < span class = "dt" > Context< / span > < span class = "dt" > String< / span > < / a >
< a class = "sourceLine" id = "cb13-3" title = "3" > < span class = "co" > -- can be reached using $metaKeywords$ in the templates< / span > < / a >
< a class = "sourceLine" id = "cb13-4" title = "4" > < span class = "co" > -- Use the current item (markdown file)< / span > < / a >
< a class = "sourceLine" id = "cb13-5" title = "5" > metaKeywordContext < span class = "ot" > =< / span > field < span class = "st" > " metaKeywords" < / span > < span class = "op" > $< / span > \item < span class = "ot" > -> < / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb13-6" title = "6" > < span class = "co" > -- tags contains the content of the " tags" metadata< / span > < / a >
< a class = "sourceLine" id = "cb13-7" title = "7" > < span class = "co" > -- inside the item (understand the source)< / span > < / a >
< a class = "sourceLine" id = "cb13-8" title = "8" > tags < span class = "ot" > < -< / span > getMetadataField (itemIdentifier item) < span class = "st" > " tags" < / span > < / a >
< a class = "sourceLine" id = "cb13-9" title = "9" > < span class = "co" > -- if tags is empty return an empty string< / span > < / a >
< a class = "sourceLine" id = "cb13-10" title = "10" > < span class = "co" > -- in the other case return< / span > < / a >
< a class = "sourceLine" id = "cb13-11" title = "11" > < span class = "co" > -- < meta name=" keywords" content=" $tags$" > < / span > < / a >
< a class = "sourceLine" id = "cb13-12" title = "12" > < span class = "fu" > return< / span > < span class = "op" > $< / span > < span class = "fu" > maybe< / span > < span class = "st" > " " < / span > showMetaTags tags< / a >
< a class = "sourceLine" id = "cb13-13" title = "13" > < span class = "kw" > where< / span > < / a >
< a class = "sourceLine" id = "cb13-14" title = "14" > showMetaTags t < span class = "ot" > =< / span > < span class = "st" > " < meta name=\" keywords\" content=\" " < / span > < / a >
< a class = "sourceLine" id = "cb13-15" title = "15" > < span class = "op" > ++< / span > t < span class = "op" > ++< / span > < span class = "st" > " \" > \n" < / span > < / a > < / code > < / pre > < / div >
< p > Then we pass this < code > Context< / code > to the < code > loadAndApplyTemplate< / code > function:< / p >
< div class = "sourceCode" id = "cb14" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb14-1" title = "1" > match < span class = "st" > " posts/*.md" < / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb14-2" title = "2" > route < span class = "op" > $< / span > setExtension < span class = "st" > " html" < / span > < / a >
< a class = "sourceLine" id = "cb14-3" title = "3" > compile < span class = "op" > $< / span > pandocCompiler< / a >
< a class = "sourceLine" id = "cb14-4" title = "4" > < span class = "co" > -- use the template with the current content< / span > < / a >
< a class = "sourceLine" id = "cb14-5" title = "5" > < span class = "op" > > > =< / span > loadAndApplyTemplate < span class = "st" > " templates/post.html" < / span > < / a >
< a class = "sourceLine" id = "cb14-6" title = "6" > (defaultContext < span class = "highlight" > < span class = "op" > < > < / span > metaKeywordContext< / span > )< / a > < / code > < / pre > < / div >
< blockquote >
< p > ☞ Here are the imports I use for this tutorial.< / p >
< div class = "sourceCode" id = "cb15" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb15-1" title = "1" > < span class = "ot" > {-# LANGUAGE OverloadedStrings #-}< / span > < / a >
< a class = "sourceLine" id = "cb15-2" title = "2" > < span class = "kw" > import< / span > < span class = "dt" > Control.Monad< / span > (forM,forM_)< / a >
< a class = "sourceLine" id = "cb15-3" title = "3" > < span class = "kw" > import< / span > < span class = "dt" > Data.List< / span > (sortBy,isInfixOf)< / a >
< a class = "sourceLine" id = "cb15-4" title = "4" > < span class = "kw" > import< / span > < span class = "dt" > Data.Monoid< / span > ((< > ),mconcat)< / a >
< a class = "sourceLine" id = "cb15-5" title = "5" > < span class = "kw" > import< / span > < span class = "dt" > Data.Ord< / span > (comparing)< / a >
< a class = "sourceLine" id = "cb15-6" title = "6" > < span class = "kw" > import< / span > < span class = "dt" > Hakyll< / span > < / a >
< a class = "sourceLine" id = "cb15-7" title = "7" > < span class = "kw" > import< / span > < span class = "dt" > System.Locale< / span > (defaultTimeLocale)< / a >
< a class = "sourceLine" id = "cb15-8" title = "8" > < span class = "kw" > import< / span > < span class = "dt" > System.FilePath.Posix< / span > (takeBaseName,takeDirectory< / a >
< a class = "sourceLine" id = "cb15-9" title = "9" > ,(< span class = "op" > < /> < / span > ),splitFileName)< / a > < / code > < / pre > < / div >
< / blockquote >
< h3 id = "simplify-url" > Simplify < span class = "sc" > < abbr title = "Uniform Ressource Locator" > url< / abbr > < / span > < / h3 >
< p > What I mean is to use url of the form:< / p >
< pre > < code > http://domain.name/post/title-of-the-post/< / code > < / pre >
< p > I prefer this than having to add file with < code > .html< / code > extension. We have to change the default Hakyll route behavior. We create another function < code > niceRoute< / code > .< / p >
< div class = "sourceCode" id = "cb17" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb17-1" title = "1" > < span class = "co" > -- replace a foo/bar.md by foo/bar/index.html< / span > < / a >
< a class = "sourceLine" id = "cb17-2" title = "2" > < span class = "co" > -- this way the url looks like: foo/bar in most browsers< / span > < / a >
< a class = "sourceLine" id = "cb17-3" title = "3" > < span class = "ot" > niceRoute ::< / span > < span class = "dt" > Routes< / span > < / a >
< a class = "sourceLine" id = "cb17-4" title = "4" > niceRoute < span class = "ot" > =< / span > customRoute createIndexRoute< / a >
< a class = "sourceLine" id = "cb17-5" title = "5" > < span class = "kw" > where< / span > < / a >
< a class = "sourceLine" id = "cb17-6" title = "6" > createIndexRoute ident < span class = "ot" > =< / span > < / a >
< a class = "sourceLine" id = "cb17-7" title = "7" > takeDirectory p < span class = "op" > < /> < / span > takeBaseName p < span class = "op" > < /> < / span > < span class = "st" > " index.html" < / span > < / a >
< a class = "sourceLine" id = "cb17-8" title = "8" > < span class = "kw" > where< / span > p< span class = "ot" > =< / span > toFilePath ident< / a > < / code > < / pre > < / div >
< p > Not too difficult. But! There might be a problem. What if there is a < code > foo/index.html< / code > link instead of a clean < code > foo/< / code > in some content?< / p >
< p > Very simple, we simply remove all < code > /index.html< / code > to all our links.< / p >
< div class = "sourceCode" id = "cb18" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb18-1" title = "1" > < span class = "co" > -- replace url of the form foo/bar/index.html by foo/bar< / span > < / a >
< a class = "sourceLine" id = "cb18-2" title = "2" > < span class = "ot" > removeIndexHtml ::< / span > < span class = "dt" > Item< / span > < span class = "dt" > String< / span > < span class = "ot" > -> < / span > < span class = "dt" > Compiler< / span > (< span class = "dt" > Item< / span > < span class = "dt" > String< / span > )< / a >
< a class = "sourceLine" id = "cb18-3" title = "3" > removeIndexHtml item < span class = "ot" > =< / span > < span class = "fu" > return< / span > < span class = "op" > $< / span > < span class = "fu" > fmap< / span > (withUrls removeIndexStr) item< / a >
< a class = "sourceLine" id = "cb18-4" title = "4" > < span class = "kw" > where< / span > < / a >
< a class = "sourceLine" id = "cb18-5" title = "5" > < span class = "ot" > removeIndexStr ::< / span > < span class = "dt" > String< / span > < span class = "ot" > -> < / span > < span class = "dt" > String< / span > < / a >
< a class = "sourceLine" id = "cb18-6" title = "6" > removeIndexStr url < span class = "ot" > =< / span > < span class = "kw" > case< / span > splitFileName url < span class = "kw" > of< / span > < / a >
< a class = "sourceLine" id = "cb18-7" title = "7" > (dir, < span class = "st" > " index.html" < / span > ) < span class = "op" > |< / span > isLocal dir < span class = "ot" > -> < / span > dir< / a >
< a class = "sourceLine" id = "cb18-8" title = "8" > _ < span class = "ot" > -> < / span > url< / a >
< a class = "sourceLine" id = "cb18-9" title = "9" > < span class = "kw" > where< / span > isLocal uri < span class = "ot" > =< / span > < span class = "fu" > not< / span > (isInfixOf < span class = "st" > " ://" < / span > uri)< / a > < / code > < / pre > < / div >
< p > And we apply this filter at the end of our compilation< / p >
< div class = "sourceCode" id = "cb19" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb19-1" title = "1" > match < span class = "st" > " posts/*.md" < / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb19-2" title = "2" > < span class = "highlight" > route < span class = "op" > $< / span > niceRoute< / span > < / a >
< a class = "sourceLine" id = "cb19-3" title = "3" > compile < span class = "op" > $< / span > pandocCompiler< / a >
< a class = "sourceLine" id = "cb19-4" title = "4" > < span class = "co" > -- use the template with the current content< / span > < / a >
< a class = "sourceLine" id = "cb19-5" title = "5" > < span class = "op" > > > =< / span > loadAndApplyTemplate < span class = "st" > " templates/post.html" < / span > defaultContext< / a >
< a class = "sourceLine" id = "cb19-6" title = "6" > < span class = "highlight" > < span class = "op" > > > =< / span > removeIndexHtml< / span > < / a > < / code > < / pre > < / div >
< h3 id = "create-an-archive-page" > Create an archive page< / h3 >
< p > Creating an archive start to be difficult. There is an example in the default Hakyll example. Unfortunately, it assumes all posts prefix their name with a date like in < code > 2013-03-20-My-New-Post.md< / code > .< / p >
< p > I migrated from an older blog and didn’ t want to change my < span class = "sc" > < abbr title = "Uniform Ressource Locator" > url< / abbr > < / span > . Also I prefer not to use any filename convention. Therefore, I add the date information in the metadata < code > published< / code > . And the solution is here:< / p >
< div class = "sourceCode" id = "cb20" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb20-1" title = "1" > match < span class = "st" > " archive.md" < / span > < span class = "op" > $< / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb20-2" title = "2" > route < span class = "op" > $< / span > niceRoute< / a >
< a class = "sourceLine" id = "cb20-3" title = "3" > compile < span class = "op" > $< / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb20-4" title = "4" > body < span class = "ot" > < -< / span > getResourceBody< / a >
< a class = "sourceLine" id = "cb20-5" title = "5" > < span class = "fu" > return< / span > < span class = "op" > $< / span > renderPandoc body< / a >
< a class = "sourceLine" id = "cb20-6" title = "6" > < span class = "highlight" > < span class = "op" > > > =< / span > loadAndApplyTemplate < span class = "st" > " templates/archive.html" < / span > archiveCtx< / span > < / a >
< a class = "sourceLine" id = "cb20-7" title = "7" > < span class = "op" > > > =< / span > loadAndApplyTemplate < span class = "highlight" > < span class = "st" > " templates/base.html" < / span > < / span > defaultContext< / a >
< a class = "sourceLine" id = "cb20-8" title = "8" > < span class = "op" > > > =< / span > removeIndexHtml< / a > < / code > < / pre > < / div >
< p > Where < code > templates/archive.html< / code > contains< / p >
< div class = "sourceCode" id = "cb21" > < pre class = "sourceCode html" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb21-1" title = "1" > $body$< / a >
< a class = "sourceLine" id = "cb21-2" title = "2" > < / a >
< a class = "sourceLine" id = "cb21-3" title = "3" > < span class = "kw" > < ul> < / span > < / a >
< a class = "sourceLine" id = "cb21-4" title = "4" > $posts$< / a >
< a class = "sourceLine" id = "cb21-5" title = "5" > < span class = "kw" > < /ul> < / span > < / a > < / code > < / pre > < / div >
< p > And < code > base.html< / code > is a standard template (simpler than < code > post.html< / code > ).< / p >
< p > < code > archiveCtx< / code > provide a context containing an < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > representation of a list of posts in the metadata named < code > posts< / code > . It will be used in the < code > templates/archive.html< / code > file with < code > $posts$< / code > .< / p >
< div class = "sourceCode" id = "cb22" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb22-1" title = "1" > archiveCtx < span class = "ot" > =< / span > < / a >
< a class = "sourceLine" id = "cb22-2" title = "2" > defaultContext < span class = "op" > < > < / span > < / a >
< a class = "sourceLine" id = "cb22-3" title = "3" > metaKeywordContext < span class = "op" > < > < / span > < / a >
< a class = "sourceLine" id = "cb22-4" title = "4" > < span class = "highlight" > field < span class = "st" > " posts" < / span > (\_ < span class = "ot" > -> < / span > postList createdFirst)< / span > < / a > < / code > < / pre > < / div >
< p > < code > postList< / code > returns an < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > representation of a list of posts given an Item sort function. The representation will apply a minimal template on all posts. Then it concatenate all the results. The template is < code > post-item.html< / code > :< / p >
< div class = "sourceCode" id = "cb23" > < pre class = "sourceCode html" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb23-1" title = "1" > < span class = "kw" > < li> < a< / span > < span class = "ot" > href=< / span > < span class = "st" > " $url$" < / span > < span class = "kw" > > < / span > $published$ - $title$< span class = "kw" > < /a> < /li> < / span > < / a > < / code > < / pre > < / div >
< p > Here is how it is done:< / p >
< div class = "sourceCode" id = "cb24" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb24-1" title = "1" > < span class = "ot" > postList ::< / span > [< span class = "dt" > Item< / span > < span class = "dt" > String< / span > ] < span class = "ot" > -> < / span > < span class = "dt" > Compiler< / span > [< span class = "dt" > Item< / span > < span class = "dt" > String< / span > ]< / a >
< a class = "sourceLine" id = "cb24-2" title = "2" > < span class = "ot" > -> < / span > < span class = "dt" > Compiler< / span > < span class = "dt" > String< / span > < / a >
< a class = "sourceLine" id = "cb24-3" title = "3" > postList sortFilter < span class = "ot" > =< / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb24-4" title = "4" > < span class = "co" > -- sorted posts< / span > < / a >
< a class = "sourceLine" id = "cb24-5" title = "5" > posts < span class = "ot" > < -< / span > loadAll < span class = "st" > " post/*" < / span > < span class = "op" > > > =< / span > sortFilter< / a >
< a class = "sourceLine" id = "cb24-6" title = "6" > itemTpl < span class = "ot" > < -< / span > loadBody < span class = "st" > " templates/post-item.html" < / span > < / a >
< a class = "sourceLine" id = "cb24-7" title = "7" > < span class = "co" > -- we apply the template to all post< / span > < / a >
< a class = "sourceLine" id = "cb24-8" title = "8" > < span class = "co" > -- and we concatenate the result.< / span > < / a >
< a class = "sourceLine" id = "cb24-9" title = "9" > < span class = "co" > -- list is a string< / span > < / a >
< a class = "sourceLine" id = "cb24-10" title = "10" > list < span class = "ot" > < -< / span > applyTemplateList itemTpl defaultContext posts< / a >
< a class = "sourceLine" id = "cb24-11" title = "11" > < span class = "fu" > return< / span > list< / a > < / code > < / pre > < / div >
< p > < code > createdFirst< / code > sort a list of item and put it inside < code > Compiler< / code > context. We need to be in the < code > Compiler< / code > context to access metadatas.< / p >
< div class = "sourceCode" id = "cb25" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb25-1" title = "1" > < span class = "ot" > createdFirst ::< / span > [< span class = "dt" > Item< / span > < span class = "dt" > String< / span > ] < span class = "ot" > -> < / span > < span class = "dt" > Compiler< / span > [< span class = "dt" > Item< / span > < span class = "dt" > String< / span > ]< / a >
< a class = "sourceLine" id = "cb25-2" title = "2" > createdFirst items < span class = "ot" > =< / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb25-3" title = "3" > < span class = "co" > -- itemsWithTime is a list of couple (date,item)< / span > < / a >
< a class = "sourceLine" id = "cb25-4" title = "4" > itemsWithTime < span class = "ot" > < -< / span > forM items < span class = "op" > $< / span > \item < span class = "ot" > -> < / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb25-5" title = "5" > < span class = "co" > -- getItemUTC will look for the metadata " published" or " date" < / span > < / a >
< a class = "sourceLine" id = "cb25-6" title = "6" > < span class = "co" > -- then it will try to get the date from some standard formats< / span > < / a >
< a class = "sourceLine" id = "cb25-7" title = "7" > utc < span class = "ot" > < -< / span > getItemUTC defaultTimeLocale < span class = "op" > $< / span > itemIdentifier item< / a >
< a class = "sourceLine" id = "cb25-8" title = "8" > < span class = "fu" > return< / span > (utc,item)< / a >
< a class = "sourceLine" id = "cb25-9" title = "9" > < span class = "co" > -- we return a sorted item list< / span > < / a >
< a class = "sourceLine" id = "cb25-10" title = "10" > < span class = "fu" > return< / span > < span class = "op" > $< / span > < span class = "fu" > map< / span > < span class = "fu" > snd< / span > < span class = "op" > $< / span > < span class = "fu" > reverse< / span > < span class = "op" > $< / span > sortBy (comparing < span class = "fu" > fst< / span > ) itemsWithTime< / a > < / code > < / pre > < / div >
< p > It wasn’ t so easy. But it works pretty well.< / p >
< h3 id = "create-an-rss-feed" > Create an < span class = "sc" > < abbr title = "Rich Site Summary" > rss< / abbr > < / span > feed< / h3 >
< p > To create an < span class = "sc" > < abbr title = "Rich Site Summary" > rss< / abbr > < / span > feed, we have to:< / p >
< ul >
< li > select only the lasts posts.< / li >
< li > generate partially rendered posts (no css, js, etc…)< / li >
< / ul >
< p > We could then render the posts twice. One for < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > rendering and another time for < span class = "sc" > < abbr title = "Rich Site Summary" > rss< / abbr > < / span > . Remark we need to generate the < span class = "sc" > < abbr title = "Rich Site Summary" > rss< / abbr > < / span > version to create the < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > one.< / p >
< p > One of the great feature of Hakyll is to be able to save snapshots. Here is how:< / p >
< div class = "sourceCode" id = "cb26" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb26-1" title = "1" > match < span class = "st" > " posts/*.md" < / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb26-2" title = "2" > route < span class = "op" > $< / span > setExtension < span class = "st" > " html" < / span > < / a >
< a class = "sourceLine" id = "cb26-3" title = "3" > compile < span class = "op" > $< / span > pandocCompiler< / a >
< a class = "sourceLine" id = "cb26-4" title = "4" > < span class = "co" > -- save a snapshot to be used later in rss generation< / span > < / a >
< a class = "sourceLine" id = "cb26-5" title = "5" > < span class = "highlight" > < span class = "op" > > > =< / span > saveSnapshot < span class = "st" > " content" < / span > < / span > < / a >
< a class = "sourceLine" id = "cb26-6" title = "6" > < span class = "op" > > > =< / span > loadAndApplyTemplate < span class = "st" > " templates/post.html" < / span > defaultContext< / a > < / code > < / pre > < / div >
< p > Now for each post there is a snapshot named “content” associated. The snapshots are created before applying a template and after applying pandoc. Furthermore feed don’ t need a source markdown file. Then we create a new file from no one. Instead of using < code > match< / code > , we use < code > create< / code > :< / p >
< div class = "sourceCode" id = "cb27" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb27-1" title = "1" > create [< span class = "st" > " feed.xml" < / span > ] < span class = "op" > $< / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb27-2" title = "2" > route idRoute< / a >
< a class = "sourceLine" id = "cb27-3" title = "3" > compile < span class = "op" > $< / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb27-4" title = "4" > < span class = "co" > -- load all " content" snapshots of all posts< / span > < / a >
< a class = "sourceLine" id = "cb27-5" title = "5" > loadAllSnapshots < span class = "st" > " posts/*" < / span > < span class = "st" > " content" < / span > < / a >
< a class = "sourceLine" id = "cb27-6" title = "6" > < span class = "co" > -- take the latest 10< / span > < / a >
< a class = "sourceLine" id = "cb27-7" title = "7" > < span class = "op" > > > =< / span > (< span class = "fu" > fmap< / span > (< span class = "fu" > take< / span > < span class = "dv" > 10< / span > )) < span class = "op" > .< / span > createdFirst< / a >
< a class = "sourceLine" id = "cb27-8" title = "8" > < span class = "co" > -- renderAntom feed using some configuration< / span > < / a >
< a class = "sourceLine" id = "cb27-9" title = "9" > < span class = "op" > > > =< / span > renderAtom feedConfiguration feedCtx< / a >
< a class = "sourceLine" id = "cb27-10" title = "10" > < span class = "kw" > where< / span > < / a >
< a class = "sourceLine" id = "cb27-11" title = "11" > < span class = "ot" > feedCtx ::< / span > < span class = "dt" > Context< / span > < span class = "dt" > String< / span > < / a >
< a class = "sourceLine" id = "cb27-12" title = "12" > feedCtx < span class = "ot" > =< / span > defaultContext < span class = "op" > < > < / span > < / a >
< a class = "sourceLine" id = "cb27-13" title = "13" > < span class = "co" > -- $description$ will render as the post body< / span > < / a >
< a class = "sourceLine" id = "cb27-14" title = "14" > < span class = "highlight" > bodyField < span class = "st" > " description" < / span > < / span > < / a > < / code > < / pre > < / div >
< p > The < code > feedConfiguration< / code > contains some general informations about the feed.< / p >
< div class = "sourceCode" id = "cb28" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb28-1" title = "1" > < span class = "ot" > feedConfiguration ::< / span > < span class = "dt" > FeedConfiguration< / span > < / a >
< a class = "sourceLine" id = "cb28-2" title = "2" > feedConfiguration < span class = "ot" > =< / span > < span class = "dt" > FeedConfiguration< / span > < / a >
< a class = "sourceLine" id = "cb28-3" title = "3" > { feedTitle < span class = "ot" > =< / span > < span class = "st" > " Great Old Ones" < / span > < / a >
< a class = "sourceLine" id = "cb28-4" title = "4" > , feedDescription < span class = "ot" > =< / span > < span class = "st" > " This feed provide information about Great Old Ones" < / span > < / a >
< a class = "sourceLine" id = "cb28-5" title = "5" > , feedAuthorName < span class = "ot" > =< / span > < span class = "st" > " Abdul Alhazred" < / span > < / a >
< a class = "sourceLine" id = "cb28-6" title = "6" > , feedAuthorEmail < span class = "ot" > =< / span > < span class = "st" > " abdul.alhazred@great-old-ones.com" < / span > < / a >
< a class = "sourceLine" id = "cb28-7" title = "7" > , feedRoot < span class = "ot" > =< / span > < span class = "st" > " http://great-old-ones.com" < / span > < / a >
< a class = "sourceLine" id = "cb28-8" title = "8" > }< / a > < / code > < / pre > < / div >
< p > Great idea certainly steal from < a href = "http://nanoc.ws" > nanoc< / a > (my previous blog engine)!< / p >
< h3 id = "filter-the-content" > Filter the content< / h3 >
< p > As I just said, < a href = "http://nanoc.ws" > nanoc< / a > was my preceding blog engine. It is written in Ruby and as Hakyll, it is quite awesome. And one thing Ruby does more naturally than Haskell is regular expressions. I had a < em > lot< / em > of filters in nanoc. I lost some because I don’ t use them much. But I wanted to keep some. Generally, filtering the content is just a way to apply to the body a function of type < code > String -> String< / code > .< / p >
< p > Also we generally want prefilters (to filter the markdown) and postfilters (to filter the < span class = "sc" > < abbr title = "HyperText Markup Language" > html< / abbr > < / span > after the pandoc compilation).< / p >
< p > Here is how I do it:< / p >
< div class = "sourceCode" id = "cb29" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb29-1" title = "1" > markdownPostBehavior < span class = "ot" > =< / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb29-2" title = "2" > route < span class = "op" > $< / span > niceRoute< / a >
< a class = "sourceLine" id = "cb29-3" title = "3" > compile < span class = "op" > $< / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb29-4" title = "4" > body < span class = "ot" > < -< / span > getResourceBody< / a >
< a class = "sourceLine" id = "cb29-5" title = "5" > < span class = "highlight" > prefilteredText < span class = "ot" > < -< / span > < span class = "fu" > return< / span > < span class = "op" > $< / span > (< span class = "fu" > fmap< / span > preFilters body)< / span > < / a >
< a class = "sourceLine" id = "cb29-6" title = "6" > < span class = "highlight" > < span class = "fu" > return< / span > < span class = "op" > $< / span > renderPandoc prefilteredText< / span > < / a >
< a class = "sourceLine" id = "cb29-7" title = "7" > < span class = "highlight" > < span class = "op" > > > =< / span > applyFilter postFilters< / span > < / a >
< a class = "sourceLine" id = "cb29-8" title = "8" > < span class = "op" > > > =< / span > saveSnapshot < span class = "st" > " content" < / span > < / a >
< a class = "sourceLine" id = "cb29-9" title = "9" > < span class = "op" > > > =< / span > loadAndApplyTemplate < span class = "st" > " templates/post.html" < / span > yContext< / a >
< a class = "sourceLine" id = "cb29-10" title = "10" > < span class = "op" > > > =< / span > loadAndApplyTemplate < span class = "st" > " templates/boilerplate.html" < / span > yContext< / a >
< a class = "sourceLine" id = "cb29-11" title = "11" > < span class = "op" > > > =< / span > relativizeUrls< / a >
< a class = "sourceLine" id = "cb29-12" title = "12" > < span class = "op" > > > =< / span > removeIndexHtml< / a > < / code > < / pre > < / div >
< p > Where< / p >
< div class = "sourceCode" id = "cb30" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb30-1" title = "1" > applyFilter strfilter str < span class = "ot" > =< / span > < span class = "fu" > return< / span > < span class = "op" > $< / span > (< span class = "fu" > fmap< / span > < span class = "op" > $< / span > strfilter) str< / a >
< a class = "sourceLine" id = "cb30-2" title = "2" > < span class = "ot" > preFilters ::< / span > < span class = "dt" > String< / span > < span class = "ot" > -> < / span > < span class = "dt" > String< / span > < / a >
< a class = "sourceLine" id = "cb30-3" title = "3" > < span class = "ot" > postFilters ::< / span > < span class = "dt" > String< / span > < span class = "ot" > -> < / span > < span class = "dt" > String< / span > < / a > < / code > < / pre > < / div >
< p > Now we have a simple way to filter the content. Let’ s augment the markdown ability.< / p >
< h3 id = "add-abbreviations-support" > Add abbreviations support< / h3 >
< p > Comparing to < span style = "text-transform: uppercase" > L< sup style = "vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em" > a< / sup > T< sub style = "vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em" > e< / sub > X< / span > , a very annoying markdown limitation is the lack of abbreviations.< / p >
< p > Fortunately we can filter our content. And here is the filter I use:< / p >
< div class = "sourceCode" id = "cb31" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb31-1" title = "1" > < span class = "ot" > abbreviationFilter ::< / span > < span class = "dt" > String< / span > < span class = "ot" > -> < / span > < span class = "dt" > String< / span > < / a >
< a class = "sourceLine" id = "cb31-2" title = "2" > abbreviationFilter < span class = "ot" > =< / span > replaceAll < span class = "st" > " %[a-zA-Z0-9_]*" < / span > newnaming< / a >
< a class = "sourceLine" id = "cb31-3" title = "3" > < span class = "kw" > where< / span > < / a >
< a class = "sourceLine" id = "cb31-4" title = "4" > newnaming matched < span class = "ot" > =< / span > < span class = "kw" > case< / span > M.lookup (< span class = "fu" > tail< / span > matched) abbreviations < span class = "kw" > of< / span > < / a >
< a class = "sourceLine" id = "cb31-5" title = "5" > < span class = "dt" > Nothing< / span > < span class = "ot" > -> < / span > matched< / a >
< a class = "sourceLine" id = "cb31-6" title = "6" > < span class = "dt" > Just< / span > v < span class = "ot" > -> < / span > v< / a >
< a class = "sourceLine" id = "cb31-7" title = "7" > < span class = "ot" > abbreviations ::< / span > < span class = "dt" > Map< / span > < span class = "dt" > String< / span > < span class = "dt" > String< / span > < / a >
< a class = "sourceLine" id = "cb31-8" title = "8" > abbreviations < span class = "ot" > =< / span > M.fromList< / a >
< a class = "sourceLine" id = "cb31-9" title = "9" > [ (< span class = "st" > " html" < / span > , < span class = "st" > " < span class=\" sc\" > html< /span> " < / span > )< / a >
< a class = "sourceLine" id = "cb31-10" title = "10" > , (< span class = "st" > " css" < / span > , < span class = "st" > " < span class=\" sc\" > css< /span> " < / span > )< / a >
< a class = "sourceLine" id = "cb31-11" title = "11" > , (< span class = "st" > " svg" < / span > , < span class = "st" > " < span class=\" sc\" > svg< /span> " < / span > )< / a >
< a class = "sourceLine" id = "cb31-12" title = "12" > , (< span class = "st" > " xml" < / span > , < span class = "st" > " < span class=\" sc\" > xml< /span> " < / span > )< / a >
< a class = "sourceLine" id = "cb31-13" title = "13" > , (< span class = "st" > " xslt" < / span > , < span class = "st" > " < span class=\" sc\" > xslt< /span> " < / span > ) ]< / a > < / code > < / pre > < / div >
< p > It will search for all string starting by ‘ %’ and it will search in the < code > Map< / code > if there is a corresponding abbreviation. If there is one, we replace the content. Otherwise we do nothing.< / p >
< p > Do you really believe I type< / p >
< div class = "sourceCode" id = "cb32" > < pre class = "sourceCode html wrap" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb32-1" title = "1" > < span class = "kw" > < span< / span > < span class = "ot" > style=< / span > < span class = "st" > " text-transform: uppercase" < / span > < span class = "kw" > > < / span > L< span class = "kw" > < sup< / span > < span class = "ot" > style=< / span > < span class = "st" > " vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em" < / span > < span class = "kw" > > < / span > a< span class = "kw" > < /sup> < / span > T< span class = "kw" > < sub< / span > < span class = "ot" > style=< / span > < span class = "st" > " vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em" < / span > < span class = "kw" > > < / span > e< span class = "kw" > < /sub> < / span > X< span class = "kw" > < /span> < / span > < / a > < / code > < / pre > < / div >
< p > each time I write < span style = "text-transform: uppercase" > L< sup style = "vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em" > a< / sup > T< sub style = "vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em" > e< / sub > X< / span > ?< / p >
< h3 id = "manage-two-languages" > Manage two languages< / h3 >
< p > Generally I write my post in English and French. And this is more difficult than it appears. For example, I need to filter the language in order to get the right list of posts. I also use some words in the templates and I want them to be translated.< / p >
< div class = "sourceCode" id = "cb33" > < pre class = "sourceCode html" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb33-1" title = "1" > < span class = "kw" > < a< / span > < span class = "ot" > href=< / span > < span class = "st" > " $otherLanguagePath$" < / span > < / a >
< a class = "sourceLine" id = "cb33-2" title = "2" > < span class = "ot" > onclick=< / span > < span class = "st" > " setLanguage('$otherlanguage$')" < / span > < span class = "kw" > > < / span > < / a >
< a class = "sourceLine" id = "cb33-3" title = "3" > < span class = "highlight" > $changeLanguage$< / span > < span class = "kw" > < /a> < / span > < / a > < / code > < / pre > < / div >
< p > First I create a Map containing all translations.< / p >
< div class = "sourceCode" id = "cb34" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb34-1" title = "1" > < span class = "kw" > data< / span > < span class = "dt" > Trad< / span > < span class = "ot" > =< / span > < span class = "dt" > Trad< / span > {< span class = "ot" > frTrad ::< / span > < span class = "dt" > String< / span > ,< span class = "ot" > enTrad ::< / span > < span class = "dt" > String< / span > }< / a >
< a class = "sourceLine" id = "cb34-2" title = "2" > < / a >
< a class = "sourceLine" id = "cb34-3" title = "3" > < span class = "ot" > trads ::< / span > < span class = "dt" > Map< / span > < span class = "dt" > String< / span > < span class = "dt" > Trad< / span > < / a >
< a class = "sourceLine" id = "cb34-4" title = "4" > trads < span class = "ot" > =< / span > M.fromList < span class = "op" > $< / span > < span class = "fu" > map< / span > toTrad [< / a >
< a class = "sourceLine" id = "cb34-5" title = "5" > (< span class = "st" > " changeLanguage" < / span > ,< / a >
< a class = "sourceLine" id = "cb34-6" title = "6" > (< span class = "st" > " English" < / span > < / a >
< a class = "sourceLine" id = "cb34-7" title = "7" > , < span class = "st" > " Français" < / span > ))< / a >
< a class = "sourceLine" id = "cb34-8" title = "8" > ,(< span class = "st" > " switchCss" < / span > ,< / a >
< a class = "sourceLine" id = "cb34-9" title = "9" > (< span class = "st" > " Changer de theme" < / span > < / a >
< a class = "sourceLine" id = "cb34-10" title = "10" > ,< span class = "st" > " Change Theme" < / span > ))< / a >
< a class = "sourceLine" id = "cb34-11" title = "11" > ,(< span class = "st" > " socialPrivacy" < / span > ,< / a >
< a class = "sourceLine" id = "cb34-12" title = "12" > (< span class = "st" > " Ces liens sociaux préservent votre vie privée" < / span > < / a >
< a class = "sourceLine" id = "cb34-13" title = "13" > ,< span class = "st" > " These social sharing links preserve your privacy" < / span > ))< / a >
< a class = "sourceLine" id = "cb34-14" title = "14" > ]< / a >
< a class = "sourceLine" id = "cb34-15" title = "15" > < span class = "kw" > where< / span > < / a >
< a class = "sourceLine" id = "cb34-16" title = "16" > toTrad (key,(french,english)) < span class = "ot" > =< / span > < / a >
< a class = "sourceLine" id = "cb34-17" title = "17" > (key, < span class = "dt" > Trad< / span > { frTrad < span class = "ot" > =< / span > french , enTrad < span class = "ot" > =< / span > english })< / a > < / code > < / pre > < / div >
< p > Then I create a context for all key:< / p >
< div class = "sourceCode" id = "cb35" > < pre class = "sourceCode haskell" > < code class = "sourceCode haskell" > < a class = "sourceLine" id = "cb35-1" title = "1" > < span class = "ot" > tradsContext ::< / span > < span class = "dt" > Context< / span > a< / a >
< a class = "sourceLine" id = "cb35-2" title = "2" > tradsContext < span class = "ot" > =< / span > < span class = "fu" > mconcat< / span > (< span class = "fu" > map< / span > addTrad (M.keys trads))< / a >
< a class = "sourceLine" id = "cb35-3" title = "3" > < span class = "kw" > where< / span > < / a >
< a class = "sourceLine" id = "cb35-4" title = "4" > < span class = "ot" > addTrad ::< / span > < span class = "dt" > String< / span > < span class = "ot" > -> < / span > < span class = "dt" > Context< / span > a< / a >
< a class = "sourceLine" id = "cb35-5" title = "5" > addTrad name < span class = "ot" > =< / span > < / a >
< a class = "sourceLine" id = "cb35-6" title = "6" > field name < span class = "op" > $< / span > \item < span class = "ot" > -> < / span > < span class = "kw" > do< / span > < / a >
< a class = "sourceLine" id = "cb35-7" title = "7" > lang < span class = "ot" > < -< / span > itemLang item< / a >
< a class = "sourceLine" id = "cb35-8" title = "8" > < span class = "kw" > case< / span > M.lookup name trads < span class = "kw" > of< / span > < / a >
< a class = "sourceLine" id = "cb35-9" title = "9" > < span class = "dt" > Just< / span > (< span class = "dt" > Trad< / span > lmap) < span class = "ot" > -> < / span > < span class = "kw" > case< / span > M.lookup (< span class = "dt" > L< / span > lang) lmap < span class = "kw" > of< / span > < / a >
< a class = "sourceLine" id = "cb35-10" title = "10" > < span class = "dt" > Just< / span > tr < span class = "ot" > -> < / span > < span class = "fu" > return< / span > tr< / a >
< a class = "sourceLine" id = "cb35-11" title = "11" > < span class = "dt" > Nothing< / span > < span class = "ot" > -> < / span > < span class = "fu" > return< / span > (< span class = "st" > " NO TRANSLATION FOR " < / span > < span class = "op" > ++< / span > name)< / a >
< a class = "sourceLine" id = "cb35-12" title = "12" > < span class = "dt" > Nothing< / span > < span class = "ot" > -> < / span > < span class = "fu" > return< / span > (< span class = "st" > " NO TRANSLATION FOR " < / span > < span class = "op" > ++< / span > name)< / a > < / code > < / pre > < / div >
< h2 id = "conclusion" > Conclusion< / h2 >
< p > The full code is < a href = "http://github.com/yogsototh/yblog.git" > here< / a > . And except from the main file, I use literate Haskell. This way the code should be easier to understand.< / p >
< p > If you want to know why I switched from nanoc:< / p >
< p > My preceding nanoc website was a bit too messy. So much in fact, that the dependency system recompiled the entire website for any change.< / p >
< p > So I had to do something about it. I had two choices:< / p >
< ol type = "1" >
< li > Correct my old code (in Ruby)< / li >
< li > Duplicate the core functionalities with Hakyll (in Haskell)< / li >
< / ol >
< p > I added too much functionalities in my nanoc system. Starting from scratch (almost) remove efficiently a lot of unused crap.< / p >
< p > So far I am very happy with the switch. A complete build is about 4x faster. I didn’ t broke the dependency system this time. As soon as I modify and save the markdown source, I can reload the page in the browser.< / p >
< p > I removed a lot of feature thought. Some of them will be difficult to achieve with Hakyll. A typical example:< / p >
< p > In nanoc I could take a file like this as source:< / p >
< div class = "sourceCode" id = "cb36" > < pre class = "sourceCode markdown" > < code class = "sourceCode markdown" > < a class = "sourceLine" id = "cb36-1" title = "1" > < span class = "fu" > # Title< / span > < / a >
< a class = "sourceLine" id = "cb36-2" title = "2" > < / a >
< a class = "sourceLine" id = "cb36-3" title = "3" > content< / a >
< a class = "sourceLine" id = "cb36-4" title = "4" > < / a >
< a class = "sourceLine" id = "cb36-5" title = "5" > < code file=" foo.hs" > < / a >
< a class = "sourceLine" id = "cb36-6" title = "6" > main = putStrLn " Cthulhu!" < / a >
< a class = "sourceLine" id = "cb36-7" title = "7" > < /code> < / a > < / code > < / pre > < / div >
< p > And it will create a file < code > foo.hs< / code > which could then be downloaded.< / p >
< div class = "sourceCode" id = "cb37" > < pre class = "sourceCode html" > < code class = "sourceCode html" > < a class = "sourceLine" id = "cb37-1" title = "1" > < span class = "kw" > < h1> < / span > Title< span class = "kw" > < /h1> < / span > < / a >
< a class = "sourceLine" id = "cb37-2" title = "2" > < / a >
< a class = "sourceLine" id = "cb37-3" title = "3" > < span class = "kw" > < p> < / span > content< span class = "kw" > < /p> < / span > < / a >
< a class = "sourceLine" id = "cb37-4" title = "4" > < / a >
< a class = "sourceLine" id = "cb37-5" title = "5" > < span class = "kw" > < a< / span > < span class = "ot" > href=< / span > < span class = "st" > " code/foo.hs" < / span > < span class = "kw" > > < / span > Download foo.hs< span class = "kw" > < /a> < / span > < / a >
< a class = "sourceLine" id = "cb37-6" title = "6" > < span class = "kw" > < pre> < code> < / span > main = putStrLn " Cthulhu!" < span class = "kw" > < /code> < /pre> < / span > < / a > < / code > < / pre > < / div >
< section class = "footnotes" >
< hr / >
< ol >
< li id = "fn1" > < p > We could also add the metadatas in an external file (< code > foo.md.metadata< / code > ).< a href = "#fnref1" class = "footnote-back" > ↩< / a > < / p > < / li >
< / ol >
< / section >
< / div >
< div id = "afterarticle" >
< div id = "social" >
2021-05-25 20:25:47 +00:00
< a href = "/rss.xml" target = "_blank" rel = "noopener noreferrer nofollow" class = "social" > RSS< / a >
2021-04-18 10:23:24 +00:00
·
< a href = "https://twitter.com/home?status=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Hakyll-setup/%20via%20@yogsototh" target = "_blank" rel = "noopener noreferrer nofollow" class = "social" > Tweet< / a >
·
< a href = "http://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Hakyll-setup/" target = "_blank" rel = "noopener noreferrer nofollow" class = "social" > FB< / a >
< br / >
< a class = "message" href = "../../../../Scratch/en/blog/Social-link-the-right-way/" > These social sharing links preserve your privacy< / a >
< / div >
< div id = "navigation" >
< a href = "../../../../" > Home< / a >
< span class = "sep" > ¦< / span >
< a href = "../../../../Scratch/en/blog" > Blog< / a >
< span class = "sep" > ¦< / span >
< a href = "../../../../Scratch/en/softwares" > Softwares< / a >
< span class = "sep" > ¦< / span >
< a href = "../../../../Scratch/en/about" > About< / a >
< / div >
< div id = "totop" > < a href = "#header" > ↑ Top ↑< / a > < / div >
< div id = "bottom" >
< div >
Published on 2013-03-16
< / div >
< div >
< a href = "https://twitter.com/yogsototh" > Follow @yogsototh< / a >
< / div >
< div >
< a rel = "license" href = "http://creativecommons.org/licenses/by/3.0/deed.en_US" > Yann Esposito©< / a >
< / div >
< div >
Done with
< a href = "http://www.vim.org" target = "_blank" rel = "noopener noreferrer nofollow" > < strike > Vim< / strike > < / a >
< a href = "http://spacemacs.org" target = "_blank" rel = "noopener noreferrer nofollow" > spacemacs< / a >
< span class = "pala" > & < / span >
< a href = "http://nanoc.ws" target = "_blank" rel = "noopener noreferrer nofollow" > < strike > nanoc< / strike > < / a >
< a href = "http://jaspervdj.be/hakyll" target = "_blank" rel = "noopener noreferrer nofollow" > Hakyll< / a >
< / div >
< hr / >
< div style = "max-width: 100%" >
< a href = "https://cardanohub.org" >
< img src = "../../../../Scratch/img/ada-logo.png" class = "simple" style = "height: 16px ;
border-radius: 50%;
vertical-align:middle;
display:inline-block;" />
ADA:
< / a >
< code style = "display:inline-block;
word-wrap:break-word;
text-align: left;
vertical-align: top;
max-width: 85%;">
DdzFFzCqrhtAvdkmATx5Fm8NPJViDy85ZBw13p4XcNzVzvQg8e3vWLXq23JQWFxPEXK6Kvhaxxe7oJt4VMYHxpA2vtCFiP8fziohN6Yp
< / code >
< / div >
< / div >
< / div >
< / div >
< / div >
< / body >
< / html >