From 5cc710d548a94c4b56d3eed0276e269f6be0026a Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Mon, 4 Apr 2011 12:16:38 +0200 Subject: Don't ignore dotfiles in getRecursiveContents --- src/Hakyll/Core/Util/File.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Util/File.hs b/src/Hakyll/Core/Util/File.hs index 9babc8b..24814ae 100644 --- a/src/Hakyll/Core/Util/File.hs +++ b/src/Hakyll/Core/Util/File.hs @@ -47,7 +47,7 @@ getRecursiveContents includeDirs topdir = do return $ if includeDirs then topdir : concat paths else concat paths where - isProper = not . (== ".") . take 1 + isProper = (`notElem` [".", ".."]) -- | Check if a timestamp is obsolete compared to the timestamps of a number of -- files. When they are no files, it is never obsolete. -- cgit v1.2.3 From f6c65aadd7e07bad9deda2d1d9ecc7ca5610e429 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Mon, 4 Apr 2011 20:49:22 +0200 Subject: Works-for-me implementation of nested rules --- src/Hakyll/Core/Routes.hs | 17 ++++++++-------- src/Hakyll/Core/Rules.hs | 42 +++++++++++++++++++++++++++++---------- src/Hakyll/Core/Rules/Internal.hs | 15 ++++++++++++-- 3 files changed, 52 insertions(+), 22 deletions(-) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Routes.hs b/src/Hakyll/Core/Routes.hs index fcab28d..386635f 100644 --- a/src/Hakyll/Core/Routes.hs +++ b/src/Hakyll/Core/Routes.hs @@ -30,7 +30,7 @@ module Hakyll.Core.Routes , runRoutes , idRoute , setExtension - , ifMatch + , matchRoute , customRoute , gsubRoute , composeRoutes @@ -41,7 +41,6 @@ import Control.Monad (mplus) import System.FilePath (replaceExtension) import Hakyll.Core.Identifier -import Hakyll.Core.Identifier.Pattern import Hakyll.Core.Util.String -- | Type used for a route @@ -85,15 +84,15 @@ setExtension :: String -> Routes setExtension extension = Routes $ fmap (`replaceExtension` extension) . unRoutes idRoute --- | Modify a route: apply the route if the identifier matches the given --- pattern, fail otherwise. +-- | Apply the route if the identifier matches the given predicate, fail +-- otherwise -- -ifMatch :: Pattern -> Routes -> Routes -ifMatch pattern (Routes route) = Routes $ \id' -> - if doesMatch pattern id' then route id' - else Nothing +matchRoute :: (Identifier -> Bool) -> Routes -> Routes +matchRoute predicate (Routes route) = Routes $ \id' -> + if predicate id' then route id' else Nothing --- | Create a custom route. This should almost always be used with 'ifMatch'. +-- | Create a custom route. This should almost always be used with +-- 'matchRoute' -- customRoute :: (Identifier -> FilePath) -> Routes customRoute f = Routes $ Just . f diff --git a/src/Hakyll/Core/Rules.hs b/src/Hakyll/Core/Rules.hs index eba3fb9..319e10b 100644 --- a/src/Hakyll/Core/Rules.hs +++ b/src/Hakyll/Core/Rules.hs @@ -14,6 +14,8 @@ module Hakyll.Core.Rules ( RulesM , Rules + , matchPattern + , matchPredicate , compile , create , route @@ -23,7 +25,7 @@ module Hakyll.Core.Rules import Control.Applicative ((<$>)) import Control.Monad.Writer (tell) -import Control.Monad.Reader (ask) +import Control.Monad.Reader (ask, local) import Control.Arrow (second, (>>>), arr, (>>^)) import Control.Monad.State (get, put) import Data.Monoid (mempty) @@ -63,21 +65,37 @@ tellResources :: [Resource] -> Rules tellResources resources = RulesM $ tell $ RuleSet mempty mempty $ S.fromList resources +-- | Only compile/route items matching the given pattern +-- +matchPattern :: Pattern -> Rules -> Rules +matchPattern pattern = matchPredicate (doesMatch pattern) + +-- | Only compile/route items satisfying the given predicate +-- +matchPredicate :: (Identifier -> Bool) -> Rules -> Rules +matchPredicate predicate = RulesM . local addPredicate . unRulesM + where + addPredicate env = env + { rulesMatcher = \id' -> rulesMatcher env id' && predicate id' + } + -- | Add a compilation rule to the rules. -- --- This instructs all resources matching the given pattern to be compiled using --- the given compiler. When no resources match the given pattern, nothing will --- happen. In this case, you might want to have a look at 'create'. +-- This instructs all resources to be compiled using the given compiler. When +-- no resources match the current selection, nothing will happen. In this case, +-- you might want to have a look at 'create'. -- compile :: (Binary a, Typeable a, Writable a) - => Pattern -> Compiler Resource a -> Rules -compile pattern compiler = RulesM $ do - identifiers <- matches pattern . map unResource . resourceList <$> ask + => Compiler Resource a -> Rules +compile compiler = RulesM $ do + matcher <- rulesMatcher <$> ask + provider <- rulesResourceProvider <$> ask + let identifiers = filter matcher $ map unResource $ resourceList provider unRulesM $ do tellCompilers $ flip map identifiers $ \identifier -> (identifier, constA (Resource identifier) >>> compiler) tellResources $ map Resource identifiers - + -- | Add a compilation rule -- -- This sets a compiler for the given identifier. No resource is needed, since @@ -91,10 +109,12 @@ create identifier compiler = tellCompilers [(identifier, compiler)] -- | Add a route. -- --- This adds a route for all items matching the given pattern. +-- This adds a route for all items matching the current pattern. -- -route :: Pattern -> Routes -> Rules -route pattern route' = tellRoute $ ifMatch pattern route' +route :: Routes -> Rules +route route' = RulesM $ do + matcher <- rulesMatcher <$> ask + unRulesM $ tellRoute $ matchRoute matcher route' -- | Apart from regular compilers, one is also able to specify metacompilers. -- Metacompilers are a special class of compilers: they are compilers which diff --git a/src/Hakyll/Core/Rules/Internal.hs b/src/Hakyll/Core/Rules/Internal.hs index 2895257..dc669c1 100644 --- a/src/Hakyll/Core/Rules/Internal.hs +++ b/src/Hakyll/Core/Rules/Internal.hs @@ -5,6 +5,7 @@ module Hakyll.Core.Rules.Internal ( CompileRule (..) , RuleSet (..) , RuleState (..) + , RuleEnvironment (..) , RulesM (..) , Rules , runRules @@ -55,10 +56,17 @@ data RuleState = RuleState { rulesMetaCompilerIndex :: Int } deriving (Show) +-- | Rule environment +-- +data RuleEnvironment = RuleEnvironment + { rulesResourceProvider :: ResourceProvider + , rulesMatcher :: Identifier -> Bool + } + -- | The monad used to compose rules -- newtype RulesM a = RulesM - { unRulesM :: ReaderT ResourceProvider (WriterT RuleSet (State RuleState)) a + { unRulesM :: ReaderT RuleEnvironment (WriterT RuleSet (State RuleState)) a } deriving (Monad, Functor, Applicative) -- | Simplification of the RulesM type; usually, it will not return any @@ -70,6 +78,9 @@ type Rules = RulesM () -- runRules :: Rules -> ResourceProvider -> RuleSet runRules rules provider = - evalState (execWriterT $ runReaderT (unRulesM rules) provider) state + evalState (execWriterT $ runReaderT (unRulesM rules) env) state where state = RuleState {rulesMetaCompilerIndex = 0} + env = RuleEnvironment { rulesResourceProvider = provider + , rulesMatcher = const True + } -- cgit v1.2.3 From 041ec5c3096684d045637ddd72741192b9050e36 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Mon, 4 Apr 2011 22:58:43 +0200 Subject: Add getIdentifiers --- src/Hakyll/Core/Compiler.hs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Compiler.hs b/src/Hakyll/Core/Compiler.hs index 371594f..bd78adf 100644 --- a/src/Hakyll/Core/Compiler.hs +++ b/src/Hakyll/Core/Compiler.hs @@ -89,6 +89,7 @@ module Hakyll.Core.Compiler ( Compiler , runCompiler , getIdentifier + , getIdentifiers , getRoute , getRouteFor , getResourceString @@ -165,6 +166,13 @@ runCompiler compiler identifier provider routes store modified logger = do getIdentifier :: Compiler a Identifier getIdentifier = fromJob $ const $ CompilerM $ compilerIdentifier <$> ask +-- | Get all identifiers matching the given pattern +-- +getIdentifiers :: Pattern -> Compiler a [Identifier] +getIdentifiers pattern = fromJob $ const $ CompilerM $ + matches pattern . map unResource . resourceList + . compilerResourceProvider <$> ask + -- | Get the route we are using for this item -- getRoute :: Compiler a (Maybe FilePath) -- cgit v1.2.3 From 091014a60e29700017c8d77b7561f37ae4a611be Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Tue, 5 Apr 2011 10:55:09 +0200 Subject: Restructure pattern internals --- src/Hakyll/Core/Identifier/Pattern.hs | 101 ++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 42 deletions(-) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Identifier/Pattern.hs b/src/Hakyll/Core/Identifier/Pattern.hs index a1e36df..52b998b 100644 --- a/src/Hakyll/Core/Identifier/Pattern.hs +++ b/src/Hakyll/Core/Identifier/Pattern.hs @@ -1,4 +1,12 @@ -- | Module providing pattern matching and capturing on 'Identifier's. +-- 'Pattern's come in two kinds: +-- +-- * Simple glob patterns, like @foo\/*@; +-- +-- * Custom, arbitrary predicates of the type @Identifier -> Bool@. +-- +-- They both have advantages and disadvantages. By default, globs are used, +-- unless you construct your 'Pattern' using the 'predicate' function. -- -- A very simple pattern could be, for example, @foo\/bar@. This pattern will -- only match the exact @foo\/bar@ identifier. @@ -20,15 +28,15 @@ -- -- * @foo\/*.html@ will match all HTML files in the @foo\/@ directory. -- --- The 'match' function allows the user to get access to the elements captured +-- The 'capture' function allows the user to get access to the elements captured -- by the capture elements in the pattern. -- module Hakyll.Core.Identifier.Pattern ( Pattern - , parsePattern - , match - , doesMatch + , parseGlob , matches + , filterMatches + , capture , fromCapture , fromCaptureString , fromCaptures @@ -46,23 +54,23 @@ import Hakyll.Core.Identifier -- | One base element of a pattern -- -data PatternComponent = Capture - | CaptureMany - | Literal String - deriving (Eq, Show) +data GlobComponent = Capture + | CaptureMany + | Literal String + deriving (Eq, Show) -- | Type that allows matching on identifiers -- -newtype Pattern = Pattern {unPattern :: [PatternComponent]} - deriving (Eq, Show) +data Pattern = Glob [GlobComponent] + | Predicate (Identifier -> Bool) instance IsString Pattern where - fromString = parsePattern + fromString = parseGlob -- | Parse a pattern from a string -- -parsePattern :: String -> Pattern -parsePattern = Pattern . parse' -- undefined -- Pattern . map toPattern . unIdentifier . parseIdentifier +parseGlob :: String -> Pattern +parseGlob = Glob . parse' where parse' str = let (chunk, rest) = break (`elem` "\\*") str @@ -72,20 +80,16 @@ parsePattern = Pattern . parse' -- undefined -- Pattern . map toPattern . unIden ('*' : xs) -> Literal chunk : Capture : parse' xs xs -> Literal chunk : Literal xs : [] --- | Match an identifier against a pattern, generating a list of captures --- -match :: Pattern -> Identifier -> Maybe [Identifier] -match p (Identifier i) = fmap (map Identifier) $ match' (unPattern p) i - -- | Check if an identifier matches a pattern -- -doesMatch :: Pattern -> Identifier -> Bool -doesMatch p = isJust . match p +matches :: Pattern -> Identifier -> Bool +matches (Glob p) = isJust . capture (Glob p) +matches (Predicate p) = (p $) -- | Given a list of identifiers, retain only those who match the given pattern -- -matches :: Pattern -> [Identifier] -> [Identifier] -matches p = filter (doesMatch p) +filterMatches :: Pattern -> [Identifier] -> [Identifier] +filterMatches = filter . matches -- | Split a list at every possible point, generate a list of (init, tail) -- cases. The result is sorted with inits decreasing in length. @@ -93,30 +97,35 @@ matches p = filter (doesMatch p) splits :: [a] -> [([a], [a])] splits = inits &&& tails >>> uncurry zip >>> reverse --- | Internal verion of 'match' +-- | Match a glob against a pattern, generating a list of captures -- -match' :: [PatternComponent] -> String -> Maybe [String] -match' [] [] = Just [] -- An empty match -match' [] _ = Nothing -- No match --- match' _ [] = Nothing -- No match -match' (Literal l : ms) str +capture :: Pattern -> Identifier -> Maybe [Identifier] +capture (Glob p) (Identifier i) = fmap (map Identifier) $ capture' p i +capture (Predicate _) _ = Nothing + +-- | Internal verion of 'capture' +-- +capture' :: [GlobComponent] -> String -> Maybe [String] +capture' [] [] = Just [] -- An empty match +capture' [] _ = Nothing -- No match +capture' (Literal l : ms) str -- Match the literal against the string - | l `isPrefixOf` str = match' ms $ drop (length l) str + | l `isPrefixOf` str = capture' ms $ drop (length l) str | otherwise = Nothing -match' (Capture : ms) str = +capture' (Capture : ms) str = -- Match until the next / let (chunk, rest) = break (== '/') str - in msum $ [ fmap (i :) (match' ms (t ++ rest)) | (i, t) <- splits chunk ] -match' (CaptureMany : ms) str = + in msum $ [ fmap (i :) (capture' ms (t ++ rest)) | (i, t) <- splits chunk ] +capture' (CaptureMany : ms) str = -- Match everything - msum $ [ fmap (i :) (match' ms t) | (i, t) <- splits str ] + msum $ [ fmap (i :) (capture' ms t) | (i, t) <- splits str ] -- | Create an identifier from a pattern by filling in the captures with a given -- string -- -- Example: -- --- > fromCapture (parsePattern "tags/*") (parseIdentifier "foo") +-- > fromCapture (parseGlob "tags/*") (parseIdentifier "foo") -- -- Result: -- @@ -128,7 +137,7 @@ fromCapture pattern = fromCaptures pattern . repeat -- | Simplified version of 'fromCapture' which takes a 'String' instead of an -- 'Identifier' -- --- > fromCaptureString (parsePattern "tags/*") "foo" +-- > fromCaptureString (parseGlob "tags/*") "foo" -- -- Result: -- @@ -141,11 +150,19 @@ fromCaptureString pattern = fromCapture pattern . parseIdentifier -- given list of strings -- fromCaptures :: Pattern -> [Identifier] -> Identifier -fromCaptures (Pattern []) _ = mempty -fromCaptures (Pattern (m : ms)) [] = case m of - Literal l -> Identifier l `mappend` fromCaptures (Pattern ms) [] - _ -> error $ "Hakyll.Core.Identifier.Pattern.fromCaptures: " +fromCaptures (Glob p) = fromCaptures' p +fromCaptures (Predicate _) = error $ + "Hakyll.Core.Identifier.Pattern.fromCaptures: fromCaptures called on a " ++ + "predicate instead of a glob" + +-- | Internally used version of 'fromCaptures' +-- +fromCaptures' :: [GlobComponent] -> [Identifier] -> Identifier +fromCaptures' [] _ = mempty +fromCaptures' (m : ms) [] = case m of + Literal l -> Identifier l `mappend` fromCaptures' ms [] + _ -> error $ "Hakyll.Core.Identifier.Pattern.fromCaptures': " ++ "identifier list exhausted" -fromCaptures (Pattern (m : ms)) ids@(i : is) = case m of - Literal l -> Identifier l `mappend` fromCaptures (Pattern ms) ids - _ -> i `mappend` fromCaptures (Pattern ms) is +fromCaptures' (m : ms) ids@(i : is) = case m of + Literal l -> Identifier l `mappend` fromCaptures' ms ids + _ -> i `mappend` fromCaptures' ms is -- cgit v1.2.3 From b7059a0f1a05db0b2e33efae935ece65f97ea3fc Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Tue, 5 Apr 2011 11:46:16 +0200 Subject: Add function to construct predicates --- src/Hakyll/Core/Identifier/Pattern.hs | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Identifier/Pattern.hs b/src/Hakyll/Core/Identifier/Pattern.hs index 52b998b..42f67f0 100644 --- a/src/Hakyll/Core/Identifier/Pattern.hs +++ b/src/Hakyll/Core/Identifier/Pattern.hs @@ -34,6 +34,7 @@ module Hakyll.Core.Identifier.Pattern ( Pattern , parseGlob + , predicate , matches , filterMatches , capture @@ -80,6 +81,15 @@ parseGlob = Glob . parse' ('*' : xs) -> Literal chunk : Capture : parse' xs xs -> Literal chunk : Literal xs : [] +-- | Create a 'Pattern' from an arbitrary predicate +-- +-- Example: +-- +-- > predicate (\i -> matches "foo/*" i && not (matches "foo/bar" i)) +-- +predicate :: (Identifier -> Bool) -> Pattern +predicate = Predicate + -- | Check if an identifier matches a pattern -- matches :: Pattern -> Identifier -> Bool -- cgit v1.2.3 From ecf4c64f620db72495289f7bce182bf262dc4de4 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Tue, 5 Apr 2011 11:50:46 +0200 Subject: Monoid instance for pattern --- src/Hakyll/Core/Identifier/Pattern.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Identifier/Pattern.hs b/src/Hakyll/Core/Identifier/Pattern.hs index 42f67f0..28e23ad 100644 --- a/src/Hakyll/Core/Identifier/Pattern.hs +++ b/src/Hakyll/Core/Identifier/Pattern.hs @@ -47,7 +47,7 @@ import Data.List (isPrefixOf, inits, tails) import Control.Arrow ((&&&), (>>>)) import Control.Monad (msum) import Data.Maybe (isJust) -import Data.Monoid (mempty, mappend) +import Data.Monoid (Monoid, mempty, mappend) import GHC.Exts (IsString, fromString) @@ -68,6 +68,12 @@ data Pattern = Glob [GlobComponent] instance IsString Pattern where fromString = parseGlob +instance Monoid Pattern where + mempty = Predicate (const True) + g@(Glob _) `mappend` x = Predicate (matches g) `mappend` x + x `mappend` g@(Glob _) = x `mappend` Predicate (matches g) + Predicate f `mappend` Predicate g = Predicate $ \i -> f i && g i + -- | Parse a pattern from a string -- parseGlob :: String -> Pattern -- cgit v1.2.3 From 4925dd828ee8618eec4f209ebb0456826df7c5a4 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Tue, 5 Apr 2011 11:52:50 +0200 Subject: Bring tests up-to-date --- src/Hakyll/Core/Compiler.hs | 2 +- tests/Hakyll/Core/Identifier/Tests.hs | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Compiler.hs b/src/Hakyll/Core/Compiler.hs index 371594f..70e9a37 100644 --- a/src/Hakyll/Core/Compiler.hs +++ b/src/Hakyll/Core/Compiler.hs @@ -237,7 +237,7 @@ requireAll_ :: (Binary a, Typeable a, Writable a) -> Compiler b [a] requireAll_ pattern = fromDependencies (const getDeps) >>> fromJob requireAll_' where - getDeps = matches pattern . map unResource . resourceList + getDeps = filterMatches pattern . map unResource . resourceList requireAll_' = const $ CompilerM $ do deps <- getDeps . compilerResourceProvider <$> ask mapM (unCompilerM . getDependency) deps diff --git a/tests/Hakyll/Core/Identifier/Tests.hs b/tests/Hakyll/Core/Identifier/Tests.hs index 64b5abc..5b5d34d 100644 --- a/tests/Hakyll/Core/Identifier/Tests.hs +++ b/tests/Hakyll/Core/Identifier/Tests.hs @@ -10,18 +10,18 @@ import Hakyll.Core.Identifier.Pattern import TestSuite.Util tests :: [Test] -tests = fromAssertions "match" - [ Just ["bar"] @=? match "foo/**" "foo/bar" - , Just ["foo/bar"] @=? match "**" "foo/bar" - , Nothing @=? match "*" "foo/bar" - , Just [] @=? match "foo" "foo" - , Just ["foo"] @=? match "*/bar" "foo/bar" - , Just ["foo/bar"] @=? match "**/qux" "foo/bar/qux" - , Just ["foo/bar", "qux"] @=? match "**/*" "foo/bar/qux" - , Just ["foo", "bar/qux"] @=? match "*/**" "foo/bar/qux" - , Just ["foo"] @=? match "*.html" "foo.html" - , Nothing @=? match "*.html" "foo/bar.html" - , Just ["foo/bar"] @=? match "**.html" "foo/bar.html" - , Just ["foo/bar", "wut"] @=? match "**/qux/*" "foo/bar/qux/wut" - , Just ["lol", "fun/large"] @=? match "*cat/**.jpg" "lolcat/fun/large.jpg" +tests = fromAssertions "capture" + [ Just ["bar"] @=? capture "foo/**" "foo/bar" + , Just ["foo/bar"] @=? capture "**" "foo/bar" + , Nothing @=? capture "*" "foo/bar" + , Just [] @=? capture "foo" "foo" + , Just ["foo"] @=? capture "*/bar" "foo/bar" + , Just ["foo/bar"] @=? capture "**/qux" "foo/bar/qux" + , Just ["foo/bar", "qux"] @=? capture "**/*" "foo/bar/qux" + , Just ["foo", "bar/qux"] @=? capture "*/**" "foo/bar/qux" + , Just ["foo"] @=? capture "*.html" "foo.html" + , Nothing @=? capture "*.html" "foo/bar.html" + , Just ["foo/bar"] @=? capture "**.html" "foo/bar.html" + , Just ["foo/bar", "wut"] @=? capture "**/qux/*" "foo/bar/qux/wut" + , Just ["lol", "fun/large"] @=? capture "*cat/**.jpg" "lolcat/fun/large.jpg" ] -- cgit v1.2.3 From ff118fec98ef02e2eead2a752d9c6619a2e891df Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Tue, 5 Apr 2011 11:58:26 +0200 Subject: Simplify match implementation(s) --- src/Hakyll/Core/Routes.hs | 9 +++++---- src/Hakyll/Core/Rules.hs | 36 +++++++++++++++++------------------- src/Hakyll/Core/Rules/Internal.hs | 5 +++-- 3 files changed, 25 insertions(+), 25 deletions(-) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Routes.hs b/src/Hakyll/Core/Routes.hs index 386635f..abbd0a7 100644 --- a/src/Hakyll/Core/Routes.hs +++ b/src/Hakyll/Core/Routes.hs @@ -41,6 +41,7 @@ import Control.Monad (mplus) import System.FilePath (replaceExtension) import Hakyll.Core.Identifier +import Hakyll.Core.Identifier.Pattern import Hakyll.Core.Util.String -- | Type used for a route @@ -84,12 +85,12 @@ setExtension :: String -> Routes setExtension extension = Routes $ fmap (`replaceExtension` extension) . unRoutes idRoute --- | Apply the route if the identifier matches the given predicate, fail +-- | Apply the route if the identifier matches the given pattern, fail -- otherwise -- -matchRoute :: (Identifier -> Bool) -> Routes -> Routes -matchRoute predicate (Routes route) = Routes $ \id' -> - if predicate id' then route id' else Nothing +matchRoute :: Pattern -> Routes -> Routes +matchRoute pattern (Routes route) = Routes $ \id' -> + if matches pattern id' then route id' else Nothing -- | Create a custom route. This should almost always be used with -- 'matchRoute' diff --git a/src/Hakyll/Core/Rules.hs b/src/Hakyll/Core/Rules.hs index 319e10b..19df85e 100644 --- a/src/Hakyll/Core/Rules.hs +++ b/src/Hakyll/Core/Rules.hs @@ -7,15 +7,18 @@ -- A typical usage example would be: -- -- > main = hakyll $ do --- > route "posts/*" (setExtension "html") --- > compile "posts/*" someCompiler +-- > match "posts/*" $ do +-- > route (setExtension "html") +-- > compile someCompiler +-- > match "css/*" $ do +-- > route idRoute +-- > compile compressCssCompiler -- {-# LANGUAGE GeneralizedNewtypeDeriving, OverloadedStrings #-} module Hakyll.Core.Rules ( RulesM , Rules - , matchPattern - , matchPredicate + , match , compile , create , route @@ -28,7 +31,7 @@ import Control.Monad.Writer (tell) import Control.Monad.Reader (ask, local) import Control.Arrow (second, (>>>), arr, (>>^)) import Control.Monad.State (get, put) -import Data.Monoid (mempty) +import Data.Monoid (mempty, mappend) import qualified Data.Set as S import Data.Typeable (Typeable) @@ -65,18 +68,13 @@ tellResources :: [Resource] -> Rules tellResources resources = RulesM $ tell $ RuleSet mempty mempty $ S.fromList resources --- | Only compile/route items matching the given pattern --- -matchPattern :: Pattern -> Rules -> Rules -matchPattern pattern = matchPredicate (doesMatch pattern) - -- | Only compile/route items satisfying the given predicate -- -matchPredicate :: (Identifier -> Bool) -> Rules -> Rules -matchPredicate predicate = RulesM . local addPredicate . unRulesM +match :: Pattern -> Rules -> Rules +match pattern = RulesM . local addPredicate . unRulesM where addPredicate env = env - { rulesMatcher = \id' -> rulesMatcher env id' && predicate id' + { rulesPattern = rulesPattern env `mappend` pattern } -- | Add a compilation rule to the rules. @@ -88,13 +86,13 @@ matchPredicate predicate = RulesM . local addPredicate . unRulesM compile :: (Binary a, Typeable a, Writable a) => Compiler Resource a -> Rules compile compiler = RulesM $ do - matcher <- rulesMatcher <$> ask + pattern <- rulesPattern <$> ask provider <- rulesResourceProvider <$> ask - let identifiers = filter matcher $ map unResource $ resourceList provider + let ids = filterMatches pattern $ map unResource $ resourceList provider unRulesM $ do - tellCompilers $ flip map identifiers $ \identifier -> + tellCompilers $ flip map ids $ \identifier -> (identifier, constA (Resource identifier) >>> compiler) - tellResources $ map Resource identifiers + tellResources $ map Resource ids -- | Add a compilation rule -- @@ -113,8 +111,8 @@ create identifier compiler = tellCompilers [(identifier, compiler)] -- route :: Routes -> Rules route route' = RulesM $ do - matcher <- rulesMatcher <$> ask - unRulesM $ tellRoute $ matchRoute matcher route' + pattern <- rulesPattern <$> ask + unRulesM $ tellRoute $ matchRoute pattern route' -- | Apart from regular compilers, one is also able to specify metacompilers. -- Metacompilers are a special class of compilers: they are compilers which diff --git a/src/Hakyll/Core/Rules/Internal.hs b/src/Hakyll/Core/Rules/Internal.hs index dc669c1..592194d 100644 --- a/src/Hakyll/Core/Rules/Internal.hs +++ b/src/Hakyll/Core/Rules/Internal.hs @@ -20,6 +20,7 @@ import Data.Set (Set) import Hakyll.Core.ResourceProvider import Hakyll.Core.Identifier +import Hakyll.Core.Identifier.Pattern import Hakyll.Core.Compiler.Internal import Hakyll.Core.Routes import Hakyll.Core.CompiledItem @@ -60,7 +61,7 @@ data RuleState = RuleState -- data RuleEnvironment = RuleEnvironment { rulesResourceProvider :: ResourceProvider - , rulesMatcher :: Identifier -> Bool + , rulesPattern :: Pattern } -- | The monad used to compose rules @@ -82,5 +83,5 @@ runRules rules provider = where state = RuleState {rulesMetaCompilerIndex = 0} env = RuleEnvironment { rulesResourceProvider = provider - , rulesMatcher = const True + , rulesPattern = mempty } -- cgit v1.2.3 From 015663657ceca7b168bf7d91fbc2fccc41c40904 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Tue, 5 Apr 2011 22:14:49 +0200 Subject: New resource module hierarchy --- hakyll.cabal | 5 +- src-inotify/Hakyll/Web/Preview/Poll.hs | 2 +- src-interval/Hakyll/Web/Preview/Poll.hs | 2 +- src/Hakyll.hs | 6 +- src/Hakyll/Core/Compiler.hs | 11 +--- src/Hakyll/Core/Compiler/Internal.hs | 2 +- src/Hakyll/Core/Resource.hs | 14 ++++ src/Hakyll/Core/Resource/Provider.hs | 68 ++++++++++++++++++++ src/Hakyll/Core/Resource/Provider/File.hs | 30 +++++++++ src/Hakyll/Core/ResourceProvider.hs | 75 ---------------------- .../Core/ResourceProvider/FileResourceProvider.hs | 29 --------- src/Hakyll/Core/Rules.hs | 3 +- src/Hakyll/Core/Rules/Internal.hs | 3 +- src/Hakyll/Core/Run.hs | 5 +- src/Hakyll/Core/Writable/CopyFile.hs | 2 +- src/Hakyll/Web/CompressCss.hs | 2 +- src/Hakyll/Web/Page.hs | 2 +- src/Hakyll/Web/Template.hs | 2 +- 18 files changed, 135 insertions(+), 128 deletions(-) create mode 100644 src/Hakyll/Core/Resource.hs create mode 100644 src/Hakyll/Core/Resource/Provider.hs create mode 100644 src/Hakyll/Core/Resource/Provider/File.hs delete mode 100644 src/Hakyll/Core/ResourceProvider.hs delete mode 100644 src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs (limited to 'src/Hakyll/Core') diff --git a/hakyll.cabal b/hakyll.cabal index aa44fef..a71224b 100644 --- a/hakyll.cabal +++ b/hakyll.cabal @@ -81,14 +81,15 @@ library Hakyll.Web.RelativizeUrls Hakyll.Web.Page.Read Hakyll.Web.Page.Metadata - Hakyll.Core.ResourceProvider.FileResourceProvider Hakyll.Core.Configuration Hakyll.Core.Identifier.Pattern Hakyll.Core.UnixFilter Hakyll.Core.Util.Arrow Hakyll.Core.Util.File Hakyll.Core.Util.String - Hakyll.Core.ResourceProvider + Hakyll.Core.Resource + Hakyll.Core.Resource.Provider + Hakyll.Core.Resource.Provider.File Hakyll.Core.CompiledItem Hakyll.Core.Compiler Hakyll.Core.Run diff --git a/src-inotify/Hakyll/Web/Preview/Poll.hs b/src-inotify/Hakyll/Web/Preview/Poll.hs index 686f045..2e028cc 100644 --- a/src-inotify/Hakyll/Web/Preview/Poll.hs +++ b/src-inotify/Hakyll/Web/Preview/Poll.hs @@ -13,7 +13,7 @@ import Data.List (isPrefixOf) import System.INotify import Hakyll.Core.Configuration -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource import Hakyll.Core.Identifier -- | Calls the given callback when the directory tree changes diff --git a/src-interval/Hakyll/Web/Preview/Poll.hs b/src-interval/Hakyll/Web/Preview/Poll.hs index ec6df0c..bb18b51 100644 --- a/src-interval/Hakyll/Web/Preview/Poll.hs +++ b/src-interval/Hakyll/Web/Preview/Poll.hs @@ -15,7 +15,7 @@ import System.Directory (getModificationTime) import Hakyll.Core.Configuration import Hakyll.Core.Identifier -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource -- | A preview thread that periodically recompiles the site. -- diff --git a/src/Hakyll.hs b/src/Hakyll.hs index 5fe1f26..341bb53 100644 --- a/src/Hakyll.hs +++ b/src/Hakyll.hs @@ -5,7 +5,8 @@ module Hakyll , module Hakyll.Core.Configuration , module Hakyll.Core.Identifier , module Hakyll.Core.Identifier.Pattern - , module Hakyll.Core.ResourceProvider + , module Hakyll.Core.Resource + , module Hakyll.Core.Resource.Provider , module Hakyll.Core.Routes , module Hakyll.Core.Rules , module Hakyll.Core.UnixFilter @@ -34,7 +35,8 @@ import Hakyll.Core.Compiler import Hakyll.Core.Configuration import Hakyll.Core.Identifier import Hakyll.Core.Identifier.Pattern -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource +import Hakyll.Core.Resource.Provider import Hakyll.Core.Routes import Hakyll.Core.Rules import Hakyll.Core.UnixFilter diff --git a/src/Hakyll/Core/Compiler.hs b/src/Hakyll/Core/Compiler.hs index 7fe1754..db51131 100644 --- a/src/Hakyll/Core/Compiler.hs +++ b/src/Hakyll/Core/Compiler.hs @@ -89,7 +89,6 @@ module Hakyll.Core.Compiler ( Compiler , runCompiler , getIdentifier - , getIdentifiers , getRoute , getRouteFor , getResourceString @@ -125,7 +124,8 @@ import Hakyll.Core.Identifier import Hakyll.Core.Identifier.Pattern import Hakyll.Core.CompiledItem import Hakyll.Core.Writable -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource +import Hakyll.Core.Resource.Provider import Hakyll.Core.Compiler.Internal import Hakyll.Core.Store import Hakyll.Core.Rules.Internal @@ -166,13 +166,6 @@ runCompiler compiler identifier provider routes store modified logger = do getIdentifier :: Compiler a Identifier getIdentifier = fromJob $ const $ CompilerM $ compilerIdentifier <$> ask --- | Get all identifiers matching the given pattern --- -getIdentifiers :: Pattern -> Compiler a [Identifier] -getIdentifiers pattern = fromJob $ const $ CompilerM $ - matches pattern . map unResource . resourceList - . compilerResourceProvider <$> ask - -- | Get the route we are using for this item -- getRoute :: Compiler a (Maybe FilePath) diff --git a/src/Hakyll/Core/Compiler/Internal.hs b/src/Hakyll/Core/Compiler/Internal.hs index 4eef91c..1a3c4c3 100644 --- a/src/Hakyll/Core/Compiler/Internal.hs +++ b/src/Hakyll/Core/Compiler/Internal.hs @@ -26,7 +26,7 @@ import Control.Category (Category, (.), id) import Control.Arrow (Arrow, ArrowChoice, arr, first, left) import Hakyll.Core.Identifier -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource.Provider import Hakyll.Core.Store import Hakyll.Core.Routes import Hakyll.Core.Logger diff --git a/src/Hakyll/Core/Resource.hs b/src/Hakyll/Core/Resource.hs new file mode 100644 index 0000000..d60fda9 --- /dev/null +++ b/src/Hakyll/Core/Resource.hs @@ -0,0 +1,14 @@ +-- | Module exporting the simple 'Resource' type +-- +module Hakyll.Core.Resource + ( Resource (..) + ) where + +import Hakyll.Core.Identifier + +-- | A resource +-- +-- Invariant: the resource specified by the given identifier must exist +-- +newtype Resource = Resource {unResource :: Identifier} + deriving (Eq, Show, Ord) diff --git a/src/Hakyll/Core/Resource/Provider.hs b/src/Hakyll/Core/Resource/Provider.hs new file mode 100644 index 0000000..377b029 --- /dev/null +++ b/src/Hakyll/Core/Resource/Provider.hs @@ -0,0 +1,68 @@ +-- | This module provides an API for resource providers. Resource providers +-- allow Hakyll to get content from resources; the type of resource depends on +-- the concrete instance. +-- +-- A resource is represented by the 'Resource' type. This is basically just a +-- newtype wrapper around 'Identifier' -- but it has an important effect: it +-- guarantees that a resource with this identifier can be provided by one or +-- more resource providers. +-- +-- Therefore, it is not recommended to read files directly -- you should use the +-- provided 'Resource' methods. +-- +module Hakyll.Core.Resource.Provider + ( ResourceProvider (..) + , resourceExists + , resourceDigest + , resourceModified + ) where + +import Control.Monad ((<=<)) +import Data.Word (Word8) + +import qualified Data.ByteString.Lazy as LB +import OpenSSL.Digest.ByteString.Lazy (digest) +import OpenSSL.Digest (MessageDigest (MD5)) + +import Hakyll.Core.Identifier +import Hakyll.Core.Store +import Hakyll.Core.Resource + +-- | A value responsible for retrieving and listing resources +-- +data ResourceProvider = ResourceProvider + { -- | A list of all resources this provider is able to provide + resourceList :: [Resource] + , -- | Retrieve a certain resource as string + resourceString :: Resource -> IO String + , -- | Retrieve a certain resource as lazy bytestring + resourceLazyByteString :: Resource -> IO LB.ByteString + } + +-- | Check if a given identifier has a resource +-- +resourceExists :: ResourceProvider -> Identifier -> Bool +resourceExists provider = flip elem $ map unResource $ resourceList provider + +-- | Retrieve a digest for a given resource +-- +resourceDigest :: ResourceProvider -> Resource -> IO [Word8] +resourceDigest provider = digest MD5 <=< resourceLazyByteString provider + +-- | Check if a resource was modified +-- +resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool +resourceModified provider resource store = do + -- Get the latest seen digest from the store + lastDigest <- storeGet store itemName $ unResource resource + -- Calculate the digest for the resource + newDigest <- resourceDigest provider resource + -- Check digests + if Just newDigest == lastDigest + -- All is fine, not modified + then return False + -- Resource modified; store new digest + else do storeSet store itemName (unResource resource) newDigest + return True + where + itemName = "Hakyll.Core.ResourceProvider.resourceModified" diff --git a/src/Hakyll/Core/Resource/Provider/File.hs b/src/Hakyll/Core/Resource/Provider/File.hs new file mode 100644 index 0000000..a795fac --- /dev/null +++ b/src/Hakyll/Core/Resource/Provider/File.hs @@ -0,0 +1,30 @@ +-- | A concrete 'ResourceProvider' that gets it's resources from the filesystem +-- +module Hakyll.Core.Resource.Provider.File + ( fileResourceProvider + ) where + +import Control.Applicative ((<$>)) + +import qualified Data.ByteString.Lazy as LB + +import Hakyll.Core.Resource +import Hakyll.Core.Resource.Provider +import Hakyll.Core.Identifier +import Hakyll.Core.Util.File +import Hakyll.Core.Configuration + +-- | Create a filesystem-based 'ResourceProvider' +-- +fileResourceProvider :: HakyllConfiguration -> IO ResourceProvider +fileResourceProvider configuration = do + -- Retrieve a list of identifiers + list <- map parseIdentifier . filter (not . ignoreFile configuration) <$> + getRecursiveContents False "." + + -- Construct a resource provider + return ResourceProvider + { resourceList = map Resource list + , resourceString = readFile . toFilePath . unResource + , resourceLazyByteString = LB.readFile . toFilePath . unResource + } diff --git a/src/Hakyll/Core/ResourceProvider.hs b/src/Hakyll/Core/ResourceProvider.hs deleted file mode 100644 index dcd4af0..0000000 --- a/src/Hakyll/Core/ResourceProvider.hs +++ /dev/null @@ -1,75 +0,0 @@ --- | This module provides an API for resource providers. Resource providers --- allow Hakyll to get content from resources; the type of resource depends on --- the concrete instance. --- --- A resource is represented by the 'Resource' type. This is basically just a --- newtype wrapper around 'Identifier' -- but it has an important effect: it --- guarantees that a resource with this identifier can be provided by one or --- more resource providers. --- --- Therefore, it is not recommended to read files directly -- you should use the --- provided 'Resource' methods. --- -module Hakyll.Core.ResourceProvider - ( Resource (..) - , ResourceProvider (..) - , resourceExists - , resourceDigest - , resourceModified - ) where - -import Control.Monad ((<=<)) -import Data.Word (Word8) - -import qualified Data.ByteString.Lazy as LB -import OpenSSL.Digest.ByteString.Lazy (digest) -import OpenSSL.Digest (MessageDigest (MD5)) - -import Hakyll.Core.Identifier -import Hakyll.Core.Store - --- | A resource --- --- Invariant: the resource specified by the given identifier must exist --- -newtype Resource = Resource {unResource :: Identifier} - deriving (Eq, Show, Ord) - --- | A value responsible for retrieving and listing resources --- -data ResourceProvider = ResourceProvider - { -- | A list of all resources this provider is able to provide - resourceList :: [Resource] - , -- | Retrieve a certain resource as string - resourceString :: Resource -> IO String - , -- | Retrieve a certain resource as lazy bytestring - resourceLazyByteString :: Resource -> IO LB.ByteString - } - --- | Check if a given identifier has a resource --- -resourceExists :: ResourceProvider -> Identifier -> Bool -resourceExists provider = flip elem $ map unResource $ resourceList provider - --- | Retrieve a digest for a given resource --- -resourceDigest :: ResourceProvider -> Resource -> IO [Word8] -resourceDigest provider = digest MD5 <=< resourceLazyByteString provider - --- | Check if a resource was modified --- -resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool -resourceModified provider resource store = do - -- Get the latest seen digest from the store - lastDigest <- storeGet store itemName $ unResource resource - -- Calculate the digest for the resource - newDigest <- resourceDigest provider resource - -- Check digests - if Just newDigest == lastDigest - -- All is fine, not modified - then return False - -- Resource modified; store new digest - else do storeSet store itemName (unResource resource) newDigest - return True - where - itemName = "Hakyll.Core.ResourceProvider.resourceModified" diff --git a/src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs b/src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs deleted file mode 100644 index 0d89b21..0000000 --- a/src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs +++ /dev/null @@ -1,29 +0,0 @@ --- | A concrete 'ResourceProvider' that gets it's resources from the filesystem --- -module Hakyll.Core.ResourceProvider.FileResourceProvider - ( fileResourceProvider - ) where - -import Control.Applicative ((<$>)) - -import qualified Data.ByteString.Lazy as LB - -import Hakyll.Core.ResourceProvider -import Hakyll.Core.Identifier -import Hakyll.Core.Util.File -import Hakyll.Core.Configuration - --- | Create a filesystem-based 'ResourceProvider' --- -fileResourceProvider :: HakyllConfiguration -> IO ResourceProvider -fileResourceProvider configuration = do - -- Retrieve a list of identifiers - list <- map parseIdentifier . filter (not . ignoreFile configuration) <$> - getRecursiveContents False "." - - -- Construct a resource provider - return ResourceProvider - { resourceList = map Resource list - , resourceString = readFile . toFilePath . unResource - , resourceLazyByteString = LB.readFile . toFilePath . unResource - } diff --git a/src/Hakyll/Core/Rules.hs b/src/Hakyll/Core/Rules.hs index 19df85e..892cf7c 100644 --- a/src/Hakyll/Core/Rules.hs +++ b/src/Hakyll/Core/Rules.hs @@ -37,7 +37,8 @@ import qualified Data.Set as S import Data.Typeable (Typeable) import Data.Binary (Binary) -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource +import Hakyll.Core.Resource.Provider import Hakyll.Core.Identifier import Hakyll.Core.Identifier.Pattern import Hakyll.Core.Compiler.Internal diff --git a/src/Hakyll/Core/Rules/Internal.hs b/src/Hakyll/Core/Rules/Internal.hs index 592194d..0e117ec 100644 --- a/src/Hakyll/Core/Rules/Internal.hs +++ b/src/Hakyll/Core/Rules/Internal.hs @@ -18,7 +18,8 @@ import Control.Monad.State (State, evalState) import Data.Monoid (Monoid, mempty, mappend) import Data.Set (Set) -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource +import Hakyll.Core.Resource.Provider import Hakyll.Core.Identifier import Hakyll.Core.Identifier.Pattern import Hakyll.Core.Compiler.Internal diff --git a/src/Hakyll/Core/Run.hs b/src/Hakyll/Core/Run.hs index 9a5245d..8e1ba6d 100644 --- a/src/Hakyll/Core/Run.hs +++ b/src/Hakyll/Core/Run.hs @@ -23,8 +23,9 @@ import Hakyll.Core.Identifier import Hakyll.Core.Util.File import Hakyll.Core.Compiler import Hakyll.Core.Compiler.Internal -import Hakyll.Core.ResourceProvider -import Hakyll.Core.ResourceProvider.FileResourceProvider +import Hakyll.Core.Resource +import Hakyll.Core.Resource.Provider +import Hakyll.Core.Resource.Provider.File import Hakyll.Core.Rules.Internal import Hakyll.Core.DirectedGraph import Hakyll.Core.DirectedGraph.DependencySolver diff --git a/src/Hakyll/Core/Writable/CopyFile.hs b/src/Hakyll/Core/Writable/CopyFile.hs index 1cd5fd2..ab9c698 100644 --- a/src/Hakyll/Core/Writable/CopyFile.hs +++ b/src/Hakyll/Core/Writable/CopyFile.hs @@ -12,7 +12,7 @@ import System.Directory (copyFile) import Data.Typeable (Typeable) import Data.Binary (Binary) -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource import Hakyll.Core.Writable import Hakyll.Core.Compiler import Hakyll.Core.Identifier diff --git a/src/Hakyll/Web/CompressCss.hs b/src/Hakyll/Web/CompressCss.hs index 2df08fd..090fe10 100644 --- a/src/Hakyll/Web/CompressCss.hs +++ b/src/Hakyll/Web/CompressCss.hs @@ -11,7 +11,7 @@ import Data.List (isPrefixOf) import Control.Arrow ((>>^)) import Hakyll.Core.Compiler -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource import Hakyll.Core.Util.String -- | Compiler form of 'compressCss' diff --git a/src/Hakyll/Web/Page.hs b/src/Hakyll/Web/Page.hs index c41647b..5146bdc 100644 --- a/src/Hakyll/Web/Page.hs +++ b/src/Hakyll/Web/Page.hs @@ -67,7 +67,7 @@ import Data.Ord (comparing) import Hakyll.Core.Identifier import Hakyll.Core.Compiler -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource import Hakyll.Web.Page.Internal import Hakyll.Web.Page.Read import Hakyll.Web.Page.Metadata diff --git a/src/Hakyll/Web/Template.hs b/src/Hakyll/Web/Template.hs index 9c49278..33e7a9b 100644 --- a/src/Hakyll/Web/Template.hs +++ b/src/Hakyll/Web/Template.hs @@ -59,7 +59,7 @@ import Text.Hamlet (HamletSettings, defaultHamletSettings) import Hakyll.Core.Compiler import Hakyll.Core.Identifier -import Hakyll.Core.ResourceProvider +import Hakyll.Core.Resource import Hakyll.Web.Template.Internal import Hakyll.Web.Template.Read import Hakyll.Web.Page.Internal -- cgit v1.2.3 From 3d2b2506d040546d74e83f6d9b8b4e0c45026f09 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Wed, 6 Apr 2011 09:30:24 +0200 Subject: Move modified cache to resource provider Currently, it's kept twice: in Run and in the Provider. On the long term, it should be migrated entirely to the Provider, this can be done when the new dependency manager is finished. --- src/Hakyll/Core/Resource/Provider.hs | 25 ++++++++++++++++++++++++- src/Hakyll/Core/Resource/Provider/File.hs | 6 ++++++ src/Hakyll/Core/Run.hs | 4 +--- 3 files changed, 31 insertions(+), 4 deletions(-) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Resource/Provider.hs b/src/Hakyll/Core/Resource/Provider.hs index 377b029..90e93f8 100644 --- a/src/Hakyll/Core/Resource/Provider.hs +++ b/src/Hakyll/Core/Resource/Provider.hs @@ -17,8 +17,11 @@ module Hakyll.Core.Resource.Provider , resourceModified ) where +import Control.Concurrent (MVar, readMVar, modifyMVar_) import Control.Monad ((<=<)) import Data.Word (Word8) +import Data.Map (Map) +import qualified Data.Map as M import qualified Data.ByteString.Lazy as LB import OpenSSL.Digest.ByteString.Lazy (digest) @@ -37,6 +40,8 @@ data ResourceProvider = ResourceProvider resourceString :: Resource -> IO String , -- | Retrieve a certain resource as lazy bytestring resourceLazyByteString :: Resource -> IO LB.ByteString + , -- | Cache keeping track of modified items + resourceModifiedCache :: MVar (Map Resource Bool) } -- | Check if a given identifier has a resource @@ -53,6 +58,24 @@ resourceDigest provider = digest MD5 <=< resourceLazyByteString provider -- resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool resourceModified provider resource store = do + cache <- readMVar mvar + case M.lookup resource cache of + -- Already in the cache + Just m -> return m + -- Not yet in the cache, check digests (if it exists) + Nothing -> do + m <- if resourceExists provider (unResource resource) + then digestModified provider resource store + else return False + modifyMVar_ mvar (return . M.insert resource m) + return m + where + mvar = resourceModifiedCache provider + +-- | Check if a resource digest was modified +-- +digestModified :: ResourceProvider -> Resource -> Store -> IO Bool +digestModified provider resource store = do -- Get the latest seen digest from the store lastDigest <- storeGet store itemName $ unResource resource -- Calculate the digest for the resource @@ -65,4 +88,4 @@ resourceModified provider resource store = do else do storeSet store itemName (unResource resource) newDigest return True where - itemName = "Hakyll.Core.ResourceProvider.resourceModified" + itemName = "Hakyll.Core.ResourceProvider.digestModified" diff --git a/src/Hakyll/Core/Resource/Provider/File.hs b/src/Hakyll/Core/Resource/Provider/File.hs index a795fac..953d61c 100644 --- a/src/Hakyll/Core/Resource/Provider/File.hs +++ b/src/Hakyll/Core/Resource/Provider/File.hs @@ -5,6 +5,8 @@ module Hakyll.Core.Resource.Provider.File ) where import Control.Applicative ((<$>)) +import Control.Concurrent (newMVar) +import qualified Data.Map as M import qualified Data.ByteString.Lazy as LB @@ -22,9 +24,13 @@ fileResourceProvider configuration = do list <- map parseIdentifier . filter (not . ignoreFile configuration) <$> getRecursiveContents False "." + -- MVar for the cache + mvar <- newMVar M.empty + -- Construct a resource provider return ResourceProvider { resourceList = map Resource list , resourceString = readFile . toFilePath . unResource , resourceLazyByteString = LB.readFile . toFilePath . unResource + , resourceModifiedCache = mvar } diff --git a/src/Hakyll/Core/Run.hs b/src/Hakyll/Core/Run.hs index 8e1ba6d..54bb104 100644 --- a/src/Hakyll/Core/Run.hs +++ b/src/Hakyll/Core/Run.hs @@ -100,9 +100,7 @@ modified :: ResourceProvider -- ^ Resource provider -> [Identifier] -- ^ Identifiers to check -> IO (Set Identifier) -- ^ Modified resources modified provider store ids = fmap S.fromList $ flip filterM ids $ \id' -> - if resourceExists provider id' - then resourceModified provider (Resource id') store - else return False + resourceModified provider (Resource id') store -- | Add a number of compilers and continue using these compilers -- -- cgit v1.2.3 From 78dbe8a3d127546c8c0cc5b464da0f2b8af7c9b0 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Wed, 6 Apr 2011 09:39:20 +0200 Subject: Add regex predicate helper --- src/Hakyll/Core/Identifier/Pattern.hs | 13 ++++++++++++- tests/Hakyll/Core/Identifier/Tests.hs | 14 +++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) (limited to 'src/Hakyll/Core') diff --git a/src/Hakyll/Core/Identifier/Pattern.hs b/src/Hakyll/Core/Identifier/Pattern.hs index 28e23ad..8f3ac01 100644 --- a/src/Hakyll/Core/Identifier/Pattern.hs +++ b/src/Hakyll/Core/Identifier/Pattern.hs @@ -35,6 +35,7 @@ module Hakyll.Core.Identifier.Pattern ( Pattern , parseGlob , predicate + , regex , matches , filterMatches , capture @@ -46,10 +47,11 @@ module Hakyll.Core.Identifier.Pattern import Data.List (isPrefixOf, inits, tails) import Control.Arrow ((&&&), (>>>)) import Control.Monad (msum) -import Data.Maybe (isJust) +import Data.Maybe (isJust, fromMaybe) import Data.Monoid (Monoid, mempty, mappend) import GHC.Exts (IsString, fromString) +import Text.Regex.PCRE ((=~~)) import Hakyll.Core.Identifier @@ -96,6 +98,15 @@ parseGlob = Glob . parse' predicate :: (Identifier -> Bool) -> Pattern predicate = Predicate +-- | Create a 'Pattern' from a regex +-- +-- Example: +-- +-- > regex "^foo/[^x]*$ +-- +regex :: String -> Pattern +regex str = predicate $ fromMaybe False . (=~~ str) . toFilePath + -- | Check if an identifier matches a pattern -- matches :: Pattern -> Identifier -> Bool diff --git a/tests/Hakyll/Core/Identifier/Tests.hs b/tests/Hakyll/Core/Identifier/Tests.hs index 5b5d34d..0d7bfb8 100644 --- a/tests/Hakyll/Core/Identifier/Tests.hs +++ b/tests/Hakyll/Core/Identifier/Tests.hs @@ -10,7 +10,13 @@ import Hakyll.Core.Identifier.Pattern import TestSuite.Util tests :: [Test] -tests = fromAssertions "capture" +tests = concat + [ captureTests + , regexTests + ] + +captureTests :: [Test] +captureTests = fromAssertions "capture" [ Just ["bar"] @=? capture "foo/**" "foo/bar" , Just ["foo/bar"] @=? capture "**" "foo/bar" , Nothing @=? capture "*" "foo/bar" @@ -25,3 +31,9 @@ tests = fromAssertions "capture" , Just ["foo/bar", "wut"] @=? capture "**/qux/*" "foo/bar/qux/wut" , Just ["lol", "fun/large"] @=? capture "*cat/**.jpg" "lolcat/fun/large.jpg" ] + +regexTests :: [Test] +regexTests = fromAssertions "regex" + [ True @=? matches (regex "^foo/[^x]*$") "foo/bar" + , False @=? matches (regex "^foo/[^x]*$") "foo/barx" + ] -- cgit v1.2.3