Started on the Handler typeclasses

This commit is contained in:
Michael Snoyman 2013-03-11 06:00:50 +02:00
parent 8d5f207c8d
commit 8f8e986839
3 changed files with 118 additions and 39 deletions

View File

@ -0,0 +1,84 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
module Yesod.Core.Handler.Class where
import Yesod.Core.Types
import Yesod.Core.Trans.Class (lift)
import Control.Monad.Trans.Class (MonadTrans)
import Data.IORef.Lifted (atomicModifyIORef)
import Control.Exception.Lifted (throwIO)
class Monad m => HandlerReader m where
type HandlerReaderSub m
type HandlerReaderMaster m
askYesodRequest :: m YesodRequest
askHandlerEnv :: m (RunHandlerEnv (HandlerReaderSub m) (HandlerReaderMaster m))
instance HandlerReader (GHandler sub master) where
type HandlerReaderSub (GHandler sub master) = sub
type HandlerReaderMaster (GHandler sub master) = master
askYesodRequest = GHandler $ return . handlerRequest
askHandlerEnv = GHandler $ return . handlerEnv
instance HandlerReader (GWidget sub master) where
type HandlerReaderSub (GWidget sub master) = sub
type HandlerReaderMaster (GWidget sub master) = master
askYesodRequest = lift askYesodRequest
askHandlerEnv = lift askHandlerEnv
instance (MonadTrans t, HandlerReader m, Monad (t m)) => HandlerReader (t m) where
type HandlerReaderSub (t m) = HandlerReaderSub m
type HandlerReaderMaster (t m) = HandlerReaderMaster m
askYesodRequest = lift askYesodRequest
askHandlerEnv = lift askHandlerEnv
class HandlerReader m => HandlerState m where
type HandlerStateSub m
type HandlerStateMaster m
stateGHState :: (GHState -> (a, GHState)) -> m a
getGHState :: m GHState
getGHState = stateGHState $ \s -> (s, s)
putGHState :: GHState -> m ()
putGHState s = stateGHState $ const ((), s)
instance HandlerState (GHandler sub master) where
type HandlerStateSub (GHandler sub master) = sub
type HandlerStateMaster (GHandler sub master) = master
stateGHState f =
GHandler $ flip atomicModifyIORef f' . handlerState
where
f' z = let (x, y) = f z in (y, x)
instance HandlerState (GWidget sub master) where
type HandlerStateSub (GWidget sub master) = sub
type HandlerStateMaster (GWidget sub master) = master
stateGHState = lift . stateGHState
instance (MonadTrans t, HandlerState m, Monad (t m)) => HandlerState (t m) where
type HandlerStateSub (t m) = HandlerStateSub m
type HandlerStateMaster (t m) = HandlerStateMaster m
stateGHState = lift . stateGHState
class Monad m => HandlerError m where
handlerError :: ErrorResponse -> m a
instance HandlerError (GHandler sub master) where
handlerError = throwIO . HCError
instance HandlerError (GWidget sub master) where
handlerError = lift . handlerError
instance (HandlerError m, MonadTrans t, Monad (t m)) => HandlerError (t m) where
handlerError = lift . handlerError

View File

@ -1,4 +1,6 @@
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
@ -138,6 +140,7 @@ import Control.Applicative
import Control.Monad (liftM) import Control.Monad (liftM)
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Trans.Resource (MonadResource, liftResourceT)
import qualified Network.Wai as W import qualified Network.Wai as W
import qualified Network.HTTP.Types as H import qualified Network.HTTP.Types as H
@ -173,28 +176,26 @@ import Yesod.Core.Trans.Class
import Data.Maybe (listToMaybe) import Data.Maybe (listToMaybe)
import Data.Typeable (Typeable, typeOf) import Data.Typeable (Typeable, typeOf)
import Data.Dynamic (fromDynamic, toDyn) import Data.Dynamic (fromDynamic, toDyn)
import Yesod.Core.Handler.Class
class YesodSubRoute s y where class YesodSubRoute s y where
fromSubRoute :: s -> y -> Route s -> Route y fromSubRoute :: s -> y -> Route s -> Route y
get :: GHandler sub master GHState get :: HandlerState m => m GHState
get = do get = getGHState
hd <- ask
liftIO $ I.readIORef $ handlerState hd
put :: GHState -> GHandler sub master () put :: HandlerState m => GHState -> m ()
put g = do put = putGHState
hd <- ask
liftIO $ I.writeIORef (handlerState hd) g
modify :: (GHState -> GHState) -> GHandler sub master () modify :: HandlerState m => (GHState -> GHState) -> m ()
modify f = do modify = stateGHState . (((), ) .)
hd <- ask
liftIO $ I.atomicModifyIORef (handlerState hd) $ \g -> (f g, ())
tell :: Endo [Header] -> GHandler sub master () tell :: HandlerState m => Endo [Header] -> m ()
tell hs = modify $ \g -> g { ghsHeaders = ghsHeaders g `mappend` hs } tell hs = modify $ \g -> g { ghsHeaders = ghsHeaders g `mappend` hs }
hcError :: HandlerError m => ErrorResponse -> m a
hcError = handlerError
class SubsiteGetter g m s | g -> s where class SubsiteGetter g m s | g -> s where
runSubsiteGetter :: g -> m s runSubsiteGetter :: g -> m s
@ -207,26 +208,22 @@ instance (anySub ~ anySub'
) => SubsiteGetter (GHandler anySub master sub) (GHandler anySub' master') sub where ) => SubsiteGetter (GHandler anySub master sub) (GHandler anySub' master') sub where
runSubsiteGetter = id runSubsiteGetter = id
getRequest :: GHandler s m YesodRequest getRequest :: HandlerReader m => m YesodRequest
getRequest = handlerRequest `liftM` ask getRequest = askYesodRequest
hcError :: ErrorResponse -> GHandler sub master a runRequestBody :: (MonadResource m, HandlerReader m, HandlerState m)
hcError = liftIO . throwIO . HCError => m RequestBodyContents
runRequestBody :: GHandler s m RequestBodyContents
runRequestBody = do runRequestBody = do
hd <- ask RunHandlerEnv {..} <- askHandlerEnv
let getUpload = rheUpload $ handlerEnv hd req <- askYesodRequest
len = W.requestBodyLength let len = W.requestBodyLength $ reqWaiRequest req
$ reqWaiRequest upload = rheUpload len
$ handlerRequest hd
upload = getUpload len
x <- get x <- get
case ghsRBC x of case ghsRBC x of
Just rbc -> return rbc Just rbc -> return rbc
Nothing -> do Nothing -> do
rr <- waiRequest rr <- waiRequest
rbc <- lift $ rbHelper upload rr rbc <- liftResourceT $ rbHelper upload rr
put x { ghsRBC = Just rbc } put x { ghsRBC = Just rbc }
return rbc return rbc
@ -257,33 +254,33 @@ rbHelper' backend mkFI req =
go = decodeUtf8With lenientDecode go = decodeUtf8With lenientDecode
-- | Get the sub application argument. -- | Get the sub application argument.
getYesodSub :: GHandler sub master sub getYesodSub :: HandlerReader m => m (HandlerReaderSub m)
getYesodSub = (rheSub . handlerEnv) `liftM` ask getYesodSub = rheSub `liftM` askHandlerEnv
-- | Get the master site appliation argument. -- | Get the master site appliation argument.
getYesod :: GHandler sub master master getYesod :: HandlerReader m => m (HandlerReaderMaster m)
getYesod = (rheMaster . handlerEnv) `liftM` ask getYesod = rheMaster `liftM` askHandlerEnv
-- | Get the URL rendering function. -- | Get the URL rendering function.
getUrlRender :: GHandler sub master (Route master -> Text) getUrlRender :: GHandler sub master (Route master -> Text)
getUrlRender = do getUrlRender = do
x <- (rheRender . handlerEnv) `liftM` ask x <- rheRender `liftM` askHandlerEnv
return $ flip x [] return $ flip x []
-- | The URL rendering function with query-string parameters. -- | The URL rendering function with query-string parameters.
getUrlRenderParams getUrlRenderParams
:: GHandler sub master (Route master -> [(Text, Text)] -> Text) :: GHandler sub master (Route master -> [(Text, Text)] -> Text)
getUrlRenderParams = (rheRender . handlerEnv) `liftM` ask getUrlRenderParams = rheRender `liftM` askHandlerEnv
-- | 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 :: GHandler sub master (Maybe (Route sub))
getCurrentRoute = (rheRoute . handlerEnv) `liftM` ask getCurrentRoute = rheRoute `liftM` askHandlerEnv
-- | 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 :: GHandler sub master (Route sub -> Route master)
getRouteToMaster = (rheToMaster . handlerEnv) `liftM` ask getRouteToMaster = rheToMaster `liftM` askHandlerEnv
-- | Returns a function that runs 'GHandler' actions inside @IO@. -- | Returns a function that runs 'GHandler' actions inside @IO@.
@ -400,7 +397,7 @@ setUltDestCurrent = do
Nothing -> return () Nothing -> return ()
Just r -> do Just r -> do
tm <- getRouteToMaster tm <- getRouteToMaster
gets' <- reqGetParams `liftM` handlerRequest `liftM` ask gets' <- reqGetParams `liftM` askYesodRequest
setUltDest (tm r, gets') setUltDest (tm r, gets')
-- | Sets the ultimate destination to the referer request header, if present. -- | Sets the ultimate destination to the referer request header, if present.
@ -709,7 +706,7 @@ hamletToRepHtml :: HtmlUrl (Route master) -> GHandler sub master RepHtml
hamletToRepHtml = liftM RepHtml . hamletToContent hamletToRepHtml = liftM RepHtml . hamletToContent
-- | Get the request\'s 'W.Request' value. -- | Get the request\'s 'W.Request' value.
waiRequest :: GHandler sub master W.Request waiRequest :: HandlerReader m => m W.Request
waiRequest = reqWaiRequest `liftM` getRequest waiRequest = reqWaiRequest `liftM` getRequest
getMessageRender :: RenderMessage master message => GHandler s master (message -> Text) getMessageRender :: RenderMessage master message => GHandler s master (message -> Text)
@ -747,9 +744,6 @@ cached f = do
cinsert :: Typeable a => a -> Cache -> Cache cinsert :: Typeable a => a -> Cache -> Cache
cinsert v (Cache m) = Cache (Map.insert (typeOf v) (toDyn v) m) cinsert v (Cache m) = Cache (Map.insert (typeOf v) (toDyn v) m)
ask :: GHandler sub master (HandlerData sub master)
ask = GHandler return
-- | Get the list of supported languages supplied by the user. -- | Get the list of supported languages supplied by the user.
-- --
-- Languages are determined based on the following three (in descending order -- Languages are determined based on the following three (in descending order

View File

@ -91,6 +91,7 @@ library
exposed-modules: Yesod.Content exposed-modules: Yesod.Content
Yesod.Core Yesod.Core
Yesod.Core.Json Yesod.Core.Json
Yesod.Core.Handler.Class
Yesod.Dispatch Yesod.Dispatch
Yesod.Handler Yesod.Handler
Yesod.Widget Yesod.Widget