Skip to content

Commit

Permalink
add scottyMiddleware method to Trans, and example in globastate.hs
Browse files Browse the repository at this point in the history
  • Loading branch information
echaozh committed Mar 9, 2014
1 parent 8596f58 commit 0fdfbc6
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 5 deletions.
20 changes: 16 additions & 4 deletions Web/Scotty/Trans.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ module Web.Scotty.Trans
-- | 'Middleware' and routes are run in the order in which they
-- are defined. All middleware is run first, followed by the first
-- route that matches. If no route matches, a 404 response is given.
, middleware, get, post, put, delete, patch, addroute, matchAny, notFound
, middleware, scottyMiddleware, get, post, put, delete, patch, addroute
, matchAny, notFound
-- ** Route Patterns
, capture, regex, function, literal
-- ** Accessing the Request, Captures, and Query Parameters
Expand All @@ -32,7 +33,7 @@ module Web.Scotty.Trans
-- * Types
, RoutePattern, File
-- * Monad Transformers
, ScottyT, ActionT
, ScottyT, ActionT, Scotty.Middleware, Scotty.Application
) where

import Blaze.ByteString.Builder (fromByteString)
Expand Down Expand Up @@ -85,8 +86,10 @@ scottyAppT :: (Monad m, Monad n)
-> n Application
scottyAppT runM runActionToIO defs = do
s <- runM $ execStateT (runS defs) def
let rapp = runActionToIO . foldl (flip ($)) notFoundApp (routes s)
return $ foldl (flip ($)) rapp (middlewares s)
let chain = foldl $ flip ($)
rapp' = chain notFoundApp (routes s)
rapp = runActionToIO . chain rapp' (scottyMiddlewares s)
return $ chain rapp (middlewares s)

notFoundApp :: Monad m => Scotty.Application m
notFoundApp _ = return $ responseBuilder status404 [("Content-Type","text/html")]
Expand All @@ -104,3 +107,12 @@ defaultHandler f = ScottyT $ modify $ addHandler $ Just f
-- on the response). Every middleware is run on each request.
middleware :: Monad m => Middleware -> ScottyT e m ()
middleware = ScottyT . modify . addMiddleware

-- | Use given Scotty middleware. Middleware is nested such that the first
-- declared is the outermost middleware (it has first dibs on the request and
-- last action on the response). Every middleware is run on each request.
--
-- Scotty middlewares are different from WAI middlewares, as they can access the
-- state in the monad wrapped by 'ScottyT'.
scottyMiddleware :: Monad m => Scotty.Middleware m -> ScottyT e m ()
scottyMiddleware = ScottyT . modify . addScottyMiddleware
6 changes: 5 additions & 1 deletion Web/Scotty/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,22 @@ type Application m = Request -> m Response
data ScottyState e m =
ScottyState { middlewares :: [Wai.Middleware]
, routes :: [Middleware m]
, scottyMiddlewares :: [Middleware m]
, handler :: ErrorHandler e m
}

instance Monad m => Default (ScottyState e m) where
def = ScottyState [] [] Nothing
def = ScottyState [] [] [] Nothing

addMiddleware :: Wai.Middleware -> ScottyState e m -> ScottyState e m
addMiddleware m s@(ScottyState {middlewares = ms}) = s { middlewares = m:ms }

addRoute :: Monad m => Middleware m -> ScottyState e m -> ScottyState e m
addRoute r s@(ScottyState {routes = rs}) = s { routes = r:rs }

addScottyMiddleware :: Monad m => Middleware m -> ScottyState e m -> ScottyState e m
addScottyMiddleware m s@(ScottyState {scottyMiddlewares = ms}) = s { scottyMiddlewares = m:ms }

addHandler :: ErrorHandler e m -> ScottyState e m -> ScottyState e m
addHandler h s = s { handler = h }

Expand Down
11 changes: 11 additions & 0 deletions examples/globalstate.hs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ main = do
app :: ScottyT Text WebM ()
app = do
middleware logStdoutDev
scottyMiddleware logTickCount
get "/" $ do
c <- webM $ gets tickCount
text $ fromString $ show c
Expand All @@ -79,3 +80,13 @@ app = do
get "/plustwo" $ do
webM $ modify $ \ st -> st { tickCount = tickCount st + 2 }
redirect "/"

-- Log tick count after every request, but before the 'logStdoutDev'
-- logs the status. As you can see, it can access AppState.
-- However, unlike in actions, you do not need 'WebM' to lift state accessing.
logTickCount :: Application WebM -> Application WebM
logTickCount a req = do
r <- a req
c <- gets tickCount
liftIO $ putStrLn $ "* tick count after request handled: " ++ show c
return r

0 comments on commit 0fdfbc6

Please sign in to comment.