RouteAttrs (fixes #531)
This commit is contained in:
parent
4cde171285
commit
16260f3e37
@ -11,6 +11,7 @@ module Yesod.Core
|
|||||||
, YesodSubDispatch (..)
|
, YesodSubDispatch (..)
|
||||||
, RenderRoute (..)
|
, RenderRoute (..)
|
||||||
, ParseRoute (..)
|
, ParseRoute (..)
|
||||||
|
, RouteAttrs (..)
|
||||||
-- ** Breadcrumbs
|
-- ** Breadcrumbs
|
||||||
, YesodBreadcrumbs (..)
|
, YesodBreadcrumbs (..)
|
||||||
, breadcrumbs
|
, breadcrumbs
|
||||||
|
|||||||
@ -66,6 +66,7 @@ mkYesodGeneral :: String -- ^ foundation type
|
|||||||
-> Q([Dec],[Dec])
|
-> Q([Dec],[Dec])
|
||||||
mkYesodGeneral name args isSub resS = do
|
mkYesodGeneral name args isSub resS = do
|
||||||
renderRouteDec <- mkRenderRouteInstance site res
|
renderRouteDec <- mkRenderRouteInstance site res
|
||||||
|
routeAttrsDec <- mkRouteAttrsInstance site res
|
||||||
dispatchDec <- mkDispatchInstance site res
|
dispatchDec <- mkDispatchInstance site res
|
||||||
parse <- mkParseRouteInstance site res
|
parse <- mkParseRouteInstance site res
|
||||||
let rname = mkName $ "resources" ++ name
|
let rname = mkName $ "resources" ++ name
|
||||||
@ -77,6 +78,7 @@ mkYesodGeneral name args isSub resS = do
|
|||||||
let dataDec = concat
|
let dataDec = concat
|
||||||
[ [parse]
|
[ [parse]
|
||||||
, renderRouteDec
|
, renderRouteDec
|
||||||
|
, [routeAttrsDec]
|
||||||
, resourcesDec
|
, resourcesDec
|
||||||
, if isSub then [] else masterTypeSyns site
|
, if isSub then [] else masterTypeSyns site
|
||||||
]
|
]
|
||||||
|
|||||||
@ -12,12 +12,15 @@ import Data.Text (Text)
|
|||||||
import Data.Maybe (fromJust)
|
import Data.Maybe (fromJust)
|
||||||
import Data.Monoid (Endo (..))
|
import Data.Monoid (Endo (..))
|
||||||
import qualified Control.Monad.Trans.Writer as Writer
|
import qualified Control.Monad.Trans.Writer as Writer
|
||||||
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
data App = App
|
data App = App
|
||||||
|
|
||||||
mkYesod "App" [parseRoutes|
|
mkYesod "App" [parseRoutes|
|
||||||
/ HomeR GET
|
/ HomeR GET !home
|
||||||
/json JsonR GET
|
/json JsonR GET
|
||||||
|
/parent/#Int ParentR:
|
||||||
|
/#Text/child ChildR !child
|
||||||
|]
|
|]
|
||||||
|
|
||||||
instance Yesod App
|
instance Yesod App
|
||||||
@ -40,6 +43,9 @@ getJsonR = selectRep $ do
|
|||||||
rep typeHtml "HTML"
|
rep typeHtml "HTML"
|
||||||
provideRep $ return $ object ["message" .= ("Invalid Login" :: Text)]
|
provideRep $ return $ object ["message" .= ("Invalid Login" :: Text)]
|
||||||
|
|
||||||
|
handleChildR :: Int -> Text -> Handler ()
|
||||||
|
handleChildR _ _ = return ()
|
||||||
|
|
||||||
testRequest :: Int -- ^ http status code
|
testRequest :: Int -- ^ http status code
|
||||||
-> Request
|
-> Request
|
||||||
-> ByteString -- ^ expected body
|
-> ByteString -- ^ expected body
|
||||||
@ -63,7 +69,8 @@ acceptRequest accept = defaultRequest
|
|||||||
}
|
}
|
||||||
|
|
||||||
specs :: Spec
|
specs :: Spec
|
||||||
specs = describe "selectRep" $ do
|
specs = do
|
||||||
|
describe "selectRep" $ do
|
||||||
test "application/json" "JSON"
|
test "application/json" "JSON"
|
||||||
test (S8.unpack typeJson) "JSON"
|
test (S8.unpack typeJson) "JSON"
|
||||||
test "text/xml" "XML"
|
test "text/xml" "XML"
|
||||||
@ -77,3 +84,7 @@ specs = describe "selectRep" $ do
|
|||||||
testRequest 406 (acceptRequest "text/foo") "no match found for accept header"
|
testRequest 406 (acceptRequest "text/foo") "no match found for accept header"
|
||||||
test "text/*" "HTML"
|
test "text/*" "HTML"
|
||||||
test "*/*" "HTML"
|
test "*/*" "HTML"
|
||||||
|
describe "routeAttrs" $ do
|
||||||
|
it "HomeR" $ routeAttrs HomeR `shouldBe` Set.singleton "home"
|
||||||
|
it "JsonR" $ routeAttrs JsonR `shouldBe` Set.empty
|
||||||
|
it "ChildR" $ routeAttrs (ParentR 5 $ ChildR "ignored") `shouldBe` Set.singleton "child"
|
||||||
|
|||||||
@ -3,9 +3,11 @@
|
|||||||
module Yesod.Routes.Class
|
module Yesod.Routes.Class
|
||||||
( RenderRoute (..)
|
( RenderRoute (..)
|
||||||
, ParseRoute (..)
|
, ParseRoute (..)
|
||||||
|
, RouteAttrs (..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
import Data.Set (Set)
|
||||||
|
|
||||||
class Eq (Route a) => RenderRoute a where
|
class Eq (Route a) => RenderRoute a where
|
||||||
-- | The type-safe URLs associated with a site argument.
|
-- | The type-safe URLs associated with a site argument.
|
||||||
@ -14,3 +16,6 @@ class Eq (Route a) => RenderRoute a where
|
|||||||
|
|
||||||
class RenderRoute a => ParseRoute a where
|
class RenderRoute a => ParseRoute a where
|
||||||
parseRoute :: ([Text], [(Text, Text)]) -> Maybe (Route a)
|
parseRoute :: ([Text], [(Text, Text)]) -> Maybe (Route a)
|
||||||
|
|
||||||
|
class RenderRoute a => RouteAttrs a where
|
||||||
|
routeAttrs :: Route a -> Set Text
|
||||||
|
|||||||
@ -4,6 +4,7 @@ module Yesod.Routes.TH
|
|||||||
-- * Functions
|
-- * Functions
|
||||||
, module Yesod.Routes.TH.RenderRoute
|
, module Yesod.Routes.TH.RenderRoute
|
||||||
, module Yesod.Routes.TH.ParseRoute
|
, module Yesod.Routes.TH.ParseRoute
|
||||||
|
, module Yesod.Routes.TH.RouteAttrs
|
||||||
-- ** Dispatch
|
-- ** Dispatch
|
||||||
, module Yesod.Routes.TH.Dispatch
|
, module Yesod.Routes.TH.Dispatch
|
||||||
) where
|
) where
|
||||||
@ -11,4 +12,5 @@ module Yesod.Routes.TH
|
|||||||
import Yesod.Routes.TH.Types
|
import Yesod.Routes.TH.Types
|
||||||
import Yesod.Routes.TH.RenderRoute
|
import Yesod.Routes.TH.RenderRoute
|
||||||
import Yesod.Routes.TH.ParseRoute
|
import Yesod.Routes.TH.ParseRoute
|
||||||
|
import Yesod.Routes.TH.RouteAttrs
|
||||||
import Yesod.Routes.TH.Dispatch
|
import Yesod.Routes.TH.Dispatch
|
||||||
|
|||||||
38
yesod-routes/Yesod/Routes/TH/RouteAttrs.hs
Normal file
38
yesod-routes/Yesod/Routes/TH/RouteAttrs.hs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
module Yesod.Routes.TH.RouteAttrs
|
||||||
|
( mkRouteAttrsInstance
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Yesod.Routes.TH.Types
|
||||||
|
import Yesod.Routes.Class
|
||||||
|
import Language.Haskell.TH.Syntax
|
||||||
|
import Data.Set (fromList)
|
||||||
|
import Data.Text (pack)
|
||||||
|
|
||||||
|
mkRouteAttrsInstance :: Type -> [ResourceTree a] -> Q Dec
|
||||||
|
mkRouteAttrsInstance typ ress = do
|
||||||
|
clauses <- mapM (goTree id) ress
|
||||||
|
return $ InstanceD [] (ConT ''RouteAttrs `AppT` typ)
|
||||||
|
[ FunD 'routeAttrs $ concat clauses
|
||||||
|
]
|
||||||
|
|
||||||
|
goTree :: (Pat -> Pat) -> ResourceTree a -> Q [Clause]
|
||||||
|
goTree front (ResourceLeaf res) = fmap return $ goRes front res
|
||||||
|
goTree front (ResourceParent name pieces trees) =
|
||||||
|
fmap concat $ mapM (goTree front') trees
|
||||||
|
where
|
||||||
|
ignored = ((replicate toIgnore WildP ++) . return)
|
||||||
|
toIgnore = length $ filter (isDynamic . snd) pieces
|
||||||
|
isDynamic Dynamic{} = True
|
||||||
|
isDynamic Static{} = False
|
||||||
|
front' = front . ConP (mkName name) . ignored
|
||||||
|
|
||||||
|
goRes :: (Pat -> Pat) -> Resource a -> Q Clause
|
||||||
|
goRes front Resource {..} =
|
||||||
|
return $ Clause
|
||||||
|
[front $ RecP (mkName resourceName) []]
|
||||||
|
(NormalB $ VarE 'fromList `AppE` ListE (map toText resourceAttrs))
|
||||||
|
[]
|
||||||
|
where
|
||||||
|
toText s = VarE 'pack `AppE` LitE (StringL s)
|
||||||
@ -23,6 +23,7 @@ import Yesod.Routes.TH hiding (Dispatch)
|
|||||||
import Language.Haskell.TH.Syntax
|
import Language.Haskell.TH.Syntax
|
||||||
import Hierarchy
|
import Hierarchy
|
||||||
import qualified Data.ByteString.Char8 as S8
|
import qualified Data.ByteString.Char8 as S8
|
||||||
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
result :: ([Text] -> Maybe Int) -> Dispatch Int
|
result :: ([Text] -> Maybe Int) -> Dispatch Int
|
||||||
result f ts = f ts
|
result f ts = f ts
|
||||||
@ -105,15 +106,24 @@ getMySubParam _ = MySubParam
|
|||||||
|
|
||||||
do
|
do
|
||||||
texts <- [t|[Text]|]
|
texts <- [t|[Text]|]
|
||||||
let ress = map ResourceLeaf
|
let resLeaves = map ResourceLeaf
|
||||||
[ Resource "RootR" [] (Methods Nothing ["GET"]) []
|
[ Resource "RootR" [] (Methods Nothing ["GET"]) ["foo", "bar"]
|
||||||
, Resource "BlogPostR" (addCheck [Static "blog", Dynamic $ ConT ''Text]) (Methods Nothing ["GET", "POST"]) []
|
, Resource "BlogPostR" (addCheck [Static "blog", Dynamic $ ConT ''Text]) (Methods Nothing ["GET", "POST"]) []
|
||||||
, Resource "WikiR" (addCheck [Static "wiki"]) (Methods (Just texts) []) []
|
, Resource "WikiR" (addCheck [Static "wiki"]) (Methods (Just texts) []) []
|
||||||
, Resource "SubsiteR" (addCheck [Static "subsite"]) (Subsite (ConT ''MySub) "getMySub") []
|
, Resource "SubsiteR" (addCheck [Static "subsite"]) (Subsite (ConT ''MySub) "getMySub") []
|
||||||
, Resource "SubparamR" (addCheck [Static "subparam", Dynamic $ ConT ''Int]) (Subsite (ConT ''MySubParam) "getMySubParam") []
|
, Resource "SubparamR" (addCheck [Static "subparam", Dynamic $ ConT ''Int]) (Subsite (ConT ''MySubParam) "getMySubParam") []
|
||||||
]
|
]
|
||||||
|
resParent = ResourceParent
|
||||||
|
"ParentR"
|
||||||
|
[ (True, Static "foo")
|
||||||
|
, (True, Dynamic $ ConT ''Text)
|
||||||
|
]
|
||||||
|
[ ResourceLeaf $ Resource "ChildR" [] (Methods Nothing ["GET"]) ["child"]
|
||||||
|
]
|
||||||
|
ress = resParent : resLeaves
|
||||||
addCheck = map ((,) True)
|
addCheck = map ((,) True)
|
||||||
rrinst <- mkRenderRouteInstance (ConT ''MyApp) ress
|
rrinst <- mkRenderRouteInstance (ConT ''MyApp) ress
|
||||||
|
rainst <- mkRouteAttrsInstance (ConT ''MyApp) ress
|
||||||
prinst <- mkParseRouteInstance (ConT ''MyApp) ress
|
prinst <- mkParseRouteInstance (ConT ''MyApp) ress
|
||||||
dispatch <- mkDispatchClause MkDispatchSettings
|
dispatch <- mkDispatchClause MkDispatchSettings
|
||||||
{ mdsRunHandler = [|runHandler|]
|
{ mdsRunHandler = [|runHandler|]
|
||||||
@ -133,6 +143,7 @@ do
|
|||||||
`AppT` ConT ''MyApp)
|
`AppT` ConT ''MyApp)
|
||||||
[FunD (mkName "dispatcher") [dispatch]]
|
[FunD (mkName "dispatcher") [dispatch]]
|
||||||
: prinst
|
: prinst
|
||||||
|
: rainst
|
||||||
: rrinst
|
: rrinst
|
||||||
|
|
||||||
instance Dispatcher MySub master where
|
instance Dispatcher MySub master where
|
||||||
@ -336,6 +347,11 @@ main = hspec $ do
|
|||||||
/bar/baz Foo3
|
/bar/baz Foo3
|
||||||
|]
|
|]
|
||||||
findOverlapNames routes @?= []
|
findOverlapNames routes @?= []
|
||||||
|
describe "routeAttrs" $ do
|
||||||
|
it "works" $ do
|
||||||
|
routeAttrs RootR @?= Set.fromList [pack "foo", pack "bar"]
|
||||||
|
it "hierarchy" $ do
|
||||||
|
routeAttrs (ParentR (pack "ignored") ChildR) @?= Set.singleton (pack "child")
|
||||||
hierarchy
|
hierarchy
|
||||||
|
|
||||||
getRootR :: Text
|
getRootR :: Text
|
||||||
@ -349,3 +365,6 @@ postBlogPostR t = pack $ "POST some blog post: " ++ unpack t
|
|||||||
|
|
||||||
handleWikiR :: [Text] -> String
|
handleWikiR :: [Text] -> String
|
||||||
handleWikiR ts = "the wiki: " ++ show ts
|
handleWikiR ts = "the wiki: " ++ show ts
|
||||||
|
|
||||||
|
getChildR :: Text -> Text
|
||||||
|
getChildR = id
|
||||||
|
|||||||
@ -30,6 +30,7 @@ library
|
|||||||
other-modules: Yesod.Routes.TH.Dispatch
|
other-modules: Yesod.Routes.TH.Dispatch
|
||||||
Yesod.Routes.TH.RenderRoute
|
Yesod.Routes.TH.RenderRoute
|
||||||
Yesod.Routes.TH.ParseRoute
|
Yesod.Routes.TH.ParseRoute
|
||||||
|
Yesod.Routes.TH.RouteAttrs
|
||||||
Yesod.Routes.TH.Types
|
Yesod.Routes.TH.Types
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user