packages feed

wai-routes 0.7.3 → 0.8.0

raw patch · 6 files changed

+227/−80 lines, 6 filesdep +case-insensitivedep +cookiedep +data-default-classdep ~aesonPVP ok

version bump matches the API change (PVP)

Dependencies added: case-insensitive, cookie, data-default-class, filepath, hspec, hspec-wai, hspec-wai-json, mime-types, wai-routes

Dependency ranges changed: aeson

API changes (from Hackage documentation)

- Network.Wai.Middleware.Routes: readRoute :: ParseRoute master => Text -> Maybe (Route master)
- Network.Wai.Middleware.Routes: showRoute :: RenderRoute master => Route master -> Text
- Network.Wai.Middleware.Routes: showRouteQuery :: RenderRoute master => Route master -> [(Text, Text)] -> Text
- Network.Wai.Middleware.Routes.Handler: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Network.Wai.Middleware.Routes.Handler.HandlerMI sub master m)
- Network.Wai.Middleware.Routes.Handler: instance Control.Monad.Trans.Class.MonadTrans (Network.Wai.Middleware.Routes.Handler.HandlerMI sub master)
- Network.Wai.Middleware.Routes.Handler: instance GHC.Base.Functor m => GHC.Base.Functor (Network.Wai.Middleware.Routes.Handler.HandlerMI sub master m)
- Network.Wai.Middleware.Routes.Handler: instance GHC.Base.Monad m => Control.Monad.State.Class.MonadState (Network.Wai.Middleware.Routes.Handler.HandlerState sub master) (Network.Wai.Middleware.Routes.Handler.HandlerMI sub master m)
- Network.Wai.Middleware.Routes.Handler: instance GHC.Base.Monad m => GHC.Base.Applicative (Network.Wai.Middleware.Routes.Handler.HandlerMI sub master m)
- Network.Wai.Middleware.Routes.Handler: instance GHC.Base.Monad m => GHC.Base.Monad (Network.Wai.Middleware.Routes.Handler.HandlerMI sub master m)
- Network.Wai.Middleware.Routes.Monad: instance GHC.Base.Functor Network.Wai.Middleware.Routes.Monad.RouterF
- Network.Wai.Middleware.Routes.Overlap: [overlap1] :: Overlap t -> ResourceTree t
- Network.Wai.Middleware.Routes.Overlap: [overlap2] :: Overlap t -> ResourceTree t
- Network.Wai.Middleware.Routes.Overlap: [overlapParents] :: Overlap t -> [String] -> [String]
- Network.Wai.Middleware.Routes.Parse: instance GHC.Classes.Eq Network.Wai.Middleware.Routes.Parse.TypeTree
- Network.Wai.Middleware.Routes.Parse: instance GHC.Show.Show Network.Wai.Middleware.Routes.Parse.TypeTree
- Network.Wai.Middleware.Routes.Routes: [envMaster] :: Env sub master -> master
- Network.Wai.Middleware.Routes.Routes: [envSub] :: Env sub master -> sub
- Network.Wai.Middleware.Routes.Routes: [envToMaster] :: Env sub master -> Route sub -> Route master
- Network.Wai.Middleware.Routes.TH.Dispatch: [mds404] :: MkDispatchSettings -> Q Exp
- Network.Wai.Middleware.Routes.TH.Dispatch: [mds405] :: MkDispatchSettings -> Q Exp
- Network.Wai.Middleware.Routes.TH.Dispatch: [mdsGetHandler] :: MkDispatchSettings -> Maybe String -> String -> Q Exp
- Network.Wai.Middleware.Routes.TH.Dispatch: [mdsGetPathInfo] :: MkDispatchSettings -> Q Exp
- Network.Wai.Middleware.Routes.TH.Dispatch: [mdsMethod] :: MkDispatchSettings -> Q Exp
- Network.Wai.Middleware.Routes.TH.Dispatch: [mdsRunHandler] :: MkDispatchSettings -> Q Exp
- Network.Wai.Middleware.Routes.TH.Dispatch: [mdsSetPathInfo] :: MkDispatchSettings -> Q Exp
- Network.Wai.Middleware.Routes.TH.Dispatch: [mdsSubDispatcher] :: MkDispatchSettings -> Q Exp
- Network.Wai.Middleware.Routes.TH.Types: [frCheck] :: FlatResource a -> Bool
- Network.Wai.Middleware.Routes.TH.Types: [frDispatch] :: FlatResource a -> Dispatch a
- Network.Wai.Middleware.Routes.TH.Types: [frName] :: FlatResource a -> String
- Network.Wai.Middleware.Routes.TH.Types: [frParentPieces] :: FlatResource a -> [(String, [Piece a])]
- Network.Wai.Middleware.Routes.TH.Types: [frPieces] :: FlatResource a -> [Piece a]
- Network.Wai.Middleware.Routes.TH.Types: [methodsMethods] :: Dispatch typ -> [String]
- Network.Wai.Middleware.Routes.TH.Types: [methodsMulti] :: Dispatch typ -> Maybe typ
- Network.Wai.Middleware.Routes.TH.Types: [resourceAttrs] :: Resource typ -> [String]
- Network.Wai.Middleware.Routes.TH.Types: [resourceCheck] :: Resource typ -> CheckOverlap
- Network.Wai.Middleware.Routes.TH.Types: [resourceDispatch] :: Resource typ -> Dispatch typ
- Network.Wai.Middleware.Routes.TH.Types: [resourceName] :: Resource typ -> String
- Network.Wai.Middleware.Routes.TH.Types: [resourcePieces] :: Resource typ -> [Piece typ]
- Network.Wai.Middleware.Routes.TH.Types: [subsiteFunc] :: Dispatch typ -> String
- Network.Wai.Middleware.Routes.TH.Types: [subsiteType] :: Dispatch typ -> typ
- Network.Wai.Middleware.Routes.TH.Types: instance GHC.Base.Functor Network.Wai.Middleware.Routes.TH.Types.Dispatch
- Network.Wai.Middleware.Routes.TH.Types: instance GHC.Base.Functor Network.Wai.Middleware.Routes.TH.Types.Piece
- Network.Wai.Middleware.Routes.TH.Types: instance GHC.Base.Functor Network.Wai.Middleware.Routes.TH.Types.Resource
- Network.Wai.Middleware.Routes.TH.Types: instance GHC.Base.Functor Network.Wai.Middleware.Routes.TH.Types.ResourceTree
- Network.Wai.Middleware.Routes.TH.Types: instance GHC.Show.Show typ => GHC.Show.Show (Network.Wai.Middleware.Routes.TH.Types.Dispatch typ)
- Network.Wai.Middleware.Routes.TH.Types: instance GHC.Show.Show typ => GHC.Show.Show (Network.Wai.Middleware.Routes.TH.Types.Piece typ)
- Network.Wai.Middleware.Routes.TH.Types: instance GHC.Show.Show typ => GHC.Show.Show (Network.Wai.Middleware.Routes.TH.Types.Resource typ)
- Network.Wai.Middleware.Routes.TH.Types: instance Language.Haskell.TH.Syntax.Lift t => Language.Haskell.TH.Syntax.Lift (Network.Wai.Middleware.Routes.TH.Types.Dispatch t)
- Network.Wai.Middleware.Routes.TH.Types: instance Language.Haskell.TH.Syntax.Lift t => Language.Haskell.TH.Syntax.Lift (Network.Wai.Middleware.Routes.TH.Types.Piece t)
- Network.Wai.Middleware.Routes.TH.Types: instance Language.Haskell.TH.Syntax.Lift t => Language.Haskell.TH.Syntax.Lift (Network.Wai.Middleware.Routes.TH.Types.Resource t)
- Network.Wai.Middleware.Routes.TH.Types: instance Language.Haskell.TH.Syntax.Lift t => Language.Haskell.TH.Syntax.Lift (Network.Wai.Middleware.Routes.TH.Types.ResourceTree t)
- Util.Free: [runF] :: F f a -> forall r. (a -> r) -> (f r -> r) -> r
- Util.Free: instance GHC.Base.Functor f => GHC.Base.Applicative (Util.Free.F f)
- Util.Free: instance GHC.Base.Functor f => GHC.Base.Functor (Util.Free.F f)
- Util.Free: instance GHC.Base.Functor f => GHC.Base.Monad (Util.Free.F f)
+ Network.Wai.Middleware.Routes: filepart :: FilePath -> FilePart -> HandlerM sub master ()
+ Network.Wai.Middleware.Routes: getCookie :: ByteString -> HandlerM sub master (Maybe ByteString)
+ Network.Wai.Middleware.Routes: getCookies :: HandlerM sub master Cookies
+ Network.Wai.Middleware.Routes: readRouteMaster :: ParseRoute master => HandlerM sub master (Text -> Maybe (Route master))
+ Network.Wai.Middleware.Routes: readRouteSub :: ParseRoute sub => HandlerM sub master (Text -> Maybe (Route master))
+ Network.Wai.Middleware.Routes: reqHeader :: ByteString -> HandlerM sub master (Maybe ByteString)
+ Network.Wai.Middleware.Routes: reqHeaders :: HandlerM sub master RequestHeaders
+ Network.Wai.Middleware.Routes: setCookie :: SetCookie -> HandlerM sub master ()
+ Network.Wai.Middleware.Routes: showRouteMaster :: RenderRoute master => HandlerM sub master (Route master -> Text)
+ Network.Wai.Middleware.Routes: showRouteQueryMaster :: RenderRoute master => HandlerM sub master (Route master -> [(Text, Text)] -> Text)
+ Network.Wai.Middleware.Routes: showRouteQuerySub :: RenderRoute master => HandlerM sub master (Route sub -> [(Text, Text)] -> Text)
+ Network.Wai.Middleware.Routes: showRouteSub :: RenderRoute master => HandlerM sub master (Route sub -> Text)
+ Network.Wai.Middleware.Routes: sub :: HandlerM sub master sub
+ Network.Wai.Middleware.Routes.ContentTypes: contentTypeFromFile :: FilePath -> ByteString
+ Network.Wai.Middleware.Routes.Handler: filepart :: FilePath -> FilePart -> HandlerM sub master ()
+ Network.Wai.Middleware.Routes.Handler: getCookie :: ByteString -> HandlerM sub master (Maybe ByteString)
+ Network.Wai.Middleware.Routes.Handler: getCookies :: HandlerM sub master Cookies
+ Network.Wai.Middleware.Routes.Handler: instance (Monad m, Functor m) => Applicative (HandlerMI sub master m)
+ Network.Wai.Middleware.Routes.Handler: instance Functor m => Functor (HandlerMI sub master m)
+ Network.Wai.Middleware.Routes.Handler: instance Monad m => Monad (HandlerMI sub master m)
+ Network.Wai.Middleware.Routes.Handler: instance Monad m => MonadState (HandlerState sub master) (HandlerMI sub master m)
+ Network.Wai.Middleware.Routes.Handler: instance MonadIO m => MonadIO (HandlerMI sub master m)
+ Network.Wai.Middleware.Routes.Handler: instance MonadTrans (HandlerMI sub master)
+ Network.Wai.Middleware.Routes.Handler: readRouteMaster :: ParseRoute master => HandlerM sub master (Text -> Maybe (Route master))
+ Network.Wai.Middleware.Routes.Handler: readRouteSub :: ParseRoute sub => HandlerM sub master (Text -> Maybe (Route master))
+ Network.Wai.Middleware.Routes.Handler: reqHeader :: ByteString -> HandlerM sub master (Maybe ByteString)
+ Network.Wai.Middleware.Routes.Handler: reqHeaders :: HandlerM sub master RequestHeaders
+ Network.Wai.Middleware.Routes.Handler: setCookie :: SetCookie -> HandlerM sub master ()
+ Network.Wai.Middleware.Routes.Handler: showRouteMaster :: RenderRoute master => HandlerM sub master (Route master -> Text)
+ Network.Wai.Middleware.Routes.Handler: showRouteQueryMaster :: RenderRoute master => HandlerM sub master (Route master -> [(Text, Text)] -> Text)
+ Network.Wai.Middleware.Routes.Handler: showRouteQuerySub :: RenderRoute master => HandlerM sub master (Route sub -> [(Text, Text)] -> Text)
+ Network.Wai.Middleware.Routes.Handler: showRouteSub :: RenderRoute master => HandlerM sub master (Route sub -> Text)
+ Network.Wai.Middleware.Routes.Handler: sub :: HandlerM sub master sub
+ Network.Wai.Middleware.Routes.Monad: instance Functor RouterF
+ Network.Wai.Middleware.Routes.Overlap: overlap1 :: Overlap t -> ResourceTree t
+ Network.Wai.Middleware.Routes.Overlap: overlap2 :: Overlap t -> ResourceTree t
+ Network.Wai.Middleware.Routes.Overlap: overlapParents :: Overlap t -> [String] -> [String]
+ Network.Wai.Middleware.Routes.Parse: instance Eq TypeTree
+ Network.Wai.Middleware.Routes.Parse: instance Show TypeTree
+ Network.Wai.Middleware.Routes.Routes: envMaster :: Env sub master -> master
+ Network.Wai.Middleware.Routes.Routes: envSub :: Env sub master -> sub
+ Network.Wai.Middleware.Routes.Routes: envToMaster :: Env sub master -> Route sub -> Route master
+ Network.Wai.Middleware.Routes.TH.Dispatch: mds404 :: MkDispatchSettings -> Q Exp
+ Network.Wai.Middleware.Routes.TH.Dispatch: mds405 :: MkDispatchSettings -> Q Exp
+ Network.Wai.Middleware.Routes.TH.Dispatch: mdsGetHandler :: MkDispatchSettings -> Maybe String -> String -> Q Exp
+ Network.Wai.Middleware.Routes.TH.Dispatch: mdsGetPathInfo :: MkDispatchSettings -> Q Exp
+ Network.Wai.Middleware.Routes.TH.Dispatch: mdsMethod :: MkDispatchSettings -> Q Exp
+ Network.Wai.Middleware.Routes.TH.Dispatch: mdsRunHandler :: MkDispatchSettings -> Q Exp
+ Network.Wai.Middleware.Routes.TH.Dispatch: mdsSetPathInfo :: MkDispatchSettings -> Q Exp
+ Network.Wai.Middleware.Routes.TH.Dispatch: mdsSubDispatcher :: MkDispatchSettings -> Q Exp
+ Network.Wai.Middleware.Routes.TH.Types: frCheck :: FlatResource a -> Bool
+ Network.Wai.Middleware.Routes.TH.Types: frDispatch :: FlatResource a -> Dispatch a
+ Network.Wai.Middleware.Routes.TH.Types: frName :: FlatResource a -> String
+ Network.Wai.Middleware.Routes.TH.Types: frParentPieces :: FlatResource a -> [(String, [Piece a])]
+ Network.Wai.Middleware.Routes.TH.Types: frPieces :: FlatResource a -> [Piece a]
+ Network.Wai.Middleware.Routes.TH.Types: instance Functor Dispatch
+ Network.Wai.Middleware.Routes.TH.Types: instance Functor Piece
+ Network.Wai.Middleware.Routes.TH.Types: instance Functor Resource
+ Network.Wai.Middleware.Routes.TH.Types: instance Functor ResourceTree
+ Network.Wai.Middleware.Routes.TH.Types: instance Lift t => Lift (Dispatch t)
+ Network.Wai.Middleware.Routes.TH.Types: instance Lift t => Lift (Piece t)
+ Network.Wai.Middleware.Routes.TH.Types: instance Lift t => Lift (Resource t)
+ Network.Wai.Middleware.Routes.TH.Types: instance Lift t => Lift (ResourceTree t)
+ Network.Wai.Middleware.Routes.TH.Types: instance Show typ => Show (Dispatch typ)
+ Network.Wai.Middleware.Routes.TH.Types: instance Show typ => Show (Piece typ)
+ Network.Wai.Middleware.Routes.TH.Types: instance Show typ => Show (Resource typ)
+ Network.Wai.Middleware.Routes.TH.Types: methodsMethods :: Dispatch typ -> [String]
+ Network.Wai.Middleware.Routes.TH.Types: methodsMulti :: Dispatch typ -> Maybe typ
+ Network.Wai.Middleware.Routes.TH.Types: resourceAttrs :: Resource typ -> [String]
+ Network.Wai.Middleware.Routes.TH.Types: resourceCheck :: Resource typ -> CheckOverlap
+ Network.Wai.Middleware.Routes.TH.Types: resourceDispatch :: Resource typ -> Dispatch typ
+ Network.Wai.Middleware.Routes.TH.Types: resourceName :: Resource typ -> String
+ Network.Wai.Middleware.Routes.TH.Types: resourcePieces :: Resource typ -> [Piece typ]
+ Network.Wai.Middleware.Routes.TH.Types: subsiteFunc :: Dispatch typ -> String
+ Network.Wai.Middleware.Routes.TH.Types: subsiteType :: Dispatch typ -> typ
+ Util.Free: instance Functor f => Applicative (F f)
+ Util.Free: instance Functor f => Functor (F f)
+ Util.Free: instance Functor f => Monad (F f)
+ Util.Free: runF :: F f a -> forall r. (a -> r) -> (f r -> r) -> r
- Network.Wai.Middleware.Routes: route :: (Routable master master) => master -> RouteM ()
+ Network.Wai.Middleware.Routes: route :: Routable master master => master -> RouteM ()
- Network.Wai.Middleware.Routes.Monad: route :: (Routable master master) => master -> RouteM ()
+ Network.Wai.Middleware.Routes.Monad: route :: Routable master master => master -> RouteM ()

Files

README.md view
@@ -1,26 +1,26 @@-[Wai-Routes](https://ajnsit.github.io/wai-routes) [![Hackage](https://img.shields.io/badge/hackage-v0.7.3-brightgreen.svg)](https://hackage.haskell.org/package/wai-routes) [![Hackage-Deps](https://img.shields.io/hackage-deps/v/wai-routes.svg)](http://packdeps.haskellers.com/feed?needle=wai-routes) [![Build Status](https://img.shields.io/travis/ajnsit/wai-routes.svg)](https://travis-ci.org/ajnsit/wai-routes) [![Coverage Status](https://coveralls.io/repos/ajnsit/wai-routes/badge.svg?branch=master&service=github)](https://coveralls.io/github/ajnsit/wai-routes?branch=master)+[Wai-Routes](https://ajnsit.github.io/wai-routes) [![Hackage](https://img.shields.io/badge/hackage-v0.8.0-brightgreen.svg)](https://hackage.haskell.org/package/wai-routes) [![Hackage-Deps](https://img.shields.io/hackage-deps/v/wai-routes.svg)](http://packdeps.haskellers.com/feed?needle=wai-routes) [![Build Status](https://img.shields.io/travis/ajnsit/wai-routes.svg)](https://travis-ci.org/ajnsit/wai-routes) [![Coverage Status](https://coveralls.io/repos/ajnsit/wai-routes/badge.svg?branch=master&service=github)](https://coveralls.io/github/ajnsit/wai-routes?branch=master) ==================================== -This package provides typesafe URLs for Wai applications.+Wai-routes is a micro web framework for Haskell that focuses on typesafe URLs. +Wai-routes is based on the Haskell [Web Application Interface](http://hackage.haskell.org/package/wai) and uses it for most of the heavy lifting. It also provides a convenient but thin veneer over most of the wai API so it is unnecessary to directly use raw wai APIs when building web apps.++Much of Wai-route's typesafe URL functionality was pulled from the corresponding features in [Yesod](http://www.yesodweb.com/), and indeed the underlying aim of wai-routes is - *"To provide a similar level of typesafe URL functionality to Wai applications as is available to Yesod applications."*.+ Features ========== -  - Automatic generation of Route boilerplate using TH-  - Easy Nested Routes-  - Subsites-  - General purpose Route Attributes/Annotations-  - Sitewide Master datatype which is passed to all handlers-    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.-  - HandlerM monad that makes it easy to build a Handler-    with access to Request data and Master datatype-  - Handlers can abort processing and pass control to the next-    application in the wai stack-  - Streaming responses+Wai-routes adds the following features on top of wai - -The aim is to provide a similar level of typesafe URL functionality to Wai applications as is available to Yesod applications.+  - Typesafe URLs, including automatic boilerplate generation using TH. Including features such as -+    - Nested Routes+    - Subsites+    - Route Annotations+  - Sitewide Master data which is passed to all handlers and can be used for persistent data (like DB connections)+  - Easy to use Handler Monad which allows direct access to request and master data+  - Easy composition of multiple routes and middleware to construct an application+  - Ability to abort processing and pass control to the next application in the wai stack+  - Streaming responses   Performance@@ -68,7 +68,8 @@  The following features are planned for later releases - -- Add support for raw network responses (see http://hackage.haskell.org/package/wai-3.0.3.0/docs/Network-Wai.html#v:responseRaw)+- Support for raw network responses (see http://hackage.haskell.org/package/wai-3.0.3.0/docs/Network-Wai.html#v:responseRaw)+- Seamless websocket support - Development mode - Keter and Heroku support - Scaffolding@@ -79,6 +80,7 @@ Changelog ========= +* 0.8.0 : Replaced 'show/renderRoute' with 'show/renderRouteSub' and 'show/renderRouteMaster'. Added functions to access request headers (reqHeader/s), send a part of a file (filepart). Auto infer mime-types when sending files. Added cookie handling functions (get/setCookie/s). Added 'sub' to allow access to subsite datatype. * 0.7.3 : Added 'stream' to stream responses. Added 'asContent', 'css', and 'javascript' functions. * 0.7.2 : Added 'file' to send a raw file directly, 'rawBody' and 'jsonBody' to consume request body. Refactored RouteM to add 'catchAll' and 'waiApp'. * 0.7.1 : Added 'showRouteQuery', renamed 'text' to 'plain', 'html' now accepts Text instead of ByteString
src/Network/Wai/Middleware/Routes.hs view
@@ -26,9 +26,12 @@     , routeDispatch      -- * URL rendering and parsing-    , showRoute-    , showRouteQuery-    , readRoute+    , showRouteMaster+    , showRouteQueryMaster+    , readRouteMaster+    , showRouteSub+    , showRouteQuerySub+    , readRouteSub      -- * Application Handlers     , Handler@@ -59,14 +62,20 @@     , HandlerM()     , runHandlerM            -- | Run a HandlerM to get a Handler     , request                -- | Access the request data+    , reqHeader              -- | Get a particular request header (case insensitive)+    , reqHeaders             -- | Get all request headers (case insensitive)     , maybeRootRoute         -- | Access the current route for root route     , maybeRoute             -- | Access the current route     , routeAttrSet           -- | Access the current route attributes as a set     , rootRouteAttrSet       -- | Access the current root route attributes as a set     , master                 -- | Access the master datatype+    , sub                    -- | Access the sub datatype+    , rawBody                -- | Consume and return the request body as a lazy bytestring+    , jsonBody               -- | Consume and return the request body as JSON     , header                 -- | Add a header to the response     , status                 -- | Set the response status     , file                   -- | Send a file as response+    , filepart               -- | Send a part of a file as response     , stream                 -- | Stream a response     , raw                    -- | Set the raw response body     , json                   -- | Set the json response body@@ -76,8 +85,9 @@     , javascript             -- | Set the javascript response body     , asContent              -- | Set the contentType and a 'Text' body     , next                   -- | Run the next application in the stack-    , rawBody                -- | Consume and return the request body as a lazy bytestring-    , jsonBody               -- | Consume and return the request body as JSON+    , setCookie              -- | Add a cookie to the response+    , getCookie              -- | Get a cookie from the request+    , getCookies             -- | Get all cookies from the request   )   where 
src/Network/Wai/Middleware/Routes/ContentTypes.hs view
@@ -13,7 +13,7 @@ -} module Network.Wai.Middleware.Routes.ContentTypes     ( -- * Construct content Type-      contentType+      contentType, contentTypeFromFile       -- * Various common content types     , typeHtml, typePlain, typeJson     , typeXml, typeAtom, typeRss@@ -23,9 +23,16 @@     )     where +import qualified Data.Text as T (pack) import Data.ByteString (ByteString) import Data.ByteString.Char8 () -- Import IsString instance for ByteString import Network.HTTP.Types.Header (HeaderName())+import Network.Mime (defaultMimeLookup)+import System.FilePath (takeFileName)++-- | Construct an appropriate content type header from a file name+contentTypeFromFile :: FilePath -> ByteString+contentTypeFromFile = defaultMimeLookup . T.pack . takeFileName  -- | Creates a content type header -- Ready to be passed to `responseLBS`
src/Network/Wai/Middleware/Routes/Handler.hs view
@@ -14,14 +14,26 @@     ( HandlerM()             -- | A Monad that makes it easier to build a Handler     , runHandlerM            -- | Run a HandlerM to get a Handler     , request                -- | Access the request data+    , reqHeader              -- | Get a particular request header (case insensitive)+    , reqHeaders             -- | Get all request headers (case insensitive)     , routeAttrSet           -- | Access the route attribute list     , rootRouteAttrSet       -- | Access the route attribute list for the root route     , maybeRoute             -- | Access the route data     , maybeRootRoute         -- | Access the root route data+    , showRouteMaster        -- | Get the route rendering function for the master site+    , showRouteSub           -- | Get the route rendering function for the subsite+    , showRouteQueryMaster   -- | Get the route + query params rendering function for the master site+    , showRouteQuerySub      -- | Get the route + query params rendering function for the subsite+    , readRouteMaster        -- | Get the route parsing function for the master site+    , readRouteSub           -- | Get the route parsing function for the subsite     , master                 -- | Access the master datatype+    , sub                    -- | Access the sub datatype+    , rawBody                -- | Consume and return the request body as a lazy bytestring+    , jsonBody               -- | Consume and return the request body as JSON     , header                 -- | Add a header to the response     , status                 -- | Set the response status     , file                   -- | Send a file as response+    , filepart               -- | Send a part of a file as response     , stream                 -- | Stream a response     , raw                    -- | Set the raw response body     , json                   -- | Set the json response body@@ -31,18 +43,19 @@     , javascript             -- | Set the javascript response body     , asContent              -- | Set the contentType and a 'Text' body     , next                   -- | Run the next application in the stack-    , rawBody                -- | Consume and return the request body as a lazy bytestring-    , jsonBody               -- | Consume and return the request body as JSON+    , setCookie              -- | Add a cookie to the response+    , getCookie              -- | Get a cookie from the request+    , getCookies             -- | Get all cookies from the request     )     where -import Network.Wai (Request, Response, responseFile, responseBuilder, responseStream, pathInfo, queryString, requestBody, StreamingBody)+import Network.Wai (Request, Response, responseFile, responseBuilder, responseStream, pathInfo, queryString, requestBody, StreamingBody, requestHeaders, FilePart) #if MIN_VERSION_wai(3,0,1) import Network.Wai (strictRequestBody) #endif-import Network.Wai.Middleware.Routes.Routes (Env(..), RequestData, HandlerS, waiReq, currentRoute, runNext, ResponseHandler)-import Network.Wai.Middleware.Routes.Class (Route, RouteAttrs(..))-import Network.Wai.Middleware.Routes.ContentTypes (contentType, typeHtml, typeJson, typePlain, typeCss, typeJavascript)+import Network.Wai.Middleware.Routes.Routes (Env(..), RequestData, HandlerS, waiReq, currentRoute, runNext, ResponseHandler, showRoute, showRouteQuery, readRoute)+import Network.Wai.Middleware.Routes.Class (Route, RenderRoute, ParseRoute, RouteAttrs(..))+import Network.Wai.Middleware.Routes.ContentTypes (contentType, contentTypeFromFile, typeHtml, typeJson, typePlain, typeCss, typeJavascript)  import Control.Monad (liftM) import Control.Monad.Loops (unfoldWhileM)@@ -54,7 +67,8 @@ import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL-import Network.HTTP.Types.Header (HeaderName())+import Blaze.ByteString.Builder (Builder, toByteString, fromLazyByteString)+import Network.HTTP.Types.Header (HeaderName(), RequestHeaders) import Network.HTTP.Types.Status (Status(), status200)  import Data.Aeson (ToJSON, FromJSON, eitherDecode)@@ -64,12 +78,16 @@ import qualified Data.Set as S (empty, map)  import Data.Text.Lazy (Text)+import qualified Data.Text as TS (Text) import qualified Data.Text.Lazy as T import Data.Text.Lazy.Encoding (encodeUtf8) import Data.Text.Encoding (decodeUtf8) -import Blaze.ByteString.Builder (fromLazyByteString)+import Data.CaseInsensitive (CI, mk) +import Web.Cookie (Cookies, parseCookies, renderCookies, renderSetCookie, SetCookie(..))+import Data.Default.Class (def)+ -- | The internal implementation of the HandlerM monad -- TODO: Should change this to StateT over ReaderT (but performance may suffer) newtype HandlerMI sub master m a = H { extractH :: StateT (HandlerState sub master) m a }@@ -87,19 +105,44 @@                 , reqBody        :: Maybe BL.ByteString                 , respHeaders    :: [(HeaderName, ByteString)]                 , respStatus     :: Status-                , respResp       :: Maybe ResponseHandler+                , respResp       :: MkResponse+                , respCookies    :: [SetCookie]                 , getSub         :: sub                 , toMasterRoute  :: Route sub -> Route master                 } +-- Internal: Type of response+-- Similar to Wai's Response type+data MkResponse+    = ResponseFile FilePath (Maybe FilePart)+    | ResponseBuilder Builder+    | ResponseStream StreamingBody+    -- Experimental: ResponseNext is the default, so if you don't respond in one handler, move to next automatically+    | ResponseNext++-- The header name for request cookies+cookieHeaderName :: CI ByteString+cookieHeaderName = mk "Cookie"++-- The header name for response cookies+cookieSetHeaderName :: CI ByteString+cookieSetHeaderName = mk "Set-Cookie"+ -- | "Run" HandlerM, resulting in a Handler runHandlerM :: HandlerM sub master () -> HandlerS sub master runHandlerM h env req hh = do-  (_, st) <- runStateT (extractH h) (HandlerState (envMaster env) req Nothing [] status200 Nothing (envSub env) (envToMaster env))-  case respResp st of-    -- Experimental, if you don't respond in one handler, move to next automatically-    Nothing -> runNext (getRequestData st) hh-    Just resp -> resp hh+  (_, st) <- runStateT (extractH h) (HandlerState (envMaster env) req Nothing [] status200 ResponseNext [] (envSub env) (envToMaster env))+  -- Handle cookies (add them to headers)+  let cookieHeaders = map mkSetCookie (respCookies st)+  let st' = st {respHeaders = cookieHeaders ++ (respHeaders st)}+  -- Construct response+  mkResponse st' (respResp st')+  where+    mkSetCookie s = (cookieSetHeaderName, toByteString $ renderSetCookie s)+    mkResponse st (ResponseFile path part) = hh $ responseFile (respStatus st) (respHeaders st) path part+    mkResponse st (ResponseBuilder builder) = hh $ responseBuilder (respStatus st) (respHeaders st) builder+    mkResponse st (ResponseStream streaming) = hh $ responseStream (respStatus st) (respHeaders st) streaming+    mkResponse st ResponseNext = runNext (getRequestData st) hh  -- | Get the request body as a lazy bytestring. However consumes the entire body at once. -- TODO: Implement streaming. Prevent clash with direct use of `Network.Wai.requestBody`@@ -109,18 +152,19 @@   case reqBody s of     Just consumedBody -> return consumedBody     Nothing -> do-#if MIN_VERSION_wai(3,0,1)-      -- Use the `strictRequestBody` function available in wai > 3.0.1       req <- request-      rbody <- liftIO $ strictRequestBody req+      rbody <- liftIO $ readStrictRequestBody req       put s {reqBody = Just rbody}       return rbody++readStrictRequestBody :: Request -> IO BL.ByteString+readStrictRequestBody =+#if MIN_VERSION_wai(3,0,1)+        -- Use the `strictRequestBody` function available in wai > 3.0.1+        strictRequestBody #else-      -- Consume the entire body, and cache-      chunker <- fmap requestBody request-      consumedBody <- liftIO $ BL.fromChunks <$> unfoldWhileM (not . B.null) chunker-      put s {reqBody = Just consumedBody}-      return consumedBody+        -- Consume the entire body, and cache+        BL.fromChunks <$> unfoldWhileM (not . B.null) . requestBody #endif  -- | Parse the body as a JSON object@@ -139,6 +183,14 @@ request :: HandlerM sub master Request request = liftM (waiReq . getRequestData) get +-- | Get a particular request header (Case insensitive)+reqHeader :: ByteString -> HandlerM sub master (Maybe ByteString)+reqHeader name = liftM (lookup $ mk name) reqHeaders++-- | Get all request headers (Case insensitive)+reqHeaders :: HandlerM sub master RequestHeaders+reqHeaders = liftM requestHeaders request+ -- | Get the current route maybeRoute :: HandlerM sub master (Maybe (Route sub)) maybeRoute = liftM (currentRoute . getRequestData) get@@ -149,6 +201,36 @@   s <- get   return $ fmap (toMasterRoute s) $ currentRoute $ getRequestData s +-- | Get the route rendering function for the master site+showRouteMaster :: RenderRoute master => HandlerM sub master (Route master -> TS.Text)+showRouteMaster = return showRoute++-- | Get the route rendering function for the subsite+showRouteSub :: RenderRoute master => HandlerM sub master (Route sub -> TS.Text)+showRouteSub = do+  s <- get+  return $ showRoute . toMasterRoute s++-- | Get the route rendering function for the master site+showRouteQueryMaster :: RenderRoute master => HandlerM sub master (Route master -> [(TS.Text,TS.Text)] -> TS.Text)+showRouteQueryMaster = return showRouteQuery++-- | Get the route rendering function for the subsite+showRouteQuerySub :: RenderRoute master => HandlerM sub master (Route sub -> [(TS.Text,TS.Text)] -> TS.Text)+showRouteQuerySub = do+  s <- get+  return $ showRouteQuery . toMasterRoute s++-- | Get the route parsing function for the master site+readRouteMaster :: ParseRoute master => HandlerM sub master (TS.Text -> Maybe (Route master))+readRouteMaster = return readRoute++-- | Get the route parsing function for the subsite+readRouteSub :: ParseRoute sub => HandlerM sub master (TS.Text -> Maybe (Route master))+readRouteSub = do+  s <- get+  return $ fmap (toMasterRoute s) . readRoute+ -- | Get the current route attributes routeAttrSet :: RouteAttrs sub => HandlerM sub master (Set Text) routeAttrSet = liftM (S.map T.fromStrict . maybe S.empty routeAttrs . currentRoute . getRequestData) get@@ -174,43 +256,47 @@     setStatus :: Status -> HandlerState sub master -> HandlerState sub master     setStatus s st = st{respStatus=s} --- | Set the response body to a file+-- | Send a file as response file :: FilePath -> HandlerM sub master ()-file f = modify addFile+file f = do+  header contentType $ contentTypeFromFile f+  modify addFile   where-    addFile st = setResp st $ responseFile (respStatus st) (respHeaders st) f Nothing+    addFile st = _setResp st $ ResponseFile f Nothing +-- | Send a part of a file as response+filepart :: FilePath -> FilePart -> HandlerM sub master ()+filepart f part = do+  header contentType $ contentTypeFromFile f+  modify addFile+  where+    addFile st = _setResp st $ ResponseFile f (Just part)+ -- | Stream the response stream :: StreamingBody -> HandlerM sub master () stream s = modify addStream   where-    addStream st = setResp st $ responseStream (respStatus st) (respHeaders st) s+    addStream st = _setResp st $ ResponseStream s  -- | Set the response body raw :: BL.ByteString -> HandlerM sub master () raw bs = modify addBody   where-    addBody st = setResp st $ responseBuilder (respStatus st) (respHeaders st) (fromLazyByteString bs)+    addBody st = _setResp st $ ResponseBuilder (fromLazyByteString bs)  -- | Run the next application next :: HandlerM sub master ()-next = do-  respHandler <- fmap (runNext . getRequestData) get-  modify $ setRespHandler respHandler---- Util--- Set the response directly--- This is a bit convulated to enable clean usage in calling functions-setResp :: HandlerState sub master -> Response -> HandlerState sub master-setResp st r = setRespHandler ($ r) st+next = modify rNext+  where+    rNext st = _setResp st ResponseNext  -- Util -- Set the response handler--- Don't overwrite previous response handler-setRespHandler :: ResponseHandler -> HandlerState sub master -> HandlerState sub master-setRespHandler r st = case respResp st of-  Just _ -> st-  Nothing -> st{respResp=Just r}+-- Experimental: Don't overwrite previous response handler+_setResp :: HandlerState sub master -> MkResponse -> HandlerState sub master+_setResp st r = case respResp st of+  ResponseNext -> st{respResp=r}+  _ -> st  -- Standard response bodies @@ -249,3 +335,24 @@   header contentType ctype   raw $ encodeUtf8 content +-- | Sets a cookie to the response+setCookie :: SetCookie -> HandlerM sub master ()+setCookie s = modify setCookie+  where+    setCookie st = st {respCookies = s : respCookies st}++-- | Get all cookies+getCookies :: HandlerM sub master Cookies+getCookies = do+  -- Note: We don't cache the parsedCookies for all requests to avoid overhead+  -- However it is pretty easy to cache cookies in the app itself+  cookies <- reqHeader "Cookie"+  return $ case cookies of+    Nothing -> []+    Just cookies' -> parseCookies cookies'++-- | Get a particular cookie+getCookie :: ByteString -> HandlerM sub master (Maybe ByteString)+getCookie name = do+  cookies <- getCookies+  return $ lookup name cookies
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
wai-routes.cabal view
@@ -1,5 +1,5 @@ name          : wai-routes-version       : 0.7.3+version       : 0.8.0 cabal-version : >=1.18 build-type    : Simple license       : MIT@@ -20,23 +20,28 @@  source-repository this     type     : git-    location : http://github.com/ajnsit/wai-routes/tree/v0.7.3-    tag      : v0.7.3+    location : http://github.com/ajnsit/wai-routes/tree/v0.8.0+    tag      : v0.8.0  library-    build-depends: base             >= 4.7  && < 4.9-                 , wai              >= 3.0  && < 3.1-                 , text             >= 1.2  && < 1.3-                 , template-haskell >= 2.9  && < 2.11-                 , mtl              >= 2.1  && < 2.3-                 , aeson            >= 0.8  && < 0.10-                 , containers       >= 0.5  && < 0.6-                 , random           >= 1.1  && < 1.2-                 , path-pieces      >= 0.2  && < 0.3-                 , bytestring       >= 0.10 && < 0.11-                 , http-types       >= 0.8  && < 0.9-                 , blaze-builder    >= 0.4  && < 0.5-                 , monad-loops      >= 0.4  && < 0.5+    build-depends: base               >= 4.7  && < 4.9+                 , wai                >= 3.0  && < 3.1+                 , text               >= 1.2  && < 1.3+                 , template-haskell   >= 2.9  && < 2.11+                 , mtl                >= 2.1  && < 2.3+                 , aeson              >= 0.8  && < 0.10+                 , containers         >= 0.5  && < 0.6+                 , random             >= 1.1  && < 1.2+                 , path-pieces        >= 0.2  && < 0.3+                 , bytestring         >= 0.10 && < 0.11+                 , http-types         >= 0.8  && < 0.9+                 , blaze-builder      >= 0.4  && < 0.5+                 , monad-loops        >= 0.4  && < 0.5+                 , case-insensitive   >= 1.2  && < 1.3+                 , mime-types         >= 0.1  && < 0.2+                 , filepath           >= 1.3  && < 1.5+                 , cookie             >= 0.4  && < 0.5+                 , data-default-class >= 0.0  && < 0.1     exposed-modules: Network.Wai.Middleware.Routes                      Network.Wai.Middleware.Routes.Parse                      Network.Wai.Middleware.Routes.Overlap@@ -56,3 +61,18 @@     buildable      : True     hs-source-dirs : src     default-language   : Haskell2010++test-suite test+  main-is:             Spec.hs+  type:                exitcode-stdio-1.0+  default-language:    Haskell2010+  hs-source-dirs:      test+  GHC-options:         -Wall -threaded -fno-warn-orphans++  build-depends: base           >= 4.7 && < 4.9+               , wai            >= 3.0 && < 3.1+               , aeson          >= 0.8 && < 0.9+               , hspec          >= 2.1 && < 2.2+               , hspec-wai      >= 0.6 && < 0.7+               , hspec-wai-json >= 0.6 && < 0.7+               , wai-routes