Simplified GHandler/GWidget

This commit is contained in:
Michael Snoyman 2013-03-13 10:59:10 +02:00
parent fc6551c650
commit 4bdd01ef58
14 changed files with 233 additions and 320 deletions

View File

@ -49,7 +49,7 @@ module Yesod.Core
, BottomOfHeadAsync , BottomOfHeadAsync
-- * Subsites -- * Subsites
, defaultLayoutT , defaultLayoutT
, MonadHandlerBase (..) , MonadHandler (..)
-- * Misc -- * Misc
, yesodVersion , yesodVersion
, yesodRender , yesodRender
@ -92,8 +92,8 @@ import Data.Version (showVersion)
import Yesod.Routes.Class (RenderRoute (..)) import Yesod.Routes.Class (RenderRoute (..))
-- | Return an 'Unauthorized' value, with the given i18n message. -- | Return an 'Unauthorized' value, with the given i18n message.
unauthorizedI :: RenderMessage master msg => msg -> GHandler sub master AuthResult unauthorizedI :: RenderMessage site msg => msg -> GHandler site AuthResult
unauthorizedI msg =do unauthorizedI msg = do
mr <- getMessageRender mr <- getMessageRender
return $ Unauthorized $ mr msg return $ Unauthorized $ mr msg
@ -104,37 +104,19 @@ yesodVersion = showVersion Paths_yesod_core.version
-- --
-- Built on top of 'isAuthorized'. This is useful for building page that only -- Built on top of 'isAuthorized'. This is useful for building page that only
-- contain links to pages the user is allowed to see. -- contain links to pages the user is allowed to see.
maybeAuthorized :: Yesod a maybeAuthorized :: Yesod site
=> Route a => Route site
-> Bool -- ^ is this a write request? -> Bool -- ^ is this a write request?
-> GHandler s a (Maybe (Route a)) -> GHandler site (Maybe (Route site))
maybeAuthorized r isWrite = do maybeAuthorized r isWrite = do
x <- isAuthorized r isWrite x <- isAuthorized r isWrite
return $ if x == Authorized then Just r else Nothing return $ if x == Authorized then Just r else Nothing
class (MonadResource m, HandlerState m, Yesod (HandlerBase m)) => MonadHandlerBase m where defaultLayoutT :: ( HandlerSite m ~ sub
type HandlerBase m , Yesod (HandlerMaster m)
type HandlerSite m , MonadHandler m
liftHandler :: GHandler (HandlerBase m) (HandlerBase m) a -> m a
askHandlerData :: m (HandlerData (HandlerSite m) (HandlerSite m))
instance Yesod master => MonadHandlerBase (GHandler master master) where
type HandlerBase (GHandler master master) = master
type HandlerSite (GHandler master master) = master
liftHandler = id
askHandlerData = GHandler return
instance MonadHandlerBase m => MonadHandlerBase (HandlerT sub m) where
type HandlerBase (HandlerT sub m) = HandlerBase m
type HandlerSite (HandlerT sub m) = sub
liftHandler = lift . liftHandler
askHandlerData = HandlerT return
defaultLayoutT :: ( HandlerState m
, HandlerSite m ~ sub
, Yesod (HandlerBase m)
, MonadHandlerBase m
, MonadResource m
) )
=> GWidget sub sub () => GWidget sub ()
-> m RepHtml -> m RepHtml
defaultLayoutT (GWidget (GHandler f)) = do defaultLayoutT (GWidget (GHandler f)) = do
hd <- askHandlerData hd <- askHandlerData

View File

@ -8,14 +8,14 @@ import Data.Text (Text)
-- | A type-safe, concise method of creating breadcrumbs for pages. For each -- | A type-safe, concise method of creating breadcrumbs for pages. For each
-- resource, you declare the title of the page and the parent resource (if -- resource, you declare the title of the page and the parent resource (if
-- present). -- present).
class YesodBreadcrumbs y where class YesodBreadcrumbs site where
-- | Returns the title and the parent resource, if available. If you return -- | Returns the title and the parent resource, if available. If you return
-- a 'Nothing', then this is considered a top-level page. -- a 'Nothing', then this is considered a top-level page.
breadcrumb :: Route y -> GHandler sub y (Text , Maybe (Route y)) breadcrumb :: Route site -> GHandler site (Text , Maybe (Route site))
-- | Gets the title of the current page and the hierarchy of parent pages, -- | Gets the title of the current page and the hierarchy of parent pages,
-- along with their respective titles. -- along with their respective titles.
breadcrumbs :: YesodBreadcrumbs y => GHandler sub y (Text, [(Route y, Text)]) breadcrumbs :: YesodBreadcrumbs site => GHandler site (Text, [(Route site, Text)])
breadcrumbs = do breadcrumbs = do
x <- getCurrentRoute x <- getCurrentRoute
case x of case x of

View File

@ -18,56 +18,36 @@ 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.
class YesodDispatch sub master where class Yesod site => YesodDispatch site where
yesodDispatch yesodDispatch :: YesodRunnerEnv site -> W.Application
:: Yesod master
=> YesodRunnerEnv sub master
-> W.Application
instance YesodDispatch WaiSubsite master where
yesodDispatch YesodRunnerEnv { yreSub = WaiSubsite app } req =
app req
class YesodSubDispatch sub m where class YesodSubDispatch sub m where
yesodSubDispatch yesodSubDispatch
:: (HandlerError m, HandlerState m, master ~ HandlerMaster m, Yesod master, MonadBaseControl IO m) :: (MonadHandler m, master ~ HandlerMaster m, Yesod master)
=> (m TypedContent => (m TypedContent
-> YesodRunnerEnv master master -> YesodRunnerEnv master
-> Maybe (Route master) -> Maybe (Route master)
-> W.Application) -> W.Application)
-> (master -> sub) -> (master -> sub)
-> (Route sub -> Route master) -> (Route sub -> Route master)
-> YesodRunnerEnv master master -> YesodRunnerEnv master
-> W.Application -> W.Application
instance YesodSubDispatch WaiSubsite master where instance YesodSubDispatch WaiSubsite master where
yesodSubDispatch _ toSub _ YesodRunnerEnv { yreMaster = master } req = yesodSubDispatch _ toSub _ YesodRunnerEnv { yreSite = site } req =
app req app req
where where
WaiSubsite app = toSub master WaiSubsite app = toSub site
{- subHelper :: (HandlerSite m ~ master, MonadHandler m)
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 => (m TypedContent
-> YesodRunnerEnv master master -> YesodRunnerEnv master
-> Maybe (Route master) -> Maybe (Route master)
-> W.Application) -> W.Application)
-> (master -> sub) -> (master -> sub)
-> (Route sub -> Route master) -> (Route sub -> Route master)
-> HandlerT sub m TypedContent -> HandlerT sub m TypedContent
-> YesodRunnerEnv master master -> YesodRunnerEnv master
-> Maybe (Route sub) -> Maybe (Route sub)
-> W.Application -> W.Application
subHelper parentRunner getSub toMaster handlert env route = subHelper parentRunner getSub toMaster handlert env route =

View File

@ -5,35 +5,38 @@
module Yesod.Core.Class.Handler where module Yesod.Core.Class.Handler where
import Yesod.Core.Types import Yesod.Core.Types
import Yesod.Core.Types.Orphan ()
import Yesod.Core.Class.MonadLift (lift) import Yesod.Core.Class.MonadLift (lift)
import Control.Monad.Trans.Class (MonadTrans) import Control.Monad.Trans.Class (MonadTrans)
import Control.Monad.Trans.Resource
import Control.Monad.Trans.Control
import Data.IORef.Lifted (atomicModifyIORef) import Data.IORef.Lifted (atomicModifyIORef)
import Control.Exception.Lifted (throwIO) import Control.Exception.Lifted (throwIO)
class Monad m => HandlerReader m where class Monad m => HandlerReader m where
type HandlerSub m type HandlerSite m
type HandlerMaster m type HandlerMaster m
askYesodRequest :: m YesodRequest askYesodRequest :: m YesodRequest
askHandlerEnv :: m (RunHandlerEnv (HandlerSub m) (HandlerMaster m)) askHandlerEnv :: m (RunHandlerEnv (HandlerSite m))
instance HandlerReader (GHandler sub master) where instance HandlerReader (GHandler site) where
type HandlerSub (GHandler sub master) = sub type HandlerSite (GHandler site) = site
type HandlerMaster (GHandler sub master) = master type HandlerMaster (GHandler site) = site
askYesodRequest = GHandler $ return . handlerRequest askYesodRequest = GHandler $ return . handlerRequest
askHandlerEnv = GHandler $ return . handlerEnv askHandlerEnv = GHandler $ return . handlerEnv
instance HandlerReader (GWidget sub master) where instance HandlerReader m => HandlerReader (HandlerT site m) where
type HandlerSub (GWidget sub master) = sub type HandlerSite (HandlerT site m) = site
type HandlerMaster (GWidget sub master) = master type HandlerMaster (HandlerT site m) = HandlerMaster m
askYesodRequest = lift askYesodRequest askYesodRequest = HandlerT $ return . handlerRequest
askHandlerEnv = lift askHandlerEnv askHandlerEnv = HandlerT $ return . handlerEnv
instance (MonadTrans t, HandlerReader m, Monad (t m)) => HandlerReader (t m) where instance HandlerReader (GWidget site) where
type HandlerSub (t m) = HandlerSub m type HandlerSite (GWidget site) = site
type HandlerMaster (t m) = HandlerMaster m type HandlerMaster (GWidget site) = site
askYesodRequest = lift askYesodRequest askYesodRequest = lift askYesodRequest
askHandlerEnv = lift askHandlerEnv askHandlerEnv = lift askHandlerEnv
@ -47,26 +50,26 @@ class HandlerReader m => HandlerState m where
putGHState :: GHState -> m () putGHState :: GHState -> m ()
putGHState s = stateGHState $ const ((), s) putGHState s = stateGHState $ const ((), s)
instance HandlerState (GHandler sub master) where instance HandlerState (GHandler site) where
stateGHState f = stateGHState f =
GHandler $ flip atomicModifyIORef f' . handlerState GHandler $ flip atomicModifyIORef f' . handlerState
where where
f' z = let (x, y) = f z in (y, x) f' z = let (x, y) = f z in (y, x)
instance HandlerState (GWidget sub master) where instance HandlerState (GWidget site) where
stateGHState = lift . stateGHState stateGHState = lift . stateGHState
instance (MonadTrans t, HandlerState m, Monad (t m)) => HandlerState (t m) where instance HandlerState m => HandlerState (HandlerT site m) where
stateGHState = lift . stateGHState stateGHState = lift . stateGHState
class HandlerReader m => HandlerError m where class HandlerReader m => HandlerError m where
handlerError :: HandlerContents -> m a handlerError :: HandlerContents -> m a
instance HandlerError (GHandler sub master) where instance HandlerError (GHandler site) where
handlerError = throwIO handlerError = throwIO
instance HandlerError (GWidget sub master) where instance HandlerError (GWidget site) where
handlerError = lift . handlerError handlerError = lift . handlerError
instance (HandlerError m, MonadTrans t, Monad (t m)) => HandlerError (t m) where instance HandlerError m => HandlerError (HandlerT site m) where
handlerError = lift . handlerError handlerError = lift . handlerError

View File

@ -6,7 +6,8 @@ module Yesod.Core.Class.Yesod where
import Control.Monad.Logger (logErrorS) import Control.Monad.Logger (logErrorS)
import Yesod.Core.Content import Yesod.Core.Content
import Yesod.Core.Handler hiding (getExpires) import Yesod.Core.Handler
import Yesod.Core.Class.Handler
import Yesod.Routes.Class import Yesod.Routes.Class
@ -17,6 +18,8 @@ import Control.Monad (forM)
import Control.Monad.IO.Class (MonadIO (liftIO)) import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.Logger (LogLevel (LevelInfo, LevelOther), import Control.Monad.Logger (LogLevel (LevelInfo, LevelOther),
LogSource) LogSource)
import Control.Monad.Trans.Resource
import Control.Monad.Trans.Control
import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
import Data.Aeson (object, (.=)) import Data.Aeson (object, (.=))
@ -77,11 +80,11 @@ class RenderRoute a => Yesod a where
approot = ApprootRelative approot = ApprootRelative
-- | Output error response pages. -- | Output error response pages.
errorHandler :: ErrorResponse -> GHandler sub a TypedContent errorHandler :: ErrorResponse -> GHandler a TypedContent
errorHandler = defaultErrorHandler errorHandler = defaultErrorHandler
-- | Applies some form of layout to the contents of a page. -- | Applies some form of layout to the contents of a page.
defaultLayout :: GWidget sub a () -> GHandler sub a RepHtml defaultLayout :: GWidget a () -> GHandler a RepHtml
defaultLayout w = do defaultLayout w = do
p <- widgetToPageContent w p <- widgetToPageContent w
mmsg <- getMessage mmsg <- getMessage
@ -112,7 +115,7 @@ $doctype 5
-- If authentication is required, return 'AuthenticationRequired'. -- If authentication is required, return 'AuthenticationRequired'.
isAuthorized :: Route a isAuthorized :: Route a
-> Bool -- ^ is this a write request? -> Bool -- ^ is this a write request?
-> GHandler s a AuthResult -> GHandler a AuthResult
isAuthorized _ _ = return Authorized isAuthorized _ _ = return Authorized
-- | Determines whether the current request is a write request. By default, -- | Determines whether the current request is a write request. By default,
@ -122,7 +125,7 @@ $doctype 5
-- --
-- This function is used to determine if a request is authorized; see -- This function is used to determine if a request is authorized; see
-- 'isAuthorized'. -- 'isAuthorized'.
isWriteRequest :: Route a -> GHandler s a Bool isWriteRequest :: Route a -> GHandler a Bool
isWriteRequest _ = do isWriteRequest _ = do
wai <- waiRequest wai <- waiRequest
return $ W.requestMethod wai `notElem` return $ W.requestMethod wai `notElem`
@ -188,7 +191,7 @@ $doctype 5
addStaticContent :: Text -- ^ filename extension addStaticContent :: Text -- ^ filename extension
-> Text -- ^ mime-type -> Text -- ^ mime-type
-> L.ByteString -- ^ content -> L.ByteString -- ^ content
-> GHandler sub a (Maybe (Either Text (Route a, [(Text, Text)]))) -> GHandler a (Maybe (Either Text (Route a, [(Text, Text)])))
addStaticContent _ _ _ = return Nothing addStaticContent _ _ _ = return Nothing
{- Temporarily disabled until we have a better interface. {- Temporarily disabled until we have a better interface.
@ -274,7 +277,7 @@ $doctype 5
-- performs authorization checks. -- performs authorization checks.
-- --
-- Since: 1.1.6 -- Since: 1.1.6
yesodMiddleware :: GHandler sub a res -> GHandler sub a res yesodMiddleware :: GHandler a res -> GHandler a res
yesodMiddleware handler = do yesodMiddleware handler = do
setHeader "Vary" "Accept, Accept-Language" setHeader "Vary" "Accept, Accept-Language"
route <- getCurrentRoute route <- getCurrentRoute
@ -297,9 +300,9 @@ $doctype 5
handler handler
-- | Convert a widget to a 'PageContent'. -- | Convert a widget to a 'PageContent'.
widgetToPageContent :: (Eq (Route master), Yesod master) widgetToPageContent :: (Eq (Route site), Yesod site)
=> GWidget sub master () => GWidget site ()
-> GHandler sub master (PageContent (Route master)) -> GHandler site (PageContent (Route site))
widgetToPageContent w = do widgetToPageContent w = do
master <- getYesod master <- getYesod
((), GWData (Body body) (Last mTitle) scripts' stylesheets' style jscript (Head head')) <- unGWidget w ((), GWData (Body body) (Last mTitle) scripts' stylesheets' style jscript (Head head')) <- unGWidget w
@ -393,7 +396,7 @@ $newline never
runUniqueList (UniqueList x) = nub $ x [] runUniqueList (UniqueList x) = nub $ x []
-- | The default error handler for 'errorHandler'. -- | The default error handler for 'errorHandler'.
defaultErrorHandler :: Yesod y => ErrorResponse -> GHandler sub y TypedContent defaultErrorHandler :: Yesod site => ErrorResponse -> GHandler site TypedContent
defaultErrorHandler NotFound = selectRep $ do defaultErrorHandler NotFound = selectRep $ do
provideRep $ defaultLayout $ do provideRep $ defaultLayout $ do
r <- lift waiRequest r <- lift waiRequest
@ -557,3 +560,14 @@ fileLocationToString loc = (loc_package loc) ++ ':' : (loc_module loc) ++
where where
line = show . fst . loc_start line = show . fst . loc_start
char = show . snd . loc_start char = show . snd . loc_start
class (MonadBaseControl IO m, HandlerState m, HandlerError m, MonadResource m, Yesod (HandlerMaster m)) => MonadHandler m where
liftHandler :: GHandler (HandlerMaster m) a -> m a
askHandlerData :: m (HandlerData (HandlerSite m))
instance Yesod site => MonadHandler (GHandler site) where
liftHandler = id
askHandlerData = GHandler return
instance MonadHandler m => MonadHandler (HandlerT site m) where
liftHandler = lift . liftHandler
askHandlerData = HandlerT return

View File

@ -122,8 +122,8 @@ mkYesodGeneral name args clazzes isSub resS = do
context = if isSub then cxt $ yesod : map return clazzes context = if isSub then cxt $ yesod : map return clazzes
else return [] else return []
yesod = classP ''HandlerReader [master] yesod = classP ''HandlerReader [master]
handler = tySynD (mkName "Handler") [] [t| GHandler $master $master |] handler = tySynD (mkName "Handler") [] [t| GHandler $master |]
widget = tySynD (mkName "Widget") [] [t| GWidget $master $master () |] widget = tySynD (mkName "Widget") [] [t| GWidget $master () |]
res = map (fmap parseType) resS res = map (fmap parseType) resS
subCons = conT $ mkName name subCons = conT $ mkName name
subArgs = map (varT. mkName) args subArgs = map (varT. mkName) args
@ -139,7 +139,7 @@ mkDispatchInstance :: CxtQ -- ^ The context
-> [ResourceTree a] -- ^ The resource -> [ResourceTree a] -- ^ The resource
-> DecsQ -> DecsQ
mkDispatchInstance context _sub master res = do mkDispatchInstance context _sub master res = do
let yDispatch = conT ''YesodDispatch `appT` master `appT` master let yDispatch = conT ''YesodDispatch `appT` master
thisDispatch = do thisDispatch = do
clause' <- mkDispatchClause MkDispatchSettings clause' <- mkDispatchClause MkDispatchSettings
{ mdsRunHandler = [|yesodRunner|] { mdsRunHandler = [|yesodRunner|]
@ -199,38 +199,30 @@ mkYesodSubDispatch res = do
-- handler. This is the same as 'toWaiAppPlain', except it includes two -- handler. This is the same as 'toWaiAppPlain', except it includes two
-- middlewares: GZIP compression and autohead. This is the -- middlewares: GZIP compression and autohead. This is the
-- recommended approach for most users. -- recommended approach for most users.
toWaiApp :: ( Yesod master toWaiApp :: YesodDispatch site => site -> IO W.Application
, YesodDispatch master master
) => master -> IO W.Application
toWaiApp y = gzip (gzipSettings y) . autohead <$> toWaiAppPlain y toWaiApp y = gzip (gzipSettings y) . autohead <$> toWaiAppPlain y
-- | 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 differs from 'toWaiApp' in that it uses no middlewares. -- handler. This differs from 'toWaiApp' in that it uses no middlewares.
toWaiAppPlain :: ( Yesod master toWaiAppPlain :: YesodDispatch site => site -> IO W.Application
, YesodDispatch master master
) => master -> IO W.Application
toWaiAppPlain a = toWaiApp' a <$> getLogger a <*> makeSessionBackend a toWaiAppPlain a = toWaiApp' a <$> getLogger a <*> makeSessionBackend a
toWaiApp' :: ( Yesod master toWaiApp' :: YesodDispatch site
, YesodDispatch master master => site
)
=> master
-> Logger -> Logger
-> Maybe SessionBackend -> Maybe SessionBackend
-> W.Application -> W.Application
toWaiApp' y logger sb req = toWaiApp' site logger sb req =
case cleanPath y $ W.pathInfo req of case cleanPath site $ W.pathInfo req of
Left pieces -> sendRedirect y pieces req Left pieces -> sendRedirect site pieces req
Right pieces -> yesodDispatch yre req Right pieces -> yesodDispatch yre req
{ W.pathInfo = pieces { W.pathInfo = pieces
} }
where where
yre = YesodRunnerEnv yre = YesodRunnerEnv
{ yreLogger = logger { yreLogger = logger
, yreMaster = y , yreSite = site
, yreSub = y
, yreToMaster = id
, yreSessionBackend = sb , yreSessionBackend = sb
} }

View File

@ -25,12 +25,9 @@ module Yesod.Core.Handler
, HandlerT , HandlerT
-- ** Read information from handler -- ** Read information from handler
, getYesod , getYesod
, getYesodSub
, getUrlRender , getUrlRender
, getUrlRenderParams , getUrlRenderParams
, getCurrentRoute , getCurrentRoute
, getCurrentRouteSub
, getRouteToMaster
, getRequest , getRequest
, waiRequest , waiRequest
, runRequestBody , runRequestBody
@ -132,7 +129,7 @@ import Yesod.Core.Internal.Request (langKey, mkFileInfoFile,
import Control.Applicative ((<$>), (<|>)) import Control.Applicative ((<$>), (<|>))
import Control.Monad (ap, liftM) import Control.Monad (liftM)
import qualified Control.Monad.Trans.Writer as Writer import qualified Control.Monad.Trans.Writer as Writer
import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.IO.Class (MonadIO, liftIO)
@ -233,16 +230,12 @@ rbHelper' backend mkFI req =
| otherwise = a' | otherwise = a'
go = decodeUtf8With lenientDecode go = decodeUtf8With lenientDecode
-- | Get the sub application argument.
getYesodSub :: HandlerReader m => m (HandlerSub m)
getYesodSub = rheSub `liftM` askHandlerEnv
-- | Get the master site appliation argument. -- | Get the master site appliation argument.
getYesod :: HandlerReader m => m (HandlerMaster m) getYesod :: HandlerReader m => m (HandlerSite m)
getYesod = rheMaster `liftM` askHandlerEnv getYesod = rheSite `liftM` askHandlerEnv
-- | Get the URL rendering function. -- | Get the URL rendering function.
getUrlRender :: HandlerReader m => m (Route (HandlerMaster m) -> Text) getUrlRender :: HandlerReader m => m (Route (HandlerSite m) -> Text)
getUrlRender = do getUrlRender = do
x <- rheRender `liftM` askHandlerEnv x <- rheRender `liftM` askHandlerEnv
return $ flip x [] return $ flip x []
@ -250,23 +243,13 @@ getUrlRender = do
-- | The URL rendering function with query-string parameters. -- | The URL rendering function with query-string parameters.
getUrlRenderParams getUrlRenderParams
:: HandlerReader m :: HandlerReader m
=> m (Route (HandlerMaster m) -> [(Text, Text)] -> Text) => m (Route (HandlerSite m) -> [(Text, Text)] -> Text)
getUrlRenderParams = rheRender `liftM` askHandlerEnv 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 :: HandlerReader m => m (Maybe (Route (HandlerMaster m))) getCurrentRoute :: HandlerReader m => m (Maybe (Route (HandlerSite m)))
getCurrentRoute = fmap `liftM` getRouteToMaster `ap` getCurrentRouteSub getCurrentRoute = rheRoute `liftM` askHandlerEnv
-- | Same as 'getCurrentRoute', but for the subsite.
getCurrentRouteSub :: HandlerReader m => m (Maybe (Route (HandlerSub m)))
getCurrentRouteSub = rheRoute `liftM` askHandlerEnv
-- | Get the function to promote a route for a subsite to a route for the
-- master site.
getRouteToMaster :: HandlerReader m => m (Route (HandlerSub m) -> Route (HandlerMaster m))
getRouteToMaster = rheToMaster `liftM` askHandlerEnv
-- | Returns a function that runs 'GHandler' actions inside @IO@. -- | Returns a function that runs 'GHandler' actions inside @IO@.
-- --
@ -304,7 +287,7 @@ getRouteToMaster = rheToMaster `liftM` askHandlerEnv
-- This allows the inner 'GHandler' to outlive the outer -- This allows the inner 'GHandler' to outlive the outer
-- 'GHandler' (e.g., on the @forkIO@ example above, a response -- 'GHandler' (e.g., on the @forkIO@ example above, a response
-- may be sent to the client without killing the new thread). -- may be sent to the client without killing the new thread).
handlerToIO :: MonadIO m => GHandler sub master (GHandler sub master a -> m a) handlerToIO :: MonadIO m => GHandler site (GHandler site a -> m a)
handlerToIO = handlerToIO =
GHandler $ \oldHandlerData -> do GHandler $ \oldHandlerData -> do
-- Let go of the request body, cache and response headers. -- Let go of the request body, cache and response headers.
@ -344,7 +327,7 @@ handlerToIO =
-- --
-- If you want direct control of the final status code, or need a different -- If you want direct control of the final status code, or need a different
-- status code, please use 'redirectWith'. -- status code, please use 'redirectWith'.
redirect :: (HandlerError m, RedirectUrl (HandlerMaster m) url, HandlerReader m) redirect :: (HandlerError m, RedirectUrl (HandlerSite m) url, HandlerReader m)
=> url -> m a => url -> m a
redirect url = do redirect url = do
req <- waiRequest req <- waiRequest
@ -355,7 +338,7 @@ redirect url = do
redirectWith status url redirectWith status url
-- | Redirect to the given URL with the specified status code. -- | Redirect to the given URL with the specified status code.
redirectWith :: (HandlerError m, RedirectUrl (HandlerMaster m) url, HandlerReader m) redirectWith :: (HandlerError m, RedirectUrl (HandlerSite m) url, HandlerReader m)
=> H.Status => H.Status
-> url -> url
-> m a -> m a
@ -370,7 +353,7 @@ ultDestKey = "_ULT"
-- --
-- An ultimate destination is stored in the user session and can be loaded -- An ultimate destination is stored in the user session and can be loaded
-- later by 'redirectUltDest'. -- later by 'redirectUltDest'.
setUltDest :: (HandlerState m, RedirectUrl (HandlerMaster m) url) setUltDest :: (HandlerState m, RedirectUrl (HandlerSite m) url)
=> url => url
-> m () -> m ()
setUltDest url = do setUltDest url = do
@ -410,7 +393,7 @@ setUltDestReferer = do
-- --
-- This function uses 'redirect', and thus will perform a temporary redirect to -- This function uses 'redirect', and thus will perform a temporary redirect to
-- a GET request. -- a GET request.
redirectUltDest :: (RedirectUrl (HandlerMaster m) url, HandlerState m, HandlerError m) redirectUltDest :: (RedirectUrl (HandlerSite m) url, HandlerState m, HandlerError m)
=> url -- ^ default destination if nothing in session => url -- ^ default destination if nothing in session
-> m a -> m a
redirectUltDest def = do redirectUltDest def = do
@ -434,7 +417,7 @@ setMessage = setSession msgKey . T.concat . TL.toChunks . RenderText.renderHtml
-- | Sets a message in the user's session. -- | Sets a message in the user's session.
-- --
-- See 'getMessage'. -- See 'getMessage'.
setMessageI :: (HandlerState m, RenderMessage (HandlerMaster m) msg) setMessageI :: (HandlerState m, RenderMessage (HandlerSite m) msg)
=> msg -> m () => msg -> m ()
setMessageI msg = do setMessageI msg = do
mr <- getMessageRender mr <- getMessageRender
@ -479,7 +462,7 @@ sendResponseStatus s = handlerError . HCContent s . toTypedContent
-- | Send a 201 "Created" response with the given route as the Location -- | Send a 201 "Created" response with the given route as the Location
-- response header. -- response header.
sendResponseCreated :: HandlerError m => Route (HandlerMaster m) -> m a sendResponseCreated :: HandlerError m => Route (HandlerSite m) -> m a
sendResponseCreated url = do sendResponseCreated url = do
r <- getUrlRender r <- getUrlRender
handlerError $ HCCreated $ r url handlerError $ HCCreated $ r url
@ -507,7 +490,7 @@ permissionDenied :: HandlerError m => Text -> m a
permissionDenied = hcError . PermissionDenied permissionDenied = hcError . PermissionDenied
-- | Return a 403 permission denied page. -- | Return a 403 permission denied page.
permissionDeniedI :: (RenderMessage (HandlerMaster m) msg, HandlerError m) permissionDeniedI :: (RenderMessage (HandlerSite m) msg, HandlerError m)
=> msg => msg
-> m a -> m a
permissionDeniedI msg = do permissionDeniedI msg = do
@ -519,7 +502,7 @@ invalidArgs :: HandlerError m => [Text] -> m a
invalidArgs = hcError . InvalidArgs invalidArgs = hcError . InvalidArgs
-- | Return a 400 invalid arguments page. -- | Return a 400 invalid arguments page.
invalidArgsI :: (HandlerError m, RenderMessage (HandlerMaster m) msg) => [msg] -> m a invalidArgsI :: (HandlerError m, RenderMessage (HandlerSite m) msg) => [msg] -> m a
invalidArgsI msg = do invalidArgsI msg = do
mr <- getMessageRender mr <- getMessageRender
invalidArgs $ map mr msg invalidArgs $ map mr msg
@ -623,7 +606,7 @@ addHeader = tell . Endo . (:)
-- | Some value which can be turned into a URL for redirects. -- | Some value which can be turned into a URL for redirects.
class RedirectUrl master a where class RedirectUrl master a where
-- | Converts the value to the URL and a list of query-string parameters. -- | Converts the value to the URL and a list of query-string parameters.
toTextUrl :: (HandlerReader m, HandlerMaster m ~ master) => a -> m Text toTextUrl :: (HandlerReader m, HandlerSite m ~ master) => a -> m Text
instance RedirectUrl master Text where instance RedirectUrl master Text where
toTextUrl = return toTextUrl = return
@ -672,7 +655,7 @@ newIdent = do
-- POST form, and some Javascript to automatically submit the form. This can be -- POST form, and some Javascript to automatically submit the form. This can be
-- useful when you need to post a plain link somewhere that needs to cause -- useful when you need to post a plain link somewhere that needs to cause
-- changes on the server. -- changes on the server.
redirectToPost :: (HandlerError m, RedirectUrl (HandlerMaster m) url) redirectToPost :: (HandlerError m, RedirectUrl (HandlerSite m) url)
=> url => url
-> m a -> m a
redirectToPost url = do redirectToPost url = do
@ -692,7 +675,7 @@ $doctype 5
|] >>= sendResponse |] >>= sendResponse
-- | Wraps the 'Content' generated by 'hamletToContent' in a 'RepHtml'. -- | Wraps the 'Content' generated by 'hamletToContent' in a 'RepHtml'.
hamletToRepHtml :: HandlerReader m => HtmlUrl (Route (HandlerMaster m)) -> m Html hamletToRepHtml :: HandlerReader m => HtmlUrl (Route (HandlerSite m)) -> m Html
hamletToRepHtml = giveUrlRenderer hamletToRepHtml = giveUrlRenderer
-- | Provide a URL rendering function to the given function and return the -- | Provide a URL rendering function to the given function and return the
@ -700,7 +683,7 @@ hamletToRepHtml = giveUrlRenderer
-- --
-- Since 1.2.0 -- Since 1.2.0
giveUrlRenderer :: HandlerReader m giveUrlRenderer :: HandlerReader m
=> ((Route (HandlerMaster m) -> [(Text, Text)] -> Text) -> output) => ((Route (HandlerSite m) -> [(Text, Text)] -> Text) -> output)
-> m output -> m output
giveUrlRenderer f = do giveUrlRenderer f = do
render <- getUrlRenderParams render <- getUrlRenderParams
@ -710,7 +693,7 @@ giveUrlRenderer f = do
waiRequest :: HandlerReader m => m W.Request waiRequest :: HandlerReader m => m W.Request
waiRequest = reqWaiRequest `liftM` getRequest waiRequest = reqWaiRequest `liftM` getRequest
getMessageRender :: (HandlerReader m, RenderMessage (HandlerMaster m) message) getMessageRender :: (HandlerReader m, RenderMessage (HandlerSite m) message)
=> m (message -> Text) => m (message -> Text)
getMessageRender = do getMessageRender = do
m <- getYesod m <- getYesod

View File

@ -11,10 +11,9 @@ import Yesod.Core.Class.Handler
import Blaze.ByteString.Builder (toByteString) import Blaze.ByteString.Builder (toByteString)
import Control.Applicative ((<$>)) import Control.Applicative ((<$>))
import Control.Exception (fromException) import Control.Exception (fromException)
import Control.Exception.Lifted (catch, finally) import Control.Exception.Lifted (catch)
import Control.Monad.IO.Class (MonadIO) import Control.Monad.IO.Class (MonadIO)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Control.Monad.Base (liftBase)
import Control.Monad.Logger (LogLevel (LevelError), LogSource, import Control.Monad.Logger (LogLevel (LevelError), LogSource,
liftLoc) liftLoc)
import Control.Monad.Trans.Resource (runResourceT) import Control.Monad.Trans.Resource (runResourceT)
@ -48,8 +47,8 @@ import Yesod.Routes.Class (Route, renderRoute)
-- | 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 'Application'. Should not be needed by users. -- 'GHandler' into an 'Application'. Should not be needed by users.
runHandler :: ToTypedContent c runHandler :: ToTypedContent c
=> RunHandlerEnv sub master => RunHandlerEnv site
-> GHandler sub master c -> GHandler site c
-> YesodApp -> YesodApp
runHandler rhe@RunHandlerEnv {..} handler yreq = do runHandler rhe@RunHandlerEnv {..} handler yreq = do
let toErrorHandler e = let toErrorHandler e =
@ -149,25 +148,23 @@ safeEh log' er req = do
-- @GHandler@ is completely ignored, including changes to the -- @GHandler@ is completely ignored, including changes to the
-- session, cookies or headers. We only return you the -- session, cookies or headers. We only return you the
-- @GHandler@'s return value. -- @GHandler@'s return value.
runFakeHandler :: (Yesod master, MonadIO m) => runFakeHandler :: (Yesod site, MonadIO m) =>
SessionMap SessionMap
-> (master -> Logger) -> (site -> Logger)
-> master -> site
-> GHandler master master a -> GHandler site a
-> m (Either ErrorResponse a) -> m (Either ErrorResponse a)
runFakeHandler fakeSessionMap logger master handler = liftIO $ do runFakeHandler fakeSessionMap logger site handler = liftIO $ do
ret <- I.newIORef (Left $ InternalError "runFakeHandler: no result") ret <- I.newIORef (Left $ InternalError "runFakeHandler: no result")
let handler' = do liftIO . I.writeIORef ret . Right =<< handler let handler' = do liftIO . I.writeIORef ret . Right =<< handler
return () return ()
let yapp = runHandler let yapp = runHandler
RunHandlerEnv RunHandlerEnv
{ rheRender = yesodRender master $ resolveApproot master fakeWaiRequest { rheRender = yesodRender site $ resolveApproot site fakeWaiRequest
, rheRoute = Nothing , rheRoute = Nothing
, rheToMaster = id , rheSite = site
, rheMaster = master , rheUpload = fileUpload site
, rheSub = master , rheLog = messageLoggerSource site $ logger site
, rheUpload = fileUpload master
, rheLog = messageLoggerSource master $ logger master
, rheOnError = errHandler , rheOnError = errHandler
} }
handler' handler'
@ -210,10 +207,10 @@ runFakeHandler fakeSessionMap logger master handler = liftIO $ do
I.readIORef ret I.readIORef ret
{-# WARNING runFakeHandler "Usually you should *not* use runFakeHandler unless you really understand how it works and why you need it." #-} {-# WARNING runFakeHandler "Usually you should *not* use runFakeHandler unless you really understand how it works and why you need it." #-}
yesodRunner :: (ToTypedContent res, Yesod master) yesodRunner :: (ToTypedContent res, Yesod site)
=> GHandler sub master res => GHandler site res
-> YesodRunnerEnv sub master -> YesodRunnerEnv site
-> Maybe (Route sub) -> Maybe (Route site)
-> Application -> Application
yesodRunner handler' YesodRunnerEnv {..} route req yesodRunner handler' YesodRunnerEnv {..} route req
| KnownLength len <- requestBodyLength req, maxLen < len = return tooLargeResponse | KnownLength len <- requestBodyLength req, maxLen < len = return tooLargeResponse
@ -226,19 +223,17 @@ yesodRunner handler' YesodRunnerEnv {..} route req
case mkYesodReq of case mkYesodReq of
Left yreq -> return yreq Left yreq -> return yreq
Right needGen -> liftIO $ needGen <$> newStdGen Right needGen -> liftIO $ needGen <$> newStdGen
let ra = resolveApproot yreMaster req let ra = resolveApproot yreSite req
let log' = messageLoggerSource yreMaster yreLogger let log' = messageLoggerSource yreSite yreLogger
-- We set up two environments: the first one has a "safe" error handler -- We set up two environments: the first one has a "safe" error handler
-- which will never throw an exception. The second one uses the -- which will never throw an exception. The second one uses the
-- user-provided errorHandler function. If that errorHandler function -- user-provided errorHandler function. If that errorHandler function
-- errors out, it will use the safeEh below to recover. -- errors out, it will use the safeEh below to recover.
rheSafe = RunHandlerEnv rheSafe = RunHandlerEnv
{ rheRender = yesodRender yreMaster ra { rheRender = yesodRender yreSite ra
, rheRoute = route , rheRoute = route
, rheToMaster = yreToMaster , rheSite = yreSite
, rheMaster = yreMaster , rheUpload = fileUpload yreSite
, rheSub = yreSub
, rheUpload = fileUpload yreMaster
, rheLog = log' , rheLog = log'
, rheOnError = safeEh log' , rheOnError = safeEh log'
} }
@ -248,7 +243,7 @@ yesodRunner handler' YesodRunnerEnv {..} route req
yar <- runHandler rhe handler yreq yar <- runHandler rhe handler yreq
liftIO $ yarToResponse yar saveSession yreq liftIO $ yarToResponse yar saveSession yreq
where where
maxLen = maximumContentLength yreMaster $ fmap yreToMaster route maxLen = maximumContentLength yreSite route
handler = yesodMiddleware handler' handler = yesodMiddleware handler'
yesodRender :: Yesod y yesodRender :: Yesod y
@ -274,38 +269,20 @@ resolveApproot master req =
ApprootMaster f -> f master ApprootMaster f -> f master
ApprootRequest f -> f master req ApprootRequest f -> f master req
fixEnv :: (oldSub -> newSub) stripHandlerT :: (MonadHandler m, MonadBaseControl IO m)
-> (Route newSub -> Route oldSub)
-> YesodRunnerEnv oldSub master
-> YesodRunnerEnv newSub master
fixEnv toNewSub toOldRoute envOld =
envOld
{ yreSub = toNewSub $ yreSub envOld
, yreToMaster = yreToMaster envOld . toOldRoute
}
stripHandlerT :: (HandlerState m, MonadBaseControl IO m)
=> HandlerT sub m a => HandlerT sub m a
-> (HandlerMaster m -> sub) -> (HandlerSite m -> sub)
-> (Route sub -> Route (HandlerMaster m)) -> (Route sub -> Route (HandlerSite m))
-> Maybe (Route sub) -> Maybe (Route sub)
-> m a -> m a
stripHandlerT (HandlerT f) getSub toMaster newRoute = do stripHandlerT (HandlerT f) getSub toMaster newRoute = do
yreq <- askYesodRequest hd <- askHandlerData
env <- askHandlerEnv
ghs <- getGHState
ighs <- liftBase $ I.newIORef ghs
let sub = getSub $ rheMaster env let env = handlerEnv hd
hd = HandlerData f hd
{ handlerRequest = yreq { handlerEnv = env
, handlerEnv = env { rheSite = getSub $ rheSite env
{ rheMaster = sub , rheRoute = newRoute
, rheSub = sub , rheRender = \url params -> rheRender env (toMaster url) params
, rheToMaster = id
, rheRoute = newRoute
, rheRender = \url params -> rheRender env (toMaster url) params
}
, handlerState = ighs
} }
f hd `finally` (liftBase (I.readIORef ighs) >>= putGHState) }

View File

@ -44,10 +44,10 @@ import Data.Maybe (listToMaybe)
-- ('defaultLayout'). -- ('defaultLayout').
-- --
-- /Since: 0.3.0/ -- /Since: 0.3.0/
defaultLayoutJson :: (Yesod master, J.ToJSON a) defaultLayoutJson :: (Yesod site, J.ToJSON a)
=> GWidget sub master () -- ^ HTML => GWidget site () -- ^ HTML
-> GHandler sub master a -- ^ JSON -> GHandler site a -- ^ JSON
-> GHandler sub master TypedContent -> GHandler site TypedContent
defaultLayoutJson w json = selectRep $ do defaultLayoutJson w json = selectRep $ do
provideRep $ defaultLayout w provideRep $ defaultLayout w
provideRep $ fmap J.toJSON json provideRep $ fmap J.toJSON json
@ -56,7 +56,7 @@ defaultLayoutJson w json = selectRep $ do
-- support conversion to JSON via 'J.ToJSON'. -- support conversion to JSON via 'J.ToJSON'.
-- --
-- /Since: 0.3.0/ -- /Since: 0.3.0/
jsonToRepJson :: J.ToJSON a => a -> GHandler sub master J.Value jsonToRepJson :: J.ToJSON a => a -> GHandler site J.Value
jsonToRepJson = return . J.toJSON jsonToRepJson = return . J.toJSON
-- | Parse the request body to a data type as a JSON value. The -- | Parse the request body to a data type as a JSON value. The
@ -65,7 +65,7 @@ jsonToRepJson = return . J.toJSON
-- 'J.Value'@. -- 'J.Value'@.
-- --
-- /Since: 0.3.0/ -- /Since: 0.3.0/
parseJsonBody :: J.FromJSON a => GHandler sub master (J.Result a) parseJsonBody :: J.FromJSON a => GHandler site (J.Result a)
parseJsonBody = do parseJsonBody = do
req <- waiRequest req <- waiRequest
eValue <- lift eValue <- lift
@ -78,7 +78,7 @@ parseJsonBody = do
-- | Same as 'parseJsonBody', but return an invalid args response on a parse -- | Same as 'parseJsonBody', but return an invalid args response on a parse
-- error. -- error.
parseJsonBody_ :: J.FromJSON a => GHandler sub master a parseJsonBody_ :: J.FromJSON a => GHandler site a
parseJsonBody_ = do parseJsonBody_ = do
ra <- parseJsonBody ra <- parseJsonBody
case ra of case ra of
@ -96,10 +96,10 @@ array = J.Array . V.fromList . map J.toJSON
-- @application\/json@ (e.g. AJAX, see 'acceptsJSON'). -- @application\/json@ (e.g. AJAX, see 'acceptsJSON').
-- --
-- 2. 3xx otherwise, following the PRG pattern. -- 2. 3xx otherwise, following the PRG pattern.
jsonOrRedirect :: (Yesod master, J.ToJSON a) jsonOrRedirect :: (Yesod site, J.ToJSON a)
=> Route master -- ^ Redirect target => Route site -- ^ Redirect target
-> a -- ^ Data to send via JSON -> a -- ^ Data to send via JSON
-> GHandler sub master J.Value -> GHandler site J.Value
jsonOrRedirect r j = do jsonOrRedirect r j = do
q <- acceptsJson q <- acceptsJson
if q then jsonToRepJson (J.toJSON j) if q then jsonToRepJson (J.toJSON j)
@ -107,7 +107,7 @@ jsonOrRedirect r j = do
-- | Returns @True@ if the client prefers @application\/json@ as -- | Returns @True@ if the client prefers @application\/json@ as
-- indicated by the @Accept@ HTTP header. -- indicated by the @Accept@ HTTP header.
acceptsJson :: Yesod master => GHandler sub master Bool acceptsJson :: Yesod site => GHandler site Bool
acceptsJson = maybe False ((== "application/json") . B8.takeWhile (/= ';')) acceptsJson = maybe False ((== "application/json") . B8.takeWhile (/= ';'))
. join . join
. fmap (listToMaybe . parseHttpAccept) . fmap (listToMaybe . parseHttpAccept)

View File

@ -166,12 +166,10 @@ type Texts = [Text]
-- | Wrap up a normal WAI application as a Yesod subsite. -- | Wrap up a normal WAI application as a Yesod subsite.
newtype WaiSubsite = WaiSubsite { runWaiSubsite :: W.Application } newtype WaiSubsite = WaiSubsite { runWaiSubsite :: W.Application }
data RunHandlerEnv sub master = RunHandlerEnv data RunHandlerEnv site = RunHandlerEnv
{ rheRender :: !(Route master -> [(Text, Text)] -> Text) { rheRender :: !(Route site -> [(Text, Text)] -> Text)
, rheRoute :: !(Maybe (Route sub)) , rheRoute :: !(Maybe (Route site))
, rheToMaster :: !(Route sub -> Route master) , rheSite :: !site
, rheMaster :: !master
, rheSub :: !sub
, rheUpload :: !(RequestBodyLength -> FileUpload) , rheUpload :: !(RequestBodyLength -> FileUpload)
, rheLog :: !(Loc -> LogSource -> LogLevel -> LogStr -> IO ()) , rheLog :: !(Loc -> LogSource -> LogLevel -> LogStr -> IO ())
, rheOnError :: !(ErrorResponse -> YesodApp) , rheOnError :: !(ErrorResponse -> YesodApp)
@ -180,28 +178,26 @@ data RunHandlerEnv sub master = RunHandlerEnv
-- Since 1.2.0 -- Since 1.2.0
} }
data HandlerData sub master = HandlerData data HandlerData site = HandlerData
{ handlerRequest :: !YesodRequest { handlerRequest :: !YesodRequest
, handlerEnv :: !(RunHandlerEnv sub master) , handlerEnv :: !(RunHandlerEnv site)
, handlerState :: !(IORef GHState) , handlerState :: !(IORef GHState)
} }
data YesodRunnerEnv sub master = YesodRunnerEnv data YesodRunnerEnv site = YesodRunnerEnv
{ yreLogger :: !Logger { yreLogger :: !Logger
, yreMaster :: !master , yreSite :: !site
, yreSub :: !sub
, yreToMaster :: !(Route sub -> Route master)
, yreSessionBackend :: !(Maybe SessionBackend) , yreSessionBackend :: !(Maybe SessionBackend)
} }
-- | A generic handler monad, which can have a different subsite and master -- | A generic handler monad, which can have a different subsite and master
-- site. We define a newtype for better error message. -- site. We define a newtype for better error message.
newtype GHandler sub master a = GHandler newtype GHandler site a = GHandler
{ unGHandler :: HandlerData sub master -> ResourceT IO a { unGHandler :: HandlerData site -> ResourceT IO a
} }
newtype HandlerT sub m a = HandlerT newtype HandlerT site m a = HandlerT
{ unHandlerT :: HandlerData sub sub -> m a { unHandlerT :: HandlerData site -> m a
} }
instance Monad m => Monad (HandlerT sub m) where instance Monad m => Monad (HandlerT sub m) where
@ -229,11 +225,11 @@ type YesodApp = YesodRequest -> ResourceT IO YesodResponse
-- | A generic widget, allowing specification of both the subsite and master -- | A generic widget, allowing specification of both the subsite and master
-- site datatypes. While this is simply a @WriterT@, we define a newtype for -- site datatypes. While this is simply a @WriterT@, we define a newtype for
-- better error messages. -- better error messages.
newtype GWidget sub master a = GWidget newtype GWidget site a = GWidget -- FIXME change to WidgetT?
{ unGWidget :: GHandler sub master (a, GWData (Route master)) { unGWidget :: GHandler site (a, GWData (Route site))
} }
instance (a ~ ()) => Monoid (GWidget sub master a) where instance (a ~ ()) => Monoid (GWidget site a) where
mempty = return () mempty = return ()
mappend x y = x >> y mappend x y = x >> y
@ -349,60 +345,60 @@ instance Show HandlerContents where
instance Exception HandlerContents instance Exception HandlerContents
-- Instances for GWidget -- Instances for GWidget
instance Functor (GWidget sub master) where instance Functor (GWidget site) where
fmap f (GWidget x) = GWidget (fmap (first f) x) fmap f (GWidget x) = GWidget (fmap (first f) x)
instance Applicative (GWidget sub master) where instance Applicative (GWidget site) where
pure a = GWidget $ pure (a, mempty) pure a = GWidget $ pure (a, mempty)
GWidget f <*> GWidget v = GWidget f <*> GWidget v =
GWidget $ k <$> f <*> v GWidget $ k <$> f <*> v
where where
k (a, wa) (b, wb) = (a b, wa `mappend` wb) k (a, wa) (b, wb) = (a b, wa `mappend` wb)
instance Monad (GWidget sub master) where instance Monad (GWidget site) where
return = pure return = pure
GWidget x >>= f = GWidget $ do GWidget x >>= f = GWidget $ do
(a, wa) <- x (a, wa) <- x
(b, wb) <- unGWidget (f a) (b, wb) <- unGWidget (f a)
return (b, wa `mappend` wb) return (b, wa `mappend` wb)
instance MonadIO (GWidget sub master) where instance MonadIO (GWidget site) where
liftIO = GWidget . fmap (\a -> (a, mempty)) . liftIO liftIO = GWidget . fmap (\a -> (a, mempty)) . liftIO
instance MonadBase IO (GWidget sub master) where instance MonadBase IO (GWidget site) where
liftBase = GWidget . fmap (\a -> (a, mempty)) . liftBase liftBase = GWidget . fmap (\a -> (a, mempty)) . liftBase
instance MonadBaseControl IO (GWidget sub master) where instance MonadBaseControl IO (GWidget site) where
data StM (GWidget sub master) a = data StM (GWidget site) a =
StW (StM (GHandler sub master) (a, GWData (Route master))) StW (StM (GHandler site) (a, GWData (Route site)))
liftBaseWith f = GWidget $ liftBaseWith $ \runInBase -> liftBaseWith f = GWidget $ liftBaseWith $ \runInBase ->
liftM (\x -> (x, mempty)) liftM (\x -> (x, mempty))
(f $ liftM StW . runInBase . unGWidget) (f $ liftM StW . runInBase . unGWidget)
restoreM (StW base) = GWidget $ restoreM base restoreM (StW base) = GWidget $ restoreM base
instance MonadUnsafeIO (GWidget sub master) where instance MonadUnsafeIO (GWidget site) where
unsafeLiftIO = liftIO unsafeLiftIO = liftIO
instance MonadThrow (GWidget sub master) where instance MonadThrow (GWidget site) where
monadThrow = liftIO . throwIO monadThrow = liftIO . throwIO
instance MonadResource (GWidget sub master) where instance MonadResource (GWidget site) where
liftResourceT = lift . liftResourceT liftResourceT = lift . liftResourceT
instance MonadLogger (GWidget sub master) where instance MonadLogger (GWidget site) where
monadLoggerLog a b c = lift . monadLoggerLog a b c monadLoggerLog a b c = lift . monadLoggerLog a b c
instance MonadLift (GHandler sub master) (GWidget sub master) where instance MonadLift (GHandler site) (GWidget site) where
lift = GWidget . fmap (\x -> (x, mempty)) lift = GWidget . fmap (\x -> (x, mempty))
instance MonadLift (ResourceT IO) (GHandler sub master) where instance MonadLift (ResourceT IO) (GHandler site) where
lift = GHandler . const lift = GHandler . const
-- Instances for GHandler -- Instances for GHandler
instance Functor (GHandler sub master) where instance Functor (GHandler site) where
fmap f (GHandler x) = GHandler $ \r -> fmap f (x r) fmap f (GHandler x) = GHandler $ \r -> fmap f (x r)
instance Applicative (GHandler sub master) where instance Applicative (GHandler site) where
pure = GHandler . const . pure pure = GHandler . const . pure
GHandler f <*> GHandler x = GHandler $ \r -> f r <*> x r GHandler f <*> GHandler x = GHandler $ \r -> f r <*> x r
instance Monad (GHandler sub master) where instance Monad (GHandler site) where
return = pure return = pure
GHandler x >>= f = GHandler $ \r -> x r >>= \x' -> unGHandler (f x') r GHandler x >>= f = GHandler $ \r -> x r >>= \x' -> unGHandler (f x') r
instance MonadIO (GHandler sub master) where instance MonadIO (GHandler site) where
liftIO = GHandler . const . lift liftIO = GHandler . const . lift
instance MonadBase IO (GHandler sub master) where instance MonadBase IO (GHandler site) where
liftBase = GHandler . const . lift liftBase = GHandler . const . lift
-- | Note: although we provide a @MonadBaseControl@ instance, @lifted-base@'s -- | Note: although we provide a @MonadBaseControl@ instance, @lifted-base@'s
-- @fork@ function is incompatible with the underlying @ResourceT@ system. -- @fork@ function is incompatible with the underlying @ResourceT@ system.
@ -412,25 +408,25 @@ instance MonadBase IO (GHandler sub master) where
-- Using fork usually leads to an exception that says -- Using fork usually leads to an exception that says
-- \"Control.Monad.Trans.Resource.register\': The mutable state is being accessed -- \"Control.Monad.Trans.Resource.register\': The mutable state is being accessed
-- after cleanup. Please contact the maintainers.\" -- after cleanup. Please contact the maintainers.\"
instance MonadBaseControl IO (GHandler sub master) where instance MonadBaseControl IO (GHandler site) where
data StM (GHandler sub master) a = StH (StM (ResourceT IO) a) data StM (GHandler site) a = StH (StM (ResourceT IO) a)
liftBaseWith f = GHandler $ \reader -> liftBaseWith f = GHandler $ \reader ->
liftBaseWith $ \runInBase -> liftBaseWith $ \runInBase ->
f $ liftM StH . runInBase . (\(GHandler r) -> r reader) f $ liftM StH . runInBase . (\(GHandler r) -> r reader)
restoreM (StH base) = GHandler $ const $ restoreM base restoreM (StH base) = GHandler $ const $ restoreM base
instance MonadUnsafeIO (GHandler sub master) where instance MonadUnsafeIO (GHandler site) where
unsafeLiftIO = liftIO unsafeLiftIO = liftIO
instance MonadThrow (GHandler sub master) where instance MonadThrow (GHandler site) where
monadThrow = liftIO . throwIO monadThrow = liftIO . throwIO
instance MonadResource (GHandler sub master) where instance MonadResource (GHandler site) where
liftResourceT = lift . liftResourceT liftResourceT = lift . liftResourceT
instance MonadLogger (GHandler sub master) where instance MonadLogger (GHandler site) where
monadLoggerLog a b c d = GHandler $ \hd -> monadLoggerLog a b c d = GHandler $ \hd ->
liftIO $ rheLog (handlerEnv hd) a b c (toLogStr d) liftIO $ rheLog (handlerEnv hd) a b c (toLogStr d)
instance Exception e => Failure e (GHandler sub master) where instance Exception e => Failure e (GHandler site) where
failure = liftIO . throwIO failure = liftIO . throwIO
instance Monoid (UniqueList x) where instance Monoid (UniqueList x) where

View File

@ -25,8 +25,6 @@ module Yesod.Core.Widget
-- ** Head of page -- ** Head of page
, setTitle , setTitle
, setTitleI , setTitleI
-- ** Body
, addSubWidget
-- ** CSS -- ** CSS
, addStylesheet , addStylesheet
, addStylesheetAttrs , addStylesheetAttrs
@ -70,121 +68,109 @@ import Yesod.Core.Types
preEscapedLazyText :: TL.Text -> Html preEscapedLazyText :: TL.Text -> Html
preEscapedLazyText = preEscapedToMarkup preEscapedLazyText = preEscapedToMarkup
addSubWidget :: (Route sub -> Route master) -> sub -> GWidget sub master a -> GWidget sub' master a class ToWidget site a where
addSubWidget toMaster sub (GWidget (GHandler f)) = toWidget :: a -> GWidget site ()
GWidget $ GHandler $ f . modHD
where
modHD hd = hd
{ handlerEnv = (handlerEnv hd)
{ rheRoute = Nothing
, rheSub = sub
, rheToMaster = toMaster
}
}
class ToWidget sub master a where instance render ~ RY site => ToWidget site (render -> Html) where
toWidget :: a -> GWidget sub master ()
instance render ~ RY master => ToWidget sub master (render -> Html) where
toWidget x = tell $ GWData (Body x) mempty mempty mempty mempty mempty mempty toWidget x = tell $ GWData (Body x) mempty mempty mempty mempty mempty mempty
instance render ~ RY master => ToWidget sub master (render -> Css) where instance render ~ RY site => ToWidget site (render -> Css) where
toWidget x = toWidget $ CssBuilder . fromLazyText . renderCss . x toWidget x = toWidget $ CssBuilder . fromLazyText . renderCss . x
instance render ~ RY master => ToWidget sub master (render -> CssBuilder) where instance render ~ RY site => ToWidget site (render -> CssBuilder) where
toWidget x = tell $ GWData mempty mempty mempty mempty (Map.singleton Nothing $ unCssBuilder . x) mempty mempty toWidget x = tell $ GWData mempty mempty mempty mempty (Map.singleton Nothing $ unCssBuilder . x) mempty mempty
instance render ~ RY master => ToWidget sub master (render -> Javascript) where instance render ~ RY site => ToWidget site (render -> Javascript) where
toWidget x = tell $ GWData mempty mempty mempty mempty mempty (Just x) mempty toWidget x = tell $ GWData mempty mempty mempty mempty mempty (Just x) mempty
instance (sub' ~ sub, master' ~ master) => ToWidget sub' master' (GWidget sub master ()) where instance (site' ~ site) => ToWidget site' (GWidget site ()) where
toWidget = id toWidget = id
instance ToWidget sub master Html where instance ToWidget site Html where
toWidget = toWidget . const toWidget = toWidget . const
-- | Allows adding some CSS to the page with a specific media type. -- | Allows adding some CSS to the page with a specific media type.
-- --
-- Since 1.2 -- Since 1.2
class ToWidgetMedia sub master a where class ToWidgetMedia site a where
-- | Add the given content to the page, but only for the given media type. -- | Add the given content to the page, but only for the given media type.
-- --
-- Since 1.2 -- Since 1.2
toWidgetMedia :: Text -- ^ media value toWidgetMedia :: Text -- ^ media value
-> a -> a
-> GWidget sub master () -> GWidget site ()
instance render ~ RY master => ToWidgetMedia sub master (render -> Css) where instance render ~ RY site => ToWidgetMedia site (render -> Css) where
toWidgetMedia media x = toWidgetMedia media $ CssBuilder . fromLazyText . renderCss . x toWidgetMedia media x = toWidgetMedia media $ CssBuilder . fromLazyText . renderCss . x
instance render ~ RY master => ToWidgetMedia sub master (render -> CssBuilder) where instance render ~ RY site => ToWidgetMedia site (render -> CssBuilder) where
toWidgetMedia media x = tell $ GWData mempty mempty mempty mempty (Map.singleton (Just media) $ unCssBuilder . x) mempty mempty toWidgetMedia media x = tell $ GWData mempty mempty mempty mempty (Map.singleton (Just media) $ unCssBuilder . x) mempty mempty
class ToWidgetBody sub master a where class ToWidgetBody site a where
toWidgetBody :: a -> GWidget sub master () toWidgetBody :: a -> GWidget site ()
instance render ~ RY master => ToWidgetBody sub master (render -> Html) where instance render ~ RY site => ToWidgetBody site (render -> Html) where
toWidgetBody = toWidget toWidgetBody = toWidget
instance render ~ RY master => ToWidgetBody sub master (render -> Javascript) where instance render ~ RY site => ToWidgetBody site (render -> Javascript) where
toWidgetBody j = toWidget $ \r -> H.script $ preEscapedLazyText $ renderJavascriptUrl r j toWidgetBody j = toWidget $ \r -> H.script $ preEscapedLazyText $ renderJavascriptUrl r j
instance ToWidgetBody sub master Html where instance ToWidgetBody site Html where
toWidgetBody = toWidget toWidgetBody = toWidget
class ToWidgetHead sub master a where class ToWidgetHead site a where
toWidgetHead :: a -> GWidget sub master () toWidgetHead :: a -> GWidget site ()
instance render ~ RY master => ToWidgetHead sub master (render -> Html) where instance render ~ RY site => ToWidgetHead site (render -> Html) where
toWidgetHead = tell . GWData mempty mempty mempty mempty mempty mempty . Head toWidgetHead = tell . GWData mempty mempty mempty mempty mempty mempty . Head
instance render ~ RY master => ToWidgetHead sub master (render -> Css) where instance render ~ RY site => ToWidgetHead site (render -> Css) where
toWidgetHead = toWidget toWidgetHead = toWidget
instance render ~ RY master => ToWidgetHead sub master (render -> CssBuilder) where instance render ~ RY site => ToWidgetHead site (render -> CssBuilder) where
toWidgetHead = toWidget toWidgetHead = toWidget
instance render ~ RY master => ToWidgetHead sub master (render -> Javascript) where instance render ~ RY site => ToWidgetHead site (render -> Javascript) where
toWidgetHead j = toWidgetHead $ \r -> H.script $ preEscapedLazyText $ renderJavascriptUrl r j toWidgetHead j = toWidgetHead $ \r -> H.script $ preEscapedLazyText $ renderJavascriptUrl r j
instance ToWidgetHead sub master Html where instance ToWidgetHead site Html where
toWidgetHead = toWidgetHead . const toWidgetHead = toWidgetHead . const
-- | 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 :: Html -> GWidget site ()
setTitle x = tell $ GWData mempty (Last $ Just $ Title x) mempty mempty mempty mempty mempty setTitle x = tell $ GWData mempty (Last $ Just $ Title x) mempty mempty mempty mempty mempty
-- | Set the page title. Calling 'setTitle' multiple times overrides previously -- | Set the page title. Calling 'setTitle' multiple times overrides previously
-- set values. -- set values.
setTitleI :: RenderMessage master msg => msg -> GWidget sub master () setTitleI :: RenderMessage site msg => msg -> GWidget site ()
setTitleI msg = do setTitleI msg = do
mr <- lift getMessageRender mr <- lift getMessageRender
setTitle $ toHtml $ mr msg setTitle $ toHtml $ mr msg
-- | Link to the specified local stylesheet. -- | Link to the specified local stylesheet.
addStylesheet :: Route master -> GWidget sub master () addStylesheet :: Route site -> GWidget site ()
addStylesheet = flip addStylesheetAttrs [] addStylesheet = flip addStylesheetAttrs []
-- | Link to the specified local stylesheet. -- | Link to the specified local stylesheet.
addStylesheetAttrs :: Route master -> [(Text, Text)] -> GWidget sub master () addStylesheetAttrs :: Route site -> [(Text, Text)] -> GWidget site ()
addStylesheetAttrs x y = tell $ GWData mempty mempty mempty (toUnique $ Stylesheet (Local x) y) mempty mempty mempty addStylesheetAttrs x y = tell $ GWData mempty mempty mempty (toUnique $ Stylesheet (Local x) y) mempty mempty mempty
-- | Link to the specified remote stylesheet. -- | Link to the specified remote stylesheet.
addStylesheetRemote :: Text -> GWidget sub master () addStylesheetRemote :: Text -> GWidget site ()
addStylesheetRemote = flip addStylesheetRemoteAttrs [] addStylesheetRemote = flip addStylesheetRemoteAttrs []
-- | Link to the specified remote stylesheet. -- | Link to the specified remote stylesheet.
addStylesheetRemoteAttrs :: Text -> [(Text, Text)] -> GWidget sub master () addStylesheetRemoteAttrs :: Text -> [(Text, Text)] -> GWidget site ()
addStylesheetRemoteAttrs x y = tell $ GWData mempty mempty mempty (toUnique $ Stylesheet (Remote x) y) mempty mempty mempty addStylesheetRemoteAttrs x y = tell $ GWData mempty mempty mempty (toUnique $ Stylesheet (Remote x) y) mempty mempty mempty
addStylesheetEither :: Either (Route master) Text -> GWidget sub master () addStylesheetEither :: Either (Route site) Text -> GWidget site ()
addStylesheetEither = either addStylesheet addStylesheetRemote addStylesheetEither = either addStylesheet addStylesheetRemote
addScriptEither :: Either (Route master) Text -> GWidget sub master () addScriptEither :: Either (Route site) Text -> GWidget site ()
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 :: Route site -> GWidget site ()
addScript = flip addScriptAttrs [] addScript = flip addScriptAttrs []
-- | Link to the specified local script. -- | Link to the specified local script.
addScriptAttrs :: Route master -> [(Text, Text)] -> GWidget sub master () addScriptAttrs :: Route site -> [(Text, Text)] -> GWidget site ()
addScriptAttrs x y = tell $ GWData mempty mempty (toUnique $ Script (Local x) y) mempty mempty mempty mempty addScriptAttrs x y = tell $ GWData mempty mempty (toUnique $ Script (Local x) y) mempty mempty mempty mempty
-- | Link to the specified remote script. -- | Link to the specified remote script.
addScriptRemote :: Text -> GWidget sub master () addScriptRemote :: Text -> GWidget site ()
addScriptRemote = flip addScriptRemoteAttrs [] addScriptRemote = flip addScriptRemoteAttrs []
-- | Link to the specified remote script. -- | Link to the specified remote script.
addScriptRemoteAttrs :: Text -> [(Text, Text)] -> GWidget sub master () addScriptRemoteAttrs :: Text -> [(Text, Text)] -> GWidget site ()
addScriptRemoteAttrs x y = tell $ GWData mempty mempty (toUnique $ Script (Remote x) y) mempty mempty mempty mempty addScriptRemoteAttrs x y = tell $ GWData mempty mempty (toUnique $ Script (Remote x) y) mempty mempty mempty mempty
whamlet :: QuasiQuoter whamlet :: QuasiQuoter
@ -214,20 +200,20 @@ rules = do
return $ NP.HamletRules ah ur $ \_ b -> return $ ah `AppE` b return $ NP.HamletRules ah ur $ \_ b -> return $ ah `AppE` b
-- | Wraps the 'Content' generated by 'hamletToContent' in a 'RepHtml'. -- | Wraps the 'Content' generated by 'hamletToContent' in a 'RepHtml'.
ihamletToRepHtml :: RenderMessage master message ihamletToRepHtml :: RenderMessage site message
=> HtmlUrlI18n message (Route master) => HtmlUrlI18n message (Route site)
-> GHandler sub master Html -> GHandler site Html
ihamletToRepHtml ih = do ihamletToRepHtml ih = do
urender <- getUrlRenderParams urender <- getUrlRenderParams
mrender <- getMessageRender mrender <- getMessageRender
return $ ih (toHtml . mrender) urender return $ ih (toHtml . mrender) urender
tell :: GWData (Route master) -> GWidget sub master () tell :: GWData (Route site) -> GWidget site ()
tell w = GWidget $ return ((), w) tell w = GWidget $ return ((), w)
-- | Type-restricted version of @lift@. Used internally to create better error -- | Type-restricted version of @lift@. Used internally to create better error
-- messages. -- messages.
liftW :: GHandler sub master a -> GWidget sub master a liftW :: GHandler site a -> GWidget site a
liftW = lift liftW = lift
toUnique :: x -> UniqueList x toUnique :: x -> UniqueList x

View File

@ -30,5 +30,5 @@ specs = describe "Test.JsLoader" $ do
res <- request defaultRequest res <- request defaultRequest
assertBody "<!DOCTYPE html>\n<html><head><title></title></head><body><script src=\"load.js\"></script></body></html>" res assertBody "<!DOCTYPE html>\n<html><head><title></title></head><body><script src=\"load.js\"></script></body></html>" res
runner :: (YesodDispatch master master, Yesod master) => master -> Session () -> IO () runner :: YesodDispatch master => master -> Session () -> IO ()
runner app f = toWaiApp app >>= runSession f runner app f = toWaiApp app >>= runSession f

View File

@ -15,16 +15,16 @@ import qualified Data.ByteString.Lazy.Char8 as L8
getSubsite :: a -> Subsite getSubsite :: a -> Subsite
getSubsite = const Subsite getSubsite = const Subsite
instance YesodSubDispatch Subsite (GHandler master master) where instance YesodSubDispatch Subsite (GHandler master) where
yesodSubDispatch = $(mkYesodSubDispatch resourcesSubsite) yesodSubDispatch = $(mkYesodSubDispatch resourcesSubsite)
getBarR :: Monad m => m T.Text getBarR :: Monad m => m T.Text
getBarR = return $ T.pack "BarR" getBarR = return $ T.pack "BarR"
getBazR :: Yesod master => HandlerT Subsite (GHandler master master) RepHtml getBazR :: Yesod master => HandlerT Subsite (GHandler master) RepHtml
getBazR = lift $ defaultLayout [whamlet|Used Default Layout|] getBazR = lift $ defaultLayout [whamlet|Used Default Layout|]
getBinR :: MonadHandlerBase m => HandlerT Subsite m RepHtml getBinR :: MonadHandler m => HandlerT Subsite m RepHtml
getBinR = defaultLayoutT getBinR = defaultLayoutT
[whamlet| [whamlet|
<p>Used defaultLayoutT <p>Used defaultLayoutT

View File

@ -14,5 +14,5 @@ import Network.Wai.Test
import Network.Wai import Network.Wai
import Test.Hspec import Test.Hspec
yesod :: (YesodDispatch y y, Yesod y) => y -> Session a -> IO a yesod :: YesodDispatch y => y -> Session a -> IO a
yesod app f = toWaiApp app >>= runSession f yesod app f = toWaiApp app >>= runSession f