packages feed

wai-middleware-route 0.3.1 → 0.4.0

raw patch · 2 files changed

+246/−87 lines, 2 filesdep +textdep +yesod-routesdep −regex-posixdep ~waiPVP ok

version bump matches the API change (PVP)

Dependencies added: text, yesod-routes

Dependencies removed: regex-posix

Dependency ranges changed: wai

API changes (from Hackage documentation)

- Network.Wai.Middleware.Route: onExact :: Method -> ByteString -> Request -> Bool
- Network.Wai.Middleware.Route: onPrefix :: Method -> ByteString -> Request -> Bool
- Network.Wai.Middleware.Route: onRegex :: Method -> ByteString -> Request -> Bool
+ Network.Wai.Middleware.Route: mANY :: Text -> Application -> Route Application
+ Network.Wai.Middleware.Route: mANY' :: [Piece] -> Application -> Route Application
+ Network.Wai.Middleware.Route: mGET', mOPTIONS', mCONNECT', mTRACE', mDELETE', mPUT', mHEAD', mPOST' :: [Piece] -> Application -> Route Application
+ Network.Wai.Middleware.Route: mGET, mOPTIONS, mCONNECT, mTRACE, mDELETE, mPUT, mHEAD, mPOST :: Text -> Application -> Route Application
+ Network.Wai.Middleware.Route: mRoute :: Piece -> [Piece] -> Application -> Route Application
+ Network.Wai.Middleware.Route: mkP :: Text -> [Piece]
+ Network.Wai.Middleware.Route: sANY :: Text -> Application -> Route Application
+ Network.Wai.Middleware.Route: sANY' :: [Piece] -> Application -> Route Application
+ Network.Wai.Middleware.Route: sGET', sOPTIONS', sCONNECT', sTRACE', sDELETE', sPUT', sHEAD', sPOST' :: [Piece] -> Application -> Route Application
+ Network.Wai.Middleware.Route: sGET, sOPTIONS, sCONNECT, sTRACE, sDELETE, sPUT, sHEAD, sPOST :: Text -> Application -> Route Application
+ Network.Wai.Middleware.Route: sRoute :: Piece -> [Piece] -> Application -> Route Application
- Network.Wai.Middleware.Route: dispatch :: [(Request -> Bool, Application)] -> Application -> Application
+ Network.Wai.Middleware.Route: dispatch :: Dispatch Application -> Application -> Application

Files

src/Network/Wai/Middleware/Route.hs view
@@ -1,86 +1,243 @@-{-# LANGUAGE OverloadedStrings, NamedFieldPuns #-}
-
-{- | Route middleware for wai.
+{-# LANGUAGE OverloadedStrings #-}
 
-It's Heavy inspired by @vhost@ middleware from @wai-extra@ but 
-gives some sugar to life.
--}
+-- | This module contains helpers for use "Yesod.Routes.Dispatch" with 
+--   "Network.Wai".
+--
+--   This 'Middleware' uses first 'D.Piece' in path to route @HTTP@ method.
+--   'D.Static' means concrete method. 'D.Dynamic' means any method.
 
 module Network.Wai.Middleware.Route (
-    -- * Dispatching
-    dispatch, 
-    -- * Creating rules
-    onRegex,
-    onPrefix,
-    onExact
+
+    -- * Middleware
+    dispatch,
+
+    -- * Route helpers #route_helpers#
+    -- $route_helpers
+    
+    -- ** Simple paths
+    -- $simple_paths
+    
+    -- *** Fixed length
+    sGET, sPOST, sHEAD, sPUT, sDELETE, sTRACE, sCONNECT, sOPTIONS,
+    sANY,
+    -- *** Variable length
+    mGET, mPOST, mHEAD, mPUT, mDELETE, mTRACE, mCONNECT, mOPTIONS,
+    mANY, 
+    
+    -- ** Native paths
+    -- *** Fixed length
+    sGET', sPOST', sHEAD', sPUT', sDELETE', sTRACE', sCONNECT', sOPTIONS',
+    sANY', sRoute,
+    -- *** Variable length
+    mGET', mPOST', mHEAD', mPUT', mDELETE', mTRACE', mCONNECT', mOPTIONS',
+    mANY', mRoute,
+    -- * 'D.Piece' helper
+    mkP
 ) where
 
-import Data.List (find)
-import Data.ByteString (ByteString, isPrefixOf)
-import Text.Regex.Posix ((=~))
 import Control.Applicative ((<$>), (<*>))
-import Network.HTTP.Types (Method)
-import Network.Wai (Request(..), Application)
 
-{- | Dispatch. Seek for first route where the \"rule\" gives a positive result.
-Uses 'find' instead 'filter' in @vhost@
+import qualified Data.Text as T
+import Data.Text.Encoding (decodeUtf8)
 
-> dispatch [
->     ((=="/") . rawPathInfo, auth . cache . Issues.get)
->   , ((=="/issues") . rawPathInfo, cache . About.handle)
->   ] defaultApp
--}
+import Network.Wai (Application, requestMethod, pathInfo)
+import qualified Yesod.Routes.Dispatch as D
+
+-----------------------------------------------------------------------------
+-- Docs chunks
+-----------------------------------------------------------------------------
+
+-- $route_helpers
+-- Functions below simplify process of creating 'D.Route's. Each helper 
+-- prepends corresponding @HTTP@ method to path.
+
+-- $simple_paths
+-- All functions below is combinations of native path helpers and 'mkP'.
+
+-----------------------------------------------------------------------------
+-- Middleware.
+-----------------------------------------------------------------------------
+
+-- | Dispatch function.
+-- 
+-- > rs :: Dispatch Application
+-- > rs = toDispatch [
+-- >      -- simple paths 
+-- >      sGET  "foo"  fooGetApp
+-- >    , sPOST "foo"  fooPostApp
+-- >    , sGET  "foo/" fooGetDynApp
+-- > 
+-- >      -- native paths
+-- >    , sGET'  [Static "bar", Dynamic] barGetDynApp
+-- >
+-- >      -- simple paths, any method 
+-- >    , sANY  "any"  anyMethodApp
+-- >    ]
+-- > 
+-- > app :: Application
+-- > app = dispatch rs (error "Not dispatched")
+
 dispatch :: 
-       [(Request -> Bool, Application)]      
-                    -- ^ List of routes
-    -> Application  -- ^ Default 'Application' 
-    -> Application  -- ^ Returns founded 'Application'. If not found - returns
-                    --   default.
-dispatch routes def req =  
-    case find (\(b, _) -> b req) routes of
-        Just (_, app) -> app req
-        Nothing -> def req
+       D.Dispatch Application   
+            -- ^ Dispatch function. 
+            --   Use 'D.toDispatch' and route helpers below.
+    -> Application 
+            -- ^ Default (@404@) application. 
+    -> Application
+dispatch mappings defApp req = 
+    case mappings . needle $ req of
+        Nothing -> defApp req
+        (Just app) -> app req 
+  where
+    needle = (:) <$> decodeUtf8 . requestMethod <*> pathInfo
 
--- | Most frequently case: HTTP Method and Request path regex pattern.
---
--- > ("*" `rule` "^/issue/", app)
--- > (methodGet `rule` "^/issues$", anotherApp)
-onRegex :: 
-       Method       -- ^ HTTP Method. Use @\"*\"@ for any method
-    -> ByteString   -- ^ Request path pattern. 
-    -> Request      -- ^ Request 
-    -> Bool
-onRegex method needle = 
-    withMethod method $ (=~ needle) . rawPathInfo
-    
--- | HTTP Method and Request path prefix.
---
--- > ("*" `rule` "^/issue/", app)
--- > (methodGet `rule` "^/issues$", anotherApp)
-onPrefix :: 
-       Method       -- ^ HTTP Method. Use @\"*\"@ for any method
-    -> ByteString   -- ^ Request path prefix. 
-    -> Request      -- ^ Request 
-    -> Bool         -- ^ Routing rule
-onPrefix method needle = 
-    withMethod method $ (needle `isPrefixOf`) . rawPathInfo
+-----------------------------------------------------------------------------
+-- 'D.Route' helpers for simple paths.
+-----------------------------------------------------------------------------
 
--- | HTTP Method and Request path.
+-- | 'D.Route' helpers for concrete @HTTP@ methods with fixed-length 
+--   simple path.
+sGET, sPOST, sHEAD, sPUT, sDELETE, sTRACE, sCONNECT, sOPTIONS :: 
+       T.Text       -- ^ Path
+    -> Application  -- ^ Routable application 
+    -> D.Route Application
+sGET = sGET' . mkP
+sPOST = sPOST' . mkP
+sHEAD = sHEAD' . mkP
+sPUT = sPUT' . mkP
+sDELETE = sDELETE' . mkP
+sTRACE = sTRACE' . mkP
+sCONNECT = sCONNECT' . mkP
+sOPTIONS = sOPTIONS' . mkP
+
+-- | 'D.Route' helper for any @HTTP@ method with fixed-length simple path.
+sANY :: 
+       T.Text       -- ^ Path
+    -> Application  -- ^ Routable application 
+    -> D.Route Application
+sANY = sRoute D.Dynamic . mkP
+
+-- | 'D.Route' helpers for concrete @HTTP@ methods with vary-length 
+--   simple path.
+mGET, mPOST, mHEAD, mPUT, mDELETE, mTRACE, mCONNECT, mOPTIONS :: 
+       T.Text       -- ^ Path
+    -> Application  -- ^ Routable application 
+    -> D.Route Application
+mGET = mGET' . mkP
+mPOST = mPOST' . mkP
+mHEAD = mHEAD' . mkP
+mPUT = mPUT' . mkP
+mDELETE = mDELETE' . mkP
+mTRACE = mTRACE' . mkP
+mCONNECT = mCONNECT' . mkP
+mOPTIONS = mOPTIONS' . mkP
+
+-- | 'D.Route' helper for any @HTTP@ method with vary-length simple path.
+mANY :: 
+       T.Text       -- ^ Path
+    -> Application  -- ^ Routable application 
+    -> D.Route Application
+mANY = mRoute D.Dynamic . mkP
+
+-----------------------------------------------------------------------------
+-- 'D.Route' helpers for native 'D.Piece' paths (Fixed-length)
+-----------------------------------------------------------------------------
+
+-- | 'D.Route' helpers for concrete @HTTP@ methods with fixed-length native 
+--   @yesod-routes@ path.
+sGET', sPOST', sHEAD', sPUT', sDELETE', sTRACE', sCONNECT', sOPTIONS' :: 
+       [D.Piece]        -- ^ Path
+    -> Application      -- ^ Routable application
+    -> D.Route Application
+sGET' = sRoute (D.Static "GET")
+sPOST' = sRoute (D.Static "POST")
+sHEAD' = sRoute (D.Static "HEAD")
+sPUT' = sRoute (D.Static "PUT")
+sDELETE' = sRoute (D.Static "DELETE")
+sTRACE' = sRoute (D.Static "TRACE")
+sCONNECT' = sRoute (D.Static "CONNECT")
+sOPTIONS' = sRoute (D.Static "OPTIONS")
+
+-- | 'D.Route' helper for any @HTTP@ method with fixed-length native 
+--   @yesod-routes@ path.
+sANY' :: 
+       [D.Piece]        -- ^ Path
+    -> Application      -- ^ Routable application
+    -> D.Route Application
+sANY' = sRoute D.Dynamic
+
+-- | Generalized 'D.Route' helper for fixed-length native 
+--   @yesod-routes@ path.
+sRoute ::  
+       D.Piece      -- ^ Method piece. 'D.Dynamic' means any method. 
+    -> [D.Piece]    -- ^ Path pieces
+    -> Application  -- ^ Routed application
+    -> D.Route Application
+sRoute = (`defRoute` False)
+
+-----------------------------------------------------------------------------
+-- Generalized 'D.Route' helpers for native 'D.Piece' paths
+-----------------------------------------------------------------------------
+
+-- | 'D.Route' helpers for concrete @HTTP@ methods with vary-length native 
+--   @yesod-routes@ path.
+mGET', mPOST', mHEAD', mPUT', mDELETE', mTRACE', mCONNECT', mOPTIONS' :: 
+       [D.Piece]        -- ^ Path
+    -> Application      -- ^ Routable application
+    -> D.Route Application
+mGET' = mRoute (D.Static "GET")
+mPOST' = mRoute (D.Static "POST")
+mHEAD' = mRoute (D.Static "HEAD")
+mPUT' = mRoute (D.Static "PUT")
+mDELETE' = mRoute (D.Static "DELETE")
+mTRACE' = mRoute (D.Static "TRACE")
+mCONNECT' = mRoute (D.Static "CONNECT")
+mOPTIONS' = mRoute (D.Static "OPTIONS")
+
+-- | 'D.Route' helper for any @HTTP@ method with fixed-length native 
+--   @yesod-routes@ path.
+mANY' :: 
+       [D.Piece]        -- ^ Path
+    -> Application      -- ^ Routable application
+    -> D.Route Application
+mANY' = sRoute D.Dynamic
+
+-- | Generalized 'D.Route' helper for vary-length native 
+--   @yesod-routes@ path.
+mRoute ::  
+       D.Piece      -- ^ Method piece. 'D.Dynamic' means any method. 
+    -> [D.Piece]    -- ^ Path pieces
+    -> Application  -- ^ Routed application
+    -> D.Route Application
+mRoute = (`defRoute` True)
+
+-----------------------------------------------------------------------------
+-- 'D.Piece' helpers
+-----------------------------------------------------------------------------
+
+-- | Make 'D.Piece's from 'T.Text'. Splits path on slashes. Dual slashes means
+--   'D.Dynamic' 'D.Piece's.
 --
--- > ("*" `rule` "^/issue/", app)
--- > (methodGet `rule` "^/issues$", anotherApp)
-onExact :: 
-       Method       -- ^ HTTP Method. Use @\"*\"@ for any method
-    -> ByteString   -- ^ Request path. 
-    -> Request      -- ^ Request 
-    -> Bool         -- ^ Routing rule
-onExact method needle = 
-    withMethod method $ (needle ==) . rawPathInfo
+-- > mkP ""             -- []
+-- > mkP "foo/bar"      -- [Static "foo", Static "bar"]
+-- > mkP "foo//bar/"    -- [Static "foo", Dynamic, Static "bar", Dynamic]
+mkP :: T.Text -> [D.Piece]
+mkP = 
+    map chunk . T.split (=='/')
+  where
+    chunk "" = D.Dynamic
+    chunk c = D.Static c
 
-withMethod :: 
-       Method
-    -> (Request -> Bool) 
-    -> Request 
-    -> Bool
-withMethod "*" p = p
-withMethod m p = (&&) <$> (== m) . requestMethod <*> p
+-------------------------------------------------------------------------------
+---- Internal
+-------------------------------------------------------------------------------
+
+defRoute :: 
+       D.Piece      -- ^ Method piece. 'D.Dynamic' means any method. 
+    -> Bool         -- ^ 'D.rhHasMulti'
+    -> [D.Piece]    -- ^ Path pieces
+    -> Application  -- ^ Routed application
+    -> D.Route Application
+defRoute m hasMulti pieces = 
+    D.Route (m:pieces) hasMulti . const . Just 
+
wai-middleware-route.cabal view
@@ -1,29 +1,31 @@ name:           wai-middleware-route
-version:        0.3.1
+version:        0.4.0
 cabal-version:  >= 1.8
 build-type:     Simple
-synopsis:       Wai routing middleware
+synopsis:       Wai dispatch middleware
 stability:      Experimental
-author:         Alexander Dorofeev
+author:         Alexander Dorofeev <aka.spin@gmail.com>
+maintainer:     Alexander Dorofeev <aka.spin@gmail.com>
 category:       Web
 license:        BSD3
-description:    \"Just enough\" request dispatching Middleware for Wai.
-maintainer:     aka.spin@gmail.com
+description:    \"Just enough\" helpers for use @yesod-routes@ with @WAI@.
 license-file:   LICENSE
 homepage:       https://github.com/akaspin/wai-middleware-route
 
 Source-repository head
-  type:            git
-  location:        git://github.com/akaspin/wai-middleware-route.git
+  type:      git
+  location:  git://github.com/akaspin/wai-middleware-route.git
 
 library
-  hs-source-dirs:  src
-  build-depends:   
+  hs-source-dirs:   src
+  build-depends:    
                    base >= 4 && < 5,
-                   wai >= 1.1,
                    bytestring >= 0.9 && < 0.10,
-                   regex-posix >= 0.94.4,
-                   http-types >= 0.6 && < 0.7
-  ghc-options:     -Wall -rtsopts
-  exposed-modules: Network.Wai.Middleware.Route
+                   http-types >= 0.6 && < 0.7,
+                   text,
+                   wai >= 1.2,
+                   yesod-routes
+  ghc-options:      -Wall -rtsopts
+  exposed-modules:  Network.Wai.Middleware.Route
+