packages feed

heist 0.5.3 → 0.6.0

raw patch · 11 files changed

+241/−154 lines, 11 filesdep ~containersPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: containers

API changes (from Hackage documentation)

- Text.Templating.Heist: bindStaticTag :: MonadIO m => TemplateState m -> IO (TemplateState m, StaticTagState)
+ Text.Templating.Heist: getTemplateFilePath :: Monad m => HeistT m (Maybe FilePath)
+ Text.Templating.Heist: mkCacheTag :: MonadIO m => IO (TemplateState m -> TemplateState m, CacheTagState)
+ Text.Templating.Heist: spliceNames :: TemplateState m -> [Text]
+ Text.Templating.Heist: templateNames :: TemplateState m -> [TPath]
+ Text.Templating.Heist: type HeistT = TemplateMonad
+ Text.Templating.Heist.Splices.Markdown: NoMarkdownFileException :: NoMarkdownFileException
+ Text.Templating.Heist.Splices.Markdown: data NoMarkdownFileException
+ Text.Templating.Heist.Splices.Markdown: instance Exception NoMarkdownFileException
+ Text.Templating.Heist.Splices.Markdown: instance Show NoMarkdownFileException
+ Text.Templating.Heist.Splices.Markdown: instance Typeable NoMarkdownFileException
- Text.Templating.Heist: addTemplate :: Monad m => ByteString -> Template -> TemplateState m -> TemplateState m
+ Text.Templating.Heist: addTemplate :: Monad m => ByteString -> Template -> Maybe FilePath -> TemplateState m -> TemplateState m
- Text.Templating.Heist: addXMLTemplate :: Monad m => ByteString -> Template -> TemplateState m -> TemplateState m
+ Text.Templating.Heist: addXMLTemplate :: Monad m => ByteString -> Template -> Maybe FilePath -> TemplateState m -> TemplateState m
- Text.Templating.Heist: callTemplate :: Monad m => ByteString -> [(Text, Text)] -> TemplateMonad m (Maybe Template)
+ Text.Templating.Heist: callTemplate :: Monad m => ByteString -> [(Text, Text)] -> HeistT m (Maybe Template)
- Text.Templating.Heist: emptyTemplateState :: MonadIO m => FilePath -> TemplateState m
+ Text.Templating.Heist: emptyTemplateState :: MonadIO m => TemplateState m
- Text.Templating.Heist: evalTemplate :: Monad m => ByteString -> TemplateMonad m (Maybe Template)
+ Text.Templating.Heist: evalTemplate :: Monad m => ByteString -> HeistT m (Maybe Template)
- Text.Templating.Heist: getContext :: Monad m => TemplateMonad m TPath
+ Text.Templating.Heist: getContext :: Monad m => HeistT m TPath
- Text.Templating.Heist: getDoc :: String -> IO (Either String Document)
+ Text.Templating.Heist: getDoc :: String -> IO (Either String DocumentFile)
- Text.Templating.Heist: getXMLDoc :: String -> IO (Either String Document)
+ Text.Templating.Heist: getXMLDoc :: String -> IO (Either String DocumentFile)
- Text.Templating.Heist: stopRecursion :: Monad m => TemplateMonad m ()
+ Text.Templating.Heist: stopRecursion :: Monad m => HeistT m ()
- Text.Templating.Heist.Splices.Apply: rawApply :: Monad m => [Node] -> TPath -> [Node] -> TemplateMonad m Template
+ Text.Templating.Heist.Splices.Apply: rawApply :: Monad m => [Node] -> TPath -> [Node] -> HeistT m Template
- Text.Templating.Heist.Splices.Markdown: markdownSplice :: MonadIO m => FilePath -> Splice m
+ Text.Templating.Heist.Splices.Markdown: markdownSplice :: MonadIO m => Splice m

Files

README.SNAP.md view
@@ -16,16 +16,16 @@    * a sensible and clean monad for web programming -  * an xml-based templating system for generating HTML based on-    [expat](http://expat.sourceforge.net/) (via-    [hexpat](http://hackage.haskell.org/package/hexpat)) that allows you to-    bind Haskell functionality to XML tags without getting PHP-style tag soup-    all over your pants+  * an xml-based templating system for generating HTML that allows you to bind+    Haskell functionality to XML tags without getting PHP-style tag soup all+    over your pants -Snap currently only runs on Unix platforms; it has been tested on Linux and Mac-OSX Snow Leopard.+  * a "snaplet" system for building web sites from composable pieces. +Snap is currently only officially supported on Unix platforms; it has been+tested on Linux and Mac OSX Snow Leopard, and is reported to work on Windows. + Snap Philosophy --------------- @@ -40,17 +40,3 @@   * Excellent documentation    * Robustness and high test coverage---Snap Roadmap---------------Where are we going?--1. First prerelease: HTTP server, monad, template system--2. Second prerelease: component system with a collection of useful stock-modules (called "Snaplets") for things like user and session management,-caching, an administrative interface, etc.--3. Third prerelease: where we figure out what to do about data access
README.md view
@@ -1,9 +1,9 @@ # Heist -Heist, part of the [Snap Framework](http://www.snapframework.com/), is a -Haskell library for xhtml templating. It uses simple XML tags to bind values-to your templates in a straightforward way. For example, if you were to put-the following in a template:+Heist, part of the [Snap Framework](http://www.snapframework.com/), is a+Haskell library for xml/html templating. It uses simple XML tags to bind+values to your templates in a straightforward way. For example, if you were to+put the following in a template:      <bind tag="message">some text</bind>     <p><message/></p>
heist.cabal view
@@ -1,5 +1,5 @@ name:           heist-version:        0.5.3+version:        0.6.0 synopsis:       An xhtml templating system description:    An xhtml templating system license:        BSD3@@ -86,7 +86,7 @@     base >= 4 && < 5,     blaze-builder >= 0.2 && <0.4,     bytestring,-    containers,+    containers >= 0.2 && < 0.5,     directory,     directory-tree,     filepath,
src/Text/Templating/Heist.hs view
@@ -13,7 +13,7 @@   of as functions that transform a node into a list of nodes.  Heist then   substitutes the resulting list of nodes into your template in place of the   input node.  'Splice' is implemented as a type synonym @type Splice m =-  TemplateMonad m [Node]@, and 'TemplateMonad' has a function 'getParamNode'+  HeistT m [Node]@, and 'HeistT' has a function 'getParamNode'   that lets you get the input node.    Suppose you have a place on your page where you want to display a link with@@ -73,7 +73,10 @@   , MIMEType   , Splice   , TemplateMonad+  , HeistT   , TemplateState+  , templateNames+  , spliceNames      -- * Functions and declarations on TemplateState values   , addTemplate@@ -93,11 +96,12 @@   , addPreRunHook   , addPostRunHook -    -- * TemplateMonad functions+    -- * HeistT functions   , stopRecursion   , getParamNode   , runNodeList   , getContext+  , getTemplateFilePath    , localParamNode   , getsTS@@ -127,7 +131,7 @@     -- * Misc functions   , getDoc   , getXMLDoc-  , bindStaticTag+  , mkCacheTag    ) where @@ -140,12 +144,12 @@  ------------------------------------------------------------------------------ -- | The default set of built-in splices.-defaultSpliceMap :: MonadIO m => FilePath -> SpliceMap m-defaultSpliceMap templatePath = Map.fromList+defaultSpliceMap :: MonadIO m => SpliceMap m+defaultSpliceMap = Map.fromList     [(applyTag, applyImpl)     ,(bindTag, bindImpl)     ,(ignoreTag, ignoreImpl)-    ,(markdownTag, markdownSplice templatePath)+    ,(markdownTag, markdownSplice)     ]  @@ -153,10 +157,10 @@ -- | An empty template state, with Heist's default splices (@\<apply\>@, -- @\<bind\>@, @\<ignore\>@, and @\<markdown\>@) mapped.  The static tag is -- not mapped here because it must be mapped manually in your application.-emptyTemplateState :: MonadIO m => FilePath -> TemplateState m-emptyTemplateState templatePath =-    TemplateState (defaultSpliceMap templatePath) Map.empty-                  True [] 0 return return return []+emptyTemplateState :: MonadIO m => TemplateState m+emptyTemplateState =+    TemplateState (defaultSpliceMap) Map.empty True [] 0+                  return return return [] Nothing   -- $hookDoc
src/Text/Templating/Heist/Internal.hs view
@@ -35,7 +35,7 @@  ------------------------------------------------------------------------------ -- | Mappends a doctype to the state.-addDoctype :: Monad m => [X.DocType] -> TemplateMonad m ()+addDoctype :: Monad m => [X.DocType] -> HeistT m () addDoctype dt = do     modifyTS (\s -> s { _doctypes = _doctypes s `mappend` dt }) @@ -94,6 +94,13 @@   ------------------------------------------------------------------------------+-- | Sets the current template file.+setCurTemplateFile :: Monad m+                   => Maybe FilePath -> TemplateState m -> TemplateState m+setCurTemplateFile fp ts = ts { _curTemplateFile = fp }+++------------------------------------------------------------------------------ -- | Converts 'Text' to a splice returning a single 'TextNode'. textSplice :: (Monad m) => Text -> Splice m textSplice t = return [X.TextNode t]@@ -120,8 +127,8 @@   --------------------------------------------------------------------------------- | Wrapper around runChildrenWith that applies a transformation function to the--- second item in each of the tuples before calling runChildrenWith.+-- | Wrapper around runChildrenWith that applies a transformation function to+-- the second item in each of the tuples before calling runChildrenWith. runChildrenWithTrans :: (Monad m)           => (b -> Splice m)           -- ^ Splice generating function@@ -195,7 +202,7 @@ singleLookup :: TemplateMap              -> TPath              -> ByteString-             -> Maybe (X.Document, TPath)+             -> Maybe (DocumentFile, TPath) singleLookup tm path name = fmap (\a -> (a,path)) $ Map.lookup (name:path) tm  @@ -205,7 +212,7 @@ traversePath :: TemplateMap              -> TPath              -> ByteString-             -> Maybe (X.Document, TPath)+             -> Maybe (DocumentFile, TPath) traversePath tm [] name = fmap (\a -> (a,[])) (Map.lookup [name] tm) traversePath tm path name =     singleLookup tm path name `mplus`@@ -226,7 +233,7 @@ lookupTemplate :: Monad m =>                   ByteString                -> TemplateState m-               -> Maybe (X.Document, TPath)+               -> Maybe (DocumentFile, TPath) lookupTemplate nameStr ts =     f (_templateMap ts) path name   where (name:p) = case splitTemplatePath nameStr of@@ -249,7 +256,7 @@ -- | Adds a template to the template state. insertTemplate :: Monad m =>                TPath-            -> X.Document+            -> DocumentFile             -> TemplateState m             -> TemplateState m insertTemplate p t st =@@ -258,24 +265,38 @@  ------------------------------------------------------------------------------ -- | Adds an HTML format template to the template state.-addTemplate :: Monad m =>-               ByteString+addTemplate :: Monad m+            => ByteString+            -- ^ Path that the template will be referenced by             -> Template+            -- ^ The template's DOM nodes+            -> Maybe FilePath+            -- ^ An optional path to the actual file on disk where the+            -- template is stored             -> TemplateState m             -> TemplateState m-addTemplate n t st = insertTemplate (splitTemplatePath n)-                                    (X.HtmlDocument X.UTF8 Nothing t) st+addTemplate n t mfp st =+    insertTemplate (splitTemplatePath n) doc st+  where+    doc = DocumentFile (X.HtmlDocument X.UTF8 Nothing t) mfp   ------------------------------------------------------------------------------ -- | Adds an XML format template to the template state.-addXMLTemplate :: Monad m =>-                  ByteString+addXMLTemplate :: Monad m+               => ByteString+               -- ^ Path that the template will be referenced by                -> Template+               -- ^ The template's DOM nodes+               -> Maybe FilePath+               -- ^ An optional path to the actual file on disk where the+               -- template is stored                -> TemplateState m                -> TemplateState m-addXMLTemplate n t st = insertTemplate (splitTemplatePath n)-                                       (X.XmlDocument X.UTF8 Nothing t) st+addXMLTemplate n t mfp st =+    insertTemplate (splitTemplatePath n) doc st+  where+    doc = DocumentFile (X.XmlDocument X.UTF8 Nothing t) mfp   ------------------------------------------------------------------------------@@ -292,23 +313,31 @@ -- splice will result in a list of nodes @L@.  Normally @foo@ will recursively -- scan @L@ for splices and run them.  If @foo@ calls @stopRecursion@, @L@ -- will be included in the output verbatim without running any splices.-stopRecursion :: Monad m => TemplateMonad m ()+stopRecursion :: Monad m => HeistT m () stopRecursion = modifyTS (\st -> st { _recurse = False })   ------------------------------------------------------------------------------ -- | Sets the current context-setContext :: Monad m => TPath -> TemplateMonad m ()+setContext :: Monad m => TPath -> HeistT m () setContext c = modifyTS (\st -> st { _curContext = c })   ------------------------------------------------------------------------------ -- | Gets the current context-getContext :: Monad m => TemplateMonad m TPath+getContext :: Monad m => HeistT m TPath getContext = getsTS _curContext   ------------------------------------------------------------------------------+-- | Gets the full path to the file holding the template currently being+-- processed.  Returns Nothing if the template is not associated with a file+-- on disk or if there is no template being processed.+getTemplateFilePath :: Monad m => HeistT m (Maybe FilePath)+getTemplateFilePath = getsTS _curTemplateFile+++------------------------------------------------------------------------------ -- | Performs splice processing on a single node. runNode :: Monad m => X.Node -> Splice m runNode (X.Element nm at ch) = do@@ -326,7 +355,7 @@ ------------------------------------------------------------------------------ -- | Helper function for substituting a parsed attribute into an attribute -- tuple.-attSubst :: (Monad m) => (t, Text) -> TemplateMonad m (t, Text)+attSubst :: (Monad m) => (t, Text) -> HeistT m (t, Text) attSubst (n,v) = do     v' <- parseAtt v     return (n,v')@@ -335,7 +364,7 @@ ------------------------------------------------------------------------------ -- | Parses an attribute for any identifier expressions and performs -- appropriate substitution.-parseAtt :: (Monad m) => Text -> TemplateMonad m Text+parseAtt :: (Monad m) => Text -> HeistT m Text parseAtt bs = do     let ast = case AP.feed (AP.parse attParser bs) "" of             (AP.Fail _ _ _) -> []@@ -390,7 +419,7 @@ -- it would be for the former user to accept that -- \"some \<b\>text\<\/b\> foobar\" is being rendered as \"some \" because -- it's \"more intuitive\".-getAttributeSplice :: Monad m => Text -> TemplateMonad m Text+getAttributeSplice :: Monad m => Text -> HeistT m Text getAttributeSplice name = do     s <- liftM (lookupSplice name) getTS     nodes <- maybe (return []) id s@@ -422,37 +451,39 @@                 return res         else return result   where-    modRecursionDepth :: Monad m => (Int -> Int) -> TemplateMonad m ()+    modRecursionDepth :: Monad m => (Int -> Int) -> HeistT m ()     modRecursionDepth f =         modifyTS (\st -> st { _recursionDepth = f (_recursionDepth st) })   --------------------------------------------------------------------------------- | Looks up a template name runs a TemplateMonad computation on it.+-- | Looks up a template name runs a 'HeistT' computation on it. lookupAndRun :: Monad m              => ByteString-             -> ((X.Document, TPath) -> TemplateMonad m (Maybe a))-             -> TemplateMonad m (Maybe a)+             -> ((DocumentFile, TPath) -> HeistT m (Maybe a))+             -> HeistT m (Maybe a) lookupAndRun name k = do     ts <- getTS-    maybe (return Nothing) k-          (lookupTemplate name ts)+    let mt = lookupTemplate name ts+    let curPath = join $ fmap (dfFile . fst) mt+    modifyTS (setCurTemplateFile curPath)+    maybe (return Nothing) k mt   ------------------------------------------------------------------------------ -- | Looks up a template name evaluates it by calling runNodeList. evalTemplate :: Monad m             => ByteString-            -> TemplateMonad m (Maybe Template)+            -> HeistT m (Maybe Template) evalTemplate name = lookupAndRun name     (\(t,ctx) -> localTS (\ts -> ts {_curContext = ctx})-                         (liftM Just $ runNodeList $ X.docContent t))+                         (liftM Just $ runNodeList $ X.docContent $ dfDoc t))   --------------------------------------------------------------------------------- | Sets the document type of a 'X.Document' based on the 'TemplateMonad'+-- | Sets the document type of a 'X.Document' based on the 'HeistT' -- value.-fixDocType :: Monad m => X.Document -> TemplateMonad m X.Document+fixDocType :: Monad m => X.Document -> HeistT m X.Document fixDocType d = do     dts <- getsTS _doctypes     return $ d { X.docType = listToMaybe dts }@@ -464,16 +495,16 @@ -- top level. evalWithHooksInternal :: Monad m                       => ByteString-                      -> TemplateMonad m (Maybe X.Document)+                      -> HeistT m (Maybe X.Document) evalWithHooksInternal name = lookupAndRun name $ \(t,ctx) -> do-    addDoctype $ maybeToList $ X.docType t+    addDoctype $ maybeToList $ X.docType $ dfDoc t     ts <- getTS-    nodes <- lift $ _preRunHook ts $ X.docContent t+    nodes <- lift $ _preRunHook ts $ X.docContent $ dfDoc t     putTS (ts {_curContext = ctx})     res <- runNodeList nodes     restoreTS ts     newNodes <- lift (_postRunHook ts res)-    newDoc   <- fixDocType $ t { X.docContent = newNodes }+    newDoc   <- fixDocType $ (dfDoc t) { X.docContent = newNodes }     return (Just newDoc)  @@ -482,7 +513,7 @@ -- executes pre- and post-run hooks and adds the doctype. evalWithHooks :: Monad m             => ByteString-            -> TemplateMonad m (Maybe Template)+            -> HeistT m (Maybe Template) evalWithHooks name = liftM (liftM X.docContent) (evalWithHooksInternal name)  @@ -513,7 +544,7 @@              => ByteString     -- ^ The name of the template              -> [(Text, Text)] -- ^ Association list of                                -- (name,value) parameter pairs-             -> TemplateMonad m (Maybe Template)+             -> HeistT m (Maybe Template) callTemplate name params = do     modifyTS $ bindStrings params     evalTemplate name@@ -575,40 +606,34 @@  ------------------------------------------------------------------------------ -- | Reads an HTML or XML template from disk.-getDocWith :: ParserFun -> String -> IO (Either String X.Document)+getDocWith :: ParserFun -> String -> IO (Either String DocumentFile) getDocWith parser f = do     bs <- catch (liftM Right $ B.readFile f)                 (\(e::SomeException) -> return $ Left $ show e) -    let d = either Left (parser f) bs-    return $ mapLeft (\s -> f ++ " " ++ s) d+    let eitherDoc = either Left (parser f) bs+    return $ either (\s -> Left $ f ++ " " ++ s)+                    (\d -> Right $ DocumentFile d (Just f)) eitherDoc   ------------------------------------------------------------------------------ -- | Reads an HTML template from disk.-getDoc :: String -> IO (Either String X.Document)+getDoc :: String -> IO (Either String DocumentFile) getDoc = getDocWith X.parseHTML   ------------------------------------------------------------------------------ -- | Reads an XML template from disk.-getXMLDoc :: String -> IO (Either String X.Document)+getXMLDoc :: String -> IO (Either String DocumentFile) getXMLDoc = getDocWith X.parseHTML   -------------------------------------------------------------------------------mapLeft :: (a -> b) -> Either a c -> Either b c-mapLeft g = either (Left . g) Right-mapRight :: (b -> c) -> Either a b -> Either a c-mapRight g = either Left (Right . g)--------------------------------------------------------------------------------- -- | Loads a template with the specified path and filename.  The -- template is only loaded if it has a ".tpl" or ".xtpl" extension. loadTemplate :: String -- ^ path of the template root              -> String -- ^ full file path (includes the template root)-             -> IO [Either String (TPath, X.Document)] --TemplateMap+             -> IO [Either String (TPath, DocumentFile)] --TemplateMap loadTemplate templateRoot fname     | isHTMLTemplate = do         c <- getDoc fname@@ -642,19 +667,19 @@   --------------------------------------------------------------------------------- | Reversed list of directories.  This holds the path to the template+-- | Runs a template modifying function on a DocumentFile. runHook :: Monad m => (Template -> m Template)-        -> X.Document-        -> m X.Document+        -> DocumentFile+        -> m DocumentFile runHook f t = do-    n <- f $ X.docContent t-    return $ t { X.docContent = n }+    n <- f $ X.docContent $ dfDoc t+    return $ t { dfDoc = (dfDoc t) { X.docContent = n } }   --------------------------------------------------------------------------------- | Runs the onLoad hook on the template and returns the `TemplateState`+-- | Runs the onLoad hook on the template and returns the 'TemplateState' -- with the result inserted.-loadHook :: Monad m => TemplateState m -> (TPath, X.Document)+loadHook :: Monad m => TemplateState m -> (TPath, DocumentFile)          -> IO (TemplateState m) loadHook ts (tp, t) = do     t' <- runHook (_onLoadHook ts) t@@ -662,11 +687,14 @@   --------------------------------------------------------------------------------- | -addTemplatePathPrefix :: ByteString-                      -> TemplateState m -> TemplateState m-addTemplatePathPrefix dir ts =-    ts { _templateMap = Map.mapKeys f $ _templateMap ts }+-- | Adds a path prefix to all the templates in the 'TemplateState'.  If you+-- want to add multiple levels of directories, separate them with slashes as+-- in "foo/bar".  Using an empty string as a path prefix will leave the+-- 'TemplateState' unchanged.+addTemplatePathPrefix :: ByteString -> TemplateState m -> TemplateState m+addTemplatePathPrefix dir ts+  | B.null dir = ts+  | otherwise  = ts { _templateMap = Map.mapKeys f $ _templateMap ts }   where-    f ps = ps ++ [dir]+    f ps = ps++splitTemplatePath dir 
src/Text/Templating/Heist/Splices/Apply.hs view
@@ -29,7 +29,7 @@          => [X.Node]          -> TPath          -> [X.Node]-         -> TemplateMonad m Template+         -> HeistT m Template rawApply calledNodes newContext paramNodes = do     st <- getTS  -- Can't use localTS here because the modifier is not pure     processedParams <- runNodeList paramNodes@@ -48,8 +48,8 @@     st <- getTS     maybe (return []) -- TODO: error handling           (\(t,ctx) -> do-              addDoctype $ maybeToList $ X.docType t-              rawApply (X.docContent t) ctx nodes)+              addDoctype $ maybeToList $ X.docType $ dfDoc t+              rawApply (X.docContent $ dfDoc t) ctx nodes)           (lookupTemplate (T.encodeUtf8 template) st)  
src/Text/Templating/Heist/Splices/Cache.hs view
@@ -11,7 +11,6 @@ import           Data.IORef import qualified Data.Map as Map import           Data.Map (Map)-import           Data.Maybe import qualified Data.Set as Set import           Data.Text (Text) import qualified Data.Text as T@@ -27,9 +26,11 @@ import           Text.Templating.Heist.Types  +------------------------------------------------------------------------------ cacheTagName :: Text cacheTagName = "cache" + ------------------------------------------------------------------------------ -- | State for storing cache tag information newtype CacheTagState = CTS (MVar (Map Text (UTCTime, Template)))@@ -55,7 +56,7 @@         'd' -> 86400         'w' -> 604800         _   -> 0-        + ------------------------------------------------------------------------------ -- | The \"cache\" splice ensures that its contents are cached and only -- evaluated periodically.  The cached contents are returned every time the@@ -69,24 +70,27 @@ -- clearCacheTagState. cacheImpl :: (MonadIO m)            => CacheTagState-           -> TemplateMonad m Template+           -> HeistT m Template cacheImpl (CTS mv) = do     tree <- getParamNode-    let i = fromJust $ getAttribute "id" tree+    let err = error $ unwords ["cacheImpl is bound to a tag"+                              ,"that didn't get an id attribute."+                              ," This should never happen."]+    let i = maybe err id $ getAttribute "id" tree         ttl = maybe 0 parseTTL $ getAttribute "ttl" tree     mp <- liftIO $ readMVar mv      (mp',ns) <- do-                   curTime <- liftIO getCurrentTime+                   cur <- liftIO getCurrentTime                    let mbn = Map.lookup i mp                        reload = do                            nodes' <- runNodeList $ childNodes tree-                           return $! (Map.insert i (curTime,nodes') mp, nodes')+                           return $! (Map.insert i (cur,nodes') mp, nodes')                    case mbn of                        Nothing -> reload                        (Just (lastUpdate,n)) -> do-                           if ttl > 0 &&-                              diffUTCTime curTime lastUpdate > fromIntegral ttl+                           if ttl > 0 && tagName tree == Just cacheTagName &&+                              diffUTCTime cur lastUpdate > fromIntegral ttl                              then reload                              else do                                  stopRecursion@@ -98,18 +102,22 @@   --------------------------------------------------------------------------------- | Modifies a TemplateState to include a \"cache\" tag.  The cache tag is--- not bound automatically with the other default Heist tags.  This is because--- this function also returns CacheTagState, so the user will be able to--- clear it with the 'clearCacheTagState' function.+-- | Returns a function that modifies a TemplateState to include a \"cache\"+-- tag.  The cache tag is not bound automatically with the other default Heist+-- tags.  This is because this function also returns CacheTagState, so the+-- user will be able to clear it with the 'clearCacheTagState' function. mkCacheTag :: MonadIO m            => IO (TemplateState m -> TemplateState m, CacheTagState) mkCacheTag = do     sr <- newIORef $ Set.empty     mv <- liftM CTS $ newMVar Map.empty -    return $ (addOnLoadHook (assignIds sr) .-              bindSplice cacheTagName (cacheImpl mv), mv)+    return $ ( addOnLoadHook (assignIds sr) .+               -- The cache tag allows the ttl attribute.+               bindSplice cacheTagName (cacheImpl mv) .+               -- Like the old static tag...does not allow ttl+               bindSplice "static" (cacheImpl mv)+             , mv)    where     generateId :: IO Int@@ -130,7 +138,8 @@            g curs = do               let node = current curs-              curs' <- if tagName node == Just cacheTagName+              curs' <- if tagName node == Just cacheTagName ||+                          tagName node == Just "static"                          then do                              i <- getId                              return $ modifyNode (setAttribute "id" i) curs
src/Text/Templating/Heist/Splices/Markdown.hs view
@@ -25,6 +25,7 @@ import           Text.XmlHtml  ------------------------------------------------------------------------------+import           Text.Templating.Heist.Internal import           Text.Templating.Heist.Types  data PandocMissingException = PandocMissingException@@ -47,6 +48,16 @@ instance Exception MarkdownException  +data NoMarkdownFileException = NoMarkdownFileException+    deriving (Typeable)++instance Show NoMarkdownFileException where+    show NoMarkdownFileException =+        "Markdown error: no file or template in context" +++        " during processing of markdown tag"++instance Exception NoMarkdownFileException where+ ------------------------------------------------------------------------------ -- | Default name for the markdown splice. markdownTag :: Text@@ -54,8 +65,9 @@  ------------------------------------------------------------------------------ -- | Implementation of the markdown splice.-markdownSplice :: MonadIO m => FilePath -> Splice m-markdownSplice templatePath = do+markdownSplice :: MonadIO m => Splice m+markdownSplice = do+    templateDir <- liftM (fmap takeDirectory) getTemplateFilePath     pdMD <- liftIO $ findExecutable "pandoc"      when (isNothing pdMD) $ liftIO $ throwIO PandocMissingException@@ -64,7 +76,9 @@     (source,markup) <- liftIO $         case getAttribute "file" tree of             Just f  -> do-                m <- pandoc (fromJust pdMD) templatePath $ T.unpack f+                m <- maybe (liftIO $ throwIO NoMarkdownFileException )+                           (\tp -> pandoc (fromJust pdMD) tp $ T.unpack f)+                           templateDir                 return (T.unpack f,m)             Nothing -> do                 m <- pandocBS (fromJust pdMD) $ T.encodeUtf8 $ nodeText tree@@ -78,7 +92,7 @@   pandoc :: FilePath -> FilePath -> FilePath -> IO ByteString-pandoc pandocPath templatePath inputFile = do+pandoc pandocPath templateDir inputFile = do     (ex, sout, serr) <- readProcessWithExitCode' pandocPath args ""      when (isFail ex) $ throw $ MarkdownException serr@@ -90,7 +104,7 @@     isFail ExitSuccess = False     isFail _           = True -    args = [ "-S", "--no-wrap", templatePath </> inputFile ]+    args = [ "-S", "--no-wrap", templateDir </> inputFile ]   pandocBS :: FilePath -> ByteString -> IO ByteString
src/Text/Templating/Heist/Splices/Static.hs view
@@ -1,4 +1,5 @@ module Text.Templating.Heist.Splices.Static+{-# DEPRECATED "This will go away in the future.  Use the cache splice instead." #-}   ( StaticTagState   , bindStaticTag   , clearStaticTagCache@@ -43,7 +44,7 @@ -- referenced. staticImpl :: (MonadIO m)            => StaticTagState-           -> TemplateMonad m Template+           -> HeistT m Template staticImpl (STS mv) = do     tree <- getParamNode     let i = fromJust $ getAttribute "id" tree
src/Text/Templating/Heist/Types.hs view
@@ -23,6 +23,7 @@ ------------------------------------------------------------------------------ import             Control.Applicative import             Control.Arrow+import             Control.Monad.CatchIO import             Control.Monad.Cont import             Control.Monad.Error import             Control.Monad.Reader@@ -55,9 +56,14 @@ type TPath = [ByteString]  +data DocumentFile = DocumentFile+    { dfDoc  :: X.Document+    , dfFile :: Maybe FilePath+    } deriving (Eq)+ ------------------------------------------------------------------------------ -- | All documents representing templates are stored in a map.-type TemplateMap = Map TPath X.Document+type TemplateMap = Map TPath DocumentFile   ------------------------------------------------------------------------------@@ -77,41 +83,56 @@ -- @TemplateState@ in calls to @renderTemplate@. data TemplateState m = TemplateState {     -- | A mapping of splice names to splice actions-      _spliceMap      :: SpliceMap m+      _spliceMap       :: SpliceMap m     -- | A mapping of template names to templates-    , _templateMap    :: TemplateMap+    , _templateMap     :: TemplateMap     -- | A flag to control splice recursion-    , _recurse        :: Bool+    , _recurse         :: Bool     -- | The path to the template currently being processed.-    , _curContext     :: TPath+    , _curContext      :: TPath     -- | A counter keeping track of the current recursion depth to prevent     -- infinite loops.-    , _recursionDepth :: Int+    , _recursionDepth  :: Int     -- | A hook run on all templates at load time.-    , _onLoadHook     :: Template -> IO Template+    , _onLoadHook      :: Template -> IO Template     -- | A hook run on all templates just before they are rendered.-    , _preRunHook     :: Template -> m Template+    , _preRunHook      :: Template -> m Template     -- | A hook run on all templates just after they are rendered.-    , _postRunHook    :: Template -> m Template+    , _postRunHook     :: Template -> m Template     -- | The doctypes encountered during template processing.-    , _doctypes       :: [X.DocType]+    , _doctypes        :: [X.DocType]+    -- | The full path to the current template's file on disk.+    , _curTemplateFile :: Maybe FilePath }   ------------------------------------------------------------------------------+-- | Gets the names of all the templates defined in a TemplateState.+templateNames :: TemplateState m -> [TPath]+templateNames ts = Map.keys $ _templateMap ts+++------------------------------------------------------------------------------+-- | Gets the names of all the splices defined in a TemplateState.+spliceNames :: TemplateState m -> [Text]+spliceNames ts = Map.keys $ _spliceMap ts+++------------------------------------------------------------------------------ instance (Monad m) => Monoid (TemplateState m) where     mempty = TemplateState Map.empty Map.empty True [] 0-                           return return return []+                           return return return [] Nothing -    (TemplateState s1 t1 r1 _ d1 o1 b1 a1 dt1) `mappend`-        (TemplateState s2 t2 r2 c2 d2 o2 b2 a2 dt2) =+    (TemplateState s1 t1 r1 _ d1 o1 b1 a1 dt1 ctf1) `mappend`+        (TemplateState s2 t2 r2 c2 d2 o2 b2 a2 dt2 ctf2) =         TemplateState s t r c2 d (o1 >=> o2) (b1 >=> b2) (a1 >=> a2)-            (dt1 `mappend` dt2)+            (dt1 `mappend` dt2) ctf       where         s = s1 `mappend` s2         t = t1 `mappend` t2         r = r1 && r2         d = max d1 d2+        ctf = getLast $ Last ctf1 `mappend` Last ctf2   ------------------------------------------------------------------------------@@ -132,6 +153,7 @@     typeOf _ = mkTyConApp templateStateTyCon [typeOf1 (undefined :: m ())]  +{-# DEPRECATED TemplateMonad "NOTICE: The name TemplateMonad is being phased out in favor of the more appropriate HeistT.  Change your code now to prevent breakage in the future!" #-} ------------------------------------------------------------------------------ -- | TemplateMonad is the monad used for 'Splice' processing.  TemplateMonad -- provides \"passthrough\" instances for many of the monads you might use in@@ -141,6 +163,7 @@                      -> TemplateState m                      -> m (a, TemplateState m) }+type HeistT = TemplateMonad   ------------------------------------------------------------------------------@@ -189,6 +212,16 @@     lift m = TemplateMonad $ \_ s -> do         a <- m         return (a, s)+++------------------------------------------------------------------------------+-- | MonadCatchIO instance+instance MonadCatchIO m => MonadCatchIO (TemplateMonad m) where+    catch (TemplateMonad a) h = TemplateMonad $ \r s -> do+       let handler e = runTemplateMonad (h e) r s+       catch (a r s) handler+    block (TemplateMonad m) = TemplateMonad $ \r s -> block (m r s)+    unblock (TemplateMonad m) = TemplateMonad $ \r s -> unblock (m r s)   ------------------------------------------------------------------------------
test/suite/Text/Templating/Heist/Tests.hs view
@@ -78,7 +78,7 @@          spliceResult <- run $ evalTemplateMonad (runNodeList template)                                                 (X.TextNode "")-                                                (emptyTemplateState ".")+                                                emptyTemplateState         assert $ result == spliceResult  @@ -98,19 +98,19 @@ monoidTest = do     H.assertBool "left monoid identity" $ mempty `mappend` es == es     H.assertBool "right monoid identity" $ es `mappend` mempty == es-  where es = (emptyTemplateState ".") :: TemplateState IO+  where es = emptyTemplateState :: TemplateState IO   ------------------------------------------------------------------------------ addTest :: IO () addTest = do     H.assertEqual "lookup test" (Just []) $-        fmap (X.docContent . fst) $ lookupTemplate "aoeu" ts+        fmap (X.docContent . dfDoc . fst) $ lookupTemplate "aoeu" ts      H.assertEqual "splice touched" 0 $ Map.size (_spliceMap ts)    where-    ts = addTemplate "aoeu" [] (mempty::TemplateState IO)+    ts = addTemplate "aoeu" [] Nothing (mempty::TemplateState IO)   ------------------------------------------------------------------------------@@ -118,7 +118,7 @@ hasTemplateTest = do     ets <- loadT "templates"     let tm = either (error "Error loading templates") _templateMap ets-    let ts = setTemplates tm (emptyTemplateState ".") :: TemplateState IO+    let ts = setTemplates tm emptyTemplateState :: TemplateState IO     H.assertBool "hasTemplate ts" (hasTemplate "index" ts)  @@ -146,7 +146,7 @@ fsLoadTest = do     ets <- loadT "templates"     let tm = either (error "Error loading templates") _templateMap ets-    let ts = setTemplates tm (emptyTemplateState ".") :: TemplateState IO+    let ts = setTemplates tm emptyTemplateState :: TemplateState IO     let f  = g ts      f isNothing "abc/def/xyz"@@ -272,9 +272,9 @@ -- | Markdown test on supplied text markdownTextTest :: H.Assertion markdownTextTest = do-    result <- evalTemplateMonad (markdownSplice ".")+    result <- evalTemplateMonad markdownSplice                                 (X.TextNode "This *is* a test.")-                                (emptyTemplateState ".")+                                emptyTemplateState     H.assertEqual "Markdown text" htmlExpected        (B.filter (/= '\n') $ toByteString $         X.render (X.HtmlDocument X.UTF8 Nothing result))@@ -283,7 +283,7 @@ ------------------------------------------------------------------------------ applyTest :: H.Assertion applyTest = do-    let es = (emptyTemplateState ".") :: TemplateState IO+    let es = emptyTemplateState :: TemplateState IO     res <- evalTemplateMonad applyImpl         (X.Element "apply" [("template", "nonexistant")] []) es @@ -293,7 +293,7 @@ ------------------------------------------------------------------------------ ignoreTest :: H.Assertion ignoreTest = do-    let es = (emptyTemplateState ".") :: TemplateState IO+    let es = emptyTemplateState :: TemplateState IO     res <- evalTemplateMonad ignoreImpl         (X.Element "ignore" [("tag", "ignorable")]            [X.TextNode "This should be ignored"]) es@@ -302,7 +302,7 @@  --localTSTest :: H.Assertion --localTSTest = do---    let es = (emptyTemplateState ".") :: TemplateState IO+--    let es = emptyTemplateState :: TemplateState IO  lookupTemplateTest = do     ts <- loadTS "templates"@@ -323,16 +323,27 @@  ------------------------------------------------------------------------------ loadT :: String -> IO (Either String (TemplateState IO))-loadT s = loadTemplates s (emptyTemplateState s)+loadT s = loadTemplates s emptyTemplateState   ------------------------------------------------------------------------------ loadTS :: FilePath -> IO (TemplateState IO) loadTS baseDir = do-    etm <- loadTemplates baseDir $ emptyTemplateState baseDir+    etm <- loadTemplates baseDir emptyTemplateState     return $ either error id etm  +testTemplate tname = do+    ts <- loadTS "templates"+    Just (resDoc, _) <- renderTemplate ts tname+    return $ toByteString resDoc+++testTemplateEval tname = do+    ts <- loadTS "templates"+    evalTemplateMonad (evalWithHooks tname) (X.TextNode "") ts++ ------------------------------------------------------------------------------ identStartChar :: [Char] identStartChar = ['a'..'z']@@ -483,7 +494,7 @@     , "Splice result:"     , L.unpack $ L.concat $ map formatNode $ unsafePerformIO $         evalTemplateMonad (runNodeList $ buildBindTemplate b)-                          (X.TextNode "") (emptyTemplateState ".")+                          (X.TextNode "") emptyTemplateState     , "Template:"     , L.unpack $ L.concat $ map formatNode $ buildBindTemplate b     ]@@ -562,8 +573,9 @@         (X.TextNode "") ts    where ts = setTemplates (Map.singleton [T.encodeUtf8 $ unName name]-                          (X.HtmlDocument X.UTF8 Nothing callee))-                          (emptyTemplateState ".")+                          (DocumentFile (X.HtmlDocument X.UTF8 Nothing callee)+                                        Nothing))+                          emptyTemplateState