packages feed

urldisp-happstack (empty) → 0.1

raw patch · 4 files changed

+232/−0 lines, 4 filesdep +basedep +bytestringdep +happstack-serversetup-changed

Dependencies added: base, bytestring, happstack-server, mtl

Files

+ Happstack/UrlDisp.hs view
@@ -0,0 +1,180 @@+{-# LANGUAGE FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts, MultiParamTypeClasses #-}+module Happstack.UrlDisp (+    -- * Run the monad+    runUrlDisp,+    -- * Verbose API+    path, meth, param, takePath, readPath, endPath, getInput, getInputMay,+    -- * Infix API+    h, (|/), (|//), (|?), (|\), (|\\), (|.),+    UrlS(..), UrlDisp(..),+    -- * Lifted catch+    spCatch+  ) where++import Prelude hiding (tail, init, head, last, minimum, maximum, foldr1, foldl1, (!!), read)+import qualified Happstack.Server as HS+import Happstack.Server hiding (path)+import Control.Applicative+import qualified Control.Exception as C+import Control.Monad.State.Strict+import Data.Char+import qualified Data.ByteString.Lazy.Char8 as BS++spCatch :: (C.Exception e) =>+           ServerPartT IO a -> (e -> ServerPartT IO a) -> ServerPartT IO a+spCatch sp exhandler = mapServerPartT' go sp+    where go req act = (act >>= forceIt) `C.catch` \e -> ununWebT $ runServerPartT (exhandler e) req+          forceIt Nothing = return Nothing+          forceIt x@(Just (eitherResp,_)) = do+            either (\y -> C.evaluate y >>= const (return ()))+                   (\y -> C.evaluate y >>= const (return ()))+                    eitherResp+            return x+++data UrlS = UrlS { pPath :: [String] }++newtype UrlDisp m a = UrlDisp {unUrlDisp :: (StateT UrlS m a)}+    deriving (Functor, Monad, MonadState UrlS, ServerMonad, MonadIO, Applicative, MonadPlus)+++instance (Monad m) => WebMonad Response (UrlDisp (ServerPartT m)) where+    finishWith r = lift $ anyRequest $ finishWith r++instance (FilterMonad Response m,  Monad m) => FilterMonad Response (StateT UrlS m) where+    setFilter f = lift (setFilter f)+    composeFilter f = lift (composeFilter f)+    getFilter f = StateT $ \st -> getFilter (runStateT f st) >>=+                                 \((b,s),fun) -> return ((b,fun),s)++instance (FilterMonad Response m, Monad m) => FilterMonad Response (UrlDisp m) where+    setFilter f = lift (setFilter f)+    composeFilter f = lift (composeFilter f)+    getFilter f = UrlDisp .  getFilter . unUrlDisp $ f++instance MonadTrans UrlDisp where+    lift m = UrlDisp $ lift m++instance ServerMonad m => ServerMonad (StateT UrlS m) where+    askRq = lift askRq+    localRq f m = StateT $ localRq f . runStateT m++instance Monad m => Applicative (StateT UrlS m) where+    pure = return+    (<*>) = ap++instance (Monad m, MonadPlus m, Functor m) => Alternative (UrlDisp m) where+    empty = mzero+    (<|>) = mplus+++instance Alternative (ServerPartT IO) where+    empty = mzero+    (<|>) = mplus++maybeRead :: Read a => String -> Maybe a+maybeRead s = case reads s of+    [(x,"")] -> Just x+    _ -> Nothing++-- | Unpacks a UrlDisp into a plain old ServerMonad. Used as a top-level wrapper.+runUrlDisp :: ServerMonad m => UrlDisp m a -> m a+runUrlDisp d = evalStateT (unUrlDisp d) . UrlS . rqPaths =<< askRq++-- | Filters on and consumes the next element of the url path.+-- @ path \"str\" @ will match requests whose next path element is \"str\"+-- Consumption of the path element backtracks on failure.+path :: (MonadState UrlS m, Alternative m) => String -> m ()+path x = pPath <$> get >>= f where+    f (p:ps) = if p == x then modify (\par -> par {pPath = ps}) else empty+    f _ = empty++-- | Filters on the request method.+-- @ meth \"GET\" @ will match requests made using get.+meth :: (ServerMonad m, MonadPlus m) => String -> m ()+meth m = case (maybeRead :: String -> Maybe Method) (map toUpper m) of+           Nothing -> mzero+           Just x  -> methodOnly x++-- | Filters on any parameter (via put or get).+-- @ param (\"cmd\", \"foo\") @ will match on ?cmd=foo+param :: (ServerMonad m, Alternative m) => (String, String) -> m ()+param (k,v) = getInput k >>= \x -> if x == v then return () else empty++-- | Returns a string representation of a parameter, if available. Otherwise fails.+getInput :: (ServerMonad f, Alternative f) => String -> f String+getInput x  = maybe empty return . fmap (BS.unpack . inputValue) . lookup x . rqInputs =<< askRq++-- | Returns Just a string representation of a parameter, or Nothing.+getInputMay :: (ServerMonad f, Alternative f) => String -> f (Maybe String)+getInputMay x  = fmap (BS.unpack . inputValue) . lookup x . rqInputs <$> askRq++-- | Matches and consumes the next element of the path if+-- that element can be successfully read as the proper type. The parsed+-- element is returned.+readPath :: (Read a, MonadState UrlS m, Alternative m) => m a+readPath = pPath <$> get >>= f where+    f (p:ps) = maybe empty ((modify (\par -> par {pPath = ps}) >>) . return) (maybeRead p)+    f _ = empty++-- | Combinator that consumes the next element of the path and passes it+-- as an unparsed string into the following lambda expression.+-- @ h `takePath` \\x -> output (x++\"99\") @ will match on \"\/12\" and+-- output \"1299\"+-- Consumption of the path element backtracks on failure.+takePath :: (MonadState UrlS m, Alternative m) => m String+takePath = pPath <$> get >>= f where+    f (p:ps) = modify (\par -> par {pPath = ps}) >> return p+    f _ = empty++-- | Only matches if the remaining path is empty.+endPath :: (MonadState UrlS m, Alternative m) => m ()+endPath = pPath <$> get >>= \pss -> if null pss then return () else empty+++-- another variant of the API++infixl 4 |/, |//, |?, |\, |\\, |.+++-- | A null CGI action, used to begin a string of path combinators+h :: (ServerMonad m) => m ()+h = return ()++-- | Combinator that filters on and consumes the next element of the url+-- path.+-- @ h |\/ \"dir\" |\/ \"subdir\" @ will match \"\/dir\/subdir\".+-- Consumtion of the path element backtracks on failure.+(|/) :: (MonadState UrlS m, Alternative m) => m a -> String -> m ()+x |/ y = x >> path y++-- | Combinator that filters on the request method.+-- @ h |\/\/ \"GET\" @ will match requests made using get.+(|//) :: (ServerMonad m, MonadPlus m) => m a -> String -> m ()+x |// y = x >> meth y++-- | Combinator that filters on any parameter (via put or get).+-- @ h |? (\"cmd\",\"foo\") @ will match on ?cmd=foo+(|?) :: (ServerMonad m, Alternative m) => m a -> (String, String) -> m ()+x |? y = x >> param y++-- | Combinator that matches and consumes the next element of the path+-- if path element can be successfully read as the proper type and passed+-- to the following lambda expression.+-- @ h |\\ \\x -> output (x + (1.5::Float)) @ will match on \"\/12\"+-- and output \"13.5\". Consumption of the path element backtracks+-- on failure.+(|\) :: (Read x, MonadState UrlS m, Alternative m) => m a -> (x -> m b) -> m b+x |\ f = x >> readPath >>= f++-- | Combinator that consumes the next element of the path and passes it+-- as an unparsed string into the following lambda expression.+-- @ h |\\\\ \\x -> output (x++\"99\") @ will match on \"\/12\"+-- and output \"1299\"+-- Consumtion of the path element backtracks on failure.+(|\\) :: (MonadState UrlS m, Alternative m) => m a -> (String -> m b) -> m b+x |\\ f = x >> takePath >>= f++-- | Combinator that only matches if the remaining path is empty.+(|.) :: (MonadState UrlS m, Alternative m) => m a -> m  b -> m b+x |. f = x >> endPath >> f
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) Sterling Clover 2009++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ urldisp-happstack.cabal view
@@ -0,0 +1,23 @@+name:                urldisp-happstack+version:             0.1+synopsis:            Simple, declarative, expressive URL routing -- on happstack.+description:         UrlDisp combinators ported over happstack, as well as a lifted catch.+category:            Language+license:             BSD3+license-file:        LICENSE+author:              Sterling Clover+maintainer:          s.clover@gmail.com+Tested-With:         GHC == 6.10.3+Build-Type:          Simple+Cabal-Version:       >= 1.6++library+  build-depends:     base >= 4, base < 5, mtl, bytestring, happstack-server+  exposed-modules:   Happstack.UrlDisp+  ghc-options:       -Wall+  if impl(ghc >= 6.8)+    ghc-options:     -fwarn-tabs++source-repository head+  type:     darcs+  location: http://code.haskell.org/~sclv/urldisp-happstack