{-# LANGUAGE TemplateHaskell #-}{-# LANGUAGE CPP #-}{-# LANGUAGE OverloadedStrings #-}{-# LANGUAGE TypeFamilies #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE MultiParamTypeClasses #-}moduleYesod.Dispatch(-- * Quasi-quoted routingparseRoutes,parseRoutesNoCheck,parseRoutesFile,parseRoutesFileNoCheck,mkYesod,mkYesodSub-- ** More fine-grained,mkYesodData,mkYesodSubData,mkYesodDispatch,mkYesodSubDispatch,mkDispatchInstance-- ** Path pieces,PathPiece(..),PathMultiPiece(..),Texts-- * Convert to WAI,toWaiApp,toWaiAppPlain-- * WAI subsites,WaiSubsite(..))whereimportControl.Applicative((<$>),(<*>))importPreludehiding(exp)importYesod.Internal.CoreimportYesod.Handlerhiding(lift)importYesod.Widget(GWidget)importWeb.PathPiecesimportLanguage.Haskell.THimportLanguage.Haskell.TH.SyntaximportqualifiedNetwork.WaiasWimportNetwork.Wai.Middleware.GzipimportNetwork.Wai.Middleware.AutoheadimportData.ByteString.Lazy.Char8()importData.Text(Text)importData.Text.Encoding(decodeUtf8With)importData.Text.Encoding.Error(lenientDecode)importData.Monoid(mappend)importqualifiedData.ByteStringasSimportqualifiedBlaze.ByteString.BuilderimportNetwork.HTTP.Types(status301)importYesod.Routes.THimportYesod.Content(chooseRep)importYesod.Routes.ParseimportSystem.Log.FastLogger(Logger)typeTexts=[Text]-- | Generates URL datatype and site function for the given 'Resource's. This-- is used for creating sites, /not/ subsites. See 'mkYesodSub' for the latter.-- Use 'parseRoutes' to create the 'Resource's.mkYesod::String-- ^ name of the argument datatype->[ResourceTreeString]->Q[Dec]mkYesodname=fmap(uncurry(++)).mkYesodGeneralname[][]False-- | Generates URL datatype and site function for the given 'Resource's. This-- is used for creating subsites, /not/ sites. See 'mkYesod' for the latter.-- Use 'parseRoutes' to create the 'Resource's. In general, a subsite is not-- executable by itself, but instead provides functionality to-- be embedded in other sites.mkYesodSub::String-- ^ name of the argument datatype->Cxt->[ResourceTreeString]->Q[Dec]mkYesodSubnameclazzes=fmap(uncurry(++)).mkYesodGeneralname'restclazzesTruewhere(name':rest)=wordsname-- | Sometimes, you will want to declare your routes in one file and define-- your handlers elsewhere. For example, this is the only way to break up a-- monolithic file into smaller parts. Use this function, paired with-- 'mkYesodDispatch', to do just that.mkYesodData::String->[ResourceTreeString]->Q[Dec]mkYesodDatanameres=mkYesodDataGeneralname[]FalseresmkYesodSubData::String->Cxt->[ResourceTreeString]->Q[Dec]mkYesodSubDatanameclazzesres=mkYesodDataGeneralnameclazzesTrueresmkYesodDataGeneral::String->Cxt->Bool->[ResourceTreeString]->Q[Dec]mkYesodDataGeneralnameclazzesisSubres=dolet(name':rest)=wordsname(x,_)<-mkYesodGeneralname'restclazzesisSubresletrname=mkName$"resources"++nameeres<-liftreslety=[SigDrname$ListT`AppT`(ConT''ResourceTree`AppT`ConT''String),FunDrname[Clause[](NormalBeres)[]]]return$x++y-- | See 'mkYesodData'.mkYesodDispatch::String->[ResourceTreeString]->Q[Dec]mkYesodDispatchname=fmapsnd.mkYesodGeneralname[][]FalsemkYesodSubDispatch::String->Cxt->[ResourceTreeString]->Q[Dec]mkYesodSubDispatchnameclazzes=fmapsnd.mkYesodGeneralname'restclazzesTruewhere(name':rest)=wordsnamemkYesodGeneral::String-- ^ foundation type->[String]-- ^ arguments for the type->Cxt-- ^ the type constraints->Bool-- ^ it this a subsite->[ResourceTreeString]->Q([Dec],[Dec])mkYesodGeneralnameargsclazzesisSubresS=dosubsite<-submasterTypeSyns<-ifisSubthenreturn[]elsesequence[handler,widget]renderRouteDec<-mkRenderRouteInstancesubsiteresdispatchDec<-mkDispatchInstancecontextsubmasterresreturn(renderRouteDec++masterTypeSyns,dispatchDec)wheresub=foldlappTsubConssubArgsmaster=ifisSubthen(varT$mkName"master")elsesubcontext=ifisSubthencxt$yesod:mapreturnclazzeselsereturn[]yesod=classP''Yesod[master]handler=tySynD(mkName"Handler")[][t|GHandler$master$master|]widget=tySynD(mkName"Widget")[][t|GWidget$master$master()|]res=map(fmapparseType)resSsubCons=conT$mkNamenamesubArgs=map(varT.mkName)args-- | If the generation of @'YesodDispatch'@ instance require finer-- control of the types, contexts etc. using this combinator. You will-- hardly need this generality. However, in certain situations, like-- when writing library/plugin for yesod, this combinator becomes-- handy.mkDispatchInstance::CxtQ-- ^ The context->TypeQ-- ^ The subsite type->TypeQ-- ^ The master site type->[ResourceTreea]-- ^ The resource->DecsQmkDispatchInstancecontextsubmasterres=dologger<-newName"logger"letloggerE=varEloggerloggerP=VarPloggeryDispatch=conT''YesodDispatch`appT`sub`appT`masterthisDispatch=doClausepatbodydecs<-mkDispatchClause[|yesodRunner$loggerE|][|yesodDispatch$loggerE|][|fmapchooseRep|]resreturn$FunD'yesodDispatch[Clause(loggerP:pat)bodydecs]insequence[instanceDcontextyDispatch[thisDispatch]]-- | Convert the given argument into a WAI application, executable with any WAI-- handler. This is the same as 'toWaiAppPlain', except it includes two-- middlewares: GZIP compression and autohead. This is the-- recommended approach for most users.toWaiApp::(Yesodmaster,YesodDispatchmastermaster)=>master->IOW.ApplicationtoWaiAppy=gzip(gzipSettingsy).autohead<$>toWaiAppPlainy-- | Convert the given argument into a WAI application, executable with any WAI-- handler. This differs from 'toWaiApp' in that it uses no middlewares.toWaiAppPlain::(Yesodmaster,YesodDispatchmastermaster)=>master->IOW.ApplicationtoWaiAppPlaina=toWaiApp'a<$>getLoggera<*>makeSessionBackendatoWaiApp'::(Yesodmaster,YesodDispatchmastermaster)=>master->Logger->Maybe(SessionBackendmaster)->W.ApplicationtoWaiApp'yloggersbenv=casecleanPathy$W.pathInfoenvofLeftpieces->sendRedirectypiecesenvRightpieces->yesodDispatchloggeryyidapp404handler405methodpiecessbenvwhereapp404=yesodRunnerloggernotFoundyyNothingidhandler405route=yesodRunnerloggerbadMethodyy(Justroute)idmethod=decodeUtf8WithlenientDecode$W.requestMethodenvsendRedirect::Yesodmaster=>master->[Text]->W.ApplicationsendRedirectysegments'env=return$W.responseLBSstatus301[("Content-Type","text/plain"),("Location",Blaze.ByteString.Builder.toByteStringdest')]"Redirecting"wheredest=joinPathy(resolveApprootyenv)segments'[]dest'=ifS.null(W.rawQueryStringenv)thendestelse(dest`mappend`Blaze.ByteString.Builder.fromByteString(W.rawQueryStringenv))-- | Wrap up a normal WAI application as a Yesod subsite.newtypeWaiSubsite=WaiSubsite{runWaiSubsite::W.Application}instanceRenderRouteWaiSubsitewheredataRouteWaiSubsite=WaiSubsiteRoute[Text][(Text,Text)]deriving(Show,Eq,Read,Ord)renderRoute(WaiSubsiteRoutepsqs)=(ps,qs)instanceYesodDispatchWaiSubsitemasterwhereyesodDispatch_logger_master(WaiSubsiteapp)_tomaster_404_405_method_pieces_session=app