heist 0.10.2.1 → 0.11.0
raw patch · 12 files changed
+274/−47 lines, 12 filesdep ~hashablePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: hashable
API changes (from Hackage documentation)
- Heist.Compiled: compileNode :: Monad n => Node -> Splice n
- Heist.Compiled: runSplice :: Monad n => Node -> HeistState n -> Splice n -> IO [Chunk n]
+ Heist: compiledSpliceNames :: HeistState m -> [Text]
+ Heist.Compiled: applySnd :: a -> [(d, a -> b)] -> [(d, b)]
+ Heist.Compiled: callTemplate :: Monad n => ByteString -> HeistT n IO (DList (Chunk n))
+ Heist.Compiled: defer :: Monad n => (Promise a -> Splice n) -> RuntimeSplice n a -> Splice n
+ Heist.Compiled: deferMany :: Monad n => (Promise a -> Splice n) -> RuntimeSplice n [a] -> Splice n
+ Heist.Compiled: manyWithSplices :: Monad n => Splice n -> [(Text, Promise a -> Splice n)] -> n [a] -> Splice n
+ Heist.Compiled: mapInputPromise :: Monad m => (a -> b) -> (Promise b -> Splice m) -> Promise a -> Splice m
+ Heist.Compiled: mapSnd :: (b -> c) -> [(d, b)] -> [(d, c)]
+ Heist.Compiled: nodeSplice :: (a -> [Node]) -> a -> Builder
+ Heist.Compiled: nodeSplices :: [(Text, a -> [Node])] -> [(Text, a -> Builder)]
+ Heist.Compiled: pureSplice :: Monad m => (a -> Builder) -> Promise a -> Splice m
+ Heist.Compiled: pureSplices :: Monad m => [(d, a -> Builder)] -> [(d, Promise a -> Splice m)]
+ Heist.Compiled: runChildren :: Monad n => Splice n
+ Heist.Compiled: textSplice :: (a -> Text) -> a -> Builder
+ Heist.Compiled: textSplices :: [(Text, a -> Text)] -> [(Text, a -> Builder)]
+ Heist.Compiled: withLocalSplices :: [(Text, Splice n)] -> [(Text, AttrSplice n)] -> HeistT n IO a -> HeistT n IO a
+ Heist.Compiled: withPureSplices :: Monad n => Splice n -> [(Text, a -> Builder)] -> n a -> Splice n
+ Heist.Compiled: withSplices :: Monad n => Splice n -> [(Text, Promise a -> Splice n)] -> n a -> Splice n
+ Heist.Splices: ifCSplice :: Monad m => (t -> Bool) -> Promise t -> Splice m
+ Heist.Splices: ifISplice :: Monad m => Bool -> Splice m
- Heist: type AttrSplice m = Text -> m [(Text, Text)]
+ Heist: type AttrSplice m = Text -> RuntimeSplice m [(Text, Text)]
- Heist.Compiled: codeGen :: Monad m => [Chunk m] -> RuntimeSplice m Builder
+ Heist.Compiled: codeGen :: Monad m => DList (Chunk m) -> RuntimeSplice m Builder
- Heist.Compiled: mapPromises :: Monad n => (Promise a -> HeistT n IO (RuntimeSplice n Builder)) -> n [a] -> Splice n
+ Heist.Compiled: mapPromises :: Monad n => (Promise a -> HeistT n IO (RuntimeSplice n Builder)) -> RuntimeSplice n [a] -> Splice n
- Heist.Interpreted: textSplice :: Monad n => Text -> Splice n
+ Heist.Interpreted: textSplice :: Monad m => Text -> HeistT n m Template
Files
- heist.cabal +2/−2
- src/Heist.hs +1/−0
- src/Heist/Common.hs +9/−0
- src/Heist/Compiled.hs +22/−5
- src/Heist/Compiled/Internal.hs +183/−14
- src/Heist/Interpreted.hs +1/−1
- src/Heist/Interpreted/Internal.hs +5/−12
- src/Heist/Splices.hs +39/−7
- src/Heist/Splices/Cache.hs +1/−1
- src/Heist/Types.hs +8/−2
- test/suite/Heist/Tests.hs +1/−1
- test/suite/Heist/Tutorial/CompiledSplices.lhs +2/−2
heist.cabal view
@@ -1,5 +1,5 @@ name: heist-version: 0.10.2.1+version: 0.11.0 synopsis: An Haskell template system supporting both HTML5 and XML. description: Heist is a powerful template system that supports both HTML5 and XML.@@ -149,7 +149,7 @@ dlist >= 0.5 && < 0.6, errors >= 1.3 && < 1.4, filepath >= 1.3 && < 1.4,- hashable >= 1.1 && < 1.2,+ hashable >= 1.1 && < 1.3, mtl >= 2.0 && < 2.2, process >= 1.1 && < 1.2, random >= 1.0.1.0 && < 1.1,
src/Heist.hs view
@@ -37,6 +37,7 @@ , compiledTemplateNames , hasTemplate , spliceNames+ , compiledSpliceNames , HeistT , evalHeistT , getParamNode
src/Heist/Common.hs view
@@ -280,3 +280,12 @@ enc X.UTF16LE = "utf-16" +------------------------------------------------------------------------------+-- | Binds a set of new splice declarations within a 'HeistState'.+bindAttributeSplices :: [(T.Text, AttrSplice n)] -- ^ splices to bind+ -> HeistState n -- ^ start state+ -> HeistState n+bindAttributeSplices ss hs =+ hs { _attrSpliceMap = Map.union (Map.fromList ss) (_attrSpliceMap hs) }++
src/Heist/Compiled.hs view
@@ -17,6 +17,26 @@ -- * High level compiled splice API Splice , renderTemplate+ , codeGen+ , runChildren++ -- * Functions for manipulating lists of compiled splices+ , mapSnd+ , applySnd+ , textSplices+ , nodeSplices+ , pureSplices+ , textSplice+ , nodeSplice+ , pureSplice+ , mapInputPromise+ , defer+ , deferMany+ , withSplices+ , manyWithSplices+ , withPureSplices++ -- * Old compiled splice API , mapPromises , promiseChildren , promiseChildrenWith@@ -33,23 +53,20 @@ , yieldRuntimeText , yieldLater , addSplices+ , withLocalSplices -- * Lower level promise functions , Promise , newEmptyPromise-- -- * RuntimeSplice functions , getPromise , putPromise , adjustPromise- , codeGen -- * Running nodes and splices , runNodeList , runNode- , compileNode , runAttributes- , runSplice+ , callTemplate ) where
src/Heist/Compiled/Internal.hs view
@@ -8,8 +8,11 @@ module Heist.Compiled.Internal where ++------------------------------------------------------------------------------ import Blaze.ByteString.Builder import Control.Arrow+import Control.Monad import Control.Monad.RWS.Strict import Control.Monad.State.Strict import qualified Data.Attoparsec.Text as AP@@ -25,10 +28,12 @@ import qualified Data.Vector as V import Prelude hiding (catch) import qualified Text.XmlHtml as X-+------------------------------------------------------------------------------ import Heist.Common import Heist.Types+------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ -- | A compiled Splice is a HeistT computation that returns a @DList -- (Chunk m)@.@@ -44,6 +49,16 @@ ------------------------------------------------------------------------------+-- | Runs the parameter node's children and returns the resulting compiled+-- chunks. By itself this function is a simple passthrough splice that makes+-- the spliced node disappear. In combination with locally bound splices,+-- this function makes it easier to pass the desired view into your splices.+runChildren :: Monad n => Splice n+runChildren = runNodeList . X.childNodes =<< getParamNode+{-# INLINE runChildren #-}+++------------------------------------------------------------------------------ -- | Takes a promise function and a runtime action returning a list of items -- that fit in the promise and returns a Splice that executes the promise -- function for each item and concatenates the results.@@ -55,14 +70,14 @@ => (Promise a -> HeistT n IO (RuntimeSplice n Builder)) -- ^ Use 'promiseChildrenWith' or a variant to create this -- function.- -> n [a]+ -> RuntimeSplice n [a] -- ^ Runtime computation returning a list of items -> Splice n mapPromises f getList = do singlePromise <- newEmptyPromise runSingle <- f singlePromise return $ yieldRuntime $ do- list <- lift getList+ list <- getList htmls <- forM list $ \item -> putPromise singlePromise item >> runSingle return $ mconcat htmls@@ -71,9 +86,7 @@ ------------------------------------------------------------------------------ -- | Returns a runtime computation that simply renders the node's children. promiseChildren :: Monad m => HeistT m IO (RuntimeSplice m Builder)-promiseChildren = do- res <- runNodeList . X.childNodes =<< getParamNode- return $! codeGen $! consolidate res+promiseChildren = liftM codeGen runChildren {-# INLINE promiseChildren #-} @@ -293,8 +306,9 @@ -- | Given a list of output chunks, consolidate turns consecutive runs of -- @Pure Html@ values into maximally-efficient pre-rendered strict -- 'ByteString' chunks.-codeGen :: Monad m => [Chunk m] -> RuntimeSplice m Builder-codeGen l = V.foldr mappend mempty $! V.map toAct $! V.fromList l+codeGen :: Monad m => DList (Chunk m) -> RuntimeSplice m Builder+codeGen l = V.foldr mappend mempty $!+ V.map toAct $! V.fromList $! consolidate l where toAct !(RuntimeHtml !m) = m toAct !(Pure !h) = return $! fromByteString h@@ -310,8 +324,8 @@ ------------------------------------------------------------------------------ -- | Runs a single node. If there is no splice referenced anywhere in the--- subtree, then it is rendered as a pure chunk, otherwise it is compiled to--- a runtime computation.+-- subtree, then it is rendered as a pure chunk, otherwise it calls+-- compileNode to generate the appropriate runtime computation. runNode :: Monad n => X.Node -> Splice n runNode node = localParamNode (const node) $ do isStatic <- subtreeIsStatic node@@ -393,14 +407,14 @@ return $ attrToChunk k value -- Handles attribute splices- doAttrSplice splice = DL.singleton $ RuntimeHtml $ lift $ do+ doAttrSplice splice = DL.singleton $ RuntimeHtml $ do res <- splice v return $ mconcat $ map attrToBuilder res ------------------------------------------------------------------------------ -- | Given a 'X.Node' in the DOM tree, produces a \"runtime splice\" that will--- generate html at runtime. Leaves the writer monad state untouched.+-- generate html at runtime. compileNode :: Monad n => X.Node -> Splice n compileNode (X.Element nm attrs ch) = -- Is this node a splice, or does it merely contain splices?@@ -594,9 +608,21 @@ -- during load-time splice processing. addSplices :: Monad m => [(Text, Splice n)] -> HeistT n m () addSplices ss = modifyHS (bindSplices ss)+{-# DEPRECATED addSplices "addSplices will be removed in the next release! Use withLocalSplices instead."#-} ------------------------------------------------------------------------------+-- | Adds a list of compiled splices to the splice map. This function is+-- useful because it allows compiled splices to bind other compiled splices+-- during load-time splice processing.+withLocalSplices :: [(Text, Splice n)]+ -> [(Text, AttrSplice n)]+ -> HeistT n IO a+ -> HeistT n IO a+withLocalSplices ss as = localHS (bindSplices ss . bindAttributeSplices as)+++------------------------------------------------------------------------------ -- | Looks up a compiled template and returns a runtime monad computation that -- constructs a builder. renderTemplate :: Monad n@@ -604,9 +630,152 @@ -> ByteString -> Maybe (n Builder, MIMEType) renderTemplate hs nm =- fmap (first interpret . fst) $! lookupTemplate nm hs _compiledTemplateMap+ fmap (first (interpret . DL.fromList) . fst) $!+ lookupTemplate nm hs _compiledTemplateMap -interpret :: Monad m => [Chunk m] -> m Builder++------------------------------------------------------------------------------+-- | Looks up a compiled template and returns a compiled splice.+callTemplate :: Monad n+ => ByteString+ -> HeistT n IO (DList (Chunk n))+callTemplate nm = do+ hs <- getHS+ return $ maybe DL.empty (DL.fromList . fst . fst) $+ lookupTemplate nm hs _compiledTemplateMap+++interpret :: Monad m => DList (Chunk m) -> m Builder interpret = flip evalStateT HE.empty . unRT . codeGen+++------------------------------------------------------------------------------+-- Functions for manipulating lists of compiled splices+------------------------------------------------------------------------------+++mapSnd :: (b -> c) -> [(d, b)] -> [(d, c)]+mapSnd = map . second++applySnd :: a -> [(d, a -> b)] -> [(d, b)]+applySnd a = mapSnd ($a)++textSplices :: [(Text, a -> Text)] -> [(Text, a -> Builder)]+textSplices = mapSnd textSplice++textSplice :: (a -> Text) -> a -> Builder+textSplice f = fromByteString . T.encodeUtf8 . f++nodeSplices :: [(Text, a -> [X.Node])] -> [(Text, a -> Builder)]+nodeSplices = mapSnd nodeSplice++nodeSplice :: (a -> [X.Node]) -> a -> Builder+nodeSplice f = X.renderHtmlFragment X.UTF8 . f++pureSplices :: Monad m => [(d, a -> Builder)] -> [(d, Promise a -> Splice m)]+pureSplices = mapSnd pureSplice++pureSplice :: Monad m => (a -> Builder) -> Promise a -> Splice m+pureSplice f p = do+ return $ yieldRuntime $ do+ a <- getPromise p+ return $ f a++mapInputPromise :: Monad m+ => (a -> b)+ -> (Promise b -> Splice m)+ -> Promise a -> Splice m+mapInputPromise f g p1 = do+ p2 <- newEmptyPromise+ let action = yieldRuntimeEffect $ do+ a <- getPromise p1+ putPromise p2 (f a)+ res <- g p2+ return $ action `mappend` res+++------------------------------------------------------------------------------+-- | Allows you to use deferred Promises in a compiled splice. It takes care+-- of the boilerplate of creating and storing data in a promise to be used at+-- load time when compiled splices are processed. This function is similar to+-- mapPromises but runs on a single value instead of a list.+defer :: Monad n+ => (Promise a -> Splice n)+ -> RuntimeSplice n a+ -> Splice n+defer f getItem = do+ promise <- newEmptyPromise+ chunks <- f promise+ return $ yieldRuntime $ do+ item <- getItem+ putPromise promise item+ codeGen chunks+++------------------------------------------------------------------------------+-- | Takes a promise function and a runtime action returning a list of items+-- that fit in the promise and returns a Splice that executes the promise+-- function for each item and concatenates the results.+deferMany :: Monad n+ => (Promise a -> Splice n)+ -> RuntimeSplice n [a]+ -> Splice n+deferMany f getItems = do+ promise <- newEmptyPromise+ chunks <- f promise+ return $ yieldRuntime $ do+ items <- getItems+ res <- forM items $ \item -> do+ putPromise promise item+ codeGen chunks+ return $ mconcat res+++withSplices :: Monad n+ => Splice n+ -> [(Text, Promise a -> Splice n)]+ -> n a+ -> Splice n+withSplices splice splices runtimeAction = do+ p <- newEmptyPromise+ let splices' = mapSnd ($p) splices+ chunks <- withLocalSplices splices' [] splice+ let fillPromise = yieldRuntimeEffect $ putPromise p =<< lift runtimeAction+ return $ fillPromise `mappend` chunks+++------------------------------------------------------------------------------+-- | Gets a list of items at runtime, then for each item it runs the splice+-- with the list of splices bound. There is no pure variant of this function+-- because the desired behavior can only be achieved as a function of a+-- Promise.+manyWithSplices :: Monad n+ => Splice n+ -- ^ Splice to run for each of the items in the runtime list.+ -- You'll frequently use 'runChildren' here.+ -> [(Text, Promise a -> Splice n)]+ -- ^ List of splices to bind+ -> n [a]+ -- ^ Runtime action returning a list of items to render.+ -> Splice n+manyWithSplices splice splices runtimeAction = do+ p <- newEmptyPromise+ let splices' = mapSnd ($p) splices+ chunks <- withLocalSplices splices' [] splice+ return $ yieldRuntime $ do+ items <- lift runtimeAction+ res <- forM items $ \item -> putPromise p item >> codeGen chunks+ return $ mconcat res+++withPureSplices :: Monad n+ => Splice n+ -> [(Text, a -> Builder)]+ -> n a+ -> Splice n+withPureSplices splice splices action = do+ let fieldSplice g = return $ yieldRuntime $ liftM g $ lift action+ let splices' = map (second fieldSplice) splices+ withLocalSplices splices' [] splice
src/Heist/Interpreted.hs view
@@ -78,5 +78,5 @@ ) where import Heist.Interpreted.Internal-import Heist.Common (mapSplices)+import Heist.Common (mapSplices, bindAttributeSplices)
src/Heist/Interpreted/Internal.hs view
@@ -10,11 +10,12 @@ import Blaze.ByteString.Builder import Control.Arrow hiding (loop) import Control.Monad-import Control.Monad.Trans+import Control.Monad.State.Strict import qualified Data.Attoparsec.Text as AP import Data.ByteString (ByteString) import Data.List import qualified Data.HashMap.Strict as Map+import qualified Data.HeterogeneousEnvironment as HE import Data.Maybe import Data.Monoid import qualified Data.Text as T@@ -62,17 +63,8 @@ --------------------------------------------------------------------------------- | Binds a set of new splice declarations within a 'HeistState'.-bindAttributeSplices :: [(Text, AttrSplice n)] -- ^ splices to bind- -> HeistState n -- ^ start state- -> HeistState n-bindAttributeSplices ss hs =- hs { _attrSpliceMap = Map.union (Map.fromList ss) (_attrSpliceMap hs) }--------------------------------------------------------------------------------- -- | Converts 'Text' to a splice returning a single 'TextNode'.-textSplice :: Monad n => Text -> Splice n+textSplice :: Monad m => Text -> HeistT n m Template textSplice t = return [X.TextNode t] @@ -211,7 +203,8 @@ runAttrSplice :: (Monad n) => (Text, Text) -> HeistT n n [(Text, Text)] runAttrSplice a@(k,v) = do splice <- getsHS (Map.lookup k . _attrSpliceMap)- maybe (liftM (:[]) $ attSubst a) (lift . ($v)) splice+ maybe (liftM (:[]) $ attSubst a)+ (lift . flip evalStateT HE.empty . unRT . ($v)) splice ------------------------------------------------------------------------------
src/Heist/Splices.hs view
@@ -1,5 +1,7 @@ module Heist.Splices- ( module Heist.Splices.Apply+ ( ifISplice+ , ifCSplice+ , module Heist.Splices.Apply , module Heist.Splices.Bind , module Heist.Splices.Cache , module Heist.Splices.Html@@ -7,10 +9,40 @@ , module Heist.Splices.Markdown ) where -import Heist.Splices.Apply-import Heist.Splices.Bind-import Heist.Splices.Cache-import Heist.Splices.Html-import Heist.Splices.Ignore-import Heist.Splices.Markdown+import Data.Monoid+import qualified Heist.Compiled as C+import qualified Heist.Interpreted as I+import Heist.Splices.Apply+import Heist.Splices.Bind+import Heist.Splices.Cache+import Heist.Splices.Html+import Heist.Splices.Ignore+import Heist.Splices.Markdown++------------------------------------------------------------------------------+-- | Run the splice contents if given condition is True, make splice disappear+-- if not.+ifISplice :: Monad m => Bool -> I.Splice m+ifISplice cond =+ case cond of+ False -> return []+ True -> I.runChildren+++------------------------------------------------------------------------------+-- | Function for constructing if splices that use a runtime predicate+-- function to determine whether the node's children should be rendered.+ifCSplice :: Monad m+ => (t -> Bool)+ -> C.Promise t+ -> C.Splice m+ifCSplice predicate prom = do+ chunks <- C.runChildren+ return $ C.yieldRuntime $ do+ a <- C.getPromise prom+ if predicate a+ then+ C.codeGen chunks+ else+ return mempty
src/Heist/Splices/Cache.hs view
@@ -136,7 +136,7 @@ ref <- liftIO $ newIORef Nothing liftIO $ addCompiledRef ref cts let reload curTime = do- builder <- C.codeGen $! C.consolidate compiled+ builder <- C.codeGen compiled let out = fromByteString $! toByteString $! builder liftIO $ writeIORef ref (Just (curTime, out)) return $! out
src/Heist/Types.hs view
@@ -124,7 +124,7 @@ -- | Type alias for attribute splices. The function parameter is the value of -- the bound attribute splice. The return value is a list of attribute -- key/value pairs that get substituted in the place of the bound attribute.-type AttrSplice m = Text -> m [(Text, Text)]+type AttrSplice m = Text -> RuntimeSplice m [(Text, Text)] ------------------------------------------------------------------------------@@ -212,9 +212,15 @@ --------------------------------------------------------------------------------- | Gets the names of all the splices defined in a HeistState.+-- | Gets the names of all the interpreted splices defined in a HeistState. spliceNames :: HeistState m -> [Text] spliceNames ts = H.keys $ _spliceMap ts+++------------------------------------------------------------------------------+-- | Gets the names of all the compiled splices defined in a HeistState.+compiledSpliceNames :: HeistState m -> [Text]+compiledSpliceNames ts = H.keys $ _compiledSpliceMap ts ------------------------------------------------------------------------------
test/suite/Heist/Tests.hs view
@@ -58,7 +58,7 @@ attrSpliceTest :: IO () attrSpliceTest = do- ehs <- loadT "templates" [] [] [] [("autocheck", autocheckedSplice)]+ ehs <- loadT "templates" [] [] [] [("autocheck", lift . autocheckedSplice)] let hs = either (error . show) id ehs runtime = fromJust $ C.renderTemplate hs "attr_splice"
test/suite/Heist/Tutorial/CompiledSplices.lhs view
@@ -149,12 +149,12 @@ > ] > > peopleSplice :: (Monad n)-> => n [Person]+> => RuntimeSplice n [Person] > -> C.Splice n > peopleSplice getPeople = C.mapPromises personSplice getPeople > > allPeopleSplice :: C.Splice (StateT [Person] IO)-> allPeopleSplice = peopleSplice get+> allPeopleSplice = peopleSplice (lift get) > > personListTest :: FilePath > -> IO ByteString