on master help sign in
Merge branch 'master' into dependency-analyzer
commit80596b1f56b7d6f2d4ff64d566ae845b7c7a01f6 authorJasper Van der Jeugt <jaspervdj@gmail.com> dateWed 6 Apr 2011, 14:05 (16 years ago) parentc3dbb0cce444a4
30 files changed+447−285
examples/brochure/hakyll.hs+10−8 examples/feedblog/hakyll.hs+22−26 examples/hakyll/about.markdown+1 examples/hakyll/changelog.markdown+5 examples/hakyll/hakyll.hs+18−16 examples/hakyll/tutorial.markdown+33−22 examples/morepages/hakyll.hs+12−11 examples/simpleblog/hakyll.hs+21−25 examples/tagblog/hakyll.hs+24−28 hakyll.cabal+4−3 src-inotify/Hakyll/Web/Preview/Poll.hs+1−1 src-interval/Hakyll/Web/Preview/Poll.hs+1−1 src/Hakyll.hs+4−2 src/Hakyll/Core/Compiler.hs+3−2 src/Hakyll/Core/Compiler/Internal.hs+1−1 src/Hakyll/Core/Identifier/Pattern.hs+86−42 src/Hakyll/Core/Resource.hs+14 src/Hakyll/Core/ResourceProvider.hs → src/Hakyll/Core/Resource/Provider.hs+27−11 src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs → src/Hakyll/Core/Resource/Provider/File.hs+9−2 src/Hakyll/Core/Routes.hs+8−8 src/Hakyll/Core/Rules.hs+36−17 src/Hakyll/Core/Rules/Internal.hs+16−3 src/Hakyll/Core/Run.hs+4−5 src/Hakyll/Core/Util/File.hs+1−1 src/Hakyll/Core/Writable/CopyFile.hs+1−1 src/Hakyll/Web/CompressCss.hs+1−1 src/Hakyll/Web/Page.hs+1−1 src/Hakyll/Web/Tags.hs+56−32 src/Hakyll/Web/Template.hs+1−1 tests/Hakyll/Core/Identifier/Tests.hs+26−14
examples/brochure/hakyll.hs+10−8
@@ -6,13 +6,15 @@ import Hakyll
66
77main :: IO ()
88main = hakyll $ do
9− route "css/*" idRoute
10− compile "css/*" compressCssCompiler
9+ match "css/*" $ do
10+ route idRoute
11+ compile compressCssCompiler
1112
12− compile "templates/*" templateCompiler
13+ match "templates/*" $ compile templateCompiler
1314
14− forM_ ["about.rst", "index.markdown", "code.lhs"] $ \page -> do
15− route page $ setExtension "html"
16− compile page $ pageCompiler
17− >>> applyTemplateCompiler "templates/default.html"
18− >>> relativizeUrlsCompiler
15+ forM_ ["about.rst", "index.markdown", "code.lhs"] $ \page ->
16+ match page $ do
17+ route $ setExtension "html"
18+ compile $ pageCompiler
19+ >>> applyTemplateCompiler "templates/default.html"
20+ >>> relativizeUrlsCompiler
examples/feedblog/hakyll.hs+22−26
@@ -11,47 +11,43 @@ import Hakyll
1111main :: IO ()
1212main = hakyll $ do
1313 -- Compress CSS
14− route "css/*" idRoute
15− compile "css/*" compressCssCompiler
14+ match "css/*" $ do
15+ route idRoute
16+ compile compressCssCompiler
1617
1718 -- Render posts
18− route "posts/*" $ setExtension ".html"
19− compile "posts/*" $
20− pageCompiler
19+ match "posts/*" $ do
20+ route $ setExtension ".html"
21+ compile $ pageCompiler
2122 >>> applyTemplateCompiler "templates/post.html"
2223 >>> applyTemplateCompiler "templates/default.html"
2324 >>> relativizeUrlsCompiler
2425
2526 -- Render posts list
26− route "posts.html" $ idRoute
27− create "posts.html" $
28− constA mempty
29− >>> arr (setField "title" "All posts")
30− >>> requireAllA "posts/*" addPostList
31− >>> applyTemplateCompiler "templates/posts.html"
32− >>> applyTemplateCompiler "templates/default.html"
33− >>> relativizeUrlsCompiler
27+ match "posts.html" $ route idRoute
28+ create "posts.html" $ constA mempty
29+ >>> arr (setField "title" "All posts")
30+ >>> requireAllA "posts/*" addPostList
31+ >>> applyTemplateCompiler "templates/posts.html"
32+ >>> applyTemplateCompiler "templates/default.html"
33+ >>> relativizeUrlsCompiler
3434
3535 -- Index
36− route "index.html" $ idRoute
37− create "index.html" $
38− constA mempty
39− >>> arr (setField "title" "Home")
40− >>> requireAllA "posts/*" (id *** arr (take 3 . reverse . sortByBaseName) >>> addPostList)
41− >>> applyTemplateCompiler "templates/index.html"
42− >>> applyTemplateCompiler "templates/default.html"
43− >>> relativizeUrlsCompiler
36+ match "index.html" $ route idRoute
37+ create "index.html" $ constA mempty
38+ >>> arr (setField "title" "Home")
39+ >>> requireAllA "posts/*" (id *** arr (take 3 . reverse . sortByBaseName) >>> addPostList)
40+ >>> applyTemplateCompiler "templates/index.html"
41+ >>> applyTemplateCompiler "templates/default.html"
42+ >>> relativizeUrlsCompiler
4443
4544 -- Render RSS feed
46− route "rss.xml" $ idRoute
45+ match "rss.xml" $ route idRoute
4746 create "rss.xml" $
4847 requireAll_ "posts/*" >>> renderRss feedConfiguration
4948
5049 -- Read templates
51− compile "templates/*" templateCompiler
52−
53− -- End
54− return ()
50+ match "templates/*" $ compile templateCompiler
5551
5652-- | Auxiliary compiler: generate a post list from a list of given posts, and
5753-- add it to the current page under @$posts@
examples/hakyll/about.markdown+1
@@ -33,3 +33,4 @@ who still maintains the package. Contributors:
3333- [sargon](http://github.com/sargon)
3434- [Paolo Veronelli](http://github.com/paolino)
3535- [Benedict Eastaugh](http://extralogical.net/)
36+- [Nicolas Wu](http://zenzike.com/)
examples/hakyll/changelog.markdown+5
@@ -2,6 +2,11 @@
22title: Changelog
33---
44
5+## Hakyll 3.1
6+
7+- New `match` function in rules DSL
8+- More expressive `Pattern`s
9+
510## Hakyll 3
611
712- Complete rewrite
examples/hakyll/hakyll.hs+18−16
@@ -6,35 +6,37 @@ import Text.Pandoc
66
77main :: IO ()
88main = hakyll $ do
9− route "css/*" idRoute
10− compile "css/*" compressCssCompiler
9+ match "css/*" $ do
10+ route idRoute
11+ compile compressCssCompiler
1112
1213 -- Static directories
13− forM_ ["images/*", "examples/*", "reference/*"] $ \f -> do
14− route f idRoute
15− compile f copyFileCompiler
14+ forM_ ["images/*", "examples/*", "reference/*"] $ \f -> match f $ do
15+ route idRoute
16+ compile copyFileCompiler
1617
1718 -- Pages
18− forM_ pages $ \p -> do
19− route p $ setExtension "html"
20− compile p $ pageCompiler
19+ forM_ pages $ \p -> match p $ do
20+ route $ setExtension "html"
21+ compile $ pageCompiler
2122 >>> requireA "sidebar.markdown" (setFieldA "sidebar" $ arr pageBody)
2223 >>> applyTemplateCompiler "templates/default.html"
2324 >>> relativizeUrlsCompiler
2425
2526 -- Tutorial
26− route "tutorial.markdown" $ setExtension "html"
27− compile "tutorial.markdown" $ readPageCompiler
28− >>> pageRenderPandocWith defaultHakyllParserState withToc
29− >>> requireA "sidebar.markdown" (setFieldA "sidebar" $ arr pageBody)
30− >>> applyTemplateCompiler "templates/default.html"
31− >>> relativizeUrlsCompiler
27+ match "tutorial.markdown" $ do
28+ route $ setExtension "html"
29+ compile $ readPageCompiler
30+ >>> pageRenderPandocWith defaultHakyllParserState withToc
31+ >>> requireA "sidebar.markdown" (setFieldA "sidebar" $ arr pageBody)
32+ >>> applyTemplateCompiler "templates/default.html"
33+ >>> relativizeUrlsCompiler
3234
3335 -- Sidebar
34− compile "sidebar.markdown" pageCompiler
36+ match "sidebar.markdown" $ compile pageCompiler
3537
3638 -- Templates
37− compile "templates/*" templateCompiler
39+ match "templates/*" $ compile templateCompiler
3840 where
3941 withToc = defaultHakyllWriterOptions
4042 { writerTableOfContents = True
examples/hakyll/tutorial.markdown+33−22
@@ -65,16 +65,18 @@ import Hakyll
6565
6666main :: IO ()
6767main = hakyll $ do
68− route "css/*" idRoute
69− compile "css/*" compressCssCompiler
70−
71− compile "templates/*" templateCompiler
72−
73− forM_ ["about.rst", "index.markdown", "code.lhs"] $ \page -> do
74− route page $ setExtension "html"
75− compile page $ pageCompiler
76− >>> applyTemplateCompiler "templates/default.html"
77− >>> relativizeUrlsCompiler
68+ match "css/*" $ do
69+ route idRoute
70+ compile compressCssCompiler
71+
72+ match "templates/*" $ compile templateCompiler
73+
74+ forM_ ["about.rst", "index.markdown", "code.lhs"] $ \page ->
75+ match page $ do
76+ route $ setExtension "html"
77+ compile $ pageCompiler
78+ >>> applyTemplateCompiler "templates/default.html"
79+ >>> relativizeUrlsCompiler
7880~~~~~
7981
8082This is enough code to create a small brochure site! You can find all code
@@ -111,12 +113,11 @@ main :: IO ()
111113main = hakyll $ do
112114~~~~~
113115
114−The `RulesM` monad is composed of a few functions. A first important one is
115−`route`: this creates a new rule for routing items. This rule is applied to all
116−items it matches -- and matching is done using the `"css/*"` [pattern].
117−`idRoute` simply means that an item will be routed to it's own filename. For
118−example, `css/screen.css` will be routed to `css/screen.css` -- not very
119−exciting.
116+The `RulesM` monad is composed of a few functions. Seldomly, you want to apply a
117+compiler to *all* resources. You want to apply a compiler to certain files
118+instead. That's why the `match` function exists. First, let's handle the CSS of
119+our file. We use a `"css/*"` [Pattern] to match all files in the `css/`
120+directory.
120121
121122Note that a [Pattern] matches an [Identifier], it doesn't match filenames.
122123
@@ -124,7 +125,16 @@ Note that a [Pattern] matches an [Identifier], it doesn't match filenames.
124125[Identifier]: /reference/Hakyll-Core-Identifier.html
125126
126127~~~~~{.haskell}
127−route "css/*" idRoute
128+match "css/*" $ do
129+~~~~~
130+
131+`route` creates a new rule for routing items. This rule is applied to all items
132+that are currently matched -- in this case, `"css/*"`. `idRoute` simply means
133+that an item will be routed to it's own filename. For example, `css/screen.css`
134+will be routed to `css/screen.css` -- not very exciting.
135+
136+~~~~~{.haskell}
137+route idRoute
128138~~~~~
129139
130140Apart from specifying where the items should go (using `route`), we also have to
@@ -135,12 +145,12 @@ good default compilers. The `compressCssCompiler` compiler will simply compress
135145the CSS found in the files.
136146
137147~~~~~{.haskell}
138−compile "css/*" compressCssCompiler
148+compile compressCssCompiler
139149~~~~~
140150
141151Next, we're going to render some pages. We're going to style the results a
142152little, so we're going to need a [Template]. We simply compile a template using
143−the `defaultTemplateRead` compiler, it's good enough in most cases.
153+the `templateCompiler` compiler, it's good enough in most cases.
144154
145155[Template]: /reference/Hakyll-Web-Template.html
146156
@@ -148,7 +158,7 @@ We don't use a route for these templates, after all, we don't want to route them
148158anywhere, we just want to use them to style our pages a little.
149159
150160~~~~~{.haskell}
151−compile "templates/*" templateCompiler
161+match "templates/*" $ compile templateCompiler
152162~~~~~
153163
154164We can conclude that some rules do not *directly* add an output page on our
@@ -164,13 +174,14 @@ manually).
164174
165175~~~~~{.haskell}
166176forM_ ["about.rst", "index.markdown", "code.lhs"] $ \page -> do
177+ match page $ do
167178~~~~~
168179
169180The pages all have different extensions. In our website, we only want to see
170181`.html` files. Hakyll provides a route to do just that:
171182
172183~~~~~{.haskell}
173−route page $ setExtension "html"
184+route setExtension "html"
174185~~~~~
175186
176187The [Rules] reference page has a complete listing of the API used.
@@ -189,7 +200,7 @@ reference page has some more readable information on this subject.
189200[Compiler]: /reference/Hakyll-Core-Compiler.html
190201
191202~~~~~{.haskell}
192−compile page $ pageCompiler
203+compile pageCompiler
193204 >>> applyTemplateCompiler "templates/default.html"
194205 >>> relativizeUrlsCompiler
195206~~~~~
examples/morepages/hakyll.hs+12−11
@@ -9,20 +9,21 @@ import Hakyll
99main :: IO ()
1010main = hakyll $ do
1111 -- Compress CSS
12− route "css/*" idRoute
13− compile "css/*" compressCssCompiler
12+ match "css/*" $ do
13+ route idRoute
14+ compile compressCssCompiler
1415
1516 -- Render static pages
16− forM_ ["about.markdown", "index.markdown", "products.markdown"] $ \p -> do
17− route p $ setExtension ".html"
18− compile p $
19− pageCompiler
20− >>> requireA "footer.markdown" (setFieldA "footer" $ arr pageBody)
21− >>> applyTemplateCompiler "templates/default.html"
22− >>> relativizeUrlsCompiler
17+ forM_ ["about.markdown", "index.markdown", "products.markdown"] $ \p ->
18+ match p $ do
19+ route $ setExtension ".html"
20+ compile $ pageCompiler
21+ >>> requireA "footer.markdown" (setFieldA "footer" $ arr pageBody)
22+ >>> applyTemplateCompiler "templates/default.html"
23+ >>> relativizeUrlsCompiler
2324
2425 -- Compile footer
25− compile "footer.markdown" pageCompiler
26+ match "footer.markdown" $ compile pageCompiler
2627
2728 -- Read templates
28− compile "templates/*" templateCompiler
29+ match "templates/*" $ compile templateCompiler
examples/simpleblog/hakyll.hs+21−25
@@ -11,42 +11,38 @@ import Hakyll
1111main :: IO ()
1212main = hakyll $ do
1313 -- Compress CSS
14− route "css/*" idRoute
15− compile "css/*" compressCssCompiler
14+ match "css/*" $ do
15+ route idRoute
16+ compile compressCssCompiler
1617
1718 -- Render posts
18− route "posts/*" $ setExtension ".html"
19− compile "posts/*" $
20− pageCompiler
19+ match "posts/*" $ do
20+ route $ setExtension ".html"
21+ compile $ pageCompiler
2122 >>> applyTemplateCompiler "templates/post.html"
2223 >>> applyTemplateCompiler "templates/default.html"
2324 >>> relativizeUrlsCompiler
2425
2526 -- Render posts list
26− route "posts.html" $ idRoute
27− create "posts.html" $
28− constA mempty
29− >>> arr (setField "title" "All posts")
30− >>> requireAllA "posts/*" addPostList
31− >>> applyTemplateCompiler "templates/posts.html"
32− >>> applyTemplateCompiler "templates/default.html"
33− >>> relativizeUrlsCompiler
27+ match "posts.html" $ route idRoute
28+ create "posts.html" $ constA mempty
29+ >>> arr (setField "title" "All posts")
30+ >>> requireAllA "posts/*" addPostList
31+ >>> applyTemplateCompiler "templates/posts.html"
32+ >>> applyTemplateCompiler "templates/default.html"
33+ >>> relativizeUrlsCompiler
3434
3535 -- Index
36− route "index.html" idRoute
37− create "index.html" $
38− constA mempty
39− >>> arr (setField "title" "Home")
40− >>> requireAllA "posts/*" (id *** arr (take 3 . reverse . sortByBaseName) >>> addPostList)
41− >>> applyTemplateCompiler "templates/index.html"
42− >>> applyTemplateCompiler "templates/default.html"
43− >>> relativizeUrlsCompiler
36+ match "index.html" $ route idRoute
37+ create "index.html" $ constA mempty
38+ >>> arr (setField "title" "Home")
39+ >>> requireAllA "posts/*" (id *** arr (take 3 . reverse . sortByBaseName) >>> addPostList)
40+ >>> applyTemplateCompiler "templates/index.html"
41+ >>> applyTemplateCompiler "templates/default.html"
42+ >>> relativizeUrlsCompiler
4443
4544 -- Read templates
46− compile "templates/*" templateCompiler
47−
48− -- End
49− return ()
45+ match "templates/*" $ compile templateCompiler
5046
5147-- | Auxiliary compiler: generate a post list from a list of given posts, and
5248-- add it to the current page under @$posts@
examples/tagblog/hakyll.hs+24−28
@@ -12,13 +12,14 @@ import Hakyll
1212main :: IO ()
1313main = hakyll $ do
1414 -- Compress CSS
15− route "css/*" idRoute
16− compile "css/*" compressCssCompiler
15+ match "css/*" $ do
16+ route idRoute
17+ compile compressCssCompiler
1718
1819 -- Render posts
19− route "posts/*" $ setExtension ".html"
20− compile "posts/*" $
21− pageCompiler
20+ match "posts/*" $ do
21+ route $ setExtension ".html"
22+ compile $ pageCompiler
2223 >>> arr (renderDateField "date" "%B %e, %Y" "Date unknown")
2324 >>> renderTagsField "prettytags" (fromCaptureString "tags/*")
2425 >>> applyTemplateCompiler "templates/post.html"
@@ -26,48 +27,43 @@ main = hakyll $ do
2627 >>> relativizeUrlsCompiler
2728
2829 -- Render posts list
29− route "posts.html" $ idRoute
30− create "posts.html" $
31− constA mempty
32− >>> arr (setField "title" "All posts")
33− >>> requireAllA "posts/*" addPostList
34− >>> applyTemplateCompiler "templates/posts.html"
35− >>> applyTemplateCompiler "templates/default.html"
36− >>> relativizeUrlsCompiler
30+ match "posts.html" $ route idRoute
31+ create "posts.html" $ constA mempty
32+ >>> arr (setField "title" "All posts")
33+ >>> requireAllA "posts/*" addPostList
34+ >>> applyTemplateCompiler "templates/posts.html"
35+ >>> applyTemplateCompiler "templates/default.html"
36+ >>> relativizeUrlsCompiler
3737
3838 -- Index
39− route "index.html" $ idRoute
40− create "index.html" $
41− constA mempty
42− >>> arr (setField "title" "Home")
43− >>> requireA "tags" (setFieldA "tagcloud" (renderTagCloud'))
44− >>> requireAllA "posts/*" (id *** arr (take 3 . reverse . sortByBaseName) >>> addPostList)
45− >>> applyTemplateCompiler "templates/index.html"
46− >>> applyTemplateCompiler "templates/default.html"
47− >>> relativizeUrlsCompiler
39+ match "index.html" $ route idRoute
40+ create "index.html" $ constA mempty
41+ >>> arr (setField "title" "Home")
42+ >>> requireA "tags" (setFieldA "tagcloud" (renderTagCloud'))
43+ >>> requireAllA "posts/*" (id *** arr (take 3 . reverse . sortByBaseName) >>> addPostList)
44+ >>> applyTemplateCompiler "templates/index.html"
45+ >>> applyTemplateCompiler "templates/default.html"
46+ >>> relativizeUrlsCompiler
4847
4948 -- Tags
5049 create "tags" $
5150 requireAll "posts/*" (\_ ps -> readTags ps :: Tags String)
5251
5352 -- Add a tag list compiler for every tag
54− route "tags/*" $ setExtension ".html"
53+ match "tags/*" $ route $ setExtension ".html"
5554 metaCompile $ require_ "tags"
5655 >>> arr (M.toList . tagsMap)
5756 >>> arr (map (\(t, p) -> (tagIdentifier t, makeTagList t p)))
5857
5958 -- Render RSS feed
60− route "rss.xml" $ idRoute
59+ match "rss.xml" $ route idRoute
6160 create "rss.xml" $
6261 requireAll_ "posts/*"
6362 >>> mapCompiler (arr $ copyBodyToField "description")
6463 >>> renderRss feedConfiguration
6564
6665 -- Read templates
67− compile "templates/*" templateCompiler
68−
69− -- End
70− return ()
66+ match "templates/*" $ compile templateCompiler
7167 where
7268 renderTagCloud' :: Compiler (Tags String) String
7369 renderTagCloud' = renderTagCloud tagIdentifier 100 120
hakyll.cabal+4−3
@@ -1,5 +1,5 @@
11Name: hakyll
2−Version: 3.0.2.0
2+Version: 3.1.0.0
33
44Synopsis: A simple static site generator library.
55Description: A simple static site generator library, mainly aimed at
@@ -81,7 +81,6 @@ library
8181 Hakyll.Web.RelativizeUrls
8282 Hakyll.Web.Page.Read
8383 Hakyll.Web.Page.Metadata
84− Hakyll.Core.ResourceProvider.FileResourceProvider
8584 Hakyll.Core.Configuration
8685 Hakyll.Core.DependencyAnalyzer
8786 Hakyll.Core.Identifier.Pattern
@@ -89,7 +88,9 @@ library
8988 Hakyll.Core.Util.Arrow
9089 Hakyll.Core.Util.File
9190 Hakyll.Core.Util.String
92− Hakyll.Core.ResourceProvider
91+ Hakyll.Core.Resource
92+ Hakyll.Core.Resource.Provider
93+ Hakyll.Core.Resource.Provider.File
9394 Hakyll.Core.CompiledItem
9495 Hakyll.Core.Compiler
9596 Hakyll.Core.Run
src-inotify/Hakyll/Web/Preview/Poll.hs+1−1
@@ -13,7 +13,7 @@ import Data.List (isPrefixOf)
1313import System.INotify
1414
1515import Hakyll.Core.Configuration
16−import Hakyll.Core.ResourceProvider
16+import Hakyll.Core.Resource
1717import Hakyll.Core.Identifier
1818
1919-- | Calls the given callback when the directory tree changes
src-interval/Hakyll/Web/Preview/Poll.hs+1−1
@@ -15,7 +15,7 @@ import System.Directory (getModificationTime)
1515
1616import Hakyll.Core.Configuration
1717import Hakyll.Core.Identifier
18−import Hakyll.Core.ResourceProvider
18+import Hakyll.Core.Resource
1919
2020-- | A preview thread that periodically recompiles the site.
2121--
src/Hakyll.hs+4−2
@@ -5,7 +5,8 @@ module Hakyll
55 , module Hakyll.Core.Configuration
66 , module Hakyll.Core.Identifier
77 , module Hakyll.Core.Identifier.Pattern
8− , module Hakyll.Core.ResourceProvider
8+ , module Hakyll.Core.Resource
9+ , module Hakyll.Core.Resource.Provider
910 , module Hakyll.Core.Routes
1011 , module Hakyll.Core.Rules
1112 , module Hakyll.Core.UnixFilter
@@ -34,7 +35,8 @@ import Hakyll.Core.Compiler
3435import Hakyll.Core.Configuration
3536import Hakyll.Core.Identifier
3637import Hakyll.Core.Identifier.Pattern
37−import Hakyll.Core.ResourceProvider
38+import Hakyll.Core.Resource
39+import Hakyll.Core.Resource.Provider
3840import Hakyll.Core.Routes
3941import Hakyll.Core.Rules
4042import Hakyll.Core.UnixFilter
src/Hakyll/Core/Compiler.hs+3−2
@@ -124,7 +124,8 @@ import Hakyll.Core.Identifier
124124import Hakyll.Core.Identifier.Pattern
125125import Hakyll.Core.CompiledItem
126126import Hakyll.Core.Writable
127−import Hakyll.Core.ResourceProvider
127+import Hakyll.Core.Resource
128+import Hakyll.Core.Resource.Provider
128129import Hakyll.Core.Compiler.Internal
129130import Hakyll.Core.Store
130131import Hakyll.Core.Rules.Internal
@@ -237,7 +238,7 @@ requireAll_ :: (Binary a, Typeable a, Writable a)
237238 -> Compiler b [a]
238239requireAll_ pattern = fromDependencies (const getDeps) >>> fromJob requireAll_'
239240 where
240− getDeps = matches pattern . map unResource . resourceList
241+ getDeps = filterMatches pattern . map unResource . resourceList
241242 requireAll_' = const $ CompilerM $ do
242243 deps <- getDeps . compilerResourceProvider <$> ask
243244 mapM (unCompilerM . getDependency) deps
src/Hakyll/Core/Compiler/Internal.hs+1−1
@@ -26,7 +26,7 @@ import Control.Category (Category, (.), id)
2626import Control.Arrow (Arrow, ArrowChoice, arr, first, left)
2727
2828import Hakyll.Core.Identifier
29−import Hakyll.Core.ResourceProvider
29+import Hakyll.Core.Resource.Provider
3030import Hakyll.Core.Store
3131import Hakyll.Core.Routes
3232import Hakyll.Core.Logger
src/Hakyll/Core/Identifier/Pattern.hs+86−42
@@ -1,4 +1,12 @@
11-- | Module providing pattern matching and capturing on 'Identifier's.
2+-- 'Pattern's come in two kinds:
3+--
4+-- * Simple glob patterns, like @foo\/*@;
5+--
6+-- * Custom, arbitrary predicates of the type @Identifier -> Bool@.
7+--
8+-- They both have advantages and disadvantages. By default, globs are used,
9+-- unless you construct your 'Pattern' using the 'predicate' function.
210--
311-- A very simple pattern could be, for example, @foo\/bar@. This pattern will
412-- only match the exact @foo\/bar@ identifier.
@@ -20,15 +28,17 @@
2028--
2129-- * @foo\/*.html@ will match all HTML files in the @foo\/@ directory.
2230--
23−-- The 'match' function allows the user to get access to the elements captured
31+-- The 'capture' function allows the user to get access to the elements captured
2432-- by the capture elements in the pattern.
2533--
2634module Hakyll.Core.Identifier.Pattern
2735 ( Pattern
28− , parsePattern
29− , match
30− , doesMatch
36+ , parseGlob
37+ , predicate
38+ , regex
3139 , matches
40+ , filterMatches
41+ , capture
3242 , fromCapture
3343 , fromCaptureString
3444 , fromCaptures
@@ -37,32 +47,39 @@ module Hakyll.Core.Identifier.Pattern
3747import Data.List (isPrefixOf, inits, tails)
3848import Control.Arrow ((&&&), (>>>))
3949import Control.Monad (msum)
40−import Data.Maybe (isJust)
41−import Data.Monoid (mempty, mappend)
50+import Data.Maybe (isJust, fromMaybe)
51+import Data.Monoid (Monoid, mempty, mappend)
4252
4353import GHC.Exts (IsString, fromString)
54+import Text.Regex.PCRE ((=~~))
4455
4556import Hakyll.Core.Identifier
4657
4758-- | One base element of a pattern
4859--
49−data PatternComponent = Capture
50− | CaptureMany
51− | Literal String
52− deriving (Eq, Show)
60+data GlobComponent = Capture
61+ | CaptureMany
62+ | Literal String
63+ deriving (Eq, Show)
5364
5465-- | Type that allows matching on identifiers
5566--
56−newtype Pattern = Pattern {unPattern :: [PatternComponent]}
57− deriving (Eq, Show)
67+data Pattern = Glob [GlobComponent]
68+ | Predicate (Identifier -> Bool)
5869
5970instance IsString Pattern where
60− fromString = parsePattern
71+ fromString = parseGlob
72+
73+instance Monoid Pattern where
74+ mempty = Predicate (const True)
75+ g@(Glob _) `mappend` x = Predicate (matches g) `mappend` x
76+ x `mappend` g@(Glob _) = x `mappend` Predicate (matches g)
77+ Predicate f `mappend` Predicate g = Predicate $ \i -> f i && g i
6178
6279-- | Parse a pattern from a string
6380--
64−parsePattern :: String -> Pattern
65−parsePattern = Pattern . parse' -- undefined -- Pattern . map toPattern . unIdentifier . parseIdentifier
81+parseGlob :: String -> Pattern
82+parseGlob = Glob . parse'
6683 where
6784 parse' str =
6885 let (chunk, rest) = break (`elem` "\\*") str
@@ -72,20 +89,34 @@ parsePattern = Pattern . parse' -- undefined -- Pattern . map toPattern . unIden
7289 ('*' : xs) -> Literal chunk : Capture : parse' xs
7390 xs -> Literal chunk : Literal xs : []
7491
75−-- | Match an identifier against a pattern, generating a list of captures
92+-- | Create a 'Pattern' from an arbitrary predicate
93+--
94+-- Example:
95+--
96+-- > predicate (\i -> matches "foo/*" i && not (matches "foo/bar" i))
97+--
98+predicate :: (Identifier -> Bool) -> Pattern
99+predicate = Predicate
100+
101+-- | Create a 'Pattern' from a regex
102+--
103+-- Example:
104+--
105+-- > regex "^foo/[^x]*$
76106--
77−match :: Pattern -> Identifier -> Maybe [Identifier]
78−match p (Identifier i) = fmap (map Identifier) $ match' (unPattern p) i
107+regex :: String -> Pattern
108+regex str = predicate $ fromMaybe False . (=~~ str) . toFilePath
79109
80110-- | Check if an identifier matches a pattern
81111--
82−doesMatch :: Pattern -> Identifier -> Bool
83−doesMatch p = isJust . match p
112+matches :: Pattern -> Identifier -> Bool
113+matches (Glob p) = isJust . capture (Glob p)
114+matches (Predicate p) = (p $)
84115
85116-- | Given a list of identifiers, retain only those who match the given pattern
86117--
87−matches :: Pattern -> [Identifier] -> [Identifier]
88−matches p = filter (doesMatch p)
118+filterMatches :: Pattern -> [Identifier] -> [Identifier]
119+filterMatches = filter . matches
89120
90121-- | Split a list at every possible point, generate a list of (init, tail)
91122-- cases. The result is sorted with inits decreasing in length.
@@ -93,30 +124,35 @@ matches p = filter (doesMatch p)
93124splits :: [a] -> [([a], [a])]
94125splits = inits &&& tails >>> uncurry zip >>> reverse
95126
96−-- | Internal verion of 'match'
127+-- | Match a glob against a pattern, generating a list of captures
97128--
98−match' :: [PatternComponent] -> String -> Maybe [String]
99−match' [] [] = Just [] -- An empty match
100−match' [] _ = Nothing -- No match
101−-- match' _ [] = Nothing -- No match
102−match' (Literal l : ms) str
129+capture :: Pattern -> Identifier -> Maybe [Identifier]
130+capture (Glob p) (Identifier i) = fmap (map Identifier) $ capture' p i
131+capture (Predicate _) _ = Nothing
132+
133+-- | Internal verion of 'capture'
134+--
135+capture' :: [GlobComponent] -> String -> Maybe [String]
136+capture' [] [] = Just [] -- An empty match
137+capture' [] _ = Nothing -- No match
138+capture' (Literal l : ms) str
103139 -- Match the literal against the string
104− | l `isPrefixOf` str = match' ms $ drop (length l) str
140+ | l `isPrefixOf` str = capture' ms $ drop (length l) str
105141 | otherwise = Nothing
106−match' (Capture : ms) str =
142+capture' (Capture : ms) str =
107143 -- Match until the next /
108144 let (chunk, rest) = break (== '/') str
109− in msum $ [ fmap (i :) (match' ms (t ++ rest)) | (i, t) <- splits chunk ]
110−match' (CaptureMany : ms) str =
145+ in msum $ [ fmap (i :) (capture' ms (t ++ rest)) | (i, t) <- splits chunk ]
146+capture' (CaptureMany : ms) str =
111147 -- Match everything
112− msum $ [ fmap (i :) (match' ms t) | (i, t) <- splits str ]
148+ msum $ [ fmap (i :) (capture' ms t) | (i, t) <- splits str ]
113149
114150-- | Create an identifier from a pattern by filling in the captures with a given
115151-- string
116152--
117153-- Example:
118154--
119−-- > fromCapture (parsePattern "tags/*") (parseIdentifier "foo")
155+-- > fromCapture (parseGlob "tags/*") (parseIdentifier "foo")
120156--
121157-- Result:
122158--
@@ -128,7 +164,7 @@ fromCapture pattern = fromCaptures pattern . repeat
128164-- | Simplified version of 'fromCapture' which takes a 'String' instead of an
129165-- 'Identifier'
130166--
131−-- > fromCaptureString (parsePattern "tags/*") "foo"
167+-- > fromCaptureString (parseGlob "tags/*") "foo"
132168--
133169-- Result:
134170--
@@ -141,11 +177,19 @@ fromCaptureString pattern = fromCapture pattern . parseIdentifier
141177-- given list of strings
142178--
143179fromCaptures :: Pattern -> [Identifier] -> Identifier
144−fromCaptures (Pattern []) _ = mempty
145−fromCaptures (Pattern (m : ms)) [] = case m of
146− Literal l -> Identifier l `mappend` fromCaptures (Pattern ms) []
147− _ -> error $ "Hakyll.Core.Identifier.Pattern.fromCaptures: "
180+fromCaptures (Glob p) = fromCaptures' p
181+fromCaptures (Predicate _) = error $
182+ "Hakyll.Core.Identifier.Pattern.fromCaptures: fromCaptures called on a " ++
183+ "predicate instead of a glob"
184+
185+-- | Internally used version of 'fromCaptures'
186+--
187+fromCaptures' :: [GlobComponent] -> [Identifier] -> Identifier
188+fromCaptures' [] _ = mempty
189+fromCaptures' (m : ms) [] = case m of
190+ Literal l -> Identifier l `mappend` fromCaptures' ms []
191+ _ -> error $ "Hakyll.Core.Identifier.Pattern.fromCaptures': "
148192 ++ "identifier list exhausted"
149−fromCaptures (Pattern (m : ms)) ids@(i : is) = case m of
150− Literal l -> Identifier l `mappend` fromCaptures (Pattern ms) ids
151− _ -> i `mappend` fromCaptures (Pattern ms) is
193+fromCaptures' (m : ms) ids@(i : is) = case m of
194+ Literal l -> Identifier l `mappend` fromCaptures' ms ids
195+ _ -> i `mappend` fromCaptures' ms is
src/Hakyll/Core/Resource.hsnew file+14
@@ -0,0 +1,14 @@
1+-- | Module exporting the simple 'Resource' type
2+--
3+module Hakyll.Core.Resource
4+ ( Resource (..)
5+ ) where
6+
7+import Hakyll.Core.Identifier
8+
9+-- | A resource
10+--
11+-- Invariant: the resource specified by the given identifier must exist
12+--
13+newtype Resource = Resource {unResource :: Identifier}
14+ deriving (Eq, Show, Ord)
src/Hakyll/Core/ResourceProvider.hs → src/Hakyll/Core/Resource/Provider.hsrenamed+27−11
@@ -10,16 +10,18 @@
1010-- Therefore, it is not recommended to read files directly -- you should use the
1111-- provided 'Resource' methods.
1212--
13−module Hakyll.Core.ResourceProvider
14− ( Resource (..)
15− , ResourceProvider (..)
13+module Hakyll.Core.Resource.Provider
14+ ( ResourceProvider (..)
1615 , resourceExists
1716 , resourceDigest
1817 , resourceModified
1918 ) where
2019
20+import Control.Concurrent (MVar, readMVar, modifyMVar_)
2121import Control.Monad ((<=<))
2222import Data.Word (Word8)
23+import Data.Map (Map)
24+import qualified Data.Map as M
2325
2426import qualified Data.ByteString.Lazy as LB
2527import OpenSSL.Digest.ByteString.Lazy (digest)
@@ -27,13 +29,7 @@ import OpenSSL.Digest (MessageDigest (MD5))
2729
2830import Hakyll.Core.Identifier
2931import Hakyll.Core.Store
30−
31−-- | A resource
32−--
33−-- Invariant: the resource specified by the given identifier must exist
34−--
35−newtype Resource = Resource {unResource :: Identifier}
36− deriving (Eq, Show, Ord)
32+import Hakyll.Core.Resource
3733
3834-- | A value responsible for retrieving and listing resources
3935--
@@ -44,6 +40,8 @@ data ResourceProvider = ResourceProvider
4440 resourceString :: Resource -> IO String
4541 , -- | Retrieve a certain resource as lazy bytestring
4642 resourceLazyByteString :: Resource -> IO LB.ByteString
43+ , -- | Cache keeping track of modified items
44+ resourceModifiedCache :: MVar (Map Resource Bool)
4745 }
4846
4947-- | Check if a given identifier has a resource
@@ -60,6 +58,24 @@ resourceDigest provider = digest MD5 <=< resourceLazyByteString provider
6058--
6159resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool
6260resourceModified provider resource store = do
61+ cache <- readMVar mvar
62+ case M.lookup resource cache of
63+ -- Already in the cache
64+ Just m -> return m
65+ -- Not yet in the cache, check digests (if it exists)
66+ Nothing -> do
67+ m <- if resourceExists provider (unResource resource)
68+ then digestModified provider resource store
69+ else return False
70+ modifyMVar_ mvar (return . M.insert resource m)
71+ return m
72+ where
73+ mvar = resourceModifiedCache provider
74+
75+-- | Check if a resource digest was modified
76+--
77+digestModified :: ResourceProvider -> Resource -> Store -> IO Bool
78+digestModified provider resource store = do
6379 -- Get the latest seen digest from the store
6480 lastDigest <- storeGet store itemName $ unResource resource
6581 -- Calculate the digest for the resource
@@ -72,4 +88,4 @@ resourceModified provider resource store = do
7288 else do storeSet store itemName (unResource resource) newDigest
7389 return True
7490 where
75− itemName = "Hakyll.Core.ResourceProvider.resourceModified"
91+ itemName = "Hakyll.Core.ResourceProvider.digestModified"
src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs → src/Hakyll/Core/Resource/Provider/File.hsrenamed+9−2
@@ -1,14 +1,17 @@
11-- | A concrete 'ResourceProvider' that gets it's resources from the filesystem
22--
3−module Hakyll.Core.ResourceProvider.FileResourceProvider
3+module Hakyll.Core.Resource.Provider.File
44 ( fileResourceProvider
55 ) where
66
77import Control.Applicative ((<$>))
8+import Control.Concurrent (newMVar)
9+import qualified Data.Map as M
810
911import qualified Data.ByteString.Lazy as LB
1012
11−import Hakyll.Core.ResourceProvider
13+import Hakyll.Core.Resource
14+import Hakyll.Core.Resource.Provider
1215import Hakyll.Core.Identifier
1316import Hakyll.Core.Util.File
1417import Hakyll.Core.Configuration
@@ -21,9 +24,13 @@ fileResourceProvider configuration = do
2124 list <- map parseIdentifier . filter (not . ignoreFile configuration) <$>
2225 getRecursiveContents False "."
2326
27+ -- MVar for the cache
28+ mvar <- newMVar M.empty
29+
2430 -- Construct a resource provider
2531 return ResourceProvider
2632 { resourceList = map Resource list
2733 , resourceString = readFile . toFilePath . unResource
2834 , resourceLazyByteString = LB.readFile . toFilePath . unResource
35+ , resourceModifiedCache = mvar
2936 }
src/Hakyll/Core/Routes.hs+8−8
@@ -30,7 +30,7 @@ module Hakyll.Core.Routes
3030 , runRoutes
3131 , idRoute
3232 , setExtension
33− , ifMatch
33+ , matchRoute
3434 , customRoute
3535 , gsubRoute
3636 , composeRoutes
@@ -85,15 +85,15 @@ setExtension :: String -> Routes
8585setExtension extension = Routes $ fmap (`replaceExtension` extension)
8686 . unRoutes idRoute
8787
88−-- | Modify a route: apply the route if the identifier matches the given
89−-- pattern, fail otherwise.
88+-- | Apply the route if the identifier matches the given pattern, fail
89+-- otherwise
9090--
91−ifMatch :: Pattern -> Routes -> Routes
92−ifMatch pattern (Routes route) = Routes $ \id' ->
93− if doesMatch pattern id' then route id'
94− else Nothing
91+matchRoute :: Pattern -> Routes -> Routes
92+matchRoute pattern (Routes route) = Routes $ \id' ->
93+ if matches pattern id' then route id' else Nothing
9594
96−-- | Create a custom route. This should almost always be used with 'ifMatch'.
95+-- | Create a custom route. This should almost always be used with
96+-- 'matchRoute'
9797--
9898customRoute :: (Identifier -> FilePath) -> Routes
9999customRoute f = Routes $ Just . f
src/Hakyll/Core/Rules.hs+36−17
@@ -7,13 +7,18 @@
77-- A typical usage example would be:
88--
99-- > main = hakyll $ do
10−-- > route "posts/*" (setExtension "html")
11−-- > compile "posts/*" someCompiler
10+-- > match "posts/*" $ do
11+-- > route (setExtension "html")
12+-- > compile someCompiler
13+-- > match "css/*" $ do
14+-- > route idRoute
15+-- > compile compressCssCompiler
1216--
1317{-# LANGUAGE GeneralizedNewtypeDeriving, OverloadedStrings #-}
1418module Hakyll.Core.Rules
1519 ( RulesM
1620 , Rules
21+ , match
1722 , compile
1823 , create
1924 , route
@@ -23,16 +28,17 @@ module Hakyll.Core.Rules
2328
2429import Control.Applicative ((<$>))
2530import Control.Monad.Writer (tell)
26−import Control.Monad.Reader (ask)
31+import Control.Monad.Reader (ask, local)
2732import Control.Arrow (second, (>>>), arr, (>>^))
2833import Control.Monad.State (get, put)
29−import Data.Monoid (mempty)
34+import Data.Monoid (mempty, mappend)
3035import qualified Data.Set as S
3136
3237import Data.Typeable (Typeable)
3338import Data.Binary (Binary)
3439
35−import Hakyll.Core.ResourceProvider
40+import Hakyll.Core.Resource
41+import Hakyll.Core.Resource.Provider
3642import Hakyll.Core.Identifier
3743import Hakyll.Core.Identifier.Pattern
3844import Hakyll.Core.Compiler.Internal
@@ -63,21 +69,32 @@ tellResources :: [Resource]
6369 -> Rules
6470tellResources resources = RulesM $ tell $ RuleSet mempty mempty $ S.fromList resources
6571
72+-- | Only compile/route items satisfying the given predicate
73+--
74+match :: Pattern -> Rules -> Rules
75+match pattern = RulesM . local addPredicate . unRulesM
76+ where
77+ addPredicate env = env
78+ { rulesPattern = rulesPattern env `mappend` pattern
79+ }
80+
6681-- | Add a compilation rule to the rules.
6782--
68−-- This instructs all resources matching the given pattern to be compiled using
69−-- the given compiler. When no resources match the given pattern, nothing will
70−-- happen. In this case, you might want to have a look at 'create'.
83+-- This instructs all resources to be compiled using the given compiler. When
84+-- no resources match the current selection, nothing will happen. In this case,
85+-- you might want to have a look at 'create'.
7186--
7287compile :: (Binary a, Typeable a, Writable a)
73− => Pattern -> Compiler Resource a -> Rules
74−compile pattern compiler = RulesM $ do
75− identifiers <- matches pattern . map unResource . resourceList <$> ask
88+ => Compiler Resource a -> Rules
89+compile compiler = RulesM $ do
90+ pattern <- rulesPattern <$> ask
91+ provider <- rulesResourceProvider <$> ask
92+ let ids = filterMatches pattern $ map unResource $ resourceList provider
7693 unRulesM $ do
77− tellCompilers $ flip map identifiers $ \identifier ->
94+ tellCompilers $ flip map ids $ \identifier ->
7895 (identifier, constA (Resource identifier) >>> compiler)
79− tellResources $ map Resource identifiers
80−
96+ tellResources $ map Resource ids
97+
8198-- | Add a compilation rule
8299--
83100-- This sets a compiler for the given identifier. No resource is needed, since
@@ -91,10 +108,12 @@ create identifier compiler = tellCompilers [(identifier, compiler)]
91108
92109-- | Add a route.
93110--
94−-- This adds a route for all items matching the given pattern.
111+-- This adds a route for all items matching the current pattern.
95112--
96−route :: Pattern -> Routes -> Rules
97−route pattern route' = tellRoute $ ifMatch pattern route'
113+route :: Routes -> Rules
114+route route' = RulesM $ do
115+ pattern <- rulesPattern <$> ask
116+ unRulesM $ tellRoute $ matchRoute pattern route'
98117
99118-- | Apart from regular compilers, one is also able to specify metacompilers.
100119-- Metacompilers are a special class of compilers: they are compilers which
src/Hakyll/Core/Rules/Internal.hs+16−3
@@ -5,6 +5,7 @@ module Hakyll.Core.Rules.Internal
55 ( CompileRule (..)
66 , RuleSet (..)
77 , RuleState (..)
8+ , RuleEnvironment (..)
89 , RulesM (..)
910 , Rules
1011 , runRules
@@ -17,8 +18,10 @@ import Control.Monad.State (State, evalState)
1718import Data.Monoid (Monoid, mempty, mappend)
1819import Data.Set (Set)
1920
20−import Hakyll.Core.ResourceProvider
21+import Hakyll.Core.Resource
22+import Hakyll.Core.Resource.Provider
2123import Hakyll.Core.Identifier
24+import Hakyll.Core.Identifier.Pattern
2225import Hakyll.Core.Compiler.Internal
2326import Hakyll.Core.Routes
2427import Hakyll.Core.CompiledItem
@@ -55,10 +58,17 @@ data RuleState = RuleState
5558 { rulesMetaCompilerIndex :: Int
5659 } deriving (Show)
5760
61+-- | Rule environment
62+--
63+data RuleEnvironment = RuleEnvironment
64+ { rulesResourceProvider :: ResourceProvider
65+ , rulesPattern :: Pattern
66+ }
67+
5868-- | The monad used to compose rules
5969--
6070newtype RulesM a = RulesM
61− { unRulesM :: ReaderT ResourceProvider (WriterT RuleSet (State RuleState)) a
71+ { unRulesM :: ReaderT RuleEnvironment (WriterT RuleSet (State RuleState)) a
6272 } deriving (Monad, Functor, Applicative)
6373
6474-- | Simplification of the RulesM type; usually, it will not return any
@@ -70,6 +80,9 @@ type Rules = RulesM ()
7080--
7181runRules :: Rules -> ResourceProvider -> RuleSet
7282runRules rules provider =
73− evalState (execWriterT $ runReaderT (unRulesM rules) provider) state
83+ evalState (execWriterT $ runReaderT (unRulesM rules) env) state
7484 where
7585 state = RuleState {rulesMetaCompilerIndex = 0}
86+ env = RuleEnvironment { rulesResourceProvider = provider
87+ , rulesPattern = mempty
88+ }
src/Hakyll/Core/Run.hs+4−5
@@ -25,8 +25,9 @@ import Hakyll.Core.Identifier
2525import Hakyll.Core.Util.File
2626import Hakyll.Core.Compiler
2727import Hakyll.Core.Compiler.Internal
28−import Hakyll.Core.ResourceProvider
29−import Hakyll.Core.ResourceProvider.FileResourceProvider
28+import Hakyll.Core.Resource
29+import Hakyll.Core.Resource.Provider
30+import Hakyll.Core.Resource.Provider.File
3031import Hakyll.Core.Rules.Internal
3132import Hakyll.Core.DirectedGraph
3233import Hakyll.Core.DirectedGraph.Dot
@@ -108,9 +109,7 @@ modified :: ResourceProvider -- ^ Resource provider
108109 -> [Identifier] -- ^ Identifiers to check
109110 -> IO (Set Identifier) -- ^ Modified resources
110111modified provider store ids = fmap S.fromList $ flip filterM ids $ \id' ->
111− if resourceExists provider id'
112− then resourceModified provider (Resource id') store
113− else return False
112+ resourceModified provider (Resource id') store
114113
115114-- | Add a number of compilers and continue using these compilers
116115--
src/Hakyll/Core/Util/File.hs+1−1
@@ -47,7 +47,7 @@ getRecursiveContents includeDirs topdir = do
4747 return $ if includeDirs then topdir : concat paths
4848 else concat paths
4949 where
50− isProper = not . (== ".") . take 1
50+ isProper = (`notElem` [".", ".."])
5151
5252-- | Check if a timestamp is obsolete compared to the timestamps of a number of
5353-- files. When they are no files, it is never obsolete.
src/Hakyll/Core/Writable/CopyFile.hs+1−1
@@ -12,7 +12,7 @@ import System.Directory (copyFile)
1212import Data.Typeable (Typeable)
1313import Data.Binary (Binary)
1414
15−import Hakyll.Core.ResourceProvider
15+import Hakyll.Core.Resource
1616import Hakyll.Core.Writable
1717import Hakyll.Core.Compiler
1818import Hakyll.Core.Identifier
src/Hakyll/Web/CompressCss.hs+1−1
@@ -11,7 +11,7 @@ import Data.List (isPrefixOf)
1111import Control.Arrow ((>>^))
1212
1313import Hakyll.Core.Compiler
14−import Hakyll.Core.ResourceProvider
14+import Hakyll.Core.Resource
1515import Hakyll.Core.Util.String
1616
1717-- | Compiler form of 'compressCss'
src/Hakyll/Web/Page.hs+1−1
@@ -67,7 +67,7 @@ import Data.Ord (comparing)
6767
6868import Hakyll.Core.Identifier
6969import Hakyll.Core.Compiler
70−import Hakyll.Core.ResourceProvider
70+import Hakyll.Core.Resource
7171import Hakyll.Web.Page.Internal
7272import Hakyll.Web.Page.Read
7373import Hakyll.Web.Page.Metadata
src/Hakyll/Web/Tags.hs+56−32
@@ -32,6 +32,7 @@ module Hakyll.Web.Tags
3232 , readTags
3333 , readCategory
3434 , renderTagCloud
35+ , renderTagList
3536 , renderTagsField
3637 , renderCategoryField
3738 ) where
@@ -39,9 +40,8 @@ module Hakyll.Web.Tags
3940import Prelude hiding (id)
4041import Control.Category (id)
4142import Control.Applicative ((<$>))
42−import Data.Map (Map)
4343import qualified Data.Map as M
44−import Data.List (intersperse)
44+import Data.List (intersperse, intercalate)
4545import Control.Arrow (arr, (&&&), (>>>), (***), (<<^), returnA)
4646import Data.Maybe (catMaybes, fromMaybe)
4747import Data.Monoid (mconcat)
@@ -64,7 +64,7 @@ import Hakyll.Core.Util.String
6464-- | Data about tags
6565--
6666data Tags a = Tags
67− { tagsMap :: Map String [Page a]
67+ { tagsMap :: [(String, [Page a])]
6868 } deriving (Show, Typeable)
6969
7070instance Binary a => Binary (Tags a) where
@@ -90,7 +90,8 @@ readTagsWith :: (Page a -> [String]) -- ^ Function extracting tags from a page
9090 -> [Page a] -- ^ Pages
9191 -> Tags a -- ^ Resulting tags
9292readTagsWith f pages = Tags
93− { tagsMap = foldl (M.unionWith (++)) M.empty (map readTagsWith' pages)
93+ { tagsMap = M.toList $
94+ foldl (M.unionWith (++)) M.empty (map readTagsWith' pages)
9495 }
9596 where
9697 -- Create a tag map for one page
@@ -108,41 +109,64 @@ readTags = readTagsWith getTags
108109readCategory :: [Page a] -> Tags a
109110readCategory = readTagsWith getCategory
110111
111−-- | Render a tag cloud in HTML
112+-- | Render tags in HTML
112113--
113−renderTagCloud :: (String -> Identifier) -- ^ Produce a link for a tag
114− -> Double -- ^ Smallest font size, in percent
115− -> Double -- ^ Biggest font size, in percent
116− -> Compiler (Tags a) String -- ^ Tag cloud renderer
117−renderTagCloud makeUrl minSize maxSize = proc (Tags tags) -> do
114+renderTags :: (String -> Identifier)
115+ -- ^ Produce a link
116+ -> (String -> String -> Int -> Int -> Int -> String)
117+ -- ^ Produce a tag item: tag, url, count, min count, max count
118+ -> ([String] -> String)
119+ -- ^ Join items
120+ -> Compiler (Tags a) String
121+ -- ^ Tag cloud renderer
122+renderTags makeUrl makeItem concatItems = proc (Tags tags) -> do
118123 -- In tags' we create a list: [((tag, route), count)]
119124 tags' <- mapCompiler ((id &&& (getRouteFor <<^ makeUrl)) *** arr length)
120− -< M.toList tags
125+ -< tags
121126
122127 let -- Absolute frequencies of the pages
123128 freqs = map snd tags'
124129
125− -- Find out the relative count of a tag: on a scale from 0 to 1
126− relative count = (fromIntegral count - min') / (1 + max' - min')
127−
128− -- Show the relative size of one 'count' in percent
129− size count =
130− let size' = floor $ minSize + relative count * (maxSize - minSize)
131− in show (size' :: Int) ++ "%"
132−
133− -- The minimum and maximum count found, as doubles
130+ -- The minimum and maximum count found
134131 (min', max')
135132 | null freqs = (0, 1)
136− | otherwise = (minimum &&& maximum) $ map fromIntegral freqs
133+ | otherwise = (minimum &&& maximum) freqs
137134
138135 -- Create a link for one item
139− makeLink ((tag, url), count) =
140− H.a ! A.style (toValue $ "font-size: " ++ size count)
141− ! A.href (toValue $ toUrl $ fromMaybe "/" url)
142− $ toHtml tag
136+ makeItem' ((tag, url), count) =
137+ makeItem tag (toUrl $ fromMaybe "/" url) count min' max'
143138
144139 -- Render and return the HTML
145− returnA -< renderHtml $ mconcat $ intersperse " " $ map makeLink tags'
140+ returnA -< concatItems $ map makeItem' tags'
141+
142+-- | Render a tag cloud in HTML
143+--
144+renderTagCloud :: (String -> Identifier) -- ^ Produce a link for a tag
145+ -> Double -- ^ Smallest font size, in percent
146+ -> Double -- ^ Biggest font size, in percent
147+ -> Compiler (Tags a) String -- ^ Tag cloud renderer
148+renderTagCloud makeUrl minSize maxSize =
149+ renderTags makeUrl makeLink (intercalate " ")
150+ where
151+ makeLink tag url count min' max' = renderHtml $
152+ H.a ! A.style (toValue $ "font-size: " ++ size count min' max')
153+ ! A.href (toValue url)
154+ $ toHtml tag
155+
156+ -- Show the relative size of one 'count' in percent
157+ size count min' max' =
158+ let diff = 1 + fromIntegral max' - fromIntegral min'
159+ relative = (fromIntegral count - fromIntegral min') / diff
160+ size' = floor $ minSize + relative * (maxSize - minSize)
161+ in show (size' :: Int) ++ "%"
162+
163+-- | Render a simple tag list in HTML, with the tag count next to the item
164+--
165+renderTagList :: (String -> Identifier) -> Compiler (Tags a) (String)
166+renderTagList makeUrl = renderTags makeUrl makeLink (intercalate ", ")
167+ where
168+ makeLink tag url count _ _ = renderHtml $
169+ H.a ! A.href (toValue url) $ toHtml (tag ++ "(" ++ show count ++ ")")
146170
147171-- | Render tags with links
148172--
@@ -151,14 +175,14 @@ renderTagsFieldWith :: (Page a -> [String]) -- ^ Function to get the tags
151175 -> (String -> Identifier) -- ^ Create a link for a tag
152176 -> Compiler (Page a) (Page a) -- ^ Resulting compiler
153177renderTagsFieldWith tags destination makeUrl =
154− id &&& arr tags >>> setFieldA destination renderTags
178+ id &&& arr tags >>> setFieldA destination renderTags'
155179 where
156180 -- Compiler creating a comma-separated HTML string for a list of tags
157− renderTags :: Compiler [String] String
158− renderTags = arr (map $ id &&& makeUrl)
159− >>> mapCompiler (id *** getRouteFor)
160− >>> arr (map $ uncurry renderLink)
161− >>> arr (renderHtml . mconcat . intersperse ", " . catMaybes)
181+ renderTags' :: Compiler [String] String
182+ renderTags' = arr (map $ id &&& makeUrl)
183+ >>> mapCompiler (id *** getRouteFor)
184+ >>> arr (map $ uncurry renderLink)
185+ >>> arr (renderHtml . mconcat . intersperse ", " . catMaybes)
162186
163187 -- Render one tag link
164188 renderLink _ Nothing = Nothing
src/Hakyll/Web/Template.hs+1−1
@@ -59,7 +59,7 @@ import Text.Hamlet (HamletSettings, defaultHamletSettings)
5959
6060import Hakyll.Core.Compiler
6161import Hakyll.Core.Identifier
62−import Hakyll.Core.ResourceProvider
62+import Hakyll.Core.Resource
6363import Hakyll.Web.Template.Internal
6464import Hakyll.Web.Template.Read
6565import Hakyll.Web.Page.Internal
tests/Hakyll/Core/Identifier/Tests.hs+26−14
@@ -10,18 +10,30 @@ import Hakyll.Core.Identifier.Pattern
1010import TestSuite.Util
1111
1212tests :: [Test]
13−tests = fromAssertions "match"
14− [ Just ["bar"] @=? match "foo/**" "foo/bar"
15− , Just ["foo/bar"] @=? match "**" "foo/bar"
16− , Nothing @=? match "*" "foo/bar"
17− , Just [] @=? match "foo" "foo"
18− , Just ["foo"] @=? match "*/bar" "foo/bar"
19− , Just ["foo/bar"] @=? match "**/qux" "foo/bar/qux"
20− , Just ["foo/bar", "qux"] @=? match "**/*" "foo/bar/qux"
21− , Just ["foo", "bar/qux"] @=? match "*/**" "foo/bar/qux"
22− , Just ["foo"] @=? match "*.html" "foo.html"
23− , Nothing @=? match "*.html" "foo/bar.html"
24− , Just ["foo/bar"] @=? match "**.html" "foo/bar.html"
25− , Just ["foo/bar", "wut"] @=? match "**/qux/*" "foo/bar/qux/wut"
26− , Just ["lol", "fun/large"] @=? match "*cat/**.jpg" "lolcat/fun/large.jpg"
13+tests = concat
14+ [ captureTests
15+ , regexTests
16+ ]
17+
18+captureTests :: [Test]
19+captureTests = fromAssertions "capture"
20+ [ Just ["bar"] @=? capture "foo/**" "foo/bar"
21+ , Just ["foo/bar"] @=? capture "**" "foo/bar"
22+ , Nothing @=? capture "*" "foo/bar"
23+ , Just [] @=? capture "foo" "foo"
24+ , Just ["foo"] @=? capture "*/bar" "foo/bar"
25+ , Just ["foo/bar"] @=? capture "**/qux" "foo/bar/qux"
26+ , Just ["foo/bar", "qux"] @=? capture "**/*" "foo/bar/qux"
27+ , Just ["foo", "bar/qux"] @=? capture "*/**" "foo/bar/qux"
28+ , Just ["foo"] @=? capture "*.html" "foo.html"
29+ , Nothing @=? capture "*.html" "foo/bar.html"
30+ , Just ["foo/bar"] @=? capture "**.html" "foo/bar.html"
31+ , Just ["foo/bar", "wut"] @=? capture "**/qux/*" "foo/bar/qux/wut"
32+ , Just ["lol", "fun/large"] @=? capture "*cat/**.jpg" "lolcat/fun/large.jpg"
33+ ]
34+
35+regexTests :: [Test]
36+regexTests = fromAssertions "regex"
37+ [ True @=? matches (regex "^foo/[^x]*$") "foo/bar"
38+ , False @=? matches (regex "^foo/[^x]*$") "foo/barx"
2739 ]