diff options
-rw-r--r-- | examples/categoryblog/hakyll.hs | 8 | ||||
-rw-r--r-- | examples/hakyll/css/default.css | 6 | ||||
-rw-r--r-- | examples/hakyll/images/tutorial6-categories.png | bin | 0 -> 9377 bytes | |||
-rw-r--r-- | examples/hakyll/images/tutorial6-tags.png | bin | 0 -> 8631 bytes | |||
-rw-r--r-- | examples/hakyll/tutorial6.markdown | 87 |
5 files changed, 97 insertions, 4 deletions
diff --git a/examples/categoryblog/hakyll.hs b/examples/categoryblog/hakyll.hs index 2fef98e..e6d87ea 100644 --- a/examples/categoryblog/hakyll.hs +++ b/examples/categoryblog/hakyll.hs @@ -2,12 +2,11 @@ module Main where import Text.Hakyll (hakyll) import Text.Hakyll.Render -import Text.Hakyll.Tags (readCategoryMap) -import Text.Hakyll.File (getRecursiveContents, directory, removeSpaces) +import Text.Hakyll.Tags (TagMap, readCategoryMap) +import Text.Hakyll.File (getRecursiveContents, directory, removeSpaces, sortByBaseName) import Text.Hakyll.Renderables (createPagePath, createCustomPage, createListingWith, createListing) import Text.Hakyll.Context (ContextManipulation, renderDate) import Text.Hakyll.Util (link) -import Data.List (sort) import Data.Map (toList) import Control.Monad (mapM_, liftM, (<=<)) import Data.Either (Either(..)) @@ -17,7 +16,7 @@ main = hakyll $ do directory css "css" -- Find all post paths. - postPaths <- liftM (reverse . sort) $ getRecursiveContents "posts" + postPaths <- liftM (reverse . sortByBaseName) $ getRecursiveContents "posts" let renderablePosts = map createPagePath postPaths -- Read category map. @@ -54,6 +53,7 @@ main = hakyll $ do categoryToURL category = "$root/categories/" ++ removeSpaces category ++ ".html" + categoryList :: TagMap -> String categoryList = uncurry categoryListItem <=< toList categoryListItem category posts = "<li>" ++ link category (categoryToURL category) diff --git a/examples/hakyll/css/default.css b/examples/hakyll/css/default.css index 8cc4030..9701720 100644 --- a/examples/hakyll/css/default.css +++ b/examples/hakyll/css/default.css @@ -83,6 +83,12 @@ div.column p { padding-right: 15px; } +img { + display: block; + margin: 10px auto 10px auto; + border: none; +} + ul { list-style-type: square; padding-left: 1em; diff --git a/examples/hakyll/images/tutorial6-categories.png b/examples/hakyll/images/tutorial6-categories.png Binary files differnew file mode 100644 index 0000000..0567917 --- /dev/null +++ b/examples/hakyll/images/tutorial6-categories.png diff --git a/examples/hakyll/images/tutorial6-tags.png b/examples/hakyll/images/tutorial6-tags.png Binary files differnew file mode 100644 index 0000000..d8ae73e --- /dev/null +++ b/examples/hakyll/images/tutorial6-tags.png diff --git a/examples/hakyll/tutorial6.markdown b/examples/hakyll/tutorial6.markdown new file mode 100644 index 0000000..12ef3a3 --- /dev/null +++ b/examples/hakyll/tutorial6.markdown @@ -0,0 +1,87 @@ +--- +title: Tutorial (Part VI) +what: explains how to use categories instead of tags +--- + +## Categories + +Most people familiar with "tags" will also know the concept "categories". + +![Tags illustration]($root/images/tutorial6-tags.png) + +In fact, tags are harder to implement because they have to be represented as a +many-to-many relation, and categories are a simple 1-to-many relation. + +![Tags illustration]($root/images/tutorial6-categories.png) + +This is also the reason you can "simulate" categories using tags. In this +tutorial we will adapt the blog to use categories instead of tags. Here is +[a zip file](examples/categoryblog.zip) containing the files used in this +tutorial. + +## Reading Categories + +Tags are located in the `tags` metadata field. Since one post can only belong +in one category, a different approach was chosen here. The category of a post +is determined by the subfolder it is in. Here you see the directory layout for +our posts using categories: + + posts + |-- coding + | |-- 2009-11-05-a-first-post.markdown + | |-- 2009-11-28-a-third-post.markdown + | `-- 2009-12-04-this-blog-aint-dead.markdown + `-- random + |-- 2009-11-10-another-post.markdown + `-- 2009-12-23-almost-christmas.markdown + +Because we find all our posts in different subdirectories, sorting them is a +little harder: we still want them sorted by date, so it boils down to sorting +them by "base name". I hope it does not surprise you Hakyll provides a function +for that: + +~~~~~{.haskell} +postPaths <- liftM (reverse . sortByBaseName) + (getRecursiveContents "posts") +~~~~~ + +Now, we can use the `readCategoryMap` function instead of `readTagMap`, which +has the same signature, but assigns categories based on the folders the posts +are in. + +~~~~~{.haskell} +categoryMap <- readCategoryMap "categoryMap" renderablePosts +~~~~~ + +The rest of the `hakyll.hs` is very similar to the one in the previous +tutorial, except we want to render a category list instead of a tag cloud. + +## Rendering a category list + +Because rendering a category list is quite easy, and it would be hard to +write a "general" function for this, hakyll does not provide such a function - +but it is not hard to write. First, we write an auxiliary function that produces +a list item for one category: + +~~~~~{.haskell} +categoryListItem category posts = + "<li>" ++ link category (categoryToURL category) + ++ " - " ++ show (length posts) ++ " items.</li>" +~~~~~ + +This is nothing more that some string concatenation. The function that applies +this on every element in the `TagMap` is more interesting: + +~~~~~{.haskell} +categoryList :: TagMap -> String +categoryList = uncurry categoryListItem <=< toList +~~~~~ + +This function might seem a little harder to understand, and figuring out how it +works is left as an exercise for the reader[^1]. + +[^1]: Hint: `>>=` is `concatMap` in the list monad. + +We then add this to our index page, and we are done. Feel free to hack around +with the source code. If you still have questions, feel free to ask them at the +[google discussion group](http://groups.google.com/group/hakyll). |