YesodSubDispatch
This commit is contained in:
parent
e928991410
commit
2aefef4414
@ -4,6 +4,7 @@ module Yesod.Core
|
|||||||
( -- * Type classes
|
( -- * Type classes
|
||||||
Yesod (..)
|
Yesod (..)
|
||||||
, YesodDispatch (..)
|
, YesodDispatch (..)
|
||||||
|
, YesodSubDispatch (..)
|
||||||
, RenderRoute (..)
|
, RenderRoute (..)
|
||||||
-- ** Breadcrumbs
|
-- ** Breadcrumbs
|
||||||
, YesodBreadcrumbs (..)
|
, YesodBreadcrumbs (..)
|
||||||
|
|||||||
@ -1,13 +1,20 @@
|
|||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE RankNTypes #-}
|
||||||
{-# LANGUAGE ViewPatterns #-}
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
module Yesod.Core.Class.Dispatch where
|
module Yesod.Core.Class.Dispatch where
|
||||||
|
|
||||||
import Yesod.Routes.Class
|
import Yesod.Routes.Class
|
||||||
import qualified Network.Wai as W
|
import qualified Network.Wai as W
|
||||||
import Yesod.Core.Types
|
import Yesod.Core.Types
|
||||||
|
import Yesod.Core.Content
|
||||||
import Yesod.Core.Class.Yesod
|
import Yesod.Core.Class.Yesod
|
||||||
|
import Yesod.Core.Class.Handler
|
||||||
import Yesod.Core.Internal.Request (textQueryString)
|
import Yesod.Core.Internal.Request (textQueryString)
|
||||||
|
import Yesod.Core.Internal.Run
|
||||||
|
import Control.Monad.Trans.Control (MonadBaseControl)
|
||||||
|
|
||||||
-- | This class is automatically instantiated when you use the template haskell
|
-- | This class is automatically instantiated when you use the template haskell
|
||||||
-- mkYesod function. You should never need to deal with it directly.
|
-- mkYesod function. You should never need to deal with it directly.
|
||||||
@ -20,3 +27,50 @@ class YesodDispatch sub master where
|
|||||||
instance YesodDispatch WaiSubsite master where
|
instance YesodDispatch WaiSubsite master where
|
||||||
yesodDispatch YesodRunnerEnv { yreSub = WaiSubsite app } req =
|
yesodDispatch YesodRunnerEnv { yreSub = WaiSubsite app } req =
|
||||||
app req
|
app req
|
||||||
|
|
||||||
|
class YesodSubDispatch sub m where
|
||||||
|
yesodSubDispatch
|
||||||
|
:: (HandlerError m, HandlerState m, master ~ HandlerMaster m, Yesod master, MonadBaseControl IO m)
|
||||||
|
=> (m TypedContent
|
||||||
|
-> YesodRunnerEnv master master
|
||||||
|
-> Maybe (Route master)
|
||||||
|
-> W.Application)
|
||||||
|
-> (master -> sub)
|
||||||
|
-> (Route sub -> Route master)
|
||||||
|
-> YesodRunnerEnv master master
|
||||||
|
-> W.Application
|
||||||
|
|
||||||
|
instance YesodSubDispatch WaiSubsite master where
|
||||||
|
yesodSubDispatch _ toSub _ YesodRunnerEnv { yreMaster = master } req =
|
||||||
|
app req
|
||||||
|
where
|
||||||
|
WaiSubsite app = toSub master
|
||||||
|
|
||||||
|
{-
|
||||||
|
subHelper :: Yesod master => (YesodRunnerEnv sub master -> W.Application)
|
||||||
|
-> (forall res. ToTypedContent res
|
||||||
|
=> m res
|
||||||
|
-> YesodRunnerEnv master master
|
||||||
|
-> Maybe (Route master)
|
||||||
|
-> W.Application)
|
||||||
|
-> (master -> sub)
|
||||||
|
-> (Route sub -> Route master)
|
||||||
|
-> W.Application
|
||||||
|
subHelper runBase getSub toMaster = error "subHelper"
|
||||||
|
-}
|
||||||
|
|
||||||
|
subHelper :: (HandlerMaster m ~ master, HandlerState m, MonadBaseControl IO m)
|
||||||
|
=> (m TypedContent
|
||||||
|
-> YesodRunnerEnv master master
|
||||||
|
-> Maybe (Route master)
|
||||||
|
-> W.Application)
|
||||||
|
-> (master -> sub)
|
||||||
|
-> (Route sub -> Route master)
|
||||||
|
-> HandlerT sub m TypedContent
|
||||||
|
-> YesodRunnerEnv master master
|
||||||
|
-> Maybe (Route sub)
|
||||||
|
-> W.Application
|
||||||
|
subHelper parentRunner getSub toMaster handlert env route =
|
||||||
|
parentRunner base env (fmap toMaster route)
|
||||||
|
where
|
||||||
|
base = stripHandlerT (fmap toTypedContent handlert) getSub toMaster route
|
||||||
|
|||||||
@ -51,9 +51,11 @@ import Yesod.Routes.TH
|
|||||||
import Yesod.Routes.Parse
|
import Yesod.Routes.Parse
|
||||||
import System.Log.FastLogger (Logger)
|
import System.Log.FastLogger (Logger)
|
||||||
import Yesod.Core.Types
|
import Yesod.Core.Types
|
||||||
|
import Yesod.Core.Content
|
||||||
import Yesod.Core.Class.Yesod
|
import Yesod.Core.Class.Yesod
|
||||||
import Yesod.Core.Class.Dispatch
|
import Yesod.Core.Class.Dispatch
|
||||||
import Yesod.Core.Internal.Run
|
import Yesod.Core.Internal.Run
|
||||||
|
import Yesod.Core.Class.Handler
|
||||||
|
|
||||||
-- | Generates URL datatype and site function for the given 'Resource's. This
|
-- | Generates URL datatype and site function for the given 'Resource's. This
|
||||||
-- is used for creating sites, /not/ subsites. See 'mkYesodSub' for the latter.
|
-- is used for creating sites, /not/ subsites. See 'mkYesodSub' for the latter.
|
||||||
@ -117,13 +119,13 @@ mkYesodGeneral name args clazzes isSub resS = do
|
|||||||
masterTypeSyns <- if isSub then return []
|
masterTypeSyns <- if isSub then return []
|
||||||
else sequence [handler, widget]
|
else sequence [handler, widget]
|
||||||
renderRouteDec <- mkRenderRouteInstance subsite res
|
renderRouteDec <- mkRenderRouteInstance subsite res
|
||||||
dispatchDec <- mkDispatchInstance context sub master res
|
dispatchDec <- mkDispatchInstance context (if isSub then Just sub else Nothing) master res
|
||||||
return (renderRouteDec ++ masterTypeSyns, dispatchDec)
|
return (renderRouteDec ++ masterTypeSyns, dispatchDec)
|
||||||
where sub = foldl appT subCons subArgs
|
where sub = foldl appT subCons subArgs
|
||||||
master = if isSub then (varT $ mkName "master") else sub
|
master = if isSub then (varT $ mkName "master") else sub
|
||||||
context = if isSub then cxt $ yesod : map return clazzes
|
context = if isSub then cxt $ yesod : map return clazzes
|
||||||
else return []
|
else return []
|
||||||
yesod = classP ''Yesod [master]
|
yesod = classP ''HandlerReader [master]
|
||||||
handler = tySynD (mkName "Handler") [] [t| GHandler $master $master |]
|
handler = tySynD (mkName "Handler") [] [t| GHandler $master $master |]
|
||||||
widget = tySynD (mkName "Widget") [] [t| GWidget $master $master () |]
|
widget = tySynD (mkName "Widget") [] [t| GWidget $master $master () |]
|
||||||
res = map (fmap parseType) resS
|
res = map (fmap parseType) resS
|
||||||
@ -136,25 +138,65 @@ mkYesodGeneral name args clazzes isSub resS = do
|
|||||||
-- when writing library/plugin for yesod, this combinator becomes
|
-- when writing library/plugin for yesod, this combinator becomes
|
||||||
-- handy.
|
-- handy.
|
||||||
mkDispatchInstance :: CxtQ -- ^ The context
|
mkDispatchInstance :: CxtQ -- ^ The context
|
||||||
-> TypeQ -- ^ The subsite type
|
-> Maybe TypeQ -- ^ The subsite type
|
||||||
-> TypeQ -- ^ The master site type
|
-> TypeQ -- ^ The master site type
|
||||||
-> [ResourceTree a] -- ^ The resource
|
-> [ResourceTree a] -- ^ The resource
|
||||||
-> DecsQ
|
-> DecsQ
|
||||||
mkDispatchInstance context sub master res = do
|
mkDispatchInstance context Nothing master res = do
|
||||||
let yDispatch = conT ''YesodDispatch `appT` sub `appT` master
|
let yDispatch = conT ''YesodDispatch `appT` master `appT` master
|
||||||
thisDispatch = do
|
thisDispatch = do
|
||||||
clause' <- mkDispatchClause MkDispatchSettings
|
clause' <- mkDispatchClause MkDispatchSettings
|
||||||
{ mdsRunHandler = [|yesodRunner|]
|
{ mdsRunHandler = [|yesodRunner|]
|
||||||
, mdsDispatcher = [|yesodDispatch |]
|
, mdsSubDispatcher = [|yesodSubDispatch|]
|
||||||
, mdsFixEnv = [|fixEnv|]
|
|
||||||
, mdsGetPathInfo = [|W.pathInfo|]
|
, mdsGetPathInfo = [|W.pathInfo|]
|
||||||
, mdsSetPathInfo = [|\p r -> r { W.pathInfo = p }|]
|
, mdsSetPathInfo = [|\p r -> r { W.pathInfo = p }|]
|
||||||
, mdsMethod = [|W.requestMethod|]
|
, mdsMethod = [|W.requestMethod|]
|
||||||
, mds404 = [|\env -> yesodRunner (notFound >> return ()) env Nothing|]
|
, mds404 = [|notFound >> return ()|]
|
||||||
, mds405 = [|\env route -> yesodRunner (badMethod >> return ()) env (Just route)|]
|
, mds405 = [|badMethod >> return ()|]
|
||||||
} res
|
} res
|
||||||
return $ FunD 'yesodDispatch [clause']
|
return $ FunD 'yesodDispatch [clause']
|
||||||
in sequence [instanceD context yDispatch [thisDispatch]]
|
in sequence [instanceD context yDispatch [thisDispatch]]
|
||||||
|
mkDispatchInstance context (Just sub) master res = do
|
||||||
|
yDispatch <- conT ''YesodSubDispatch `appT` sub `appT` master
|
||||||
|
parentRunner <- newName "parentRunner"
|
||||||
|
getSub <- newName "getSub"
|
||||||
|
toMaster <- newName "toMaster"
|
||||||
|
runner <- newName "runner"
|
||||||
|
clause' <- mkDispatchClause MkDispatchSettings
|
||||||
|
{ mdsRunHandler = [|subHelper
|
||||||
|
$(return $ VarE parentRunner)
|
||||||
|
$(return $ VarE getSub)
|
||||||
|
$(return $ VarE toMaster)
|
||||||
|
. fmap toTypedContent
|
||||||
|
|]
|
||||||
|
, mdsSubDispatcher = [|yesodSubDispatch|]
|
||||||
|
, mdsGetPathInfo = [|W.pathInfo|]
|
||||||
|
, mdsSetPathInfo = [|\p r -> r { W.pathInfo = p }|]
|
||||||
|
, mdsMethod = [|W.requestMethod|]
|
||||||
|
, mds404 = [|notFound >> return ()|]
|
||||||
|
, mds405 = [|badMethod >> return ()|]
|
||||||
|
} res
|
||||||
|
inner <- newName "inner"
|
||||||
|
err <- [|error "FIXME"|]
|
||||||
|
let innerFun = FunD inner [clause']
|
||||||
|
runnerFun = FunD runner
|
||||||
|
[ Clause
|
||||||
|
[]
|
||||||
|
(NormalB $ VarE 'subHelper
|
||||||
|
`AppE` VarE parentRunner
|
||||||
|
`AppE` VarE getSub
|
||||||
|
`AppE` VarE toMaster
|
||||||
|
)
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
context' <- context
|
||||||
|
let fun = FunD 'yesodSubDispatch
|
||||||
|
[ Clause
|
||||||
|
[VarP parentRunner, VarP getSub, VarP toMaster]
|
||||||
|
(NormalB $ VarE inner)
|
||||||
|
[innerFun, runnerFun]
|
||||||
|
]
|
||||||
|
return [InstanceD context' yDispatch [fun]]
|
||||||
|
|
||||||
-- | Convert the given argument into a WAI application, executable with any WAI
|
-- | Convert the given argument into a WAI application, executable with any WAI
|
||||||
-- handler. This is the same as 'toWaiAppPlain', except it includes two
|
-- handler. This is the same as 'toWaiAppPlain', except it includes two
|
||||||
|
|||||||
@ -284,7 +284,7 @@ fixEnv toNewSub toOldRoute envOld =
|
|||||||
, yreToMaster = yreToMaster envOld . toOldRoute
|
, yreToMaster = yreToMaster envOld . toOldRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
stripHandlerT :: (HandlerReader m, HandlerState m, MonadBaseControl IO m)
|
stripHandlerT :: (HandlerState m, MonadBaseControl IO m)
|
||||||
=> HandlerT sub m a
|
=> HandlerT sub m a
|
||||||
-> (HandlerMaster m -> sub)
|
-> (HandlerMaster m -> sub)
|
||||||
-> (Route sub -> Route (HandlerMaster m))
|
-> (Route sub -> Route (HandlerMaster m))
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import Control.Arrow (first)
|
|||||||
import Control.Exception (Exception, throwIO)
|
import Control.Exception (Exception, throwIO)
|
||||||
import Control.Failure (Failure (..))
|
import Control.Failure (Failure (..))
|
||||||
import Control.Monad (liftM)
|
import Control.Monad (liftM)
|
||||||
|
import Control.Monad.Trans.Class (MonadTrans)
|
||||||
import Control.Monad.Base (MonadBase (liftBase))
|
import Control.Monad.Base (MonadBase (liftBase))
|
||||||
import Control.Monad.IO.Class (MonadIO (liftIO))
|
import Control.Monad.IO.Class (MonadIO (liftIO))
|
||||||
import Control.Monad.Logger (LogLevel, LogSource,
|
import Control.Monad.Logger (LogLevel, LogSource,
|
||||||
@ -201,6 +202,13 @@ newtype HandlerT sub m a = HandlerT
|
|||||||
{ unHandlerT :: HandlerData sub sub -> m a
|
{ unHandlerT :: HandlerData sub sub -> m a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance MonadTrans (HandlerT sub)
|
||||||
|
instance Monad m => Monad (HandlerT sub m) where
|
||||||
|
return = HandlerT . const . return
|
||||||
|
HandlerT f >>= g = HandlerT $ \hd -> f hd >>= \x -> unHandlerT (g x) hd
|
||||||
|
instance Monad m => Functor (HandlerT sub m) where
|
||||||
|
fmap = liftM
|
||||||
|
|
||||||
data GHState = GHState
|
data GHState = GHState
|
||||||
{ ghsSession :: SessionMap
|
{ ghsSession :: SessionMap
|
||||||
, ghsRBC :: Maybe RequestBodyContents
|
, ghsRBC :: Maybe RequestBodyContents
|
||||||
|
|||||||
@ -29,8 +29,8 @@ instance RenderRoute Subsite where
|
|||||||
deriving (Eq, Show, Read)
|
deriving (Eq, Show, Read)
|
||||||
renderRoute (SubsiteRoute x) = (x, [])
|
renderRoute (SubsiteRoute x) = (x, [])
|
||||||
|
|
||||||
instance YesodDispatch Subsite master where
|
instance YesodSubDispatch Subsite master where
|
||||||
yesodDispatch _getEnv req = return $ responseLBS
|
yesodSubDispatch _ _ _ _ req = return $ responseLBS
|
||||||
status200
|
status200
|
||||||
[ ("Content-Type", "SUBSITE")
|
[ ("Content-Type", "SUBSITE")
|
||||||
] $ L8.pack $ show (pathInfo req)
|
] $ L8.pack $ show (pathInfo req)
|
||||||
|
|||||||
@ -51,8 +51,8 @@ case_subsite = runner $ do
|
|||||||
res <- request defaultRequest
|
res <- request defaultRequest
|
||||||
{ pathInfo = map T.pack ["subsite", "bar"]
|
{ pathInfo = map T.pack ["subsite", "bar"]
|
||||||
}
|
}
|
||||||
assertStatus 200 res
|
|
||||||
assertBody (L8.pack "BarR") res
|
assertBody (L8.pack "BarR") res
|
||||||
|
assertStatus 200 res
|
||||||
|
|
||||||
noOverloadedTest :: Spec
|
noOverloadedTest :: Spec
|
||||||
noOverloadedTest = describe "Test.NoOverloadedStrings" $ do
|
noOverloadedTest = describe "Test.NoOverloadedStrings" $ do
|
||||||
|
|||||||
@ -31,8 +31,7 @@ flatten =
|
|||||||
|
|
||||||
data MkDispatchSettings = MkDispatchSettings
|
data MkDispatchSettings = MkDispatchSettings
|
||||||
{ mdsRunHandler :: Q Exp
|
{ mdsRunHandler :: Q Exp
|
||||||
, mdsDispatcher :: Q Exp
|
, mdsSubDispatcher :: Q Exp
|
||||||
, mdsFixEnv :: Q Exp
|
|
||||||
, mdsGetPathInfo :: Q Exp
|
, mdsGetPathInfo :: Q Exp
|
||||||
, mdsSetPathInfo :: Q Exp
|
, mdsSetPathInfo :: Q Exp
|
||||||
, mdsMethod :: Q Exp
|
, mdsMethod :: Q Exp
|
||||||
@ -145,7 +144,11 @@ mkDispatchClause mds ress' = do
|
|||||||
u <- [|case $(return dispatched) of
|
u <- [|case $(return dispatched) of
|
||||||
Just f -> f $(return $ VarE getEnv0)
|
Just f -> f $(return $ VarE getEnv0)
|
||||||
$(return $ VarE req0)
|
$(return $ VarE req0)
|
||||||
Nothing -> $(mds404 mds) $(return $ VarE getEnv0) $(return $ VarE req0)
|
Nothing -> $(mdsRunHandler mds)
|
||||||
|
$(mds404 mds)
|
||||||
|
$(return $ VarE getEnv0)
|
||||||
|
Nothing
|
||||||
|
$(return $ VarE req0)
|
||||||
|]
|
|]
|
||||||
return $ Clause pats (NormalB u) $ dispatchFun : methodMaps
|
return $ Clause pats (NormalB u) $ dispatchFun : methodMaps
|
||||||
where
|
where
|
||||||
@ -323,9 +326,10 @@ buildCaller mds xrest parents name resDisp ys = do
|
|||||||
f <- newName "f"
|
f <- newName "f"
|
||||||
let apply = foldl' (\a b -> a `AppE` VarE b) (VarE f) ys
|
let apply = foldl' (\a b -> a `AppE` VarE b) (VarE f) ys
|
||||||
body405 <-
|
body405 <-
|
||||||
[|$(mds405 mds)
|
[|$(mdsRunHandler mds)
|
||||||
|
$(mds405 mds)
|
||||||
$(return $ VarE getEnv)
|
$(return $ VarE getEnv)
|
||||||
$(return route)
|
(Just $(return route))
|
||||||
$(return $ VarE req)
|
$(return $ VarE req)
|
||||||
|]
|
|]
|
||||||
return $ CaseE mf
|
return $ CaseE mf
|
||||||
@ -337,12 +341,11 @@ buildCaller mds xrest parents name resDisp ys = do
|
|||||||
sub <- newName "sub"
|
sub <- newName "sub"
|
||||||
let sub2 = LamE [VarP sub]
|
let sub2 = LamE [VarP sub]
|
||||||
(foldl' (\a b -> a `AppE` VarE b) (VarE (mkName getSub) `AppE` VarE sub) ys)
|
(foldl' (\a b -> a `AppE` VarE b) (VarE (mkName getSub) `AppE` VarE sub) ys)
|
||||||
[|$(mdsDispatcher mds)
|
[|$(mdsSubDispatcher mds)
|
||||||
($(mdsFixEnv mds)
|
$(mdsRunHandler mds)
|
||||||
$(return sub2)
|
$(return sub2)
|
||||||
$(return route)
|
$(return route)
|
||||||
$(return $ VarE getEnv)
|
$(return $ VarE getEnv)
|
||||||
)
|
|
||||||
($(mdsSetPathInfo mds)
|
($(mdsSetPathInfo mds)
|
||||||
$(return $ VarE xrest)
|
$(return $ VarE xrest)
|
||||||
$(return $ VarE req)
|
$(return $ VarE req)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user