templateHtml and templateHtmlJson

This commit is contained in:
Michael Snoyman 2010-02-03 16:23:32 +02:00
parent 0e96af34be
commit 38a15e4692
7 changed files with 85 additions and 63 deletions

View File

@ -25,8 +25,13 @@ module Yesod.Response
, HasReps (..) , HasReps (..)
, defChooseRep , defChooseRep
, ioTextToContent , ioTextToContent
, hoToJsonContent
-- ** Convenience wrappers -- ** Convenience wrappers
, staticRep , staticRep
-- ** Specific content types
, RepHtml (..)
, RepJson (..)
, RepHtmlJson (..)
-- * Response type -- * Response type
, Response (..) , Response (..)
-- * Special responses -- * Special responses
@ -98,6 +103,9 @@ type ChooseRep = [ContentType] -> IO (ContentType, Content)
ioTextToContent :: IO Text -> Content ioTextToContent :: IO Text -> Content
ioTextToContent t = ContentEnum $ WE.fromLBS' $ fmap DTLE.encodeUtf8 t ioTextToContent t = ContentEnum $ WE.fromLBS' $ fmap DTLE.encodeUtf8 t
hoToJsonContent :: HtmlObject -> Content
hoToJsonContent = cs . unJsonDoc . cs
-- | Any type which can be converted to representations. -- | Any type which can be converted to representations.
class HasReps a where class HasReps a where
chooseRep :: a -> ChooseRep chooseRep :: a -> ChooseRep
@ -146,6 +154,19 @@ staticRep :: ConvertSuccess x Content
-> [(ContentType, Content)] -> [(ContentType, Content)]
staticRep ct x = [(ct, cs x)] staticRep ct x = [(ct, cs x)]
newtype RepHtml = RepHtml Content
instance HasReps RepHtml where
chooseRep (RepHtml c) _ = return (TypeHtml, c)
newtype RepJson = RepJson Content
instance HasReps RepJson where
chooseRep (RepJson c) _ = return (TypeJson, c)
data RepHtmlJson = RepHtmlJson Content Content
instance HasReps RepHtmlJson where
chooseRep (RepHtmlJson html json) = chooseRep
[ (TypeHtml, html)
, (TypeJson, json)
]
data Response = Response W.Status [Header] ContentType Content data Response = Response W.Status [Header] ContentType Content
-- | Different types of redirects. -- | Different types of redirects.

View File

@ -2,13 +2,15 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
module Yesod.Template module Yesod.Template
( YesodTemplate (..) ( YesodTemplate (..)
, template
, NoSuchTemplate , NoSuchTemplate
, Template , Template
, TemplateGroup , TemplateGroup
, TemplateFile (..)
, setAttribute
, loadTemplateGroup , loadTemplateGroup
-- * HTML templates
, HtmlTemplate (..)
, templateHtml
, templateHtmlJson
, setHtmlAttrib
) where ) where
import Data.Object.Html import Data.Object.Html
@ -16,8 +18,6 @@ import Data.Typeable (Typeable)
import Control.Exception (Exception) import Control.Exception (Exception)
import Data.Object.Text (Text) import Data.Object.Text (Text)
import Text.StringTemplate import Text.StringTemplate
import Data.Object.Json
import Web.Mime
import Yesod.Response import Yesod.Response
import Yesod.Yesod import Yesod.Yesod
import Yesod.Handler import Yesod.Handler
@ -27,41 +27,52 @@ type TemplateGroup = STGroup Text
class Yesod y => YesodTemplate y where class Yesod y => YesodTemplate y where
getTemplateGroup :: y -> TemplateGroup getTemplateGroup :: y -> TemplateGroup
-- FIXME defaultTemplateAttribs :: y -> HtmlTemplate -> Handler y HtmlTemplate
getTemplateGroup' :: YesodTemplate y => Handler y TemplateGroup getTemplateGroup' :: YesodTemplate y => Handler y TemplateGroup
getTemplateGroup' = getTemplateGroup `fmap` getYesod getTemplateGroup' = getTemplateGroup `fmap` getYesod
template :: YesodTemplate y
=> String -- ^ template name
-> HtmlObject -- ^ object
-> (HtmlObject -> Template -> IO Template)
-> Handler y ChooseRep
template tn ho f = do
tg <- getTemplateGroup'
t <- case getStringTemplate tn tg of
Nothing -> failure $ NoSuchTemplate tn
Just x -> return x
return $ chooseRep
[ (TypeHtml, tempToContent t ho f)
, (TypeJson, cs $ unJsonDoc $ cs ho)
]
newtype NoSuchTemplate = NoSuchTemplate String newtype NoSuchTemplate = NoSuchTemplate String
deriving (Show, Typeable) deriving (Show, Typeable)
instance Exception NoSuchTemplate instance Exception NoSuchTemplate
tempToContent :: Template
-> HtmlObject
-> (HtmlObject -> Template -> IO Template)
-> Content
tempToContent t ho f = ioTextToContent $ fmap render $ f ho t
data TemplateFile = TemplateFile FilePath HtmlObject
instance HasReps TemplateFile where
chooseRep (TemplateFile fp (Mapping m)) _ = do
t <- fmap newSTMP $ readFile fp
let t' = setManyAttrib m t :: Template
return (TypeHtml, cs $ render t')
chooseRep _ _ = error "Please fix type of TemplateFile"
loadTemplateGroup :: FilePath -> IO TemplateGroup loadTemplateGroup :: FilePath -> IO TemplateGroup
loadTemplateGroup = directoryGroupRecursiveLazy loadTemplateGroup = directoryGroupRecursiveLazy
type TemplateName = String
newtype HtmlTemplate = HtmlTemplate { unHtmlTemplate :: Template }
-- | Return a result using a template generating HTML alone.
templateHtml :: YesodTemplate y
=> TemplateName
-> (HtmlTemplate -> IO HtmlTemplate)
-> Handler y RepHtml
templateHtml tn f = do
tg <- getTemplateGroup'
t <- case getStringTemplate tn tg of
Nothing -> failure $ NoSuchTemplate tn
Just x -> return x
return $ RepHtml $ ioTextToContent $ fmap (render . unHtmlTemplate)
$ f $ HtmlTemplate t
setHtmlAttrib :: ConvertSuccess x HtmlObject
=> String -> x -> HtmlTemplate -> HtmlTemplate
setHtmlAttrib k v (HtmlTemplate t) =
HtmlTemplate $ setAttribute k (toHtmlObject v) t
-- | Return a result using a template and 'HtmlObject' generating either HTML
-- or JSON output.
templateHtmlJson :: YesodTemplate y
=> TemplateName
-> HtmlObject
-> (HtmlObject -> HtmlTemplate -> IO HtmlTemplate)
-> Handler y RepHtmlJson
templateHtmlJson tn ho f = do
tg <- getTemplateGroup'
t <- case getStringTemplate tn tg of
Nothing -> failure $ NoSuchTemplate tn
Just x -> return x
return $ RepHtmlJson
(ioTextToContent $ fmap (render . unHtmlTemplate)
$ f ho $ HtmlTemplate t)
(hoToJsonContent ho)

View File

@ -7,13 +7,13 @@ signatures.
> {-# OPTIONS_GHC -fno-warn-missing-signatures #-} > {-# OPTIONS_GHC -fno-warn-missing-signatures #-}
There are only two imports: Yesod includes all of the code we need for creating There are only two imports: Yesod includes all of the code we need for creating
a web application, while Hack.Handler.SimpleServer allows us to test our a web application, while Network.Wai.Handler.SimpleServer allows us to test our
application easily. A Yesod app can in general run on any Hack handler, so this application easily. A Yesod app can in general run on any WAI handler, so this
application is easily convertible to CGI, FastCGI, or even run on the Happstack application is easily convertible to CGI, FastCGI, or even run on the Happstack
server. server.
> import Yesod > import Yesod
> import Hack.Handler.SimpleServer > import Network.Wai.Handler.SimpleServer
The easiest way to start writing a Yesod app is to follow the Yesod typeclass. The easiest way to start writing a Yesod app is to follow the Yesod typeclass.
You define some data type which will contain all the specific settings and data You define some data type which will contain all the specific settings and data
@ -100,9 +100,9 @@ factRedirect.
> return () > return ()
You could replace this main to use any Hack handler you want. For production, You could replace this main to use any WAI handler you want. For production,
you could use CGI, FastCGI or a more powerful server. Just check out Hackage you could use CGI, FastCGI or a more powerful server. Just check out Hackage
for options (any package starting hack-handler- should suffice). for options (any package starting hack-handler- should suffice).
> main :: IO () > main :: IO ()
> main = putStrLn "Running..." >> toHackApp Fact >>= run 3000 > main = putStrLn "Running..." >> toWaiApp Fact >>= run 3000

View File

@ -2,7 +2,7 @@
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
import Yesod import Yesod
import Hack.Handler.SimpleServer import Network.Wai.Handler.SimpleServer
data HelloWorld = HelloWorld TemplateGroup data HelloWorld = HelloWorld TemplateGroup
instance YesodTemplate HelloWorld where instance YesodTemplate HelloWorld where
@ -15,18 +15,17 @@ instance Yesod HelloWorld where
Get: helloGroup Get: helloGroup
|] |]
helloWorld :: Handler HelloWorld TemplateFile helloWorld :: Handler HelloWorld RepHtml
helloWorld = return $ TemplateFile "examples/template.html" $ cs helloWorld = templateHtml "template" $ return
[ ("title", "Hello world!") . setHtmlAttrib "title" "Hello world!"
, ("content", "Hey look!! I'm <auto escaped>!") . setHtmlAttrib "content" "Hey look!! I'm <auto escaped>!"
]
helloGroup :: YesodTemplate y => Handler y ChooseRep helloGroup :: YesodTemplate y => Handler y RepHtmlJson
helloGroup = template "real-template" (cs "bar") $ \ho -> helloGroup = templateHtmlJson "real-template" (cs "bar") $ \ho ->
return . setAttribute "foo" ho return . setHtmlAttrib "foo" ho
main :: IO () main :: IO ()
main = do main = do
putStrLn "Running..." putStrLn "Running..."
loadTemplateGroup "examples" >>= toHackApp . HelloWorld >>= run 3000 loadTemplateGroup "examples" >>= toWaiApp . HelloWorld >>= run 3000
\end{code} \end{code}

View File

@ -2,7 +2,7 @@
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
import Yesod import Yesod
import Hack.Handler.SimpleServer import Network.Wai.Handler.SimpleServer
data HelloWorld = HelloWorld data HelloWorld = HelloWorld
instance Yesod HelloWorld where instance Yesod HelloWorld where
@ -15,5 +15,5 @@ helloWorld :: Handler HelloWorld ChooseRep
helloWorld = applyLayout' "Hello World" $ cs "Hello world!" helloWorld = applyLayout' "Hello World" $ cs "Hello world!"
main :: IO () main :: IO ()
main = putStrLn "Running..." >> toHackApp HelloWorld >>= run 3000 main = putStrLn "Running..." >> toWaiApp HelloWorld >>= run 3000
\end{code} \end{code}

View File

@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>$o.title$</title> <title>$title$</title>
<style> <style>
body { body {
background-color: #ffc; background-color: #ffc;
@ -20,7 +20,7 @@
</head> </head>
<body> <body>
<div id="wrapper"> <div id="wrapper">
$o.content$ $content$
</div> </div>
</body> </body>
</html> </html>

View File

@ -4,7 +4,7 @@ license: BSD3
license-file: LICENSE license-file: LICENSE
author: Michael Snoyman <michael@snoyman.com> author: Michael Snoyman <michael@snoyman.com>
maintainer: Michael Snoyman <michael@snoyman.com> maintainer: Michael Snoyman <michael@snoyman.com>
synopsis: A Restful front controller built on Hack. synopsis: A library for creating RESTful web applications.
description: This package stradles the line between framework and simply a controller. It provides minimal support for model and view, mostly focusing on making a controller which adheres strictly to RESTful principles. description: This package stradles the line between framework and simply a controller. It provides minimal support for model and view, mostly focusing on making a controller which adheres strictly to RESTful principles.
category: Web category: Web
stability: unstable stability: unstable
@ -17,7 +17,7 @@ flag buildtests
default: False default: False
flag buildsamples flag buildsamples
description: Build the executable to run unit tests description: Build the executable sample applications.
default: False default: False
flag nolib flag nolib
@ -85,21 +85,12 @@ executable runtests
ghc-options: -Wall ghc-options: -Wall
main-is: runtests.hs main-is: runtests.hs
executable quasi-test
if flag(buildsamples)
Buildable: True
else
Buildable: False
ghc-options: -Wall
main-is: test/quasi-resource.hs
executable helloworld executable helloworld
if flag(buildsamples) if flag(buildsamples)
Buildable: True Buildable: True
else else
Buildable: False Buildable: False
ghc-options: -Wall ghc-options: -Wall
build-depends: hack-handler-simpleserver >= 0.2.0 && < 0.3
main-is: examples/helloworld.lhs main-is: examples/helloworld.lhs
executable hellotemplate executable hellotemplate