hamlet 0.4.1 → 0.4.2
raw patch · 6 files changed
+291/−17 lines, 6 filesdep +failurePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: failure
API changes (from Hackage documentation)
+ Text.Hamlet: HDBool :: Bool -> HamletData url
+ Text.Hamlet: HDHtml :: (Html ()) -> HamletData url
+ Text.Hamlet: HDList :: [HamletData url] -> HamletData url
+ Text.Hamlet: HDMap :: [(String, HamletData url)] -> HamletData url
+ Text.Hamlet: HDMaybe :: (Maybe (HamletData url)) -> HamletData url
+ Text.Hamlet: HDTemplate :: HamletRT -> HamletData url
+ Text.Hamlet: HDUrl :: url -> HamletData url
+ Text.Hamlet: HDUrlParams :: url -> [(String, String)] -> HamletData url
+ Text.Hamlet: HamletParseException :: String -> HamletException
+ Text.Hamlet: HamletRenderException :: String -> HamletException
+ Text.Hamlet: HamletUnsupportedDocException :: Doc -> HamletException
+ Text.Hamlet: data HamletData url
+ Text.Hamlet: data HamletException
+ Text.Hamlet: data HamletRT
+ Text.Hamlet: hamlet' :: QuasiQuoter
+ Text.Hamlet: hamletCloseNewline :: HamletSettings -> Bool
+ Text.Hamlet: hamletDebug :: QuasiQuoter
+ Text.Hamlet: parseHamletRT :: (Failure HamletException m) => HamletSettings -> String -> m HamletRT
+ Text.Hamlet: renderHamletRT :: (Failure HamletException m) => HamletRT -> HamletData url -> (url -> String) -> m (Html ())
+ Text.Hamlet: xhamlet' :: QuasiQuoter
+ Text.Hamlet: xhtmlHamletSettings :: HamletSettings
+ Text.Hamlet.RT: HDBool :: Bool -> HamletData url
+ Text.Hamlet.RT: HDHtml :: (Html ()) -> HamletData url
+ Text.Hamlet.RT: HDList :: [HamletData url] -> HamletData url
+ Text.Hamlet.RT: HDMap :: [(String, HamletData url)] -> HamletData url
+ Text.Hamlet.RT: HDMaybe :: (Maybe (HamletData url)) -> HamletData url
+ Text.Hamlet.RT: HDTemplate :: HamletRT -> HamletData url
+ Text.Hamlet.RT: HDUrl :: url -> HamletData url
+ Text.Hamlet.RT: HDUrlParams :: url -> [(String, String)] -> HamletData url
+ Text.Hamlet.RT: HamletParseException :: String -> HamletException
+ Text.Hamlet.RT: HamletRT :: [SimpleDoc] -> HamletRT
+ Text.Hamlet.RT: HamletRenderException :: String -> HamletException
+ Text.Hamlet.RT: HamletUnsupportedDocException :: Doc -> HamletException
+ Text.Hamlet.RT: SDCond :: [([String], [SimpleDoc])] -> [SimpleDoc] -> SimpleDoc
+ Text.Hamlet.RT: SDForall :: [String] -> String -> [SimpleDoc] -> SimpleDoc
+ Text.Hamlet.RT: SDMaybe :: [String] -> String -> [SimpleDoc] -> [SimpleDoc] -> SimpleDoc
+ Text.Hamlet.RT: SDRaw :: String -> SimpleDoc
+ Text.Hamlet.RT: SDTemplate :: [String] -> SimpleDoc
+ Text.Hamlet.RT: SDUrl :: Bool -> [String] -> SimpleDoc
+ Text.Hamlet.RT: SDVar :: [String] -> SimpleDoc
+ Text.Hamlet.RT: data HamletData url
+ Text.Hamlet.RT: data HamletException
+ Text.Hamlet.RT: data SimpleDoc
+ Text.Hamlet.RT: instance Exception HamletException
+ Text.Hamlet.RT: instance Show HamletException
+ Text.Hamlet.RT: instance Typeable HamletException
+ Text.Hamlet.RT: newtype HamletRT
+ Text.Hamlet.RT: parseHamletRT :: (Failure HamletException m) => HamletSettings -> String -> m HamletRT
+ Text.Hamlet.RT: renderHamletRT :: (Failure HamletException m) => HamletRT -> HamletData url -> (url -> String) -> m (Html ())
- Text.Hamlet: HamletSettings :: String -> Bool -> HamletSettings
+ Text.Hamlet: HamletSettings :: String -> Bool -> Bool -> HamletSettings
Files
- Text/Hamlet.hs +11/−0
- Text/Hamlet/Parse.hs +19/−1
- Text/Hamlet/Quasi.hs +34/−11
- Text/Hamlet/RT.hs +151/−0
- hamlet.cabal +4/−2
- runtests.hs +72/−3
Text/Hamlet.hs view
@@ -2,6 +2,9 @@ ( -- * Basic quasiquoters hamlet , xhamlet+ , hamlet'+ , xhamlet'+ , hamletDebug -- * Load from external file , hamletFile , xhamletFile@@ -10,6 +13,7 @@ , hamletFileWithSettings , HamletSettings (..) , defaultHamletSettings+ , xhtmlHamletSettings -- * Datatypes , Html , Hamlet@@ -20,10 +24,17 @@ , string , unsafeByteString , cdata+ -- * Runtime Hamlet+ , HamletRT+ , HamletData (..)+ , HamletException (..)+ , parseHamletRT+ , renderHamletRT ) where import Text.Hamlet.Parse import Text.Hamlet.Quasi+import Text.Hamlet.RT import Text.Blaze import qualified Data.ByteString.Lazy as L import Data.Monoid (mappend)
Text/Hamlet/Parse.hs view
@@ -8,6 +8,8 @@ , parseDoc , HamletSettings (..) , defaultHamletSettings+ , xhtmlHamletSettings+ , debugHamletSettings ) where @@ -265,6 +267,8 @@ _ -> DocContent $ ContentRaw ">" start = DocContent $ ContentRaw $ "<" ++ tn attrs'' = concatMap attrToContent attrs'+ newline' = DocContent $ ContentRaw+ $ if hamletCloseNewline set then "\n" else "" inside' <- nestToDoc set inside rest' <- nestToDoc set rest Ok $ start@@ -273,6 +277,7 @@ : map DocContent content ++ inside' ++ end+ : newline' : rest' nestToDoc set (Nest (LineContent content) inside:rest) = do inside' <- nestToDoc set inside@@ -326,11 +331,24 @@ -- | 'True' means to close empty tags (eg, img) with a trailing slash, ie -- XML-style empty tags. 'False' uses HTML-style. , hamletCloseEmpties :: Bool+ -- | Should we put a newline after closing a tag?+ , hamletCloseNewline :: Bool } -- | Defaults settings: HTML5 doctype and HTML-style empty tags. defaultHamletSettings :: HamletSettings-defaultHamletSettings = HamletSettings "<!DOCTYPE html>" False+defaultHamletSettings = HamletSettings "<!DOCTYPE html>" False False++xhtmlHamletSettings :: HamletSettings+xhtmlHamletSettings =+ HamletSettings doctype True False+ where+ doctype =+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " +++ "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"++debugHamletSettings :: HamletSettings+debugHamletSettings = HamletSettings "<!DOCTYPE html>" False True data CloseStyle = NoClose | CloseInside | CloseSeparate
Text/Hamlet/Quasi.hs view
@@ -4,10 +4,15 @@ module Text.Hamlet.Quasi ( hamlet , xhamlet+ , hamlet'+ , xhamlet'+ , hamletDebug , hamletWithSettings+ , hamletWithSettings' , hamletFile , xhamletFile , hamletFileWithSettings+ , showParams ) where import Text.Hamlet.Parse@@ -101,16 +106,25 @@ let d' = deref scope d return (d' `AppE` render) +-- | Calls 'hamletWithSettings'' with 'defaultHamletSettings'.+hamlet' :: QuasiQuoter+hamlet' = hamletWithSettings' defaultHamletSettings++-- | Calls 'hamletWithSettings'' using XHTML 1.0 Strict settings.+xhamlet' :: QuasiQuoter+xhamlet' = hamletWithSettings' xhtmlHamletSettings+ -- | Calls 'hamletWithSettings' with 'defaultHamletSettings'. hamlet :: QuasiQuoter hamlet = hamletWithSettings defaultHamletSettings +-- | Calls 'hamletWithSettings' with 'debugHamletSettings'.+hamletDebug :: QuasiQuoter+hamletDebug = hamletWithSettings debugHamletSettings+ -- | Calls 'hamletWithSettings' using XHTML 1.0 Strict settings. xhamlet :: QuasiQuoter-xhamlet = hamletWithSettings $ HamletSettings doctype True where- doctype =- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " ++- "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"+xhamlet = hamletWithSettings xhtmlHamletSettings -- | A quasi-quoter that converts Hamlet syntax into a function of form: --@@ -122,6 +136,17 @@ QuasiQuoter (hamletFromString set) $ error "Cannot quasi-quote Hamlet to patterns" +-- | A quasi-quoter that converts Hamlet syntax into a 'Html' ().+--+-- Please see accompanying documentation for a description of Hamlet syntax.+hamletWithSettings' :: HamletSettings -> QuasiQuoter+hamletWithSettings' set =+ QuasiQuoter (\s -> do+ x <- hamletFromString set s+ id' <- [|id|]+ return $ x `AppE` id')+ $ error "Cannot quasi-quote Hamlet to patterns"+ hamletFromString :: HamletSettings -> String -> Q Exp hamletFromString set s = do case parseDoc set s of@@ -142,12 +167,7 @@ -- | Calls 'hamletFileWithSettings' using XHTML 1.0 Strict settings. xhamletFile :: FilePath -> Q Exp-xhamletFile =- hamletFileWithSettings $ HamletSettings doctype True- where- doctype =- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " ++- "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"+xhamletFile = hamletFileWithSettings xhtmlHamletSettings deref :: Scope -> Deref -> Exp deref scope (DerefBranch x y) =@@ -197,8 +217,11 @@ outputUrlParams render (u, params) = mappend (outputUrl render u) (string $ showParams params)++showParams :: [(String, String)] -> String+showParams z =+ '?' : intercalate "&" (map go z) where- showParams x = '?' : intercalate "&" (map go x) go (x, y) = go' x ++ '=' : go' y go' = concatMap encodeUrlChar
+ Text/Hamlet/RT.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE DeriveDataTypeable #-}+-- | Most everything exported here is exported also by "Text.Hamlet". The+-- exceptions to that rule should not be necessary for normal usage.+module Text.Hamlet.RT+ ( -- * Public API+ HamletRT (..)+ , HamletData (..)+ , HamletException (..)+ , parseHamletRT+ , renderHamletRT+ , SimpleDoc (..)+ ) where++import Data.Monoid (mconcat)+import Control.Monad (liftM, forM)+import Control.Exception (Exception)+import Data.Typeable (Typeable)+import Control.Failure+import Text.Blaze+import Text.Hamlet.Parse+import Text.Hamlet.Quasi (showParams)+import Data.List (intercalate)++data HamletData url = HDHtml (Html ())+ | HDUrl url+ | HDUrlParams url [(String, String)]+ | HDTemplate HamletRT+ | HDBool Bool+ | HDMaybe (Maybe (HamletData url))+ | HDList [HamletData url]+ | HDMap [(String, HamletData url)]++data SimpleDoc = SDRaw String+ | SDVar [String]+ | SDUrl Bool [String]+ | SDTemplate [String]+ | SDForall [String] String [SimpleDoc]+ | SDMaybe [String] String [SimpleDoc] [SimpleDoc]+ | SDCond [([String], [SimpleDoc])] [SimpleDoc]++newtype HamletRT = HamletRT [SimpleDoc]++data HamletException = HamletParseException String+ | HamletUnsupportedDocException Doc+ | HamletRenderException String+ deriving (Show, Typeable)+instance Exception HamletException++parseHamletRT :: Failure HamletException m+ => HamletSettings -> String -> m HamletRT+parseHamletRT set s =+ case parseDoc set s of+ Error s' -> failure $ HamletParseException s'+ Ok x -> liftM HamletRT $ mapM convert x+ where+ convert x@(DocForall deref (Ident ident) docs) = do+ deref' <- flattenDeref x deref+ docs' <- mapM convert docs+ return $ SDForall deref' ident docs'+ convert x@(DocMaybe deref (Ident ident) jdocs ndocs) = do+ deref' <- flattenDeref x deref+ jdocs' <- mapM convert jdocs+ ndocs' <- maybe (return []) (mapM convert) ndocs+ return $ SDMaybe deref' ident jdocs' ndocs'+ convert (DocContent (ContentRaw s')) = return $ SDRaw s'+ convert x@(DocContent (ContentVar deref)) = do+ y <- flattenDeref x deref+ return $ SDVar y+ convert x@(DocContent (ContentUrl p deref)) = do+ y <- flattenDeref x deref+ return $ SDUrl p y+ convert x@(DocContent (ContentEmbed deref)) = do+ y <- flattenDeref x deref+ return $ SDTemplate y+ convert x@(DocCond conds els) = do+ conds' <- mapM go conds+ els' <- maybe (return []) (mapM convert) els+ return $ SDCond conds' els'+ where+ go (deref, docs') = do+ deref' <- flattenDeref x deref+ docs'' <- mapM convert docs'+ return (deref', docs'')+ flattenDeref _ (DerefLeaf (Ident x)) = return [x]+ flattenDeref orig (DerefBranch (DerefLeaf (Ident x)) y) = do+ y' <- flattenDeref orig y+ return $ x : y'+ flattenDeref orig _ = failure $ HamletUnsupportedDocException orig++renderHamletRT :: Failure HamletException m+ => HamletRT+ -> HamletData url+ -> (url -> String)+ -> m (Html ())+renderHamletRT (HamletRT docs) (HDMap scope0) renderUrl =+ liftM mconcat $ mapM (go scope0) docs+ where+ go _ (SDRaw s) = return $ preEscapedString s+ go scope (SDVar n) = do+ v <- lookup' n n $ HDMap scope+ case v of+ HDHtml h -> return h+ _ -> fa $ intercalate "." n ++ ": expected HDHtml"+ go scope (SDUrl p n) = do+ v <- lookup' n n $ HDMap scope+ case (p, v) of+ (False, HDUrl u) -> return $ preEscapedString $ renderUrl u+ (True, HDUrlParams u q) ->+ return $ preEscapedString $ renderUrl u ++ showParams q+ (False, _) -> fa $ intercalate "." n ++ ": expected HDUrl"+ (True, _) -> fa $ intercalate "." n ++ ": expected HDUrlParams"+ go scope (SDTemplate n) = do+ v <- lookup' n n $ HDMap scope+ case v of+ HDTemplate h -> renderHamletRT h (HDMap scope) renderUrl+ _ -> fa $ intercalate "." n ++ ": expected HDTemplate"+ go scope (SDForall n ident docs') = do+ v <- lookup' n n $ HDMap scope+ case v of+ HDList os -> do+ liftM mconcat $ forM os $ \o -> do+ let scope' = HDMap $ (ident, o) : scope+ renderHamletRT (HamletRT docs') scope' renderUrl+ _ -> fa $ intercalate "." n ++ ": expected HDList"+ go scope (SDMaybe n ident jdocs ndocs) = do+ v <- lookup' n n $ HDMap scope+ (scope', docs') <-+ case v of+ HDMaybe Nothing -> return (scope, ndocs)+ HDMaybe (Just o) -> return ((ident, o) : scope, jdocs)+ _ -> fa $ intercalate "." n ++ ": expected HDMaybe"+ renderHamletRT (HamletRT docs') (HDMap scope') renderUrl+ go scope (SDCond [] docs') =+ renderHamletRT (HamletRT docs') (HDMap scope) renderUrl+ go scope (SDCond ((b, docs'):cs) els) = do+ v <- lookup' b b $ HDMap scope+ case v of+ HDBool True ->+ renderHamletRT (HamletRT docs') (HDMap scope) renderUrl+ HDBool False -> go scope (SDCond cs els)+ _ -> fa $ intercalate "." b ++ ": expected HDBool"+ lookup' _ [] x = return x+ lookup' orig (n:ns) (HDMap m) =+ case lookup n m of+ Nothing -> fa $ intercalate "." orig ++ " not found"+ Just o -> lookup' orig ns o+ lookup' orig _ _ = fa $ intercalate "." orig ++ ": unexpected type"+ fa = failure . HamletRenderException+renderHamletRT _ _ _ =+ failure $ HamletRenderException "renderHamletRT must be given a HDMap"
hamlet.cabal view
@@ -1,5 +1,5 @@ name: hamlet-version: 0.4.1+version: 0.4.2 license: BSD3 license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>@@ -44,8 +44,10 @@ utf8-string >= 0.3.4 && < 0.4, template-haskell, blaze-html >= 0.1.1 && < 0.2,- parsec >= 2 && < 4+ parsec >= 2 && < 4,+ failure >= 0.1.0 && < 0.2 exposed-modules: Text.Hamlet+ Text.Hamlet.RT other-modules: Text.Hamlet.Parse Text.Hamlet.Quasi ghc-options: -Wall
runtests.hs view
@@ -56,6 +56,9 @@ , testCase "currency symbols" caseCurrency , testCase "external" caseExternal , testCase "parens" caseParens + , testCase "hamlet' and xhamlet'" caseHamlet' + , testCase "hamletDebug" caseHamletDebug + , testCase "hamlet runtime" caseHamletRT ] data Url = Home | Sub SubUrl @@ -354,7 +357,7 @@ where users = ["1", "2"] name = "foo" - val = 5 + val = 5 :: Int isBoolBlank _ = True isBoolTrue _ = False isBoolFalse _ = False @@ -384,8 +387,74 @@ helper "xy" [$hamlet|$(plus x) y$|] helper "xy" [$hamlet|$(plus.x).y$|] helper "xxy" [$hamlet|$(plus (plus x).x).y$|] - let list = ["1", "2", "3"] + let alist = ["1", "2", "3"] helper "123" [$hamlet| -$forall (id id.id id.list) x +$forall (id id.id id.alist) x $x$ |] + +helper' :: String -> Html ()-> Assertion +helper' res h = do + let x = renderHtml h + res @=? toString x + +caseHamlet' :: Assertion +caseHamlet' = do + helper' "foo" [$hamlet'|foo|] + helper' "foo" [$xhamlet'|foo|] + +caseHamletDebug :: Assertion +caseHamletDebug = do + helper "<p>foo</p>\n<p>bar</p>\n" [$hamletDebug| +%p foo +%p bar +|] + +caseHamletRT :: Assertion +caseHamletRT = do + temp <- parseHamletRT defaultHamletSettings "$var$" + rt <- parseHamletRT defaultHamletSettings $ + unlines + [ "$foo.bar.baz$ bin $" + , "$forall list l" + , " $l$" + , "$maybe just j" + , " $j$" + , "$maybe nothing n" + , "$nothing" + , " nothing" + , "^template^" + , "@url@" + , "$if false" + , "$elseif false" + , "$elseif true" + , " a" + , "$if false" + , "$else" + , " b" + , "@?urlp@" + ] + let scope = + HDMap + [ ("foo", HDMap + [ ("bar", HDMap + [ ("baz", HDHtml $ preEscapedString "foo<bar>baz") + ]) + ]) + , ("list", HDList + [ HDHtml $ string "1" + , HDHtml $ string "2" + , HDHtml $ string "3" + ]) + , ("just", HDMaybe $ Just $ HDHtml $ string "just") + , ("nothing", HDMaybe Nothing) + , ("template", HDTemplate temp) + , ("var", HDHtml $ string "var") + , ("url", HDUrl Home) + , ("urlp", HDUrlParams Home [("foo", "bar")]) + , ("true", HDBool True) + , ("false", HDBool False) + ] + rend <- renderHamletRT rt scope render + toString (renderHtml rend) @?= + "foo<bar>baz bin 123justnothingvarurlaburl?foo=bar"