View source on Github

Status Update

We're coming along very nicely on the Yesod 0.9 release. The goal is to have the release candidate out on Monday. However, at this point, the code in the repo is in active use in a number of different sites, and we aren't running into any issues any more. So all signs indicate that this will be the most solid release of Yesod to date.

One thing that's still necessary is updating the book for the changes in the packages. There are four chapters that need to be reworked: templates, widgets, forms and persistent. I present to you the first of these revised chapters; as usual, feedback appreciated.

Shakespearean Templates

As little interference to the underlying template language as possible, while providing conveniences where possible.

Compile-time guarantees on well-formed content.

Strict type safety, extending to the prevention of XSS (cross-site scripting) attacks.

Automated checking of valid URLs, whenever possible, through type-safe URLs .

Synopsis Hamlet (HTML) !!! <html> <head> <title> #{pageTitle} - My Site <link rel = "stylesheet" href =@{Stylesheet} <body > <h1 .page-title > #{pageTitle} <p> Here is a list of your friends: $if null friends <p> Sorry, I lied, you don't have any friends. $else <ul> $forall friend <- friends <li> #{friendName friend} (#{show $ friendAge friend} years old) <footer> ^{copyright} Cassius (CSS) #myid color: #{red} font-size: #{bodyFontSize} foo bar baz background-image: url(@{MyBackgroundR}) Lucius (CSS) section.blog { padding: 1em; border: 1px solid #000; h1 { color: #{headingColor}; } } Julius (Javascript) $( function () { $( "section.#{sectionClass}" ). hide (); $( "#mybutton" ). click ( function () { document.location = "@{SomeRouteR}" ; } ); ^ { addBling } } );

Types <p> Hello, my name is #{name} Yes, #{...} is how we do variable interpolation in Shakespeare. The Html datatype, as well as the functions mentioned, are all provided by the blaze-html package. This allows Hamlet to interact very nicely with all other blaze-html packages, and lets Hamlet provide a general solution for producing blaze-html values. Also, we get to take advantage of blaze-html's amazing performance. One other advantage on the CSS side is some helper datatypes for colors and units. While this feature is not covered in this chapter, I encourage you to look at the API documentation and try it out yourself. Type-safe URLs As a relative link: ../display/time As an absolute link, without a domain: /display/time As an absolute link, with a domain: http://example.com/display/time data MyRoute = Home | Time renderMyRoute :: MyRoute -> Text renderMyRoute Home = "http://example.com/profile/home" renderMyRoute Time = "http://example.com/display/time" type Query = [( Text , Text )] type Render url :: url -> Query -> Text renderMyRoute :: Render MyRoute renderMyRoute Home _ = ... <a href =@{Time} > The time this would translate roughly into the Haskell code: \render -> mconcat [ "<a href='" , render Time , "'>The time</a>" ] this would translate roughly into the Haskell code:

Syntax Hamlet Syntax Tags <body> <p> Some paragraph. </p> <ul> <li> Item 1 </li> <li> Item 2 </li> </ul> </body> would be <body> <p> Some paragraph. <ul> <li> Item 1 <li> Item 2 <p> Paragraph <i> italic </i> end. </p> We want to make sure that there is a whitespace preserved after the word "Paragraph" and before the word "end". To do so, we use two simple escape characters: <p> Paragraph # <i> italic \ end. The whitespace escape rules are actually very simple: <p> Paragraph <i> italic </i> end. Notice that the first tag will be automatically closed by Hamlet, while the inner "i" tag will not. You are free to use whichever approach you want, there is no penalty for either choice. would beWe want to make sure that there is a whitespace preserved after the word "Paragraph" and before the word "end". To do so, we use two simple escape characters:The whitespace escape rules are actually very simple:Notice that the first tag will be automatically closed by Hamlet, while the inner "i" tag will not. You are free to use whichever approach you want, there is no penalty for either choice. Interpolation <head> <title> #{title} The hash followed be a pair of braces denotes variable interpolation. In the case above, the title variable from the scope in which the template was called will be used. Let me state that again: Hamlet automatically has access to the variables in scope when it's called. There is no need to specifically pass variables in. Variable Interpolation -- Just ignore the quasiquote stuff for now, and that shamlet thing. -- It will be explained later. {-# LANGUAGE QuasiQuotes #-} import Text.Hamlet (shamlet) import Text.Blaze.Renderer.String (renderHtml) import Data.Char (toLower) import Data.List (sort) data Person = Person { name :: String , age :: Int } main :: IO () main = putStrLn $ renderHtml [shamlet| <p> Hello , my name is #{name person} and I am #{show $ age person}. <p> Let's do some funny stuff with my name: # <b>#{sort $ map toLower (name person)} <p> Oh , and in 5 years I'll be #{show $ (+) 5 (age person)} years old. |] where person = Person "Michael" 26 URL Interpolation and Embedding {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} import Text.Hamlet ( HtmlUrl , hamlet) import Text.Blaze.Renderer.String (renderHtml) import Data.Text ( Text ) data MyRoute = Home render :: MyRoute -> [( Text , Text )] -> Text render Home _ = "/home" footer :: HtmlUrl MyRoute footer = [hamlet| <footer> Return to # <a href=@{ Home }> Homepage . |] main :: IO () main = putStrLn $ renderHtml $ [hamlet| <body> <p> This is my page. ^{footer} |] render The hash followed be a pair of braces denotes. In the case above, thevariable from the scope in which the template was called will be used. Let me state that again: Hamlet automatically has access to the variables in scope when it's called. There is no need to specifically pass variables in. Attributes You can have interpolations on the right-hand-side of the equals sign.

The equals sign and value for an attribute are optional, just like in HTML. So <input type=checkbox checked> is perfectly valid.

is perfectly valid. There are two convenience attributes: for id, you can use the hash, and for classes, the period. In other words, <p #paragraphid .class1 .class2> .

. While quotes around the attribute value are optional, they are required if you want to embed spaces.

You can add an attribute optionally by using colons. To check a checkbox only checked if the variable isChecked is True, you would write <input type=checkbox :isChecked:checked> . To have a paragraph be optionally red, you could use <p :isRed:style="color:red"> . Conditionals $if isAdmin <p> Welcome to the admin section. $elseif isLoggedIn <p> You are not the administrator. $else <p> I don't know who you are. Please log in so I can decide if you get access. All the same rules of normal interpolation apply to the content of the conditionals. All the same rules of normal interpolation apply to the content of the conditionals. Maybe $maybe name <- maybeName <p> Your name is #{name} $nothing <p> I don't know your name. The left hand side of the <- must be a simple identifier; the right can be anything that goes in an interpolation. The left hand side of the Forall $if null people <p> No people. $else <ul> $forall person <- people <li> #{person} With $with foo <- some very (long ugly) expression that $ should only $ happen once <p> But I'm going to use #{foo} multiple times. #{foo} Doctype !!! <html> <head> <title> Hamlet is Awesome <body> <p> All done. Cassius Syntax #banner border: 1px solid #{bannerColor} background-image: url(@{BannerImageR}) Lucius Syntax Like Cassius, we allow both variable and URL interpolation.

CSS blocks are allowed to nest. article code { background-color : grey; } article p { text-indent : 2 em; } article a { text-decoration : none; } In this case, there aren't that many clauses, but having to type out article each time is still a bit of a nuisance. Imagine if you had a dozen or so of these. Not the worst thing in the world, but a bit of an annoyance. Lucius helps you out here: article { code { background-color: grey; } p { text-indent: 2em; } a { text-decoration: none; } } In this case, there aren't that many clauses, but having to type out article each time is still a bit of a nuisance. Imagine if you had a dozen or so of these. Not the worst thing in the world, but a bit of an annoyance. Lucius helps you out here: Julius Syntax

Calling Shakespeare Quasiquotes Quasiquotes allow you to embed arbitrary content within your Haskell, and for it to be converted into Haskell code at compile time. External file In this case, the template code is in a separate file which is referenced via Template Haskell. Debug mode Both of the above modes require a full recompile to see any changes. In debug mode, your template is kept in a separate file and referenced via Template Haskell. But at runtime, the external file is reparsed from scratch each time. Debug mode is not available for Hamlet, only for Cassius, Lucius and Julius. It follows nicely in the tradition of separate logic from presentation.

You can easily switch between external file and debug mode with some simple CPP macros, meaning you can keep rapid development and still achieve high performance in production. Quasiquoter {-# LANGUAGE OverloadedStrings #-} -- we're using Text below {-# LANGUAGE QuasiQuotes #-} import Text.Hamlet ( HtmlUrl , hamlet) import Data.Text ( Text ) import Text.Blaze.Renderer.String (renderHtml) data MyRoute = Home | Time | Stylesheet render :: MyRoute -> [( Text , Text )] -> Text render Home _ = "/home" render Time _ = "/time" render Stylesheet _ = "/style.css" template :: Text -> HtmlUrl MyRoute template title = [hamlet| !!! <html> <head> <title>#{title} <link rel=stylesheet href=@{ Stylesheet }> <body> <h1>#{title} |] main :: IO () main = putStrLn $ renderHtml $ template "My Title" render External file {-# LANGUAGE OverloadedStrings #-} -- we're using Text below {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE CPP #-} -- to control production versus debug import Text.Lucius ( CssUrl , luciusFile, luciusFileDebug, renderCss) import Data.Text ( Text ) import qualified Data.Text.Lazy.IO as TLIO data MyRoute = Home | Time | Stylesheet render :: MyRoute -> [( Text , Text )] -> Text render Home _ = "/home" render Time _ = "/time" render Stylesheet _ = "/style.css" template :: CssUrl MyRoute #if PRODUCTION template = $(luciusFile "template.lucius" ) #else template = $(luciusFileDebug "template.lucius" ) #endif main :: IO () main = TLIO. putStrLn $ renderCss $ template render -- @template.lucius foo { bar: baz } Language Quasiquoter External file Debug Hamlet hamlet hamletFile N/A Cassius cassius cassiusFile cassiusFileDebug Lucius lucius luciusFile luciusFileDebug Julius julius juliusFile juliusFileDebug Alternate Hamlet Types We use a different set of functions, prefixed with an "s". So the quasiquoter is shamlet and the external file function is shamletFile . How we pronounce those is still up for debate.

and the external file function is . How we pronounce those is still up for debate. No URL interpolation is allowed. Doing so will result in a compile-time error.

Embedding (the caret-interpolator) no longer allows arbitrary HtmlUrl values. The rule is that the embedded value must have the same type as the template itself, so in this case it must be Html . That means that for shamlet, embedding can be completely replaced with normal variable interpolation (with a hash). data Msg = Hello | Apples Int Next, we would want to be able to convert that into something human-readable, so we define some render functions: renderEnglish :: Msg -> Text renderEnglish Hello = "Hello" renderEnglish ( Apples 0 ) = "You did not buy any apples." renderEnglish ( Apples 1 ) = "You bought 1 apple." renderEnglish ( Apples i) = T. concat [ "You bought " , T. pack $ show i, " apples." ] Now we want to interpolate those Msg values directly in the template. For that, we use underscore interpolation. !!! <html> <head> <title> i18n <body> <h1> _{Hello} <p> _{Apples count} type Render url = url -> [( Text , Text )] -> Text type Translate msg = msg -> Html type HtmlUrlI18n msg url = Translate msg -> Render url -> Html At this point, you can pass off renderEnglish, renderSpanish, or renderKlingon to this template, and it will generate nicely translated output (depending, of course, on the quality of your translators). A full sample program follows. i18n Example {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} import Data.Text ( Text ) import qualified Data.Text as T import Text.Hamlet ( HtmlUrlI18n , ihamlet) import Text.Blaze (toHtml) import Text.Blaze.Renderer.String (renderHtml) data MyRoute = Home | Time | Stylesheet renderUrl :: MyRoute -> [( Text , Text )] -> Text renderUrl Home _ = "/home" renderUrl Time _ = "/time" renderUrl Stylesheet _ = "/style.css" data Msg = Hello | Apples Int renderEnglish :: Msg -> Text renderEnglish Hello = "Hello" renderEnglish ( Apples 0 ) = "You did not buy any apples." renderEnglish ( Apples 1 ) = "You bought 1 apple." renderEnglish ( Apples i) = T. concat [ "You bought " , T. pack $ show i, " apples." ] template :: Int -> HtmlUrlI18n Msg MyRoute template count = [ihamlet| !!! <html> <head> <title>i18n <body> <h1>_{ Hello } <p>_{ Apples count} |] main :: IO () main = putStrLn $ renderHtml $ (template 5 ) (toHtml . renderEnglish) renderUrl Next, we would want to be able to convert that into something human-readable, so we define some render functions:Now we want to interpolate those Msg values directly in the template. For that, we use underscore interpolation.At this point, you can pass off renderEnglish, renderSpanish, or renderKlingon to this template, and it will generate nicely translated output (depending, of course, on the quality of your translators). A full sample program follows.