Remove requestBody, live in Iteratee

This commit is contained in:
Michael Snoyman 2010-12-31 00:56:40 +02:00
parent 5c730104c8
commit db5e987797
4 changed files with 99 additions and 71 deletions

View File

@ -43,6 +43,7 @@ import Network.Wai.Middleware.Gzip
import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString as S import qualified Data.ByteString as S
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
import Blaze.ByteString.Builder (toLazyByteString) import Blaze.ByteString.Builder (toLazyByteString)
@ -70,7 +71,8 @@ import System.Random (randomR, newStdGen)
import qualified Data.Map as Map import qualified Data.Map as Map
import Control.Applicative ((<$>)) import Control.Applicative ((<$>))
import Data.Enumerator (($$), run_) import Data.Enumerator (($$), run_, Iteratee)
import Control.Monad.IO.Class (liftIO)
#if TEST #if TEST
import Test.Framework (testGroup, Test) import Test.Framework (testGroup, Test)
@ -251,10 +253,9 @@ toWaiApp' :: (Yesod y, YesodSite y)
=> y => y
-> Maybe Key -> Maybe Key
-> [String] -> [String]
-> W.Request -> W.Application
-> IO W.Response
toWaiApp' y key' segments env = do toWaiApp' y key' segments env = do
now <- getCurrentTime now <- liftIO getCurrentTime
let getExpires m = fromIntegral (m * 60) `addUTCTime` now let getExpires m = fromIntegral (m * 60) `addUTCTime` now
let exp' = getExpires $ clientSessionDuration y let exp' = getExpires $ clientSessionDuration y
let host = if sessionIpAddress y then W.remoteHost env else "" let host = if sessionIpAddress y then W.remoteHost env else ""
@ -276,7 +277,7 @@ toWaiApp' y key' segments env = do
(joinPath y (approot y) ps $ qs ++ qs') (joinPath y (approot y) ps $ qs ++ qs')
(urlRenderOverride y u) (urlRenderOverride y u)
let errorHandler' = localNoCurrent . errorHandler let errorHandler' = localNoCurrent . errorHandler
rr <- parseWaiRequest env session' key' rr <- liftIO $ parseWaiRequest env session' key'
let h = do let h = do
onRequest onRequest
case eurl of case eurl of
@ -389,11 +390,10 @@ parseWaiRequest env session' key' = do
nonceKey :: String nonceKey :: String
nonceKey = "_NONCE" nonceKey = "_NONCE"
rbHelper :: W.Request -> IO RequestBodyContents rbHelper :: W.Request -> Iteratee ByteString IO RequestBodyContents
rbHelper req = rbHelper req =
(map fix1 *** map fix2) <$> run_ (enum $$ iter) (map fix1 *** map fix2) <$> iter
where where
enum = W.requestBody req
iter = parseRequestBody lbsSink req iter = parseRequestBody lbsSink req
fix1 = bsToChars *** bsToChars fix1 = bsToChars *** bsToChars
fix2 (x, NWP.FileInfo a b c) = fix2 (x, NWP.FileInfo a b c) =
@ -402,11 +402,18 @@ rbHelper req =
-- | Produces a \"compute on demand\" value. The computation will be run once -- | Produces a \"compute on demand\" value. The computation will be run once
-- it is requested, and then the result will be stored. This will happen only -- it is requested, and then the result will be stored. This will happen only
-- once. -- once.
iothunk :: IO a -> IO (IO a) iothunk :: Iteratee ByteString IO a -> IO (Iteratee ByteString IO a)
iothunk = fmap go . newMVar . Left where iothunk =
go :: MVar (Either (IO a) a) -> IO a fmap go . liftIO . newMVar . Left
go mvar = modifyMVar mvar go' where
go' :: Either (IO a) a -> IO (Either (IO a) a, a) go :: MVar (Either (Iteratee ByteString IO a) a) -> Iteratee ByteString IO a
go mvar = do
x <- liftIO $ takeMVar mvar
(x', a) <- go' x
liftIO $ putMVar mvar x'
return a
go' :: Either (Iteratee ByteString IO a) a
-> Iteratee ByteString IO (Either (Iteratee ByteString IO a) a, a)
go' (Right val) = return (Right val, val) go' (Right val) = return (Right val, val)
go' (Left comp) = do go' (Left comp) = do
val <- comp val <- comp

View File

@ -101,6 +101,8 @@ import Control.Exception hiding (Handler, catch, finally)
import qualified Control.Exception as E import qualified Control.Exception as E
import Control.Applicative import Control.Applicative
import Control.Monad (liftM)
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
import Control.Monad.Trans.Writer import Control.Monad.Trans.Writer
@ -117,6 +119,8 @@ import Text.Hamlet
import Control.Monad.IO.Peel (MonadPeelIO) import Control.Monad.IO.Peel (MonadPeelIO)
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Char8 as S8
import Data.ByteString (ByteString)
import Data.Enumerator (Iteratee (..))
#if TEST #if TEST
import Test.Framework (testGroup, Test) import Test.Framework (testGroup, Test)
@ -203,18 +207,20 @@ toMasterHandlerMaybe tm ts route (GHandler h) =
-- 'WriterT' for headers and session, and an 'MEitherT' monad for handling -- 'WriterT' for headers and session, and an 'MEitherT' monad for handling
-- special responses. It is declared as a newtype to make compiler errors more -- special responses. It is declared as a newtype to make compiler errors more
-- readable. -- readable.
newtype GHandler sub master a = newtype GGHandler sub master m a =
GHandler GHandler
{ unGHandler :: GHInner sub master a { unGHandler :: GHInner sub master m a
} }
deriving (Functor, Applicative, Monad, MonadIO, MonadPeelIO) deriving (Functor, Applicative, Monad, MonadIO, MonadPeelIO)
type GHInner s m = type GHandler sub master = GGHandler sub master (Iteratee ByteString IO)
type GHInner s m monad =
ReaderT (HandlerData s m) ( ReaderT (HandlerData s m) (
ErrorT HandlerContents ( ErrorT HandlerContents (
WriterT (Endo [Header]) ( WriterT (Endo [Header]) (
StateT SessionMap ( -- session StateT SessionMap ( -- session
IO monad
)))) ))))
type SessionMap = Map.Map String String type SessionMap = Map.Map String String
@ -230,7 +236,7 @@ newtype YesodApp = YesodApp
-> Request -> Request
-> [ContentType] -> [ContentType]
-> SessionMap -> SessionMap
-> IO YesodAppResult -> Iteratee ByteString IO YesodAppResult
} }
data YesodAppResult data YesodAppResult
@ -248,38 +254,43 @@ data HandlerContents =
instance Error HandlerContents where instance Error HandlerContents where
strMsg = HCError . InternalError strMsg = HCError . InternalError
instance Failure ErrorResponse (GHandler sub master) where instance Monad monad => Failure ErrorResponse (GGHandler sub master monad) where
failure = GHandler . lift . throwError . HCError failure = GHandler . lift . throwError . HCError
instance RequestReader (GHandler sub master) where instance RequestReader (GHandler sub master) where
getRequest = handlerRequest <$> GHandler ask getRequest = handlerRequest <$> GHandler ask
runRequestBody = do
rr <- getRequest
GHandler $ lift $ lift $ lift $ lift $ reqRequestBody rr
-- | Get the sub application argument. -- | Get the sub application argument.
getYesodSub :: GHandler sub master sub getYesodSub :: Monad m => GGHandler sub master m sub
getYesodSub = handlerSub <$> GHandler ask getYesodSub = handlerSub `liftM` GHandler ask
-- | Get the master site appliation argument. -- | Get the master site appliation argument.
getYesod :: GHandler sub master master getYesod :: Monad m => GGHandler sub master m master
getYesod = handlerMaster <$> GHandler ask getYesod = handlerMaster `liftM` GHandler ask
-- | Get the URL rendering function. -- | Get the URL rendering function.
getUrlRender :: GHandler sub master (Route master -> String) getUrlRender :: Monad m => GGHandler sub master m (Route master -> String)
getUrlRender = do getUrlRender = do
x <- handlerRender <$> GHandler ask x <- handlerRender `liftM` GHandler ask
return $ flip x [] return $ flip x []
-- | The URL rendering function with query-string parameters. -- | The URL rendering function with query-string parameters.
getUrlRenderParams :: GHandler sub master (Route master -> [(String, String)] -> String) getUrlRenderParams
getUrlRenderParams = handlerRender <$> GHandler ask :: Monad m
=> GGHandler sub master m (Route master -> [(String, String)] -> String)
getUrlRenderParams = handlerRender `liftM` GHandler ask
-- | Get the route requested by the user. If this is a 404 response- where the -- | Get the route requested by the user. If this is a 404 response- where the
-- user requested an invalid route- this function will return 'Nothing'. -- user requested an invalid route- this function will return 'Nothing'.
getCurrentRoute :: GHandler sub master (Maybe (Route sub)) getCurrentRoute :: Monad m => GGHandler sub master m (Maybe (Route sub))
getCurrentRoute = handlerRoute <$> GHandler ask getCurrentRoute = handlerRoute `liftM` GHandler ask
-- | Get the function to promote a route for a subsite to a route for the -- | Get the function to promote a route for a subsite to a route for the
-- master site. -- master site.
getRouteToMaster :: GHandler sub master (Route sub -> Route master) getRouteToMaster :: Monad m => GGHandler sub master m (Route sub -> Route master)
getRouteToMaster = handlerToMaster <$> GHandler ask getRouteToMaster = handlerToMaster `liftM` GHandler ask
-- | Function used internally by Yesod in the process of converting a -- | Function used internally by Yesod in the process of converting a
-- 'GHandler' into an 'W.Application'. Should not be needed by users. -- 'GHandler' into an 'W.Application'. Should not be needed by users.
@ -304,7 +315,7 @@ runHandler handler mrender sroute tomr ma tosa =
, handlerRender = mrender , handlerRender = mrender
, handlerToMaster = tomr , handlerToMaster = tomr
} }
((contents', headers), finalSession) <- E.catch ( ((contents', headers), finalSession) <- catchIter (
flip runStateT initSession flip runStateT initSession
$ runWriterT $ runWriterT
$ runErrorT $ runErrorT
@ -323,7 +334,7 @@ runHandler handler mrender sroute tomr ma tosa =
return $ YARPlain W.status200 (headers []) ct (ContentFile fp) finalSession return $ YARPlain W.status200 (headers []) ct (ContentFile fp) finalSession
case contents of case contents of
HCContent status a -> do HCContent status a -> do
(ct, c) <- chooseRep a cts (ct, c) <- liftIO $ chooseRep a cts
return $ YARPlain status (headers []) ct c finalSession return $ YARPlain status (headers []) ct c finalSession
HCError e -> handleError e HCError e -> handleError e
HCRedirect rt loc -> do HCRedirect rt loc -> do
@ -331,7 +342,7 @@ runHandler handler mrender sroute tomr ma tosa =
return $ YARPlain return $ YARPlain
(getRedirectStatus rt) hs typePlain emptyContent (getRedirectStatus rt) hs typePlain emptyContent
finalSession finalSession
HCSendFile ct fp -> E.catch HCSendFile ct fp -> catchIter
(sendFile' ct fp) (sendFile' ct fp)
(handleError . toErrorHandler) (handleError . toErrorHandler)
HCCreated loc -> do -- FIXME add status201 to WAI HCCreated loc -> do -- FIXME add status201 to WAI
@ -344,6 +355,12 @@ runHandler handler mrender sroute tomr ma tosa =
finalSession finalSession
HCEnum e -> return $ YAREnum e HCEnum e -> return $ YAREnum e
catchIter :: Exception e
=> Iteratee ByteString IO a
-> (e -> Iteratee ByteString IO a)
-> Iteratee ByteString IO a
catchIter (Iteratee mstep) f = Iteratee $ mstep `E.catch` (runIteratee . f)
safeEh :: ErrorResponse -> YesodApp safeEh :: ErrorResponse -> YesodApp
safeEh er = YesodApp $ \_ _ _ session -> do safeEh er = YesodApp $ \_ _ _ session -> do
liftIO $ hPutStrLn stderr $ "Error handler errored out: " ++ show er liftIO $ hPutStrLn stderr $ "Error handler errored out: " ++ show er

View File

@ -41,6 +41,8 @@ module Yesod.Request
) where ) where
import qualified Network.Wai as W import qualified Network.Wai as W
import Data.ByteString (ByteString)
import Data.Enumerator (Iteratee)
import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy as BL
import "transformers" Control.Monad.IO.Class import "transformers" Control.Monad.IO.Class
import Control.Monad (liftM) import Control.Monad (liftM)
@ -51,11 +53,12 @@ type ParamName = String
type ParamValue = String type ParamValue = String
type ParamError = String type ParamError = String
-- FIXME perhaps remove RequestReader typeclass, include Request datatype in Handler
-- | The reader monad specialized for 'Request'. -- | The reader monad specialized for 'Request'.
class Monad m => RequestReader m where class Monad m => RequestReader m where
getRequest :: m Request getRequest :: m Request
instance RequestReader ((->) Request) where runRequestBody :: m RequestBodyContents
getRequest = id
-- | Get the list of supported languages supplied by the user. -- | Get the list of supported languages supplied by the user.
-- --
@ -107,7 +110,7 @@ data Request = Request
-- service, you may want to accept JSON-encoded data. Just be aware that -- service, you may want to accept JSON-encoded data. Just be aware that
-- if you do such parsing, the standard POST form parsing functions will -- if you do such parsing, the standard POST form parsing functions will
-- no longer work. -- no longer work.
, reqRequestBody :: IO RequestBodyContents , reqRequestBody :: Iteratee ByteString IO RequestBodyContents
, reqWaiRequest :: W.Request , reqWaiRequest :: W.Request
-- | Languages which the client supports. -- | Languages which the client supports.
, reqLangs :: [String] , reqLangs :: [String]
@ -129,12 +132,11 @@ lookupGetParam :: RequestReader m => ParamName -> m (Maybe ParamValue)
lookupGetParam = liftM listToMaybe . lookupGetParams lookupGetParam = liftM listToMaybe . lookupGetParams
-- | Lookup for POST parameters. -- | Lookup for POST parameters.
lookupPostParams :: (MonadIO m, RequestReader m) lookupPostParams :: RequestReader m
=> ParamName => ParamName
-> m [ParamValue] -> m [ParamValue]
lookupPostParams pn = do lookupPostParams pn = do
rr <- getRequest (pp, _) <- runRequestBody
(pp, _) <- liftIO $ reqRequestBody rr
return $ lookup' pn pp return $ lookup' pn pp
lookupPostParam :: (MonadIO m, RequestReader m) lookupPostParam :: (MonadIO m, RequestReader m)
@ -149,12 +151,11 @@ lookupFile :: (MonadIO m, RequestReader m)
lookupFile = liftM listToMaybe . lookupFiles lookupFile = liftM listToMaybe . lookupFiles
-- | Lookup for POSTed files. -- | Lookup for POSTed files.
lookupFiles :: (MonadIO m, RequestReader m) lookupFiles :: RequestReader m
=> ParamName => ParamName
-> m [FileInfo] -> m [FileInfo]
lookupFiles pn = do lookupFiles pn = do
rr <- getRequest (_, files) <- runRequestBody
(_, files) <- liftIO $ reqRequestBody rr
return $ lookup' pn files return $ lookup' pn files
-- | Lookup for cookie data. -- | Lookup for cookie data.

View File

@ -7,7 +7,8 @@
-- generator, allowing you to create truly modular HTML components. -- generator, allowing you to create truly modular HTML components.
module Yesod.Widget module Yesod.Widget
( -- * Datatype ( -- * Datatype
GWidget (..) GWidget
, GGWidget (..)
, liftHandler , liftHandler
, PageContent (..) , PageContent (..)
-- * Creating -- * Creating
@ -54,15 +55,17 @@ import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
import Yesod.Internal import Yesod.Internal
import Yesod.Content (RepHtml (RepHtml), Content, toContent) import Yesod.Content (RepHtml (RepHtml), Content, toContent)
import Control.Monad (liftM)
import Control.Monad.IO.Peel (MonadPeelIO) import Control.Monad.IO.Peel (MonadPeelIO)
-- | A generic widget, allowing specification of both the subsite and master -- | A generic widget, allowing specification of both the subsite and master
-- site datatypes. This is basically a large 'WriterT' stack keeping track of -- site datatypes. This is basically a large 'WriterT' stack keeping track of
-- dependencies along with a 'StateT' to track unique identifiers. -- dependencies along with a 'StateT' to track unique identifiers.
newtype GWidget s m a = GWidget { unGWidget :: GWInner s m a } newtype GGWidget s m monad a = GWidget { unGWidget :: GWInner s m monad a }
deriving (Functor, Applicative, Monad, MonadIO, MonadPeelIO) deriving (Functor, Applicative, Monad, MonadIO, MonadPeelIO)
type GWInner sub master = type GWidget s m = GGWidget s m (GHandler s m)
type GWInner sub master monad =
WriterT (Body (Route master)) ( WriterT (Body (Route master)) (
WriterT (Last Title) ( WriterT (Last Title) (
WriterT (UniqueList (Script (Route master))) ( WriterT (UniqueList (Script (Route master))) (
@ -71,28 +74,28 @@ type GWInner sub master =
WriterT (Maybe (Julius (Route master))) ( WriterT (Maybe (Julius (Route master))) (
WriterT (Head (Route master)) ( WriterT (Head (Route master)) (
StateT Int ( StateT Int (
GHandler sub master monad
)))))))) ))))))))
instance Monoid (GWidget sub master ()) where instance Monad monad => Monoid (GGWidget sub master monad ()) where
mempty = return () mempty = return ()
mappend x y = x >> y mappend x y = x >> y
instance HamletValue (GWidget s m ()) where instance Monad monad => HamletValue (GGWidget s m monad ()) where
newtype HamletMonad (GWidget s m ()) a = newtype HamletMonad (GGWidget s m monad ()) a =
GWidget' { runGWidget' :: GWidget s m a } GWidget' { runGWidget' :: GGWidget s m monad a }
type HamletUrl (GWidget s m ()) = Route m type HamletUrl (GGWidget s m monad ()) = Route m
toHamletValue = runGWidget' toHamletValue = runGWidget'
htmlToHamletMonad = GWidget' . addHtml htmlToHamletMonad = GWidget' . addHtml
urlToHamletMonad url params = GWidget' $ urlToHamletMonad url params = GWidget' $
addHamlet $ \r -> preEscapedString (r url params) addHamlet $ \r -> preEscapedString (r url params)
fromHamletValue = GWidget' fromHamletValue = GWidget'
instance Monad (HamletMonad (GWidget s m ())) where instance Monad monad => Monad (HamletMonad (GGWidget s m monad ())) where
return = GWidget' . return return = GWidget' . return
x >>= y = GWidget' $ runGWidget' x >>= runGWidget' . y x >>= y = GWidget' $ runGWidget' x >>= runGWidget' . y
-- | Lift an action in the 'GHandler' monad into an action in the 'GWidget' -- | Lift an action in the 'GHandler' monad into an action in the 'GWidget'
-- monad. -- monad.
liftHandler :: GHandler sub master a -> GWidget sub master a liftHandler :: Monad monad => monad a -> GGWidget sub master monad a
liftHandler = GWidget . lift . lift . lift . lift . lift . lift . lift . lift liftHandler = GWidget . lift . lift . lift . lift . lift . lift . lift . lift
addSubWidget :: (YesodSubRoute sub master) => sub -> GWidget sub master a -> GWidget sub' master a addSubWidget :: (YesodSubRoute sub master) => sub -> GWidget sub master a -> GWidget sub' master a
@ -125,32 +128,32 @@ addSubWidget sub w = do master <- liftHandler getYesod
-- | Set the page title. Calling 'setTitle' multiple times overrides previously -- | Set the page title. Calling 'setTitle' multiple times overrides previously
-- set values. -- set values.
setTitle :: Html -> GWidget sub master () setTitle :: Monad m => Html -> GGWidget sub master m ()
setTitle = GWidget . lift . tell . Last . Just . Title setTitle = GWidget . lift . tell . Last . Just . Title
-- | Add a 'Hamlet' to the head tag. -- | Add a 'Hamlet' to the head tag.
addHamletHead :: Hamlet (Route master) -> GWidget sub master () addHamletHead :: Monad m => Hamlet (Route master) -> GGWidget sub master m ()
addHamletHead = GWidget . lift . lift . lift . lift . lift . lift . tell . Head addHamletHead = GWidget . lift . lift . lift . lift . lift . lift . tell . Head
-- | Add a 'Html' to the head tag. -- | Add a 'Html' to the head tag.
addHtmlHead :: Html -> GWidget sub master () addHtmlHead :: Monad m => Html -> GGWidget sub master m ()
addHtmlHead = GWidget . lift . lift . lift . lift . lift . lift . tell . Head . const addHtmlHead = GWidget . lift . lift . lift . lift . lift . lift . tell . Head . const
-- | Add a 'Hamlet' to the body tag. -- | Add a 'Hamlet' to the body tag.
addHamlet :: Hamlet (Route master) -> GWidget sub master () addHamlet :: Monad m => Hamlet (Route master) -> GGWidget sub master m ()
addHamlet = GWidget . tell . Body addHamlet = GWidget . tell . Body
-- | Add a 'Html' to the body tag. -- | Add a 'Html' to the body tag.
addHtml :: Html -> GWidget sub master () addHtml :: Monad m => Html -> GGWidget sub master m ()
addHtml = GWidget . tell . Body . const addHtml = GWidget . tell . Body . const
-- | Add another widget. This is defined as 'id', by can help with types, and -- | Add another widget. This is defined as 'id', by can help with types, and
-- makes widget blocks look more consistent. -- makes widget blocks look more consistent.
addWidget :: GWidget s m () -> GWidget s m () addWidget :: Monad mo => GGWidget s m mo () -> GGWidget s m mo ()
addWidget = id addWidget = id
-- | Get a unique identifier. -- | Get a unique identifier.
newIdent :: GWidget sub master String newIdent :: Monad mo => GGWidget sub master mo String
newIdent = GWidget $ lift $ lift $ lift $ lift $ lift $ lift $ lift $ do newIdent = GWidget $ lift $ lift $ lift $ lift $ lift $ lift $ lift $ do
i <- get i <- get
let i' = i + 1 let i' = i + 1
@ -158,42 +161,42 @@ newIdent = GWidget $ lift $ lift $ lift $ lift $ lift $ lift $ lift $ do
return $ "w" ++ show i' return $ "w" ++ show i'
-- | Add some raw CSS to the style tag. -- | Add some raw CSS to the style tag.
addCassius :: Cassius (Route master) -> GWidget sub master () addCassius :: Monad m => Cassius (Route master) -> GGWidget sub master m ()
addCassius = GWidget . lift . lift . lift . lift . tell . Just addCassius = GWidget . lift . lift . lift . lift . tell . Just
-- | Link to the specified local stylesheet. -- | Link to the specified local stylesheet.
addStylesheet :: Route master -> GWidget sub master () addStylesheet :: Monad m => Route master -> GGWidget sub master m ()
addStylesheet = GWidget . lift . lift . lift . tell . toUnique . Stylesheet . Local addStylesheet = GWidget . lift . lift . lift . tell . toUnique . Stylesheet . Local
-- | Link to the specified remote stylesheet. -- | Link to the specified remote stylesheet.
addStylesheetRemote :: String -> GWidget sub master () addStylesheetRemote :: Monad m => String -> GGWidget sub master m ()
addStylesheetRemote = addStylesheetRemote =
GWidget . lift . lift . lift . tell . toUnique . Stylesheet . Remote GWidget . lift . lift . lift . tell . toUnique . Stylesheet . Remote
addStylesheetEither :: Either (Route master) String -> GWidget sub master () addStylesheetEither :: Monad m => Either (Route master) String -> GGWidget sub master m ()
addStylesheetEither = either addStylesheet addStylesheetRemote addStylesheetEither = either addStylesheet addStylesheetRemote
addScriptEither :: Either (Route master) String -> GWidget sub master () addScriptEither :: Monad m => Either (Route master) String -> GGWidget sub master m ()
addScriptEither = either addScript addScriptRemote addScriptEither = either addScript addScriptRemote
-- | Link to the specified local script. -- | Link to the specified local script.
addScript :: Route master -> GWidget sub master () addScript :: Monad m => Route master -> GGWidget sub master m ()
addScript = GWidget . lift . lift . tell . toUnique . Script . Local addScript = GWidget . lift . lift . tell . toUnique . Script . Local
-- | Link to the specified remote script. -- | Link to the specified remote script.
addScriptRemote :: String -> GWidget sub master () addScriptRemote :: Monad m => String -> GGWidget sub master m ()
addScriptRemote = addScriptRemote =
GWidget . lift . lift . tell . toUnique . Script . Remote GWidget . lift . lift . tell . toUnique . Script . Remote
-- | Include raw Javascript in the page's script tag. -- | Include raw Javascript in the page's script tag.
addJulius :: Julius (Route master) -> GWidget sub master () addJulius :: Monad m => Julius (Route master) -> GGWidget sub master m ()
addJulius = GWidget . lift . lift . lift . lift . lift. tell . Just addJulius = GWidget . lift . lift . lift . lift . lift. tell . Just
-- | Pull out the HTML tag contents and return it. Useful for performing some -- | Pull out the HTML tag contents and return it. Useful for performing some
-- manipulations. It can be easier to use this sometimes than 'wrapWidget'. -- manipulations. It can be easier to use this sometimes than 'wrapWidget'.
extractBody :: GWidget s m () -> GWidget s m (Hamlet (Route m)) extractBody :: Monad mo => GGWidget s m mo () -> GGWidget s m mo (Hamlet (Route m))
extractBody (GWidget w) = extractBody (GWidget w) =
GWidget $ mapWriterT (fmap go) w GWidget $ mapWriterT (liftM go) w
where where
go ((), Body h) = (h, Body mempty) go ((), Body h) = (h, Body mempty)