Dispatching works! (w00t)

This commit is contained in:
Michael Snoyman 2012-01-03 12:55:08 +02:00
parent c946fd2068
commit 09750605a8
2 changed files with 51 additions and 29 deletions

View File

@ -4,17 +4,16 @@ module Yesod.Routes.TH.Dispatch
mkDispatchClause mkDispatchClause
) where ) where
import Prelude hiding (exp)
import Yesod.Routes.TH.Types import Yesod.Routes.TH.Types
import Language.Haskell.TH.Syntax import Language.Haskell.TH.Syntax
import Data.Maybe (maybeToList, catMaybes) import Data.Maybe (catMaybes)
import Control.Monad (replicateM, forM) import Control.Monad (forM)
import Data.Text (pack) import Data.Text (pack)
import qualified Yesod.Routes.Dispatch as D import qualified Yesod.Routes.Dispatch as D
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Char (toLower) import Data.Char (toLower)
import Web.PathPieces (PathPiece (..), PathMultiPiece (..)) import Web.PathPieces (PathPiece (..), PathMultiPiece (..))
import Yesod.Routes.Class
import Data.Maybe (catMaybes)
import Control.Applicative ((<$>)) import Control.Applicative ((<$>))
import Data.List (foldl') import Data.List (foldl')
@ -60,6 +59,10 @@ mkDispatchClause runHandler dispatcher ress = do
u <- [|case $(return dispatched) of u <- [|case $(return dispatched) of
Just f -> f $(return $ VarE master0) Just f -> f $(return $ VarE master0)
$(return $ VarE sub0) $(return $ VarE sub0)
$(return $ VarE toMaster0)
$(return $ VarE app4040)
$(return $ VarE handler4050)
$(return $ VarE method0)
Nothing -> $(return $ VarE app4040) Nothing -> $(return $ VarE app4040)
|] |]
return $ Clause pats (NormalB u) $ dispatchFun : methodMaps return $ Clause pats (NormalB u) $ dispatchFun : methodMaps
@ -95,6 +98,12 @@ buildRoute runHandler dispatcher (Resource name resPieces resDisp) = do
[|D.Route $(return routePieces) $(return isMulti) $(routeArg3 runHandler dispatcher name resPieces resDisp)|] [|D.Route $(return routePieces) $(return isMulti) $(routeArg3 runHandler dispatcher name resPieces resDisp)|]
routeArg3 :: Q Exp -- ^ runHandler
-> Q Exp -- ^ dispatcher
-> String -- ^ name of resource
-> [Piece]
-> Dispatch
-> Q Exp
routeArg3 runHandler dispatcher name resPieces resDisp = do routeArg3 runHandler dispatcher name resPieces resDisp = do
pieces <- newName "pieces" pieces <- newName "pieces"
@ -134,7 +143,7 @@ routeArg3 runHandler dispatcher name resPieces resDisp = do
_ -> return ([], []) _ -> return ([], [])
-- The final expression that actually uses the values we've computed -- The final expression that actually uses the values we've computed
caller <- buildCaller runHandler dispatcher name resDisp $ map snd ys ++ yrest' caller <- buildCaller runHandler dispatcher xrest name resDisp $ map snd ys ++ yrest'
-- Put together all the statements -- Put together all the statements
just <- [|Just|] just <- [|Just|]
@ -153,13 +162,20 @@ routeArg3 runHandler dispatcher name resPieces resDisp = do
return $ LamE [VarP pieces] $ CaseE (VarE pieces) matches return $ LamE [VarP pieces] $ CaseE (VarE pieces) matches
-- | The final expression in the individual Route definitions. -- | The final expression in the individual Route definitions.
buildCaller runHandler dispatcher name resDisp ys = do buildCaller :: Q Exp -- ^ runHandler
-> Q Exp -- ^ dispatcher
-> Name -- ^ xrest
-> String -- ^ name of resource
-> Dispatch
-> [Name] -- ^ ys
-> Q Exp
buildCaller runHandler dispatcher xrest name resDisp ys = do
master <- newName "master" master <- newName "master"
sub <- newName "sub" sub <- newName "sub"
toMaster <- newName "toMaster" toMaster <- newName "toMaster"
app404 <- newName "app404" app404 <- newName "_app404"
handler405 <- newName "handler405" handler405 <- newName "_handler405"
method <- newName "method" method <- newName "_method"
let pat = map VarP [master, sub, toMaster, app404, handler405, method] let pat = map VarP [master, sub, toMaster, app404, handler405, method]
@ -202,6 +218,7 @@ buildCaller runHandler dispatcher name resDisp ys = do
$(return $ VarE app404) $(return $ VarE app404)
$(return $ VarE handler405) $(return $ VarE handler405)
$(return $ VarE method) $(return $ VarE method)
$(return $ VarE xrest)
|] |]
return $ LamE pat exp return $ LamE pat exp

View File

@ -5,6 +5,8 @@
{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TypeSynonymInstances #-}
import Test.Hspec.Monadic import Test.Hspec.Monadic
import Test.Hspec.HUnit () import Test.Hspec.HUnit ()
import Test.HUnit ((@?=)) import Test.HUnit ((@?=))
@ -17,7 +19,6 @@ import qualified Yesod.Routes.Dispatch as D
import Yesod.Routes.TH hiding (Dispatch) import Yesod.Routes.TH hiding (Dispatch)
import Language.Haskell.TH.Syntax import Language.Haskell.TH.Syntax
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
result :: ([Text] -> Maybe Int) -> Dispatch Int result :: ([Text] -> Maybe Int) -> Dispatch Int
result f ts = f ts result f ts = f ts
@ -81,25 +82,28 @@ instance RenderRoute MySubParam where
getMySubParam :: MyApp -> Int -> MySubParam getMySubParam :: MyApp -> Int -> MySubParam
getMySubParam _ = MySubParam getMySubParam _ = MySubParam
class Dispatcher handler master sub app where type Handler sub master = String
type App sub master = (String, Maybe (YRC.Route master))
class Dispatcher sub master where
dispatcher dispatcher
:: master :: master
-> sub -> sub
-> (YRC.Route sub -> YRC.Route master) -> (YRC.Route sub -> YRC.Route master)
-> app -- ^ 404 page -> App sub master -- ^ 404 page
-> handler -- ^ 405 page -> Handler sub master -- ^ 405 page
-> Text -- ^ method -> Text -- ^ method
-> [Text] -> [Text]
-> app -> App sub master
class RunHandler handler master sub app where class RunHandler sub master where
runHandler runHandler
:: handler :: Handler sub master
-> master -> master
-> sub -> sub
-> YRC.Route sub -> YRC.Route sub
-> (YRC.Route sub -> YRC.Route master) -> (YRC.Route sub -> YRC.Route master)
-> app -> App sub master
do do
texts <- [t|[Text]|] texts <- [t|[Text]|]
@ -114,20 +118,21 @@ do
dispatch <- mkDispatchClause [|runHandler|] [|dispatcher|] ress dispatch <- mkDispatchClause [|runHandler|] [|dispatcher|] ress
return return
[ rrinst [ rrinst
, FunD (mkName "thDispatch") [dispatch] , InstanceD
[]
(ConT ''Dispatcher
`AppT` ConT ''MyApp
`AppT` ConT ''MyApp)
[FunD (mkName "dispatcher") [dispatch]]
] ]
instance Dispatcher [Char] MyApp MyApp ([Char], Maybe (YRC.Route MyApp)) where instance RunHandler MyApp master where
dispatcher = thDispatchAlias
--dispatcher = thDispatch
instance RunHandler [Char] MyApp MyApp ([Char], Maybe (YRC.Route MyApp)) where
runHandler h _ _ subRoute toMaster = (h, Just $ toMaster subRoute) runHandler h _ _ subRoute toMaster = (h, Just $ toMaster subRoute)
instance Dispatcher [Char] master MySub ([Char], Maybe (YRC.Route master)) where instance Dispatcher MySub master where
dispatcher _ _ toMaster _ _ _ pieces = ("subsite: " ++ show pieces, Just $ toMaster $ MySubRoute (pieces, [])) dispatcher _ _ toMaster _ _ _ pieces = ("subsite: " ++ show pieces, Just $ toMaster $ MySubRoute (pieces, []))
instance Dispatcher [Char] master MySubParam ([Char], Maybe (YRC.Route master)) where instance Dispatcher MySubParam master where
dispatcher _ (MySubParam i) toMaster app404 _ _ pieces = dispatcher _ (MySubParam i) toMaster app404 _ _ pieces =
case map unpack pieces of case map unpack pieces of
[[c]] -> ("subparam " ++ show i ++ ' ' : [c], Just $ toMaster $ ParamRoute c) [[c]] -> ("subparam " ++ show i ++ ' ' : [c], Just $ toMaster $ ParamRoute c)
@ -153,7 +158,7 @@ thDispatchAlias master sub toMaster app404 handler405 method0 pieces0 =
[ Route [] False $ \pieces -> [ Route [] False $ \pieces ->
case pieces of case pieces of
[] -> do [] -> do
Just $ \master' sub' toMaster' app404' handler405' method -> Just $ \master' sub' toMaster' _app404' handler405' method ->
let handler = let handler =
case Map.lookup method methodsRootR of case Map.lookup method methodsRootR of
Just f -> f Just f -> f
@ -164,7 +169,7 @@ thDispatchAlias master sub toMaster app404 handler405 method0 pieces0 =
case pieces of case pieces of
[_, x2] -> do [_, x2] -> do
y2 <- fromPathPiece x2 y2 <- fromPathPiece x2
Just $ \master' sub' toMaster' app404' handler405' method -> Just $ \master' sub' toMaster' _app404' handler405' method ->
let handler = let handler =
case Map.lookup method methodsBlogPostR of case Map.lookup method methodsBlogPostR of
Just f -> f y2 Just f -> f y2
@ -175,7 +180,7 @@ thDispatchAlias master sub toMaster app404 handler405 method0 pieces0 =
case pieces of case pieces of
_:x2 -> do _:x2 -> do
y2 <- fromPathMultiPiece x2 y2 <- fromPathMultiPiece x2
Just $ \master' sub' toMaster' app404' handler405' method -> Just $ \master' sub' toMaster' _app404' _handler405' _method ->
let handler = handleWikiR y2 let handler = handleWikiR y2
in runHandler handler master' sub' (WikiR y2) toMaster' in runHandler handler master' sub' (WikiR y2) toMaster'
_ -> error "Invariant violated" _ -> error "Invariant violated"
@ -235,7 +240,7 @@ main = hspecX $ do
@?= (["subparam", "6", "c"], []) @?= (["subparam", "6", "c"], [])
describe "thDispatch" $ do describe "thDispatch" $ do
let disp = thDispatchAlias MyApp MyApp id ("404", Nothing) "405" let disp = dispatcher MyApp MyApp id ("404" :: String, Nothing) "405"
it "routes to root" $ disp "GET" [] @?= ("this is the root", Just RootR) it "routes to root" $ disp "GET" [] @?= ("this is the root", Just RootR)
it "POST root is 405" $ disp "POST" [] @?= ("405", Just RootR) it "POST root is 405" $ disp "POST" [] @?= ("405", Just RootR)
it "invalid page is a 404" $ disp "GET" ["not-found"] @?= ("404", Nothing) it "invalid page is a 404" $ disp "GET" ["not-found"] @?= ("404", Nothing)