Initial quasi-quoting of resources complete, still ugly code
This commit is contained in:
parent
f162ac54b3
commit
aedb43fe69
@ -25,9 +25,12 @@ import qualified Hack
|
|||||||
import Data.Convertible.Text
|
import Data.Convertible.Text
|
||||||
import Control.Exception (Exception)
|
import Control.Exception (Exception)
|
||||||
import Data.Typeable (Typeable)
|
import Data.Typeable (Typeable)
|
||||||
|
import Language.Haskell.TH.Syntax
|
||||||
|
|
||||||
data Verb = Get | Put | Delete | Post
|
data Verb = Get | Put | Delete | Post
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show, Enum, Bounded)
|
||||||
|
instance Lift Verb where
|
||||||
|
lift = return . ConE . mkName . show
|
||||||
instance ConvertAttempt String Verb where
|
instance ConvertAttempt String Verb where
|
||||||
convertAttempt "Get" = return Get
|
convertAttempt "Get" = return Get
|
||||||
convertAttempt "Put" = return Put
|
convertAttempt "Put" = return Put
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
--
|
--
|
||||||
-- Module : Yesod.Resource
|
-- Module : Yesod.Resource
|
||||||
@ -27,6 +28,14 @@ module Yesod.Resource
|
|||||||
, checkPatternsTH
|
, checkPatternsTH
|
||||||
, validatePatterns
|
, validatePatterns
|
||||||
, checkPatterns
|
, checkPatterns
|
||||||
|
, checkRPNodes
|
||||||
|
, rpnodesTH
|
||||||
|
, rpnodesTHCheck
|
||||||
|
, rpnodesQuasi
|
||||||
|
, RPNode (..)
|
||||||
|
, VerbMap (..)
|
||||||
|
, RP (..)
|
||||||
|
, RPP (..)
|
||||||
#if TEST
|
#if TEST
|
||||||
-- * Testing
|
-- * Testing
|
||||||
, testSuite
|
, testSuite
|
||||||
@ -35,11 +44,12 @@ module Yesod.Resource
|
|||||||
|
|
||||||
import Data.List.Split (splitOn)
|
import Data.List.Split (splitOn)
|
||||||
import Yesod.Definitions
|
import Yesod.Definitions
|
||||||
import Data.List (intercalate)
|
import Data.List (intercalate, nub)
|
||||||
import Data.Char (isDigit)
|
import Data.Char (isDigit)
|
||||||
|
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
import Language.Haskell.TH
|
import Language.Haskell.TH.Syntax
|
||||||
|
import Language.Haskell.TH.Quote
|
||||||
|
|
||||||
import Data.Typeable (Typeable)
|
import Data.Typeable (Typeable)
|
||||||
import Control.Exception (Exception)
|
import Control.Exception (Exception)
|
||||||
@ -201,6 +211,70 @@ data RPNodeException = VerbMapNonScalar TextObject
|
|||||||
deriving (Show, Typeable)
|
deriving (Show, Typeable)
|
||||||
instance Exception RPNodeException
|
instance Exception RPNodeException
|
||||||
|
|
||||||
|
checkRPNodes :: (MonadFailure OverlappingPatterns m,
|
||||||
|
MonadFailure RepeatedVerb m
|
||||||
|
)
|
||||||
|
=> [RPNode]
|
||||||
|
-> m [RPNode]
|
||||||
|
checkRPNodes nodes = do
|
||||||
|
checkPatterns $ map (\(RPNode r _) -> cs r) nodes -- FIXME ugly
|
||||||
|
mapM_ (\(RPNode _ v) -> checkVerbMap v) nodes
|
||||||
|
return nodes
|
||||||
|
where
|
||||||
|
checkVerbMap (AllVerbs _) = return ()
|
||||||
|
checkVerbMap (Verbs vs) =
|
||||||
|
let vs' = map fst vs
|
||||||
|
res = nub vs' == vs'
|
||||||
|
in if res then return () else failure $ RepeatedVerb vs
|
||||||
|
|
||||||
|
newtype RepeatedVerb = RepeatedVerb [(Verb, String)]
|
||||||
|
deriving (Show, Typeable)
|
||||||
|
instance Exception RepeatedVerb
|
||||||
|
|
||||||
|
rpnodesTHCheck :: [RPNode] -> Q Exp
|
||||||
|
rpnodesTHCheck nodes = do
|
||||||
|
nodes' <- runIO $ checkRPNodes nodes
|
||||||
|
rpnodesTH nodes'
|
||||||
|
|
||||||
|
rpnodesTH :: [RPNode] -> Q Exp
|
||||||
|
rpnodesTH = fmap ListE . mapM lift
|
||||||
|
instance Lift RPNode where
|
||||||
|
lift (RPNode rp vm) = do
|
||||||
|
rp' <- lift rp
|
||||||
|
vm' <- lift vm
|
||||||
|
return $ TupE [rp', vm']
|
||||||
|
instance Lift RP where
|
||||||
|
lift (RP rpps) = do
|
||||||
|
rpps' <- lift rpps
|
||||||
|
return $ ConE (mkName "RP") `AppE` rpps'
|
||||||
|
instance Lift RPP where
|
||||||
|
lift (Static s) =
|
||||||
|
return $ ConE (mkName "Static") `AppE` (LitE $ StringL s)
|
||||||
|
lift (Dynamic s) =
|
||||||
|
return $ ConE (mkName "Dynamic") `AppE` (LitE $ StringL s)
|
||||||
|
lift (DynInt s) =
|
||||||
|
return $ ConE (mkName "DynInt") `AppE` (LitE $ StringL s)
|
||||||
|
lift (Slurp s) =
|
||||||
|
return $ ConE (mkName "Slurp") `AppE` (LitE $ StringL s)
|
||||||
|
instance Lift VerbMap where
|
||||||
|
lift (AllVerbs s) = return $ ListE $ map helper [minBound..maxBound]
|
||||||
|
where
|
||||||
|
helper :: Verb -> Exp
|
||||||
|
helper v = TupE [(helper2 v), LitE $ StringL s]
|
||||||
|
helper2 :: Verb -> Exp
|
||||||
|
helper2 = ConE . mkName . show
|
||||||
|
lift (Verbs v) = lift v
|
||||||
|
|
||||||
|
strToExp :: String -> Q Exp
|
||||||
|
strToExp s = do
|
||||||
|
let yd :: YamlDoc
|
||||||
|
yd = YamlDoc $ cs s
|
||||||
|
rpnodes <- runIO $ convertAttemptWrap yd
|
||||||
|
rpnodesTHCheck rpnodes
|
||||||
|
|
||||||
|
rpnodesQuasi :: QuasiQuoter
|
||||||
|
rpnodesQuasi = QuasiQuoter strToExp undefined
|
||||||
|
|
||||||
#if TEST
|
#if TEST
|
||||||
---- Testing
|
---- Testing
|
||||||
testSuite :: Test
|
testSuite :: Test
|
||||||
@ -212,6 +286,7 @@ testSuite = testGroup "Yesod.Resource"
|
|||||||
, testProperty "show pattern" prop_showPattern
|
, testProperty "show pattern" prop_showPattern
|
||||||
, testCase "integers" caseIntegers
|
, testCase "integers" caseIntegers
|
||||||
, testCase "read patterns from YAML" caseFromYaml
|
, testCase "read patterns from YAML" caseFromYaml
|
||||||
|
, testCase "checkRPNodes" caseCheckRPNodes
|
||||||
]
|
]
|
||||||
|
|
||||||
deriving instance Arbitrary RP
|
deriving instance Arbitrary RP
|
||||||
@ -287,4 +362,16 @@ caseFromYaml = do
|
|||||||
]
|
]
|
||||||
contents' <- fa $ ca contents
|
contents' <- fa $ ca contents
|
||||||
expected @=? contents'
|
expected @=? contents'
|
||||||
|
|
||||||
|
caseCheckRPNodes :: Assertion
|
||||||
|
caseCheckRPNodes = do
|
||||||
|
good' <- readYamlDoc "test/resource-patterns.yaml"
|
||||||
|
good <- fa $ ca good'
|
||||||
|
Just good @=? checkRPNodes good
|
||||||
|
let bad1 = [ RPNode (cs "foo/bar") $ AllVerbs "foo"
|
||||||
|
, RPNode (cs "$foo/bar") $ AllVerbs "bar"
|
||||||
|
]
|
||||||
|
Nothing @=? checkRPNodes bad1
|
||||||
|
let bad2 = [RPNode (cs "") $ Verbs [(Get, "foo"), (Get, "bar")]]
|
||||||
|
Nothing @=? checkRPNodes bad2
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
38
test/quasi-resource.hs
Normal file
38
test/quasi-resource.hs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
|
||||||
|
import Yesod.Resource
|
||||||
|
import Yesod.Definitions
|
||||||
|
import Data.Convertible.Text
|
||||||
|
|
||||||
|
handler :: [(RP, [(Verb, [Char])])]
|
||||||
|
handler =
|
||||||
|
$(rpnodesTHCheck
|
||||||
|
[ RPNode (cs "static/*filepath") $ AllVerbs "getStatic"
|
||||||
|
, RPNode (cs "page") $ Verbs [(Get, "pageIndex"), (Put, "pageAdd")]
|
||||||
|
, RPNode (cs "page/$page") $ Verbs [ (Get, "pageDetail")
|
||||||
|
, (Delete, "pageDelete")
|
||||||
|
, (Post, "pageUpdate")
|
||||||
|
]
|
||||||
|
, RPNode (cs "user/#id") $ Verbs [(Get, "userInfo")]
|
||||||
|
])
|
||||||
|
|
||||||
|
handler2 :: [(RP, [(Verb, [Char])])]
|
||||||
|
handler2 = [$rpnodesQuasi|
|
||||||
|
/static/*filepath/: getStatic
|
||||||
|
/page/:
|
||||||
|
Get: pageIndex
|
||||||
|
Put: pageAdd
|
||||||
|
/page/$page/:
|
||||||
|
Get: pageDetail
|
||||||
|
Delete: pageDelete
|
||||||
|
Post: pageUpdate
|
||||||
|
/user/#id/:
|
||||||
|
Get: userInfo
|
||||||
|
|]
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
print handler
|
||||||
|
print handler2
|
||||||
|
print $ handler == handler2
|
||||||
Loading…
Reference in New Issue
Block a user