Subsites working with new web-routes-quasi

This commit is contained in:
Michael Snoyman 2010-06-30 14:24:36 +03:00
parent 2f17cda10d
commit 0e6f32f4a6
7 changed files with 93 additions and 52 deletions

View File

@ -57,6 +57,7 @@ import Control.Monad
import Data.Maybe import Data.Maybe
import Web.ClientSession import Web.ClientSession
import qualified Web.ClientSession as CS import qualified Web.ClientSession as CS
import Data.Char (isLower)
import Data.Serialize import Data.Serialize
import qualified Data.Serialize as Ser import qualified Data.Serialize as Ser
@ -114,17 +115,13 @@ mkYesodData name res = do
mkYesodDispatch :: String -> [Resource] -> Q [Dec] mkYesodDispatch :: String -> [Resource] -> Q [Dec]
mkYesodDispatch name = fmap snd . mkYesodGeneral name [] [] False mkYesodDispatch name = fmap snd . mkYesodGeneral name [] [] False
explodeHandler :: HasReps c typeHelper :: String -> Type
=> GHandler sub master c typeHelper =
-> (Routes master -> String) foldl1 AppT . map go . words
-> Routes sub where
-> (Routes sub -> Routes master) go s@(x:_)
-> master | isLower x = VarT $ mkName s
-> (master -> sub) | otherwise = ConT $ mkName s
-> YesodApp
-> String
-> YesodApp
explodeHandler a b c d e f _ _ = runHandler a b (Just c) d e f
mkYesodGeneral :: String -- ^ argument name mkYesodGeneral :: String -- ^ argument name
-> [String] -- ^ parameters for site argument -> [String] -- ^ parameters for site argument
@ -136,18 +133,13 @@ mkYesodGeneral name args clazzes isSub res = do
let name' = mkName name let name' = mkName name
args' = map mkName args args' = map mkName args
arg = foldl AppT (ConT name') $ map VarT args' arg = foldl AppT (ConT name') $ map VarT args'
let site = mkName $ "site" ++ name let clazzes' = map (\(x, y) -> ClassP x [typeHelper y])
let gsbod = NormalB $ VarE site $ concatMap (\(x, y) -> zip y $ repeat x)
let yes' = FunD (mkName "getSite") [Clause [] gsbod []] $ compact
let yes = InstanceD [] (ConT ''YesodSite `AppT` ConT name') [yes'] $ map (\x -> (x, [])) ("master" : args) ++ clazzes
let clazzes' = compact th <- mapM (thResourceFromResource arg) res -- FIXME now we cannot have multi-nested subsites
$ map (\x -> (x, [])) ("master" : args) ++
clazzes
explode <- [|explodeHandler|]
let th = map thResourceFromResource res
w' <- createRoutes th w' <- createRoutes th
let w = DataInstD [] ''Routes [arg] w' [] let w = DataInstD [] ''Routes [arg] w' []
let x = TySynD (mkName $ name ++ "Routes") [] $ ConT ''Routes `AppT` arg
parse' <- createParse th parse' <- createParse th
parse'' <- newName "parse" parse'' <- newName "parse"
@ -157,35 +149,58 @@ mkYesodGeneral name args clazzes isSub res = do
render'' <- newName "render" render'' <- newName "render"
let render = LetE [FunD render'' render'] $ VarE render'' let render = LetE [FunD render'' render'] $ VarE render''
id' <- [|id|] tmh <- [|toMasterHandler|]
modMaster <- [|fmap chooseRep|] modMaster <- [|fmap chooseRep|]
dispatch' <- createDispatch modMaster id' th dispatch' <- createDispatch modMaster tmh th
dispatch'' <- newName "dispatch" dispatch'' <- newName "dispatch"
let dispatch = LetE [FunD dispatch'' dispatch'] $ LamE [WildP] $ VarE dispatch'' let dispatch = LetE [FunD dispatch'' dispatch'] $ LamE [WildP] $ VarE dispatch''
site <- [|Site|] site <- [|Site|]
let site' = site `AppE` dispatch `AppE` render `AppE` parse let site' = site `AppE` dispatch `AppE` render `AppE` parse
let y = InstanceD [] (ConT ''YesodSite `AppT` arg) let (ctx, ytyp, yfunc) =
[ FunD (mkName "getSite") [Clause [] (NormalB site') []] if isSub
then (clazzes', ConT ''YesodSubSite `AppT` arg `AppT` VarT (mkName "master"), "getSubSite")
else ([], ConT ''YesodSite `AppT` arg, "getSite")
let y = InstanceD ctx ytyp
[ FunD (mkName yfunc) [Clause [] (NormalB site') []]
] ]
let z = undefined return ([w], [y])
{-
QuasiSiteDecs w x y z <- createQuasiSite QuasiSiteSettings
{ crRoutes = mkName $ name ++ "Routes"
, crApplication = ConT ''YesodApp
, crArgument = arg
, crExplode = explode
, crResources = res
, crSite = site
, crMaster = if isSub
then Right clazzes'
else Left (ConT name')
}
-}
return ([w, x], [y])
thResourceFromResource :: Resource -> THResource isStatic :: Piece -> Bool
thResourceFromResource (Resource n ps (ByMethod ms)) = (n, Simple ps $ map fst ms) isStatic StaticPiece{} = True
isStatic _ = False
fromStatic :: Piece -> String
fromStatic (StaticPiece s) = s
fromStatic _ = error "fromStatic"
thResourceFromResource :: Type -> Resource -> Q THResource
thResourceFromResource master (Resource n ps atts@[stype, toSubArg])
| all isStatic ps && any (any isLower) atts = do
let stype' = ConT $ mkName stype
gss <- [|getSubSite|]
let inside = ConT ''Maybe `AppT`
(ConT ''GHandler `AppT` stype' `AppT` master `AppT`
ConT ''ChooseRep)
let typ = ConT ''Site `AppT`
(ConT ''Routes `AppT` stype') `AppT`
(ArrowT `AppT` ConT ''String `AppT` inside)
let gss' = gss `SigE` typ
parse' <- [|parsePathSegments|]
let parse = parse' `AppE` gss'
render' <- [|formatPathSegments|]
let render = render' `AppE` gss'
dispatch' <- [|flip handleSite (error "Cannot use subsite render function")|]
let dispatch = dispatch' `AppE` gss'
return (n, SubSite
{ ssType = ConT ''Routes `AppT` stype'
, ssParse = parse
, ssRender = render
, ssDispatch = dispatch
, ssToMasterArg = VarE $ mkName toSubArg
, ssPieces = map fromStatic ps
})
thResourceFromResource _ (Resource n ps attribs) = return (n, Simple ps attribs)
compact :: [(String, [a])] -> [(String, [a])] compact :: [(String, [a])] -> [(String, [a])]
compact [] = [] compact [] = []

View File

@ -64,6 +64,7 @@ module Yesod.Handler
, runHandler , runHandler
, YesodApp (..) , YesodApp (..)
, Routes , Routes
, toMasterHandler
) where ) where
import Prelude hiding (catch) import Prelude hiding (catch)
@ -104,6 +105,25 @@ data HandlerData sub master = HandlerData
, handlerToMaster :: Routes sub -> Routes master , handlerToMaster :: Routes sub -> Routes master
} }
handlerSubData :: (Routes sub -> Routes master)
-> (master -> sub)
-> Routes sub
-> HandlerData oldSub master
-> HandlerData sub master
handlerSubData tm ts route hd = hd
{ handlerSub = ts $ handlerMaster hd
, handlerToMaster = tm
, handlerRoute = Just route
}
toMasterHandler :: (Routes sub -> Routes master)
-> (master -> sub)
-> Routes sub
-> GHandler sub master a
-> Handler master a
toMasterHandler tm ts route (GHandler h) =
GHandler $ withReaderT (handlerSubData tm ts route) h
-- | 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. This monad is a combination of reader for basic arguments, a writer -- site. This monad is a combination of reader for basic arguments, a writer
-- for headers, and an error-type monad for handling special responses. -- for headers, and an error-type monad for handling special responses.

View File

@ -22,8 +22,7 @@
module Yesod.Helpers.Auth module Yesod.Helpers.Auth
( -- * Subsite ( -- * Subsite
Auth (..) Auth (..)
, AuthRoutes (..) , Routes (..)
, siteAuth
-- * Settings -- * Settings
, YesodAuth (..) , YesodAuth (..)
, Creds (..) , Creds (..)

View File

@ -1,12 +1,13 @@
{-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell #-} {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE Rank2Types #-} {-# LANGUAGE Rank2Types #-}
module Yesod.Helpers.Crud module Yesod.Helpers.Crud
( Item (..) ( Item (..)
, Crud (..) , Crud (..)
, CrudRoutes (..) , Routes (..)
, defaultCrud , defaultCrud
, siteCrud
) where ) where
import Yesod.Yesod import Yesod.Yesod

View File

@ -2,6 +2,8 @@
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
--------------------------------------------------------- ---------------------------------------------------------
-- --
-- Module : Yesod.Helpers.Static -- Module : Yesod.Helpers.Static
@ -25,8 +27,7 @@
module Yesod.Helpers.Static module Yesod.Helpers.Static
( -- * Subsite ( -- * Subsite
Static (..) Static (..)
, StaticRoutes (..) , Routes (..)
, siteStatic
-- * Lookup files in filesystem -- * Lookup files in filesystem
, fileLookupDir , fileLookupDir
, staticFiles , staticFiles
@ -52,9 +53,9 @@ import Test.HUnit hiding (Test)
-- see 'fileLookupDir'. -- see 'fileLookupDir'.
data Static = Static (FilePath -> IO (Maybe (Either FilePath Content))) data Static = Static (FilePath -> IO (Maybe (Either FilePath Content)))
$(mkYesodSub "Static" [] [$parseRoutes| mkYesodSub "Static" [] [$parseRoutes|
*Strings StaticRoute GET *Strings StaticRoute GET
|]) |]
-- | Lookup files in a specific directory. -- | Lookup files in a specific directory.
-- --
@ -117,7 +118,7 @@ staticFiles fp = do
f' <- lift f f' <- lift f
let sr = ConE $ mkName "StaticRoute" let sr = ConE $ mkName "StaticRoute"
return return
[ SigD name $ ConT ''StaticRoutes [ SigD name $ ConT ''Routes `AppT` ConT ''Static
, FunD name , FunD name
[ Clause [] (NormalB $ sr `AppE` f') [] [ Clause [] (NormalB $ sr `AppE` f') []
] ]

View File

@ -1,11 +1,13 @@
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
-- | The basic typeclass for a Yesod application. -- | The basic typeclass for a Yesod application.
module Yesod.Yesod module Yesod.Yesod
( -- * Type classes ( -- * Type classes
Yesod (..) Yesod (..)
, YesodSite (..) , YesodSite (..)
, YesodSubSite (..)
-- ** Persistence -- ** Persistence
, YesodPersist (..) , YesodPersist (..)
, module Database.Persist , module Database.Persist
@ -39,6 +41,9 @@ class YesodSite y where
getSite :: Site (Routes y) (Method -> Maybe (Handler y ChooseRep)) getSite :: Site (Routes y) (Method -> Maybe (Handler y ChooseRep))
type Method = String type Method = String
class YesodSubSite s y where
getSubSite :: Site (Routes s) (Method -> Maybe (GHandler s y ChooseRep))
-- | Define settings for a Yesod applications. The only required setting is -- | Define settings for a Yesod applications. The only required setting is
-- 'approot'; other than that, there are intelligent defaults. -- 'approot'; other than that, there are intelligent defaults.
class Yesod a where class Yesod a where

View File

@ -31,7 +31,7 @@ library
utf8-string >= 0.3.4 && < 0.4, utf8-string >= 0.3.4 && < 0.4,
template-haskell >= 2.4 && < 2.5, template-haskell >= 2.4 && < 2.5,
web-routes >= 0.22 && < 0.23, web-routes >= 0.22 && < 0.23,
web-routes-quasi >= 0.4 && < 0.5, web-routes-quasi >= 0.5 && < 0.6,
hamlet >= 0.3.1 && < 0.4, hamlet >= 0.3.1 && < 0.4,
transformers >= 0.2 && < 0.3, transformers >= 0.2 && < 0.3,
clientsession >= 0.4.0 && < 0.5, clientsession >= 0.4.0 && < 0.5,