Handler is now RWS

This commit is contained in:
Michael Snoyman 2011-11-28 23:55:30 +02:00
parent d405ef9e70
commit 3aa567a631
3 changed files with 63 additions and 77 deletions

View File

@ -125,9 +125,7 @@ import Control.Monad (liftM, join, MonadPlus)
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Control.Monad.Trans.Writer
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Control.Monad.Trans.RWS
import Control.Monad.Trans.Error (throwError, ErrorT (..), Error (..))
import System.IO
@ -153,7 +151,7 @@ import Network.Wai.Parse (parseHttpAccept)
import Yesod.Content
import Data.Maybe (fromMaybe)
import Web.Cookie (SetCookie (..), renderSetCookie)
import Control.Arrow (second, (***))
import Control.Arrow ((***))
import qualified Network.Wai.Parse as NWP
import Data.Monoid (mappend, mempty, Endo (..))
import qualified Data.ByteString.Char8 as S8
@ -198,6 +196,11 @@ handlerSubDataMaybe tm ts route hd = hd
, handlerRoute = route
}
withReaderT :: (HandlerData s m -> HandlerData s' m)
-> GGHandler s' m mo a
-> GGHandler s m mo a
withReaderT f (GHandler (ErrorT m)) = GHandler $ ErrorT $ withRWST (\r s -> (f r, s)) m
-- | Used internally for promoting subsite handler functions to master site
-- handler functions. Should not be needed by users.
toMasterHandler :: (Route sub -> Route master)
@ -205,8 +208,7 @@ toMasterHandler :: (Route sub -> Route master)
-> Route sub
-> GGHandler sub master mo a
-> GGHandler sub' master mo a
toMasterHandler tm ts route (GHandler h) =
GHandler $ withReaderT (handlerSubData tm ts route) h
toMasterHandler tm ts route = withReaderT (handlerSubData tm ts route)
toMasterHandlerDyn :: Monad mo
=> (Route sub -> Route master)
@ -214,9 +216,9 @@ toMasterHandlerDyn :: Monad mo
-> Route sub
-> GGHandler sub master mo a
-> GGHandler sub' master mo a
toMasterHandlerDyn tm getSub route (GHandler h) = do
toMasterHandlerDyn tm getSub route h = do
sub <- getSub
GHandler $ withReaderT (handlerSubData tm (const sub) route) h
withReaderT (handlerSubData tm (const sub) route) h
class SubsiteGetter g m s | g -> s where
runSubsiteGetter :: g -> m s
@ -235,8 +237,7 @@ toMasterHandlerMaybe :: (Route sub -> Route master)
-> Maybe (Route sub)
-> GGHandler sub master mo a
-> GGHandler sub' master mo a
toMasterHandlerMaybe tm ts route (GHandler h) =
GHandler $ withReaderT (handlerSubDataMaybe tm ts route) h
toMasterHandlerMaybe tm ts route = withReaderT (handlerSubDataMaybe tm ts route)
-- | A generic handler monad, which can have a different subsite and master
-- site. This monad is a combination of 'ReaderT' for basic arguments, a
@ -250,7 +251,7 @@ newtype GGHandler sub master m a =
deriving (Functor, Applicative, Monad, MonadIO, MonadControlIO, MonadPlus)
instance MonadTrans (GGHandler s m) where
lift = GHandler . lift . lift . lift . lift
lift = GHandler . lift . lift
type GHandler sub master = GGHandler sub master (Iteratee ByteString IO)
@ -260,13 +261,11 @@ data GHState = GHState
, ghsIdent :: Int
}
type GHInner s m monad = -- FIXME collapse the stack
ReaderT (HandlerData s m) (
type GHInner s m monad =
ErrorT HandlerContents (
WriterT (Endo [Header]) (
StateT GHState (
RWST (HandlerData s m) (Endo [Header]) GHState
monad
))))
)
type SessionMap = Map.Map Text Text
@ -298,20 +297,20 @@ instance Error HandlerContents where
strMsg = HCError . InternalError . T.pack
getRequest :: Monad mo => GGHandler s m mo Request
getRequest = handlerRequest `liftM` GHandler ask
getRequest = handlerRequest `liftM` gask
instance Monad monad => Failure ErrorResponse (GGHandler sub master monad) where
failure = GHandler . lift . throwError . HCError
failure = GHandler . throwError . HCError
runRequestBody :: GHandler s m RequestBodyContents
runRequestBody = do
x <- GHandler $ lift $ lift $ lift get
x <- GHandler $ lift get
case ghsRBC x of
Just rbc -> return rbc
Nothing -> do
rr <- waiRequest
rbc <- lift $ rbHelper rr
GHandler $ lift $ lift $ lift $ put x { ghsRBC = Just rbc }
GHandler $ lift $ put x { ghsRBC = Just rbc }
return rbc
rbHelper :: W.Request -> Iteratee ByteString IO RequestBodyContents
@ -326,33 +325,36 @@ rbHelper req =
-- | Get the sub application argument.
getYesodSub :: Monad m => GGHandler sub master m sub
getYesodSub = handlerSub `liftM` GHandler ask
getYesodSub = handlerSub `liftM` gask
-- | Get the master site appliation argument.
getYesod :: Monad m => GGHandler sub master m master
getYesod = handlerMaster `liftM` GHandler ask
getYesod = handlerMaster `liftM` gask
-- | Get the URL rendering function.
getUrlRender :: Monad m => GGHandler sub master m (Route master -> Text)
getUrlRender = do
x <- handlerRender `liftM` GHandler ask
x <- handlerRender `liftM` gask
return $ flip x []
gask :: Monad m => GGHandler sub master m (HandlerData sub master)
gask = GHandler (lift ask)
-- | The URL rendering function with query-string parameters.
getUrlRenderParams
:: Monad m
=> GGHandler sub master m (Route master -> [(Text, Text)] -> Text)
getUrlRenderParams = handlerRender `liftM` GHandler ask
getUrlRenderParams = handlerRender `liftM` gask
-- | 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'.
getCurrentRoute :: Monad m => GGHandler sub master m (Maybe (Route sub))
getCurrentRoute = handlerRoute `liftM` GHandler ask
getCurrentRoute = handlerRoute `liftM` gask
-- | Get the function to promote a route for a subsite to a route for the
-- master site.
getRouteToMaster :: Monad m => GGHandler sub master m (Route sub -> Route master)
getRouteToMaster = handlerToMaster `liftM` GHandler ask
getRouteToMaster = handlerToMaster `liftM` gask
-- | Function used internally by Yesod in the process of converting a
-- 'GHandler' into an 'W.Application'. Should not be needed by users.
@ -379,14 +381,12 @@ runHandler handler mrender sroute tomr ma sa =
, handlerToMaster = tomr
}
let initSession' = GHState initSession Nothing 1
((contents', headers), finalSession) <- catchIter (
fmap (second ghsSession)
$ flip runStateT initSession'
$ runWriterT
(contents', finalSession, headers) <- catchIter (
fmap (\(a, b, c) -> (a, ghsSession b, c))
$ (\m -> runRWST m hd initSession')
$ runErrorT
$ flip runReaderT hd
$ unGHandler handler
) (\e -> return ((Left $ HCError $ toErrorHandler e, mempty), initSession))
) (\e -> return (Left $ HCError $ toErrorHandler e, initSession, mempty))
let contents = either id (HCContent H.status200 . chooseRep) contents'
let handleError e = do
yar <- unYesodApp (eh e) safeEh rr cts finalSession
@ -444,7 +444,7 @@ redirectParams rt url params = do
-- | Redirect to the given URL.
redirectString, redirectText :: Monad mo => RedirectType -> Text -> GGHandler sub master mo a
redirectText rt = GHandler . lift . throwError . HCRedirect rt
redirectText rt = GHandler . throwError . HCRedirect rt
redirectString = redirectText
{-# DEPRECATED redirectString "Use redirectText instead" #-}
@ -479,7 +479,7 @@ setUltDest' = do
Nothing -> return ()
Just r -> do
tm <- getRouteToMaster
gets' <- reqGetParams `liftM` handlerRequest `liftM` GHandler ask
gets' <- reqGetParams `liftM` handlerRequest `liftM` gask
render <- getUrlRenderParams
setUltDestString $ render (tm r) gets'
@ -545,7 +545,7 @@ getMessage = do
-- For some backends, this is more efficient than reading in the file to
-- memory, since they can optimize file sending via a system call to sendfile.
sendFile :: Monad mo => ContentType -> FilePath -> GGHandler sub master mo a
sendFile ct fp = GHandler . lift . throwError $ HCSendFile ct fp Nothing
sendFile ct fp = GHandler . throwError $ HCSendFile ct fp Nothing
-- | Same as 'sendFile', but only sends part of a file.
sendFilePart :: Monad mo
@ -555,18 +555,18 @@ sendFilePart :: Monad mo
-> Integer -- ^ count
-> GGHandler sub master mo a
sendFilePart ct fp off count =
GHandler . lift . throwError $ HCSendFile ct fp $ Just $ W.FilePart off count
GHandler . throwError $ HCSendFile ct fp $ Just $ W.FilePart off count
-- | Bypass remaining handler code and output the given content with a 200
-- status code.
sendResponse :: (Monad mo, HasReps c) => c -> GGHandler sub master mo a
sendResponse = GHandler . lift . throwError . HCContent H.status200
sendResponse = GHandler . throwError . HCContent H.status200
. chooseRep
-- | Bypass remaining handler code and output the given content with the given
-- status code.
sendResponseStatus :: (Monad mo, HasReps c) => H.Status -> c -> GGHandler s m mo a
sendResponseStatus s = GHandler . lift . throwError . HCContent s
sendResponseStatus s = GHandler . throwError . HCContent s
. chooseRep
-- | Send a 201 "Created" response with the given route as the Location
@ -574,7 +574,7 @@ sendResponseStatus s = GHandler . lift . throwError . HCContent s
sendResponseCreated :: Monad mo => Route m -> GGHandler s m mo a
sendResponseCreated url = do
r <- getUrlRender
GHandler $ lift $ throwError $ HCCreated $ r url
GHandler $ throwError $ HCCreated $ r url
-- | Send a 'W.Response'. Please note: this function is rarely
-- necessary, and will /disregard/ any changes to response headers and session
@ -582,7 +582,7 @@ sendResponseCreated url = do
-- considered only for very specific needs. If you are not sure if you need it,
-- you don't.
sendWaiResponse :: Monad mo => W.Response -> GGHandler s m mo b
sendWaiResponse = GHandler . lift . throwError . HCWai
sendWaiResponse = GHandler . throwError . HCWai
-- | Return a 404 not found page. Also denotes no handler available.
notFound :: Failure ErrorResponse m => m a
@ -669,18 +669,18 @@ setSession :: Monad mo
=> Text -- ^ key
-> Text -- ^ value
-> GGHandler sub master mo ()
setSession k = GHandler . lift . lift . lift . modify . modSession . Map.insert k
setSession k = GHandler . lift . modify . modSession . Map.insert k
-- | Unsets a session variable. See 'setSession'.
deleteSession :: Monad mo => Text -> GGHandler sub master mo ()
deleteSession = GHandler . lift . lift . lift . modify . modSession . Map.delete
deleteSession = GHandler . lift . modify . modSession . Map.delete
modSession :: (SessionMap -> SessionMap) -> GHState -> GHState
modSession f x = x { ghsSession = f $ ghsSession x }
-- | Internal use only, not to be confused with 'setHeader'.
addHeader :: Monad mo => Header -> GGHandler sub master mo ()
addHeader = GHandler . lift . lift . tell . Endo . (:)
addHeader = GHandler . lift . tell . Endo . (:)
getStatus :: ErrorResponse -> H.Status
getStatus NotFound = H.status404
@ -702,17 +702,17 @@ data RedirectType = RedirectPermanent
localNoCurrent :: Monad mo => GGHandler s m mo a -> GGHandler s m mo a
localNoCurrent =
GHandler . local (\hd -> hd { handlerRoute = Nothing }) . unGHandler
GHandler . ErrorT . local (\hd -> hd { handlerRoute = Nothing }) . runErrorT . unGHandler
-- | Lookup for session data.
lookupSession :: Monad mo => Text -> GGHandler s m mo (Maybe Text)
lookupSession n = GHandler $ do
m <- liftM ghsSession $ lift $ lift $ lift get
m <- liftM ghsSession $ lift get
return $ Map.lookup n m
-- | Get all session variables.
getSession :: Monad mo => GGHandler s m mo SessionMap
getSession = liftM ghsSession $ GHandler $ lift $ lift $ lift get
getSession = liftM ghsSession $ GHandler $ lift get
handlerToYAR :: (HasReps a, HasReps b)
=> m -- ^ master site foundation
@ -803,7 +803,7 @@ headerToPair _ _ (Header key value) = (key, value)
-- | Get a unique identifier.
newIdent :: Monad mo => GGHandler sub master mo String -- FIXME use Text
newIdent = GHandler $ lift $ lift $ lift $ do
newIdent = GHandler $ lift $ do
x <- get
let i' = ghsIdent x + 1
put x { ghsIdent = i' }
@ -813,41 +813,27 @@ liftIOHandler :: MonadIO mo
=> GGHandler sub master IO a
-> GGHandler sub master mo a
liftIOHandler m = GHandler $
ReaderT $ \r ->
ErrorT $
WriterT $
StateT $ \s ->
liftIO $ runGGHandler m r s
ErrorT $
RWST $ \r s ->
liftIO (runGGHandler m r s)
runGGHandler :: GGHandler sub master m a
-> HandlerData sub master
-> GHState
-> m ( ( Either HandlerContents a
, Endo [Header]
)
, GHState
)
runGGHandler m r s = runStateT
(runWriterT
(runErrorT
(runReaderT
(unGHandler m) r))) s
-> m (Either HandlerContents a, GHState, Endo [Header])
runGGHandler (GHandler (ErrorT m)) r s = runRWST m r s
instance MonadTransControl (GGHandler s m) where
liftControl f =
GHandler $
liftControl $ \runRdr ->
liftControl $ \runErr ->
liftControl $ \runWrt ->
liftControl $ \runSt ->
f ( liftM ( GHandler
. join . lift
. join . lift
. join . lift
)
. runSt . runWrt . runErr . runRdr
. unGHandler
)
liftControl $ \runErr ->
liftControl $ \runRws ->
f ( liftM ( GHandler
. join . lift
)
. runRws . runErr
. unGHandler
)
-- | Redirect to a POST resource.
--

View File

@ -1,5 +1,5 @@
Name: yesod-examples
Version: 0.8.0.3
Version: 0.9.0
Synopsis: Example programs using the Yesod Web Framework.
Description: These are the same examples and tutorials found on the documentation site.
Homepage: http://www.yesodweb.com/

View File

@ -1,5 +1,5 @@
name: yesod
version: 0.9.3.2
version: 0.9.3.3
license: BSD3
license-file: LICENSE
author: Michael Snoyman <michael@snoyman.com>
@ -106,7 +106,7 @@ executable yesod
, Cabal >= 1.8 && < 1.13
, unix-compat >= 0.2 && < 0.4
, containers >= 0.2 && < 0.5
, attoparsec-text >= 0.8.5 && < 0.8.5.2
, attoparsec >= 0.10
, http-types >= 0.6.1 && < 0.7
, blaze-builder >= 0.2.1.4 && < 0.4
, filepath >= 1.1 && < 1.3