hakyll 3.0.2.2 → 3.1.0.0
raw patch · 21 files changed
+313/−197 lines, 21 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Hakyll.Core.Identifier.Pattern: doesMatch :: Pattern -> Identifier -> Bool
- Hakyll.Core.Identifier.Pattern: instance Eq Pattern
- Hakyll.Core.Identifier.Pattern: instance Eq PatternComponent
- Hakyll.Core.Identifier.Pattern: instance Show Pattern
- Hakyll.Core.Identifier.Pattern: instance Show PatternComponent
- Hakyll.Core.Identifier.Pattern: match :: Pattern -> Identifier -> Maybe [Identifier]
- Hakyll.Core.Identifier.Pattern: parsePattern :: String -> Pattern
- Hakyll.Core.ResourceProvider: Resource :: Identifier -> Resource
- Hakyll.Core.ResourceProvider: ResourceProvider :: [Resource] -> (Resource -> IO String) -> (Resource -> IO ByteString) -> ResourceProvider
- Hakyll.Core.ResourceProvider: data ResourceProvider
- Hakyll.Core.ResourceProvider: instance Eq Resource
- Hakyll.Core.ResourceProvider: instance Ord Resource
- Hakyll.Core.ResourceProvider: instance Show Resource
- Hakyll.Core.ResourceProvider: newtype Resource
- Hakyll.Core.ResourceProvider: resourceDigest :: ResourceProvider -> Resource -> IO [Word8]
- Hakyll.Core.ResourceProvider: resourceExists :: ResourceProvider -> Identifier -> Bool
- Hakyll.Core.ResourceProvider: resourceLazyByteString :: ResourceProvider -> Resource -> IO ByteString
- Hakyll.Core.ResourceProvider: resourceList :: ResourceProvider -> [Resource]
- Hakyll.Core.ResourceProvider: resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool
- Hakyll.Core.ResourceProvider: resourceString :: ResourceProvider -> Resource -> IO String
- Hakyll.Core.ResourceProvider: unResource :: Resource -> Identifier
- Hakyll.Core.ResourceProvider.FileResourceProvider: fileResourceProvider :: HakyllConfiguration -> IO ResourceProvider
- Hakyll.Core.Routes: ifMatch :: Pattern -> Routes -> Routes
+ Hakyll.Core.Identifier.Pattern: capture :: Pattern -> Identifier -> Maybe [Identifier]
+ Hakyll.Core.Identifier.Pattern: filterMatches :: Pattern -> [Identifier] -> [Identifier]
+ Hakyll.Core.Identifier.Pattern: instance Eq GlobComponent
+ Hakyll.Core.Identifier.Pattern: instance Monoid Pattern
+ Hakyll.Core.Identifier.Pattern: instance Show GlobComponent
+ Hakyll.Core.Identifier.Pattern: parseGlob :: String -> Pattern
+ Hakyll.Core.Identifier.Pattern: predicate :: (Identifier -> Bool) -> Pattern
+ Hakyll.Core.Identifier.Pattern: regex :: String -> Pattern
+ Hakyll.Core.Resource: Resource :: Identifier -> Resource
+ Hakyll.Core.Resource: instance Eq Resource
+ Hakyll.Core.Resource: instance Ord Resource
+ Hakyll.Core.Resource: instance Show Resource
+ Hakyll.Core.Resource: newtype Resource
+ Hakyll.Core.Resource: unResource :: Resource -> Identifier
+ Hakyll.Core.Resource.Provider: ResourceProvider :: [Resource] -> (Resource -> IO String) -> (Resource -> IO ByteString) -> MVar (Map Resource Bool) -> ResourceProvider
+ Hakyll.Core.Resource.Provider: data ResourceProvider
+ Hakyll.Core.Resource.Provider: resourceDigest :: ResourceProvider -> Resource -> IO [Word8]
+ Hakyll.Core.Resource.Provider: resourceExists :: ResourceProvider -> Identifier -> Bool
+ Hakyll.Core.Resource.Provider: resourceLazyByteString :: ResourceProvider -> Resource -> IO ByteString
+ Hakyll.Core.Resource.Provider: resourceList :: ResourceProvider -> [Resource]
+ Hakyll.Core.Resource.Provider: resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool
+ Hakyll.Core.Resource.Provider: resourceModifiedCache :: ResourceProvider -> MVar (Map Resource Bool)
+ Hakyll.Core.Resource.Provider: resourceString :: ResourceProvider -> Resource -> IO String
+ Hakyll.Core.Resource.Provider.File: fileResourceProvider :: HakyllConfiguration -> IO ResourceProvider
+ Hakyll.Core.Routes: matchRoute :: Pattern -> Routes -> Routes
+ Hakyll.Core.Rules: match :: Pattern -> Rules -> Rules
- Hakyll.Core.Identifier.Pattern: matches :: Pattern -> [Identifier] -> [Identifier]
+ Hakyll.Core.Identifier.Pattern: matches :: Pattern -> Identifier -> Bool
- Hakyll.Core.Rules: compile :: (Binary a, Typeable a, Writable a) => Pattern -> Compiler Resource a -> Rules
+ Hakyll.Core.Rules: compile :: (Binary a, Typeable a, Writable a) => Compiler Resource a -> Rules
- Hakyll.Core.Rules: route :: Pattern -> Routes -> Rules
+ Hakyll.Core.Rules: route :: Routes -> Rules
- Hakyll.Web.Tags: Tags :: Map String [Page a] -> Tags a
+ Hakyll.Web.Tags: Tags :: [(String, [Page a])] -> Tags a
- Hakyll.Web.Tags: tagsMap :: Tags a -> Map String [Page a]
+ Hakyll.Web.Tags: tagsMap :: Tags a -> [(String, [Page a])]
Files
- 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/−0
- src/Hakyll/Core/Resource/Provider.hs +91/−0
- src/Hakyll/Core/Resource/Provider/File.hs +36/−0
- src/Hakyll/Core/ResourceProvider.hs +0/−75
- src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs +0/−29
- 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/Writable/CopyFile.hs +1/−1
- src/Hakyll/Web/CompressCss.hs +1/−1
- src/Hakyll/Web/Page.hs +1/−1
- src/Hakyll/Web/Tags.hs +4/−4
- src/Hakyll/Web/Template.hs +1/−1
hakyll.cabal view
@@ -1,5 +1,5 @@ Name: hakyll-Version: 3.0.2.2+Version: 3.1.0.0 Synopsis: A simple static site generator library. Description: A simple static site generator library, mainly aimed at@@ -81,14 +81,15 @@ 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
src-inotify/Hakyll/Web/Preview/Poll.hs view
@@ -13,7 +13,7 @@ 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
src-interval/Hakyll/Web/Preview/Poll.hs view
@@ -15,7 +15,7 @@ import Hakyll.Core.Configuration import Hakyll.Core.Identifier-import Hakyll.Core.ResourceProvider+import Hakyll.Core.Resource -- | A preview thread that periodically recompiles the site. --
src/Hakyll.hs view
@@ -5,7 +5,8 @@ , 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.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
src/Hakyll/Core/Compiler.hs view
@@ -124,7 +124,8 @@ 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@@ -237,7 +238,7 @@ -> 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
src/Hakyll/Core/Compiler/Internal.hs view
@@ -26,7 +26,7 @@ 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
src/Hakyll/Core/Identifier/Pattern.hs view
@@ -1,5 +1,13 @@ -- | 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,17 @@ -- -- * @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+ , predicate+ , regex , matches+ , filterMatches+ , capture , fromCapture , fromCaptureString , fromCaptures@@ -37,32 +47,39 @@ import Data.List (isPrefixOf, inits, tails) import Control.Arrow ((&&&), (>>>)) import Control.Monad (msum)-import Data.Maybe (isJust)-import Data.Monoid (mempty, mappend)+import Data.Maybe (isJust, fromMaybe)+import Data.Monoid (Monoid, mempty, mappend) import GHC.Exts (IsString, fromString)+import Text.Regex.PCRE ((=~~)) 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 +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 ---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 +89,34 @@ ('*' : xs) -> Literal chunk : Capture : parse' xs xs -> Literal chunk : Literal xs : [] --- | Match an identifier against a pattern, generating a list of captures+-- | Create a 'Pattern' from an arbitrary predicate ---match :: Pattern -> Identifier -> Maybe [Identifier]-match p (Identifier i) = fmap (map Identifier) $ match' (unPattern p) i+-- Example:+--+-- > predicate (\i -> matches "foo/*" i && not (matches "foo/bar" i))+--+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 ---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 +124,35 @@ 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 +164,7 @@ -- | Simplified version of 'fromCapture' which takes a 'String' instead of an -- 'Identifier' ----- > fromCaptureString (parsePattern "tags/*") "foo"+-- > fromCaptureString (parseGlob "tags/*") "foo" -- -- Result: --@@ -141,11 +177,19 @@ -- 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
+ src/Hakyll/Core/Resource.hs view
@@ -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)
+ src/Hakyll/Core/Resource/Provider.hs view
@@ -0,0 +1,91 @@+-- | 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.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)+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+ , -- | Cache keeping track of modified items+ resourceModifiedCache :: MVar (Map Resource Bool)+ }++-- | 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+ 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+ 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.digestModified"
+ src/Hakyll/Core/Resource/Provider/File.hs view
@@ -0,0 +1,36 @@+-- | A concrete 'ResourceProvider' that gets it's resources from the filesystem+--+module Hakyll.Core.Resource.Provider.File+ ( fileResourceProvider+ ) where++import Control.Applicative ((<$>))+import Control.Concurrent (newMVar)+import qualified Data.Map as M++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 "."++ -- 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+ }
− src/Hakyll/Core/ResourceProvider.hs
@@ -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"
− src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs
@@ -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- }
src/Hakyll/Core/Routes.hs view
@@ -30,7 +30,7 @@ , runRoutes , idRoute , setExtension- , ifMatch+ , matchRoute , customRoute , gsubRoute , composeRoutes@@ -85,15 +85,15 @@ 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 pattern, fail+-- otherwise ---ifMatch :: Pattern -> Routes -> Routes-ifMatch pattern (Routes route) = Routes $ \id' ->- if doesMatch pattern 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 'ifMatch'.+-- | Create a custom route. This should almost always be used with+-- 'matchRoute' -- customRoute :: (Identifier -> FilePath) -> Routes customRoute f = Routes $ Just . f
src/Hakyll/Core/Rules.hs view
@@ -7,13 +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+ , match , compile , create , route@@ -23,16 +28,17 @@ 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)+import Data.Monoid (mempty, mappend) 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@@ -63,21 +69,32 @@ -> Rules tellResources resources = RulesM $ tell $ RuleSet mempty mempty $ S.fromList resources +-- | Only compile/route items satisfying the given predicate+--+match :: Pattern -> Rules -> Rules+match pattern = RulesM . local addPredicate . unRulesM+ where+ addPredicate env = env+ { rulesPattern = rulesPattern env `mappend` pattern+ }+ -- | 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+ pattern <- rulesPattern <$> ask+ provider <- rulesResourceProvider <$> ask+ 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 -- -- This sets a compiler for the given identifier. No resource is needed, since@@ -91,10 +108,12 @@ -- | 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+ 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
src/Hakyll/Core/Rules/Internal.hs view
@@ -5,6 +5,7 @@ ( CompileRule (..) , RuleSet (..) , RuleState (..)+ , RuleEnvironment (..) , RulesM (..) , Rules , runRules@@ -17,8 +18,10 @@ 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 import Hakyll.Core.Routes import Hakyll.Core.CompiledItem@@ -55,10 +58,17 @@ { rulesMetaCompilerIndex :: Int } deriving (Show) +-- | Rule environment+--+data RuleEnvironment = RuleEnvironment+ { rulesResourceProvider :: ResourceProvider+ , rulesPattern :: Pattern+ }+ -- | 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 +80,9 @@ -- 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+ , rulesPattern = mempty+ }
src/Hakyll/Core/Run.hs view
@@ -23,8 +23,9 @@ 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@@ -99,9 +100,7 @@ -> [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 --
src/Hakyll/Core/Writable/CopyFile.hs view
@@ -12,7 +12,7 @@ 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
src/Hakyll/Web/CompressCss.hs view
@@ -11,7 +11,7 @@ import Control.Arrow ((>>^)) import Hakyll.Core.Compiler-import Hakyll.Core.ResourceProvider+import Hakyll.Core.Resource import Hakyll.Core.Util.String -- | Compiler form of 'compressCss'
src/Hakyll/Web/Page.hs view
@@ -67,7 +67,7 @@ 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
src/Hakyll/Web/Tags.hs view
@@ -40,7 +40,6 @@ import Prelude hiding (id) import Control.Category (id) import Control.Applicative ((<$>))-import Data.Map (Map) import qualified Data.Map as M import Data.List (intersperse, intercalate) import Control.Arrow (arr, (&&&), (>>>), (***), (<<^), returnA)@@ -65,7 +64,7 @@ -- | Data about tags -- data Tags a = Tags- { tagsMap :: Map String [Page a]+ { tagsMap :: [(String, [Page a])] } deriving (Show, Typeable) instance Binary a => Binary (Tags a) where@@ -91,7 +90,8 @@ -> [Page a] -- ^ Pages -> Tags a -- ^ Resulting tags readTagsWith f pages = Tags- { tagsMap = foldl (M.unionWith (++)) M.empty (map readTagsWith' pages)+ { tagsMap = M.toList $+ foldl (M.unionWith (++)) M.empty (map readTagsWith' pages) } where -- Create a tag map for one page@@ -122,7 +122,7 @@ renderTags makeUrl makeItem concatItems = proc (Tags tags) -> do -- In tags' we create a list: [((tag, route), count)] tags' <- mapCompiler ((id &&& (getRouteFor <<^ makeUrl)) *** arr length)- -< M.toList tags+ -< tags let -- Absolute frequencies of the pages freqs = map snd tags'
src/Hakyll/Web/Template.hs view
@@ -59,7 +59,7 @@ 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