heist 1.0.0.0 → 1.0.1.0
raw patch · 27 files changed
+308/−67 lines, 27 filesdep −errorsdep ~HUnitdep ~QuickCheckdep ~aesonPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies removed: errors
Dependency ranges changed: HUnit, QuickCheck, aeson, blaze-html, criterion, directory, lens, process, statistics, test-framework, test-framework-hunit, time, vector
API changes (from Hackage documentation)
+ Heist: CompileException :: e -> [SpliceError] -> CompileException
+ Heist: [contextNode] :: SpliceError -> Node
+ Heist: [exceptionContext] :: CompileException -> [SpliceError]
+ Heist: [originalException] :: CompileException -> e
+ Heist: data CompileException
+ Heist.Compiled: defer :: Monad n => (RuntimeSplice n a -> Splice n) -> RuntimeSplice n a -> Splice n
+ Heist.Compiled: manyWith :: (Foldable f, Monad n) => Splice n -> Splices (RuntimeSplice n a -> Splice n) -> Splices (RuntimeSplice n a -> AttrSplice n) -> RuntimeSplice n (f a) -> Splice n
+ Heist.Internal.Types: CompileException :: e -> [SpliceError] -> CompileException
+ Heist.Internal.Types: [contextNode] :: SpliceError -> Node
+ Heist.Internal.Types: [exceptionContext] :: CompileException -> [SpliceError]
+ Heist.Internal.Types: [originalException] :: CompileException -> e
+ Heist.Internal.Types: data CompileException
+ Heist.Internal.Types: spliceErrorText :: SpliceError -> Text
- Heist: SpliceError :: [(TPath, Maybe FilePath, Text)] -> Maybe FilePath -> [Text] -> Text -> SpliceError
+ Heist: SpliceError :: [(TPath, Maybe FilePath, Text)] -> Maybe FilePath -> [Text] -> Node -> Text -> SpliceError
- Heist.Compiled: callTemplate :: Monad n => ByteString -> HeistT n IO (DList (Chunk n))
+ Heist.Compiled: callTemplate :: Monad n => ByteString -> Splice n
- Heist.Internal.Types: SpliceError :: [(TPath, Maybe FilePath, Text)] -> Maybe FilePath -> [Text] -> Text -> SpliceError
+ Heist.Internal.Types: SpliceError :: [(TPath, Maybe FilePath, Text)] -> Maybe FilePath -> [Text] -> Node -> Text -> SpliceError
Files
- heist.cabal +22/−7
- src/Heist.hs +1/−0
- src/Heist/Common.hs +2/−15
- src/Heist/Compiled.hs +2/−0
- src/Heist/Compiled/Internal.hs +49/−33
- src/Heist/Internal/Types/HeistState.hs +43/−0
- test/suite/Benchmark.hs +1/−1
- test/suite/Heist/Compiled/Tests.hs +119/−6
- test/suite/Heist/Interpreted/Tests.hs +0/−2
- test/suite/Heist/TestCommon.hs +21/−2
- test/suite/Heist/Tutorial/Imports.hs +1/−1
- test/templates-defer/test.tpl +5/−0
- test/templates-loaderror/_error.tpl +1/−0
- test/templates-loaderror/_ok.tpl +1/−0
- test/templates-loaderror/test.tpl +3/−0
- test/templates-ns-nested/test.tpl +1/−0
- test/templates-nsbind/nsbind.tpl +7/−0
- test/templates-nsbind/nsbinderror.tpl +10/−0
- test/templates-nscall/_call.tpl +2/−0
- test/templates-nscall/_invalid.tpl +1/−0
- test/templates-nscall/nscall.tpl +6/−0
- test/templates/backslash.tpl +1/−0
- test/templates/json_array.tpl +1/−0
- test/templates/namespaces.tpl +5/−0
- test/templates/pandoc.tpl +1/−0
- test/templates/pandocdiv.tpl +1/−0
- test/templates/rss.xtpl +1/−0
heist.cabal view
@@ -1,5 +1,5 @@ name: heist-version: 1.0.0.0+version: 1.0.1.0 synopsis: An Haskell template system supporting both HTML5 and XML. description: Heist is a powerful template system that supports both HTML5 and XML.@@ -67,6 +67,7 @@ test/templates/attrs.tpl, test/templates/attrsubtest1.tpl, test/templates/attrsubtest2.tpl,+ test/templates/backslash.tpl test/templates/bar/a.tpl, test/templates/bar/index.tpl, test/templates/bind-apply-interaction/_outer.tpl,@@ -86,13 +87,18 @@ test/templates/index.tpl, test/templates/ioc.tpl, test/templates/json.tpl,+ test/templates/json_array.tpl test/templates/json_object.tpl, test/templates/json_snippet.tpl, test/templates/markdown.tpl,+ test/templates/namespaces.tpl test/templates/page.tpl,+ test/templates/pandoc.tpl+ test/templates/pandocdiv.tpl test/templates/people.tpl, test/templates/post.tpl, test/templates/readme.txt,+ test/templates/rss.xtpl test/templates/test.md, test/templates/textarea_expansion.tpl, test/templates/title_expansion.tpl,@@ -104,6 +110,16 @@ test/templates-bad/apply-template-not-found.tpl, test/templates-bad/bind-infinite-loop.tpl, test/templates-bad/bind-missing-attr.tpl,+ test/templates-defer/test.tpl,+ test/templates-loaderror/_error.tpl,+ test/templates-loaderror/_ok.tpl,+ test/templates-loaderror/test.tpl,+ test/templates-nsbind/nsbind.tpl,+ test/templates-nsbind/nsbinderror.tpl,+ test/templates-ns-nested/test.tpl,+ test/templates-nscall/_call.tpl,+ test/templates-nscall/_invalid.tpl,+ test/templates-nscall/nscall.tpl, TODO @@ -135,7 +151,7 @@ Heist.Interpreted.Internal build-depends:- aeson >= 0.6 && < 0.12,+ aeson >= 0.6 && < 1.1, attoparsec >= 0.10 && < 0.14, base >= 4 && < 5, blaze-builder >= 0.2 && < 0.5,@@ -191,7 +207,7 @@ build-depends: HUnit >= 1.2 && < 2,- QuickCheck >= 2 && < 2.9,+ QuickCheck >= 2 && < 2.10, lens >= 4.3 && < 4.15, test-framework >= 0.4 && < 0.9, test-framework-hunit >= 0.2.7 && < 0.4,@@ -199,7 +215,7 @@ aeson, attoparsec, base,- bifunctors >= 5.3 && < 5.4,+ bifunctors >= 5.3 && < 5.5, blaze-builder, blaze-html, bytestring,@@ -207,7 +223,6 @@ directory, directory-tree, dlist,- errors, filepath, hashable, lifted-base,@@ -226,8 +241,9 @@ ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -threaded Extensions: OverloadedStrings -Executable benchmark+Benchmark benchmark hs-source-dirs: src test/suite+ type: exitcode-stdio-1.0 main-is: Benchmark.hs build-depends:@@ -248,7 +264,6 @@ directory, directory-tree, dlist,- errors, filepath, hashable, lifted-base,
src/Heist.hs view
@@ -41,6 +41,7 @@ , Chunk , HeistState , SpliceError(..)+ , CompileException(..) , HeistT -- * Lenses (can be used with lens or lens-family)
src/Heist/Common.hs view
@@ -91,28 +91,15 @@ tellSpliceError :: Monad m => Text -> HeistT n m () tellSpliceError msg = do hs <- getHS+ node <- getParamNode let spliceError = SpliceError { spliceHistory = _splicePath hs , spliceTemplateFile = _curTemplateFile hs , visibleSplices = Map.keys $ _compiledSpliceMap hs+ , contextNode = node , spliceMsg = msg } modifyHS (\hs' -> hs { _spliceErrors = spliceError : _spliceErrors hs' })------------------------------------------------------------------------------------ | Transform a SpliceError record to a Text message.-spliceErrorText :: SpliceError -> Text-spliceErrorText (SpliceError hist tf splices msg) =- (maybe "" ((`mappend` ": ") . T.pack) tf) `T.append` msg `T.append`- foldr (\(_, tf', tag) -> (("\n ... via " `T.append`- (maybe "" ((`mappend` ": ") . T.pack) tf')- `T.append` tag) `T.append`)) T.empty hist- `T.append`- if null splices- then T.empty- else "\nBound splices:" `T.append`- foldl (\x y -> x `T.append` " " `T.append` y) T.empty splices ------------------------------------------------------------------------------
src/Heist/Compiled.hs view
@@ -28,11 +28,13 @@ , pureSplice , deferMany+ , defer , deferMap , mayDeferMap , bindLater , withSplices , manyWithSplices+ , manyWith , withLocalSplices -- * Constructing Chunks
src/Heist/Compiled/Internal.hs view
@@ -14,6 +14,7 @@ import Blaze.ByteString.Builder import Blaze.ByteString.Builder.Char.Utf8 import Control.Arrow+import Control.Exception import Control.Monad import Control.Monad.RWS.Strict import Control.Monad.State.Strict@@ -290,14 +291,33 @@ let pre = _splicePrefix hs let hasPrefix = (T.isPrefixOf pre `fmap` X.tagName node) == Just True when (not (T.null pre) && hasPrefix) incNamespacedTags- isStatic <- subtreeIsStatic node- markup <- getsHS _curMarkup- if isStatic- then return $! yieldPure $! renderFragment markup [parseAttrs node]- else localHS (\hs' -> hs' {_splicePath =- (_curContext hs', _curTemplateFile hs',- X.elementTag node):(_splicePath hs')}) $- compileNode node+ hs' <- getHS+ -- Plain rethrows for CompileException to avoid multiple annotations.+ (res, hs'') <- liftIO $ catches (compileIO hs')+ [ Handler (\(ex :: CompileException) -> throwIO ex)+ , Handler (\(ex :: SomeException) -> handleError ex hs')]+ putHS hs''+ return res+ where+ localSplicePath =+ localHS (\hs -> hs {_splicePath = (_curContext hs,+ _curTemplateFile hs,+ X.elementTag node):+ (_splicePath hs)})+ compileIO hs = runHeistT compile node hs+ compile = do+ isStatic <- subtreeIsStatic node+ dl <- compile' isStatic+ liftIO $ evaluate $ DL.fromList $! consolidate dl+ compile' True = do+ markup <- getsHS _curMarkup+ return $! yieldPure $! renderFragment markup [parseAttrs node]+ compile' False = localSplicePath $ compileNode node+ handleError ex hs = do+ errs <- evalHeistT (do localSplicePath $ tellSpliceError $ T.pack $+ "Exception in splice compile: " ++ show ex+ getsHS _spliceErrors) node hs+ throwIO $ CompileException ex errs parseAttrs :: X.Node -> X.Node@@ -606,13 +626,14 @@ -- | Looks up a compiled template and returns a compiled splice. callTemplate :: Monad n => ByteString- -> HeistT n IO (DList (Chunk n))+ -> Splice n callTemplate nm = do hs <- getHS- runNodeList $ maybe (error err) (X.docContent . dfDoc . fst) $- lookupTemplate nm hs _templateMap+ maybe (error err) call $ lookupTemplate nm hs _templateMap where err = "callTemplate: "++(T.unpack $ T.decodeUtf8 nm)++(" does not exist")+ call (df,_) = localHS (\hs' -> hs' {_curTemplateFile = dfFile df}) $+ runNodeList $ X.docContent $ dfDoc df interpret :: Monad n => DList (Chunk n) -> n Builder@@ -735,42 +756,37 @@ ------------------------------------------------------------------------------ -- | Saves the results of a runtme computation in a 'Promise' so they don't -- get recalculated if used more than once.+--+-- Note that this is just a specialized version of function application ($)+-- done for the side effect in runtime splice.+defer :: Monad n+ => (RuntimeSplice n a -> Splice n)+ -> RuntimeSplice n a -> Splice n+defer pf n = do+ p2 <- newEmptyPromise+ let action = yieldRuntimeEffect $ putPromise p2 =<< n+ res <- pf $ getPromise p2+ return $ action `mappend` res+++------------------------------------------------------------------------------+-- | A version of defer which applies a function on the runtime value. deferMap :: Monad n => (a -> RuntimeSplice n b) -> (RuntimeSplice n b -> Splice n) -> RuntimeSplice n a -> Splice n-deferMap f pf n = do- p2 <- newEmptyPromise- let action = yieldRuntimeEffect $ putPromise p2 =<< f =<< n- res <- pf $ getPromise p2- return $ action `mappend` res+deferMap f pf n = defer pf $ f =<< n ------------------------------------------------------------------------------ -- | Like deferMap, but only runs the result if a Maybe function of the -- runtime value returns Just. If it returns Nothing, then no output is -- generated.------ This is a good example of how to do more complex flow control with--- promises. The generalization of this abstraction is too complex to be--- distilled to elegant high-level combinators. If you need to implement your--- own special flow control, then you should use functions from the--- `Heist.Compiled.LowLevel` module similarly to how it is done in the--- implementation of this function. mayDeferMap :: Monad n => (a -> RuntimeSplice n (Maybe b)) -> (RuntimeSplice n b -> Splice n) -> RuntimeSplice n a -> Splice n-mayDeferMap f pf n = do- p2 <- newEmptyPromise- action <- pf $ getPromise p2- return $ yieldRuntime $ do- mb <- f =<< n- case mb of- Nothing -> return mempty- Just b -> do- putPromise p2 b- codeGen action+mayDeferMap f pf n = deferMany pf $ f =<< n ------------------------------------------------------------------------------
src/Heist/Internal/Types/HeistState.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -23,6 +24,7 @@ import Blaze.ByteString.Builder (Builder) import Control.Applicative (Alternative (..)) import Control.Arrow (first)+import Control.Exception (Exception) import Control.Monad (MonadPlus (..), ap) import Control.Monad.Base import Control.Monad.Cont (MonadCont (..))@@ -171,11 +173,50 @@ { spliceHistory :: [(TPath, Maybe FilePath, Text)] , spliceTemplateFile :: Maybe FilePath , visibleSplices :: [Text]+ , contextNode :: X.Node , spliceMsg :: Text } deriving ( Show, Eq ) ------------------------------------------------------------------------------+-- | Transform a SpliceError record to a Text message.+spliceErrorText :: SpliceError -> Text+spliceErrorText (SpliceError hist tf splices node msg) =+ (maybe "" ((`mappend` ": ") . T.pack) tf) `T.append` msg `T.append`+ foldr (\(_, tf', tag) -> (("\n ... via " `T.append`+ (maybe "" ((`mappend` ": ") . T.pack) tf')+ `T.append` tag) `T.append`)) T.empty hist+ `T.append`+ if null splices+ then T.empty+ else "\nBound splices:" `T.append`+ foldl (\x y -> x `T.append` " " `T.append` y) T.empty splices+ `T.append`+ (T.pack $ "\nNode: " ++ (show node))+++------------------------------------------------------------------------------+-- | Exception type for splice compile errors. Wraps the original+-- exception and provides context.+--data (Exception e) => CompileException e = CompileException+data CompileException = forall e . Exception e => CompileException+ { originalException :: e+ -- The list of splice errors. The head of it has the context+ -- related to the exception.+ , exceptionContext :: [SpliceError]+ } deriving ( Typeable )+++instance Show CompileException where+ show (CompileException e []) =+ "Heist load exception (unknown context): " ++ (show e)+ show (CompileException _ (c:_)) = (T.unpack $ spliceErrorText c)+++instance Exception CompileException+++------------------------------------------------------------------------------ -- | Holds all the state information needed for template processing. You will -- build a @HeistState@ using 'initHeist' and any of Heist's @HeistState -> -- HeistState@ \"filter\" functions. Then you use the resulting @HeistState@@@ -564,6 +605,8 @@ -- spliceMap. restoreHS :: Monad m => HeistState n -> HeistT n m () restoreHS old = modifyHS (\cur -> old { _doctypes = _doctypes cur+ , _numNamespacedTags =+ _numNamespacedTags cur , _spliceErrors = _spliceErrors cur }) {-# INLINE restoreHS #-}
test/suite/Benchmark.hs view
@@ -6,9 +6,9 @@ ------------------------------------------------------------------------------ import Blaze.ByteString.Builder import Control.Concurrent-import Control.Error import Control.Exception (evaluate) import Control.Monad+import Control.Monad.Trans.Except (ExceptT(ExceptT), runExceptT) import Criterion import Criterion.Main import Criterion.Measurement hiding (getTime)
test/suite/Heist/Compiled/Tests.hs view
@@ -1,22 +1,29 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-} module Heist.Compiled.Tests where import Blaze.ByteString.Builder-import Control.Error+import Control.Applicative+import Control.Exception+import Control.Monad.Trans.Except import Control.Lens import Control.Monad.Trans import Data.Bifunctor (first) import Data.ByteString (ByteString) import qualified Data.ByteString as B import Data.Char+import Data.IORef+import Data.Maybe import Data.Map.Syntax import Data.Monoid import qualified Data.Set as Set+import qualified Data.Text as T import Data.Text.Encoding import Test.Framework (Test) import Test.Framework.Providers.HUnit import qualified Test.HUnit as H+import qualified Text.XmlHtml as X ------------------------------------------------------------------------------@@ -41,12 +48,15 @@ , testCase "compiled/namespace4" namespaceTest4 , testCase "compiled/namespace5" namespaceTest5 , testCase "compiled/no-ns-splices" noNsSplices+ , testCase "compiled/ns-nested" nsNestedUnused , testCase "compiled/nsbind" nsBindTest , testCase "compiled/nsbinderr" nsBindErrorTest , testCase "compiled/nscall" nsCallTest , testCase "compiled/nscallerr" nsCallErrTest , testCase "compiled/nsbindstack" nsBindStackTest , testCase "compiled/doctype" doctypeTest+ , testCase "compiled/exceptions" exceptionsTest+ , testCase "compiled/defer" deferTest ] simpleCompiledTest :: IO ()@@ -179,6 +189,25 @@ & scTemplateLocations .~ [loadTemplates "templates-no-ns"] +------------------------------------------------------------------------------+-- | Test that no namespace splice message works correctly when there are no+-- top level splices used+nsNestedUnused :: IO ()+nsNestedUnused = do+ res <- runExceptT $ do+ hs <- ExceptT $ initHeist hc+ runner <- noteT ["Error rendering"] $ hoistMaybe $+ renderTemplate hs "test"+ b <- lift $ fst runner+ return $ toByteString b++ H.assertEqual "ns nested unused warn test" (Right "<div>aeou</div>\n") res+ where+ hc = HeistConfig sc "h" False+ sc = mempty & scCompiledSplices .~ ("foo" ## return $ yieldPureText "aeou")+ & scTemplateLocations .~ [loadTemplates "templates-ns-nested"]++ nsBindTemplateHC :: String -> HeistConfig IO nsBindTemplateHC dir = HeistConfig sc "h" False where@@ -237,9 +266,9 @@ H.assertEqual "namespace bind error test" (Left [ err1, err2, err3 ]) res where- err1 = "templates-nsbind/nsbinderror.tpl: No splice bound for h:invalid3\n ... via templates-nsbind/nsbinderror.tpl: h:main2\nBound splices: h:sub h:recurse h:call h:main2 h:main"- err2 = "templates-nsbind/nsbinderror.tpl: No splice bound for h:invalid2\n ... via templates-nsbind/nsbinderror.tpl: h:recurse\n ... via templates-nsbind/nsbinderror.tpl: h:main\nBound splices: h:sub h:recurse h:call h:main2 h:main"- err3 = "templates-nsbind/nsbinderror.tpl: No splice bound for h:invalid1\nBound splices: h:call h:main2 h:main"+ err1 = "templates-nsbind/nsbinderror.tpl: No splice bound for h:invalid3\n ... via templates-nsbind/nsbinderror.tpl: h:main2\nBound splices: h:sub h:recurse h:call h:main2 h:main\nNode: Element {elementTag = \"h:invalid3\", elementAttrs = [], elementChildren = []}"+ err2 = "templates-nsbind/nsbinderror.tpl: No splice bound for h:invalid2\n ... via templates-nsbind/nsbinderror.tpl: h:recurse\n ... via templates-nsbind/nsbinderror.tpl: h:main\nBound splices: h:sub h:recurse h:call h:main2 h:main\nNode: Element {elementTag = \"h:invalid2\", elementAttrs = [], elementChildren = []}"+ err3 = "templates-nsbind/nsbinderror.tpl: No splice bound for h:invalid1\nBound splices: h:call h:main2 h:main\nNode: Element {elementTag = \"h:invalid1\", elementAttrs = [], elementChildren = []}" ------------------------------------------------------------------------------@@ -256,6 +285,7 @@ , "h:main2") ] (Just "templates-nsbind/nsbinderror.tpl") ["h:sub","h:recurse","h:call","h:main2","h:main"]+ (X.Element "h:invalid3" [] []) "No splice bound for h:invalid3" err2 = SpliceError [ ( ["nsbinderror"] , Just "templates-nsbind/nsbinderror.tpl"@@ -265,10 +295,12 @@ ,"h:main") ] (Just "templates-nsbind/nsbinderror.tpl") ["h:sub","h:recurse","h:call","h:main2","h:main"]+ (X.Element "h:invalid2" [] []) "No splice bound for h:invalid2" err3 = SpliceError [] (Just "templates-nsbind/nsbinderror.tpl") ["h:call","h:main2","h:main"]+ (X.Element "h:invalid1" [] []) "No splice bound for h:invalid1" @@ -302,5 +334,86 @@ (Left $ Set.fromList [ err1, err2 ]) (first Set.fromList res) where- err1 = "templates-nscall/_call.tpl: No splice bound for h:sub\nBound splices: h:call h:main2 h:main"- err2 = "templates-nscall/_invalid.tpl: No splice bound for h:invalid\nBound splices: h:call h:main2 h:main"+ err1 = "templates-nscall/_call.tpl: No splice bound for h:sub\nBound splices: h:call h:main2 h:main\nNode: Element {elementTag = \"h:sub\", elementAttrs = [], elementChildren = []}"+ err2 = "templates-nscall/_invalid.tpl: No splice bound for h:invalid\nBound splices: h:call h:main2 h:main\nNode: Element {elementTag = \"h:invalid\", elementAttrs = [], elementChildren = []}"+++------------------------------------------------------------------------------+-- | Test exception handling in template load.+exceptionsTest :: IO ()+exceptionsTest = do+ res <- Control.Exception.catch+ (runExceptT $ do+ hs <- ExceptT $ initHeist hc+ -- The rest needed only for type inference.+ runner <- noteT ["Error rendering"] $ hoistMaybe $+ renderTemplate hs ""+ _ <- lift $ fst runner+ throwE ["Unexpected success"])+ (\(e :: CompileException) -> return $ Right+ (show e, head $+ exceptionContext e))++ H.assertEqual "exceptions" (Right (msg, err)) $ res++ where+ msg = "templates-loaderror/_error.tpl: Exception in splice compile: Prelude.read: no parse\n ... via templates-loaderror/_error.tpl: h:adder\n ... via templates-loaderror/test.tpl: h:call2\nBound splices: h:adder h:call2 h:call1\nNode: Element {elementTag = \"h:adder\", elementAttrs = [(\"value\",\"noparse\")], elementChildren = []}"+ err = SpliceError [ ( ["test"]+ , Just "templates-loaderror/_error.tpl"+ , "h:adder"),+ ( ["test"]+ , Just "templates-loaderror/test.tpl"+ ,"h:call2") ]+ (Just "templates-loaderror/_error.tpl")+ ["h:adder", "h:call2", "h:call1"]+ (X.Element "h:adder" [("value", "noparse")] [])+ "Exception in splice compile: Prelude.read: no parse"+ hc = HeistConfig sc "h" True+ sc = mempty & scLoadTimeSplices .~ defaultLoadTimeSplices+ & scCompiledSplices .~ splices+ & scTemplateLocations .~ [loadTemplates "templates-loaderror"]+ splices = do+ "call1" ## callTemplate "_ok"+ "call2" ## callTemplate "_error"+ "adder" ## do+ value :: Int <- read . T.unpack . fromJust .+ X.getAttribute "value" <$> getParamNode+ return $ yieldPureText $ T.pack $ show $ 1 + value+++------------------------------------------------------------------------------+-- | Test for defer functions to see that they correctly save the result of+-- a runtime computation.+deferTest :: IO ()+deferTest = do+ rs <- mapM newIORef $ replicate 5 (0 :: Int)+ res <- runExceptT $ do+ hs <- ExceptT $ initHeist $ hc rs+ runner <- noteT ["Error rendering"] $ hoistMaybe $+ renderTemplate hs "test"+ b <- lift $ fst runner+ return $ toByteString b++ vs <- mapM readIORef rs+ H.assertEqual "defer test" ([2, 1, 1, 1, 1], Right msg) (vs, res)+ where+ msg = "1 2\n1 1\n1 1\n\n1 1\n"+ hc rs = HeistConfig (sc rs) "h" True+ sc rs = mempty & scLoadTimeSplices .~ defaultLoadTimeSplices+ & scCompiledSplices .~ (splices rs)+ & scTemplateLocations .~ [loadTemplates "templates-defer"]+ splices [r1, r2, r3, r4, r5] = do+ "plain" ## subSplice $ addAndReturn r1+ "defer" ## deferMap return subSplice $ addAndReturn r2+ "maydefer" ## mayDeferMap (return . Just) subSplice $ addAndReturn r3+ "maydefer2" ## mayDeferMap (const $ return Nothing) subSplice $+ addAndReturn r4+ "defermany" ## deferMany subSplice $ addAndReturn' r5+ subSplice =+ withSplices runChildren+ ("use" ## \n -> return $ yieldRuntimeText $ return . T.pack . show =<< n)+ addAndReturn r = liftIO $ modifyIORef r (+1) >> readIORef r+ addAndReturn' r = liftIO $ do+ modifyIORef r (+1)+ val <- readIORef r+ return [val]
test/suite/Heist/Interpreted/Tests.hs view
@@ -11,7 +11,6 @@ ------------------------------------------------------------------------------ import Blaze.ByteString.Builder-import Control.Error import Control.Monad.State import Data.Aeson import Data.ByteString.Char8 (ByteString)@@ -629,7 +628,6 @@ Nothing)) hs evalHeistT (runNodeList $ buildApplyCaller apply) (X.TextNode "") hs'-
test/suite/Heist/TestCommon.hs view
@@ -2,11 +2,11 @@ ------------------------------------------------------------------------------ import Blaze.ByteString.Builder-import Control.Error import Control.Monad.Trans+import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)+import Control.Monad.Trans.Except (ExceptT(ExceptT), runExceptT) import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B-import Data.Map.Syntax import Data.Maybe import Data.Monoid @@ -113,3 +113,22 @@ builder <- I.renderTemplate hs name return $ toByteString $ fst $ fromJust builder ++------------------------------------------------------------------------------+isLeft :: Either e a -> Bool+isLeft (Left _) = True+isLeft _ = False+++------------------------------------------------------------------------------+noteT :: Monad m => e -> MaybeT m a -> ExceptT e m a+noteT e ma = do+ x <- lift $ runMaybeT ma+ case x of+ Nothing -> ExceptT $ return (Left e)+ Just a -> ExceptT $ return (Right a)+++------------------------------------------------------------------------------+hoistMaybe :: Monad m => Maybe a -> MaybeT m a+hoistMaybe = MaybeT . return
test/suite/Heist/Tutorial/Imports.hs view
@@ -15,9 +15,9 @@ ) where import Blaze.ByteString.Builder-import Control.Error (runExceptT, ExceptT(..)) import Control.Monad import Control.Monad.Trans+import Control.Monad.Trans.Except (ExceptT(..), runExceptT) import qualified Control.Monad.Trans.State as ST import Data.ByteString.Char8 (ByteString) import Data.Maybe
+ test/templates-defer/test.tpl view
@@ -0,0 +1,5 @@+<h:plain><h:use/> <h:use/></h:plain>+<h:defer><h:use/> <h:use/></h:defer>+<h:maydefer><h:use/> <h:use/></h:maydefer>+<h:maydefer2><h:use/> <h:use/></h:maydefer2>+<h:defermany><h:use/> <h:use/></h:defermany>
+ test/templates-loaderror/_error.tpl view
@@ -0,0 +1,1 @@+<h:adder value="noparse"/>
+ test/templates-loaderror/_ok.tpl view
@@ -0,0 +1,1 @@+<h:adder value="1"/>
+ test/templates-loaderror/test.tpl view
@@ -0,0 +1,3 @@+<h:call1/>+<h:call2/>+<h:call1/>
+ test/templates-ns-nested/test.tpl view
@@ -0,0 +1,1 @@+<div><h:foo/></div>
+ test/templates-nsbind/nsbind.tpl view
@@ -0,0 +1,7 @@+Alpha+<h:main>+Beta+<h:sub/>+Gamma+<sub/>+</h:main>
+ test/templates-nsbind/nsbinderror.tpl view
@@ -0,0 +1,10 @@+Alpha+<h:invalid1/>+<h:main>+<h:recurse>+<h:invalid2/>+</h:recurse>+</h:main>+<h:main2>+<h:invalid3/>+</h:main2>
+ test/templates-nscall/_call.tpl view
@@ -0,0 +1,2 @@+Called+<h:sub/>
+ test/templates-nscall/_invalid.tpl view
@@ -0,0 +1,1 @@+<h:invalid/>
+ test/templates-nscall/nscall.tpl view
@@ -0,0 +1,6 @@+Top+<h:main>+Inside 1+<h:call/>+Inside 2+</h:main>
+ test/templates/backslash.tpl view
@@ -0,0 +1,1 @@+<foo regex='abc\d+\\e'/>
+ test/templates/json_array.tpl view
@@ -0,0 +1,1 @@+<jsonArray><value var="ctx.0"/>, <value var="ctx.1"/> and <with var="ctx.2"><value:deep/> <value:inside/></with></jsonArray>
+ test/templates/namespaces.tpl view
@@ -0,0 +1,5 @@+Alpha+<foo aoeu="htns">Inside foo</foo>+Beta+<h:foo aoeu="htns">Inside h:foo</h:foo>+End
+ test/templates/pandoc.tpl view
@@ -0,0 +1,1 @@+<pandocnodiv file="test.md"/>
+ test/templates/pandocdiv.tpl view
@@ -0,0 +1,1 @@+<pandocdiv file="test.md" id="pandoc" class="foo"/>
+ test/templates/rss.xtpl view
@@ -0,0 +1,1 @@+<rss><channel><link>http://www.devalot.com/</link></channel></rss>