wai-routes 0.2.4 → 0.3.0
raw patch · 6 files changed
+114/−51 lines, 6 filesdep +conduitdep ~waidep ~yesod-routesPVP ok
version bump matches the API change (PVP)
Dependencies added: conduit
Dependency ranges changed: wai, yesod-routes
API changes (from Hackage documentation)
+ Network.Wai.Middleware.Routes.Routes: class RenderRoute a => ParseRoute a
+ Network.Wai.Middleware.Routes.Routes: class RenderRoute a => RouteAttrs a
+ Network.Wai.Middleware.Routes.Routes: data RequestData
+ Network.Wai.Middleware.Routes.Routes: nextApp :: RequestData -> Application
+ Network.Wai.Middleware.Routes.Routes: parseRoute :: ParseRoute a => ([Text], [(Text, Text)]) -> Maybe (Route a)
+ Network.Wai.Middleware.Routes.Routes: readRoute :: ParseRoute master => Text -> Maybe (Route master)
+ Network.Wai.Middleware.Routes.Routes: routeAttrs :: RouteAttrs a => Route a -> Set Text
+ Network.Wai.Middleware.Routes.Routes: runNext :: RequestData -> ResourceT IO Response
+ Network.Wai.Middleware.Routes.Routes: waiReq :: RequestData -> Request
- Network.Wai.Middleware.Routes.Routes: dispatcher :: Routable master => master -> master -> (Route master -> Route master) -> Handler master -> (Route master -> Handler master) -> Text -> [Text] -> Handler master
+ Network.Wai.Middleware.Routes.Routes: dispatcher :: Routable master => Handler master
- Network.Wai.Middleware.Routes.Routes: type Handler master = master -> Application
+ Network.Wai.Middleware.Routes.Routes: type Handler master = master -> App
Files
- README +4/−1
- examples/Example.hs +23/−2
- src/Network/Wai/Middleware/Routes.hs +1/−1
- src/Network/Wai/Middleware/Routes/Monad.hs +1/−1
- src/Network/Wai/Middleware/Routes/Routes.hs +79/−41
- wai-routes.cabal +6/−5
README view
@@ -1,4 +1,4 @@-Wai Routes (wai-routes-0.2.3)+Wai Routes (wai-routes-0.3.0) ============================== This package provides typesafe URLs for Wai applications.@@ -10,6 +10,8 @@ and can be used for persistent data (like DB connections) - RouteM monad that makes it easy to compose an application with multiple routes and middleware.+ - Handlers can abort processing and pass control to the next+ application in the wai stack It depends on yesod-routes package for the TH functionality (but not the rest of yesod). The aim is to provide a similar level of typesafe URL functionality to Wai applications as is available to Yesod applications. @@ -69,4 +71,5 @@ 0.2.2 : Fixed license information in hs and cabal files 0.2.3 : Implemented a better showRoute function. Added blaze-builder as a dependency 0.2.4 : Put an upper bound on yesod-routes version as 1.2 breaks API compatibility+0.3.0 : yesod-routes 1.2 compatibility. Abstracted request data. Created `runNext` which skips to the next app in the wai stack
examples/Example.hs view
@@ -36,6 +36,7 @@ /user/#Int UserR: / UserRootR GET /delete UserDeleteR GET POST+/skip SkipR GET |] -- Our handlers always produce json@@ -46,12 +47,13 @@ -- Display the possible actions getHomeR :: Handler MyRoute-getHomeR _master _req = return $ responseLBS status200 jsonHeaders jsonOut+getHomeR _master req = return $ responseLBS status200 jsonHeaders jsonOut where jsonOut = encode $ M.fromList ( [("description", [["Simple User database Example"]]) ,("links" ,[["home", showRoute HomeR] ,["users", showRoute UsersR]+ ,["skip", showRoute SkipR] ] ) ] :: [(Text, [[Text]])] )@@ -96,7 +98,7 @@ -- Delete a user: GET getUserDeleteR :: Int -> Handler MyRoute-getUserDeleteR _ _master _req = return $ responseLBS status200 jsonHeaders jsonOut+getUserDeleteR _ _master req = return $ responseLBS status200 jsonHeaders jsonOut where err = ["DELETE","please use POST"]::[Text] jsonOut = encode err @@ -106,6 +108,10 @@ where err = ["DELETE","not implemented"]::[Text] jsonOut = encode err +-- Demonstrate skipping routes+getSkipR :: Handler MyRoute+getSkipR _master = runNext+ -- Initial database initdb :: [User] initdb =@@ -113,12 +119,27 @@ , User 2 "Bo Lively" 28 ] +-- A new middleware to catch all skipped routes+data MySkippedRoute = MySkippedRoute++-- Make MyRoute Routable+mkRoute "MySkippedRoute" [parseRoutes|+/skip SkippedR GET+|]++getSkippedR :: Handler MySkippedRoute+getSkippedR _req _master =+ return $ responseLBS status200 jsonHeaders jsonOut+ where err = ["SKIPPED","skipped route"]::[Text]+ jsonOut = encode err+ -- The application that uses our route -- NOTE: We use the Route Monad to simplify routing application :: RouteM () application = do db <- liftIO $ newIORef initdb route (MyRoute db)+ route MySkippedRoute defaultAction $ staticApp $ defaultFileServerSettings "static" -- Run the application
src/Network/Wai/Middleware/Routes.hs view
@@ -1,6 +1,6 @@ {- | Module : Network.Wai.Middleware.Routes-Copyright : (c) Anupam Jain 2011+Copyright : (c) Anupam Jain 2013 License : MIT (see the file LICENSE) Maintainer : ajnsit@gmail.com
src/Network/Wai/Middleware/Routes/Monad.hs view
@@ -2,7 +2,7 @@ {- | Module : Network.Wai.Middleware.Routes.Monad-Copyright : (c) Anupam Jain 2011+Copyright : (c) Anupam Jain 2013 License : MIT (see the file LICENSE) Maintainer : ajnsit@gmail.com
src/Network/Wai/Middleware/Routes/Routes.hs view
@@ -1,10 +1,11 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} {-# LANGUAGE TemplateHaskell, QuasiQuotes #-} {- | Module : Network.Wai.Middleware.Routes.Routes-Copyright : (c) Anupam Jain 2011+Copyright : (c) Anupam Jain 2013 License : MIT (see the file LICENSE) Maintainer : ajnsit@gmail.com@@ -26,95 +27,132 @@ -- * Dispatch , routeDispatch - -- * URL rendering+ -- * URL rendering and parsing , showRoute+ , readRoute -- * Application Handlers , Handler -- * Generated Datatypes- , Routable(..)+ , Routable(..) -- | Used internally. However needs to be exported for TH to work. , RenderRoute(..) -- | A `RenderRoute` instance for your site datatype is automatically generated by `mkRoute`+ , ParseRoute(..) -- | A `ParseRoute` instance for your site datatype is automatically generated by `mkRoute`+ , RouteAttrs(..) -- | A `RouteAttrs` instance for your site datatype is automatically generated by `mkRoute` + , RequestData -- | An abstract representation of the request data. You can get the wai request object by using `waiReq`+ , waiReq -- | Extract the wai `Request` object from `RequestData`+ , nextApp -- | Extract the next Application in the stack+ , runNext -- | Run the next application in the stack ) where +-- Conduit+import Data.Conduit (ResourceT)+ -- Wai-import Network.Wai (Middleware, Application, pathInfo, requestMethod)-import Network.HTTP.Types (StdMethod(GET), parseMethod, encodePath, queryTextToQuery)+import Network.Wai (Middleware, Application, pathInfo, requestMethod, requestMethod, Response(ResponseBuilder), Request(..))+import Network.HTTP.Types (decodePath, encodePath, queryTextToQuery, queryToQueryText, status405) -- Yesod Routes-import Yesod.Routes.Class (Route, RenderRoute(..))+import Yesod.Routes.Class (Route, RenderRoute(..), ParseRoute(..), RouteAttrs(..)) import Yesod.Routes.Parse (parseRoutes, parseRoutesNoCheck, parseRoutesFile, parseRoutesFileNoCheck, parseType)-import Yesod.Routes.TH (mkRenderRouteInstance, mkDispatchClause, ResourceTree(..))+import Yesod.Routes.TH (mkRenderRouteInstance, mkParseRouteInstance, mkRouteAttrsInstance, mkDispatchClause, ResourceTree(..), MkDispatchSettings(..), defaultGetHandler) -- Text and Bytestring-import qualified Data.Text as T import Data.Text (Text)-import Data.Text.Encoding (decodeUtf8)-import Blaze.ByteString.Builder (Builder, toByteString)+import Data.Text.Encoding (encodeUtf8, decodeUtf8)+import Blaze.ByteString.Builder (toByteString, fromByteString) -- TH import Language.Haskell.TH.Syntax -- Convenience import Control.Arrow (second)+import Data.Maybe (fromMaybe) +-- An abstract request+data RequestData = RequestData+ { waiReq :: Request+ , nextApp :: Application+ }++-- | Run the next application in the stack+runNext :: RequestData -> ResourceT IO Response+runNext req = nextApp req $ waiReq req++-- Internal data type for convenience+type App = RequestData -> ResourceT IO Response++-- | A `Handler` generates an App from the master datatype+type Handler master = master -> App++-- Baked in applications that handle 404 and 405 errors+-- TODO: Inspect the request to figure out acceptable output formats+-- Currently we assume text/plain is acceptable+app404 :: Handler master+app404 _master req = nextApp req $ waiReq req++app405 :: Handler master+app405 _master _req = return $ ResponseBuilder status405 [("Content-Type","text/plain")] $ fromByteString "405 - Method Not Allowed"+ -- | Generates all the things needed for efficient routing,--- including your application's `Route` datatype, and a `RenderRoute` instance+-- including your application's `Route` datatype, and+-- `RenderRoute`, `ParseRoute`, and `RouteAttr` instances mkRoute :: String -> [ResourceTree String] -> Q [Dec] mkRoute typName routes = do let typ = parseType typName- inst <- mkRenderRouteInstance typ $ map (fmap parseType) routes- disp <- mkDispatchClause [|runHandler|] [|dispatcher|] [|id|] routes+ let resourceTrees = map (fmap parseType) routes+ rinst <- mkRenderRouteInstance typ resourceTrees+ pinst <- mkParseRouteInstance typ resourceTrees+ ainst <- mkRouteAttrsInstance typ resourceTrees+ disp <- mkDispatchClause MkDispatchSettings+ { mdsRunHandler = [| runHandler |]+ -- We don't use subsites+ , mdsSubDispatcher = [| undefined |]+ , mdsGetPathInfo = [| pathInfo . waiReq |]+ , mdsMethod = [| requestMethod . waiReq |]+ -- We don't use subsites+ , mdsSetPathInfo = [| undefined |]+ , mds404 = [| app404 |]+ , mds405 = [| app405 |]+ , mdsGetHandler = defaultGetHandler+ } routes return $ InstanceD [] (ConT ''Routable `AppT` typ) [FunD (mkName "dispatcher") [disp]]- : inst+ : ainst+ : pinst+ : rinst --- | A `Handler` generates an `Application` from the master datatype-type Handler master = master -> Application -- PRIVATE runHandler- :: Handler master- -> master- -> master- -> Maybe (Route master)- -> (Route master -> Route master)- -> Handler master-runHandler h _ _ _ _ = h+ :: Handler master+ -> master+ -> Maybe (Route master)+ -> App+runHandler h master _ = h master -- | A `Routable` instance can be used in dispatching. -- An appropriate instance for your site datatype is -- automatically generated by `mkRoute` class Routable master where- dispatcher- :: master- -> master- -> (Route master -> Route master)- -> Handler master -- 404 page- -> (Route master -> Handler master) -- 405 page- -> Text -- method- -> [Text]- -> Handler master+ dispatcher :: Handler master -- | Generates the application middleware from a `Routable` master datatype routeDispatch :: Routable master => master -> Middleware-routeDispatch master def req = app master req- where- app = dispatcher master master id def404 def405 (T.pack $ show $ method req) (pathInfo req)- def404 = const def- def405 = const $ const def -- TODO: This should ideally NOT pass on handling to the next resource- method req' = case parseMethod $ requestMethod req' of- Right m -> m- Left _ -> GET+routeDispatch master def req = dispatcher master RequestData{waiReq=req, nextApp=def} -- | Renders a `Route` as Text--- Uses the `encodePath` function from http-types. Also performs utf8 encoding showRoute :: RenderRoute master => Route master -> Text showRoute = uncurry encodePathInfo . second (map $ second Just) . renderRoute where encodePathInfo :: [Text] -> [(Text, Maybe Text)] -> Text encodePathInfo segments = decodeUtf8 . toByteString . encodePath segments . queryTextToQuery++-- | Read a route from Text+-- Returns Nothing if Route reading failed. Just route otherwise+readRoute :: ParseRoute master => Text -> Maybe (Route master)+readRoute = parseRoute . second (map (second (fromMaybe "")) . queryToQueryText) . decodePath . encodeUtf8
wai-routes.cabal view
@@ -1,5 +1,5 @@ Name: wai-routes-Version: 0.2.4+Version: 0.3.0 Synopsis: Typesafe URLs for Wai applications. Homepage: https://github.com/ajnsit/wai-routes License: MIT@@ -63,19 +63,20 @@ source-repository this type: git- location: http://github.com/ajnsit/wai-routes/tree/v0.2.4- tag: v0.2.4+ location: http://github.com/ajnsit/wai-routes/tree/v0.3.0+ tag: v0.3.0 Library hs-source-dirs: src Build-Depends: base >= 3 && < 5- , wai >= 1.3+ , wai >= 1.3 && < 1.5+ , conduit >= 0.5 && < 1.1 , path-pieces , text , http-types >= 0.7 , blaze-builder >= 0.2.1.4 && < 0.4 , template-haskell- , yesod-routes >= 1.1 && < 1.2+ , yesod-routes >= 1.2 , mtl exposed-modules: Network.Wai.Middleware.Routes , Network.Wai.Middleware.Routes.Routes