hamlet 0.10.9.1 → 1.0.0
raw patch · 6 files changed
+125/−70 lines, 6 filesdep ~containersdep ~hamletdep ~shakespearePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: containers, hamlet, shakespeare
API changes (from Hackage documentation)
+ Text.Hamlet: class ToAttributes a
+ Text.Hamlet: instance ToAttributes (String, String)
+ Text.Hamlet: instance ToAttributes (Text, Text)
+ Text.Hamlet: instance ToAttributes [(String, String)]
+ Text.Hamlet: instance ToAttributes [(Text, Text)]
+ Text.Hamlet: toAttributes :: ToAttributes a => a -> [(Text, Text)]
Files
- Text/Hamlet.hs +31/−0
- Text/Hamlet/Parse.hs +38/−26
- Text/Hamlet/RT.hs +3/−0
- hamlet.cabal +7/−7
- test/HamletTest.hs +45/−36
- test/hamlets/external.hamlet +1/−1
Text/Hamlet.hs view
@@ -24,6 +24,8 @@ , HtmlUrlI18n , ihamlet , ihamletFile+ -- * Type classes+ , ToAttributes (..) -- * Internal, for making more , HamletSettings , hamletWithSettings@@ -45,7 +47,33 @@ import Text.Blaze (Html, preEscapedText, toHtml) import qualified Data.Foldable as F import Control.Monad (mplus)+import Data.Monoid (mempty, mappend)+import Control.Arrow ((***)) +-- | Convert some value to a list of attribute pairs.+class ToAttributes a where+ toAttributes :: a -> [(Text, Text)]+instance ToAttributes (Text, Text) where+ toAttributes = return+instance ToAttributes (String, String) where+ toAttributes (k, v) = [(pack k, pack v)]+instance ToAttributes [(Text, Text)] where+ toAttributes = id+instance ToAttributes [(String, String)] where+ toAttributes = map (pack *** pack)++attrsToHtml :: [(Text, Text)] -> Html+attrsToHtml =+ foldr go mempty+ where+ go (k, v) rest =+ toHtml " "+ `mappend` preEscapedText k+ `mappend` preEscapedText (pack "=\"")+ `mappend` toHtml v+ `mappend` preEscapedText (pack "\"")+ `mappend` rest+ type Render url = url -> [(Text, Text)] -> Text type Translate msg = msg -> Html @@ -165,6 +193,9 @@ Nothing -> error "Message interpolation used, but no message renderer provided" Just wrender -> wrender $ \render -> return $ hrFromHtml hr `AppE` (render `AppE` derefToExp scope d)+contentToExp _ hr scope (ContentAttrs d) = do+ html <- [|attrsToHtml . toAttributes|]+ return $ hrFromHtml hr `AppE` (html `AppE` derefToExp scope d) shamlet :: QuasiQuoter shamlet = hamletWithSettings htmlRules defaultHamletSettings
Text/Hamlet/Parse.hs view
@@ -22,7 +22,7 @@ import Text.ParserCombinators.Parsec hiding (Line) import Data.Set (Set) import qualified Data.Set as Set-import Data.Maybe (mapMaybe)+import Data.Maybe (mapMaybe, fromMaybe) data Result v = Error String | Ok v deriving (Show, Eq, Read, Data, Typeable)@@ -42,6 +42,7 @@ | ContentUrl Bool Deref -- ^ bool: does it include params? | ContentEmbed Deref | ContentMsg Deref+ | ContentAttrs Deref deriving (Show, Eq, Read, Data, Typeable) data Line = LineForall Deref Binding@@ -55,9 +56,10 @@ | LineOf [Ident] | LineTag { _lineTagName :: String- , _lineAttr :: [(Maybe Deref, String, [Content])]+ , _lineAttr :: [(Maybe Deref, String, Maybe [Content])] , _lineContent :: [Content] , _lineClasses :: [(Maybe Deref, [Content])]+ , _lineAttrs :: [Deref] } | LineContent [Content] deriving (Eq, Show, Read)@@ -236,15 +238,22 @@ tagClass (Just d) <|> tagAttrib (Just d) tagClass x = char '.' >> (TagClass . (,) x) <$> tagAttribValue NotInQuotes tagAttrib cond = do- s <- many1 $ noneOf " \t=\r\n>"- v <- (char '=' >> tagAttribValue NotInQuotesAttr) <|> return []+ s <- many1 $ noneOf " \t=\r\n><"+ v <- (char '=' >> Just <$> tagAttribValue NotInQuotesAttr) <|> return Nothing return $ TagAttrib (cond, s, v)- tag' = foldr tag'' ("div", [], [])- tag'' (TagName s) (_, y, z) = (s, y, z)- tag'' (TagIdent s) (x, y, z) = (x, (Nothing, "id", s) : y, z)- tag'' (TagClass s) (x, y, z) = (x, y, s : z)- tag'' (TagAttrib s) (x, y, z) = (x, s : y, z) + tagAttrs = do+ _ <- char '*'+ d <- between (char '{') (char '}') parseDeref+ return $ TagAttribs d++ tag' = foldr tag'' ("div", [], [], [])+ tag'' (TagName s) (_, y, z, as) = (s, y, z, as)+ tag'' (TagIdent s) (x, y, z, as) = (x, (Nothing, "id", Just s) : y, z, as)+ tag'' (TagClass s) (x, y, z, as) = (x, y, s : z, as)+ tag'' (TagAttrib s) (x, y, z, as) = (x, s : y, z, as)+ tag'' (TagAttribs s) (x, y, z, as) = (x, y, z, s : as)+ ident :: Parser Ident ident = Ident <$> many1 (alphaNum <|> char '_' <|> char '\'') @@ -264,19 +273,21 @@ _ <- char '<' name' <- many $ noneOf " \t.#\r\n!>" let name = if null name' then "div" else name'- xs <- many $ try ((many $ oneOf " \t") >>- (tagIdent <|> tagCond <|> tagClass Nothing <|> tagAttrib Nothing))- _ <- many $ oneOf " \t"- c <- (eol >> return []) <|> (char '>' >> content InContent)- let (tn, attr, classes) = tag' $ TagName name : xs+ xs <- many $ try ((many $ oneOf " \t\r\n") >>+ (tagIdent <|> tagCond <|> tagClass Nothing <|> tagAttrs <|> tagAttrib Nothing))+ _ <- many $ oneOf " \t\r\n"+ _ <- char '>'+ c <- content InContent+ let (tn, attr, classes, attrsd) = tag' $ TagName name : xs if '/' `elem` tn then fail "A tag name may not contain a slash. Perhaps you have a closing tag in your HTML."- else return $ LineTag tn attr c classes+ else return $ LineTag tn attr c classes attrsd data TagPiece = TagName String | TagIdent [Content] | TagClass (Maybe Deref, [Content])- | TagAttrib (Maybe Deref, String, [Content])+ | TagAttrib (Maybe Deref, String, Maybe [Content])+ | TagAttribs Deref deriving Show data ContentRule = InQuotes | NotInQuotes | NotInQuotesAttr | InContent@@ -330,9 +341,9 @@ cases <- mapM getOf inside rest' <- nestToDoc set rest Ok $ DocCase d cases : rest'-nestToDoc set (Nest (LineTag tn attrs content classes) inside:rest) = do+nestToDoc set (Nest (LineTag tn attrs content classes attrsD) inside:rest) = do let attrFix (x, y, z) = (x, y, [(Nothing, z)])- let takeClass (a, "class", b) = Just (a, b)+ let takeClass (a, "class", b) = Just (a, fromMaybe [] b) takeClass _ = Nothing let clazzes = classes ++ mapMaybe takeClass attrs let notClass (_, x, _) = x /= "class"@@ -340,7 +351,7 @@ let attrs' = case clazzes of [] -> map attrFix noclass- _ -> (Nothing, "class", clazzes)+ _ -> (Nothing, "class", map (second Just) clazzes) : map attrFix noclass let closeStyle = if not (null content) || not (null inside)@@ -361,6 +372,7 @@ rest' <- nestToDoc set rest Ok $ start : attrs''+ ++ map (DocContent . ContentAttrs) attrsD ++ seal : map DocContent content ++ inside'@@ -408,12 +420,12 @@ ds <- nestToDoc set ns return $ compressDoc ds -attrToContent :: (Maybe Deref, String, [(Maybe Deref, [Content])]) -> [Doc]+attrToContent :: (Maybe Deref, String, [(Maybe Deref, Maybe [Content])]) -> [Doc] attrToContent (Just cond, k, v) = [DocCond [(cond, attrToContent (Nothing, k, v))] Nothing] attrToContent (Nothing, k, []) = [DocContent $ ContentRaw $ ' ' : k]-attrToContent (Nothing, k, [(Nothing, [])]) = [DocContent $ ContentRaw $ ' ' : k]-attrToContent (Nothing, k, [(Nothing, v)]) =+attrToContent (Nothing, k, [(Nothing, Nothing)]) = [DocContent $ ContentRaw $ ' ' : k]+attrToContent (Nothing, k, [(Nothing, Just v)]) = DocContent (ContentRaw (' ' : k ++ "=\"")) : map DocContent v ++ [DocContent $ ContentRaw "\""]@@ -423,16 +435,16 @@ ++ go' (last v) ++ [DocContent $ ContentRaw "\""] where- go (Nothing, x) = map DocContent x ++ [DocContent $ ContentRaw " "]+ go (Nothing, x) = map DocContent (fromMaybe [] x) ++ [DocContent $ ContentRaw " "] go (Just b, x) = [ DocCond- [(b, map DocContent x ++ [DocContent $ ContentRaw " "])]+ [(b, map DocContent (fromMaybe [] x) ++ [DocContent $ ContentRaw " "])] Nothing ]- go' (Nothing, x) = map DocContent x+ go' (Nothing, x) = maybe [] (map DocContent) x go' (Just b, x) = [ DocCond- [(b, map DocContent x)]+ [(b, maybe [] (map DocContent) x)] Nothing ]
Text/Hamlet/RT.hs view
@@ -83,6 +83,9 @@ return $ SDTemplate y convert (DocContent ContentMsg{}) = error "Runtime hamlet does not currently support message interpolation"+ convert (DocContent ContentAttrs{}) =+ error "Runtime hamlet does not currently support attrs interpolation"+ convert x@(DocCond conds els) = do conds' <- mapM go conds els' <- maybe (return []) (mapM convert) els
hamlet.cabal view
@@ -1,14 +1,14 @@ name: hamlet-version: 0.10.9.1+version: 1.0.0 license: BSD3 license-file: LICENSE author: Michael Snoyman <michael@snoyman.com> maintainer: Michael Snoyman <michael@snoyman.com> synopsis: Haml-like template files that are compile-time checked description:- Hamlet gives you a type-safe tool for generating HTML code. It works via Quasi-Quoting, and generating extremely efficient output code. The syntax is white-space sensitive, and it helps you avoid cross-site scripting issues and 404 errors. Please see the documentation at <http://docs.yesodweb.com/book/hamlet/> for more details.+ Hamlet gives you a type-safe tool for generating HTML code. It works via Quasi-Quoting, and generating extremely efficient output code. The syntax is white-space sensitive, and it helps you avoid cross-site scripting issues and 404 errors. Please see the documentation at <http://www.yesodweb.com/book/shakespearean-templates> for more details. .- Here is a quick overview of hamlet html. Due to haddock escaping issues, we can't properly show variable insertion, but we are still going to show some conditionals. Please see <http://www.yesodweb.com/book/templates> for a thorough description+ Here is a quick overview of hamlet html. Due to haddock escaping issues, we can't properly show variable insertion, but we are still going to show some conditionals. Please see <http://www.yesodweb.com/book/shakespearean-templates> for a thorough description . > !!! > <html>@@ -25,7 +25,7 @@ stability: Stable cabal-version: >= 1.8 build-type: Simple-homepage: http://www.yesodweb.com/book/templates+homepage: http://www.yesodweb.com/book/shakespearean-templates extra-source-files: test/hamlets/double-foralls.hamlet test/hamlets/external-debug.hamlet@@ -41,14 +41,14 @@ library build-depends: base >= 4 && < 5- , shakespeare >= 0.10.2 && < 0.12+ , shakespeare >= 1.0 && < 1.1 , bytestring >= 0.9 && < 0.10 , template-haskell , blaze-html >= 0.4 && < 0.5 , parsec >= 2 && < 4 , failure >= 0.1 && < 0.3 , text >= 0.7 && < 0.12- , containers >= 0.2 && < 0.5+ , containers >= 0.2 , blaze-builder >= 0.2 && < 0.4 , process >= 1.0 && < 1.2 exposed-modules: Text.Hamlet@@ -64,7 +64,7 @@ type: exitcode-stdio-1.0 ghc-options: -Wall- build-depends: hamlet >= 0.10 && < 0.11+ build-depends: hamlet , base >= 4 && < 5 , parsec >= 2 && < 4 , containers >= 0.2 && < 0.5
test/HamletTest.hs view
@@ -83,7 +83,7 @@ , it "ignores a blank line" $ do helper "<p>foo</p>" [hamlet| -<p +<p> foo @@ -96,7 +96,7 @@ , it "hamlet angle bracket syntax" $ helper "<p class=\"foo\" height=\"100\"><span id=\"bar\" width=\"50\">HELLO</span></p>" [hamlet| -<p.foo height="100" +<p.foo height="100"> <span #bar width=50>HELLO |] @@ -158,7 +158,7 @@ helper "<p>1</p><p>2 not ignored</p>" [hamlet| <p>1 <!-- ignored comment --> -<p +<p> 2 <!-- ignored --> not ignored<!-- ignored --> |] @@ -192,13 +192,13 @@ , it "conditional class" $ do helper "<p class=\"current\"></p>" - [hamlet|<p :False:.ignored :True:.current|] + [hamlet|<p :False:.ignored :True:.current>|] helper "<p class=\"1 3 2 4\"></p>" - [hamlet|<p :True:.1 :True:class=2 :False:.a :False:class=b .3 class=4|] + [hamlet|<p :True:.1 :True:class=2 :False:.a :False:class=b .3 class=4>|] helper "<p class=\"foo bar baz\"></p>" - [hamlet|<p class=foo class=bar class=baz|] + [hamlet|<p class=foo class=bar class=baz>|] @@ -265,10 +265,12 @@ $forall num <- [1, 2, 3] <li>#{show num} |] +{- , it "infix operators" $ helper "5" [hamlet|#{show $ (4 + 5) - (2 + 2)}|] , it "infix operators with parens" $ helper "5" [hamlet|#{show (2 + 3)}|] + -} , it "doctypes" $ helper "<!DOCTYPE html>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" [hamlet| $doctype 5 $doctype strict @@ -338,6 +340,20 @@ <dt>#{name} <dd>#{show age} |] + + , it "multiline tags" $ helper + "<foo bar=\"baz\" bin=\"bin\">content</foo>" [hamlet| +<foo bar=baz + bin=bin>content +|] + , let attrs = [("bar", "baz"), ("bin", "<>\"&")] + in it "*{...} attributes" $ helper + "<foo bar=\"baz\" bin=\"<>"&\">content</foo>" [hamlet| +<foo *{attrs}>content +|] + , it "blank attr values" $ helper + "<foo bar=\"\" baz bin=\"\"></foo>" + [hamlet|<foo bar="" baz bin=>|] ] data Pair = Pair String Int @@ -425,11 +441,11 @@ caseTag :: Assertion caseTag = do helper "<p class=\"foo\"><div id=\"bar\">baz</div></p>" [hamlet| -<p .foo +<p .foo> <#bar>baz |] helper "<p class=\"foo.bar\"><div id=\"bar\">baz</div></p>" [hamlet| -<p class=foo.bar +<p class=foo.bar> <#bar>baz |] @@ -560,30 +576,23 @@ |] caseScriptNotEmpty :: Assertion -caseScriptNotEmpty = helper "<script></script>" [hamlet|<script|] +caseScriptNotEmpty = helper "<script></script>" [hamlet|<script>|] caseMetaEmpty :: Assertion caseMetaEmpty = do - helper "<meta>" [hamlet|<meta|] - helper "<meta/>" [xhamlet|<meta|] helper "<meta>" [hamlet|<meta>|] helper "<meta/>" [xhamlet|<meta>|] caseInputEmpty :: Assertion caseInputEmpty = do - helper "<input>" [hamlet|<input|] - helper "<input/>" [xhamlet|<input|] helper "<input>" [hamlet|<input>|] helper "<input/>" [xhamlet|<input>|] caseMultiClass :: Assertion -caseMultiClass = do - helper "<div class=\"foo bar\"></div>" [hamlet|<.foo.bar|] - helper "<div class=\"foo bar\"></div>" [hamlet|<.foo.bar>|] +caseMultiClass = helper "<div class=\"foo bar\"></div>" [hamlet|<.foo.bar>|] caseAttribOrder :: Assertion -caseAttribOrder = do - helper "<meta 1 2 3>" [hamlet|<meta 1 2 3|] +caseAttribOrder = helper "<meta 1 2 3>" [hamlet|<meta 1 2 3>|] caseNothing :: Assertion @@ -646,11 +655,11 @@ caseAttribCond :: Assertion caseAttribCond = do - helper "<select></select>" [hamlet|<select :False:selected|] - helper "<select selected></select>" [hamlet|<select :True:selected|] - helper "<meta var=\"foo:bar\">" [hamlet|<meta var=foo:bar|] + helper "<select></select>" [hamlet|<select :False:selected>|] + helper "<select selected></select>" [hamlet|<select :True:selected>|] + helper "<meta var=\"foo:bar\">" [hamlet|<meta var=foo:bar>|] helper "<select selected></select>" - [hamlet|<select :true theArg:selected|] + [hamlet|<select :true theArg:selected>|] helper "<select></select>" [hamlet|<select :False:selected>|] helper "<select selected></select>" [hamlet|<select :True:selected>|] @@ -685,19 +694,19 @@ caseQuotedAttribs :: Assertion caseQuotedAttribs = helper "<input type=\"submit\" value=\"Submit response\">" [hamlet| -<input type=submit value="Submit response" +<input type=submit value="Submit response"> |] caseSpacedDerefs :: Assertion caseSpacedDerefs = do helper "<var>" [hamlet|#{var theArg}|] - helper "<div class=\"<var>\"></div>" [hamlet|<.#{var theArg}|] + helper "<div class=\"<var>\"></div>" [hamlet|<.#{var theArg}>|] caseAttribVars :: Assertion caseAttribVars = do - helper "<div id=\"<var>\"></div>" [hamlet|<##{var theArg}|] - helper "<div class=\"<var>\"></div>" [hamlet|<.#{var theArg}|] - helper "<div f=\"<var>\"></div>" [hamlet|< f=#{var theArg}|] + helper "<div id=\"<var>\"></div>" [hamlet|<##{var theArg}>|] + helper "<div class=\"<var>\"></div>" [hamlet|<.#{var theArg}>|] + helper "<div f=\"<var>\"></div>" [hamlet|< f=#{var theArg}>|] helper "<div id=\"<var>\"></div>" [hamlet|<##{var theArg}>|] helper "<div class=\"<var>\"></div>" [hamlet|<.#{var theArg}>|] @@ -714,10 +723,10 @@ helper "<table><tbody><tr><td>1</td></tr><tr><td>2</td></tr></tbody></table>" [hamlet| -<table - <tbody +<table> + <tbody> $forall user <- users - <tr + <tr> <td>#{user} |] helper @@ -728,8 +737,8 @@ , "</select>" ]) [hamlet| -<select #"#{name}" name=#{name} - <option :isBoolBlank val:selected +<select #"#{name}" name=#{name}> + <option :isBoolBlank val:selected> <option value=true :isBoolTrue val:selected>Yes <option value=false :isBoolFalse val:selected>No |] @@ -785,14 +794,14 @@ caseHamlet' = do helper' "foo" [shamlet|foo|] helper' "foo" [xshamlet|foo|] - helper "<br>" $ const $ [shamlet|<br|] - helper "<br/>" $ const $ [xshamlet|<br|] + helper "<br>" $ const $ [shamlet|<br>|] + helper "<br/>" $ const $ [xshamlet|<br>|] -- new with generalized stuff helper' "foo" [shamlet|foo|] helper' "foo" [xshamlet|foo|] - helper "<br>" $ const $ [shamlet|<br|] - helper "<br/>" $ const $ [xshamlet|<br|] + helper "<br>" $ const $ [shamlet|<br>|] + helper "<br/>" $ const $ [xshamlet|<br>|] instance Show Url where
test/hamlets/external.hamlet view
@@ -1,2 +1,2 @@ #{foo}-<br+<br>