scotty 0.11.5 → 0.11.6
raw patch · 20 files changed
+358/−73 lines, 20 filesdep +base-compat-batteriesdep +luciddep +weighdep ~basedep ~bytestringdep ~data-default-classnew-uploader
Dependencies added: base-compat-batteries, lucid, weigh
Dependency ranges changed: base, bytestring, data-default-class, http-types, mtl, text, transformers
Files
- README.md +1/−1
- Web/Scotty.hs +5/−1
- Web/Scotty/Action.hs +50/−14
- Web/Scotty/Internal/Types.hs +63/−10
- Web/Scotty/Route.hs +7/−4
- Web/Scotty/Trans.hs +2/−2
- Web/Scotty/Util.hs +0/−1
- bench/Main.hs +54/−0
- changelog.md +14/−0
- examples/LICENSE +29/−0
- examples/basic.hs +2/−2
- examples/bodyecho.hs +37/−0
- examples/exceptions.hs +2/−2
- examples/globalstate.hs +2/−2
- examples/reader.hs +39/−0
- examples/upload.hs +2/−2
- examples/uploads/.keep +0/−0
- examples/urlshortener.hs +2/−2
- scotty.cabal +43/−30
- test/Web/ScottySpec.hs +4/−0
README.md view
@@ -37,6 +37,6 @@ ### Development & Support -Open an issue on GitHub or join `#scotty` on Freenode.+Open an issue on GitHub. Copyright (c) 2012-2019 Andrew Farmer
Web/Scotty.hs view
@@ -26,7 +26,7 @@ -- definition, as they completely replace the current 'Response' body. , text, html, file, json, stream, raw -- ** Exceptions- , raise, rescue, next, finish, defaultHandler, liftAndCatchIO+ , raise, raiseStatus, rescue, next, finish, defaultHandler, liftAndCatchIO -- * Parsing Parameters , Param, Trans.Parsable(..), Trans.readEither -- * Types@@ -93,6 +93,10 @@ -- turn into HTTP 500 responses. raise :: Text -> ActionM a raise = Trans.raise++-- | Throw an exception, which can be caught with 'rescue'. Uncaught exceptions turn into HTTP responses corresponding to the given status.+raiseStatus :: Status -> Text -> ActionM a+raiseStatus = Trans.raiseStatus -- | Abort execution of this action and continue pattern matching routes. -- Like an exception, any code after 'next' is not executed.
Web/Scotty/Action.hs view
@@ -18,6 +18,7 @@ , param , params , raise+ , raiseStatus , raw , readEither , redirect@@ -37,8 +38,8 @@ import qualified Control.Exception as E import Control.Monad.Error.Class-import Control.Monad.Reader-import qualified Control.Monad.State as MS+import Control.Monad.Reader hiding (mapM)+import qualified Control.Monad.State.Strict as MS import Control.Monad.Trans.Except import qualified Data.Aeson as A@@ -47,19 +48,24 @@ import qualified Data.CaseInsensitive as CI import Data.Default.Class (def) import Data.Int-#if !(MIN_VERSION_base(4,8,0))-import Data.Monoid (mconcat)-#endif import qualified Data.Text as ST+import qualified Data.Text.Encoding as STE import qualified Data.Text.Lazy as T import Data.Text.Lazy.Encoding (encodeUtf8) import Data.Word import Network.HTTP.Types+-- not re-exported until version 0.11+#if !MIN_VERSION_http_types(0,11,0)+import Network.HTTP.Types.Status+#endif import Network.Wai import Numeric.Natural +import Prelude ()+import Prelude.Compat+ import Web.Scotty.Internal.Types import Web.Scotty.Util @@ -79,18 +85,24 @@ defH _ (Redirect url) = do status status302 setHeader "Location" url-defH Nothing (ActionError e) = do- status status500- html $ mconcat ["<h1>500 Internal Server Error</h1>", showError e]-defH h@(Just f) (ActionError e) = f e `catchError` (defH h) -- so handlers can throw exceptions themselves+defH Nothing (ActionError s e) = do+ status s+ let code = T.pack $ show $ statusCode s+ let msg = T.fromStrict $ STE.decodeUtf8 $ statusMessage s+ html $ mconcat ["<h1>", code, " ", msg, "</h1>", showError e]+defH h@(Just f) (ActionError _ e) = f e `catchError` (defH h) -- so handlers can throw exceptions themselves defH _ Next = next defH _ Finish = return () -- | Throw an exception, which can be caught with 'rescue'. Uncaught exceptions -- turn into HTTP 500 responses. raise :: (ScottyError e, Monad m) => e -> ActionT e m a-raise = throwError . ActionError+raise = raiseStatus status500 +-- | Throw an exception, which can be caught with 'rescue'. Uncaught exceptions turn into HTTP responses corresponding to the given status.+raiseStatus :: (ScottyError e, Monad m) => Status -> e -> ActionT e m a+raiseStatus s = throwError . ActionError s+ -- | Abort execution of this action and continue pattern matching routes. -- Like an exception, any code after 'next' is not executed. --@@ -113,8 +125,8 @@ -- > raise "just kidding" `rescue` (\msg -> text msg) rescue :: (ScottyError e, Monad m) => ActionT e m a -> (e -> ActionT e m a) -> ActionT e m a rescue action h = catchError action $ \e -> case e of- ActionError err -> h err -- handle errors- other -> throwError other -- rethrow internal error types+ ActionError _ err -> h err -- handle errors+ other -> throwError other -- rethrow internal error types -- | Like 'liftIO', but catch any IO exceptions and turn them into 'ScottyError's. liftAndCatchIO :: (ScottyError e, MonadIO m) => IO a -> ActionT e m a@@ -172,11 +184,35 @@ bodyReader :: Monad m => ActionT e m (IO B.ByteString) bodyReader = ActionT $ getBodyChunk `liftM` ask --- | Parse the request body as a JSON object and return it. Raises an exception if parse is unsuccessful.+-- | Parse the request body as a JSON object and return it.+--+-- If the JSON object is malformed, this sets the status to+-- 400 Bad Request, and throws an exception.+--+-- If the JSON fails to parse, this sets the status to+-- 422 Unprocessable Entity.+--+-- These status codes are as per https://www.restapitutorial.com/httpstatuscodes.html. jsonData :: (A.FromJSON a, ScottyError e, MonadIO m) => ActionT e m a jsonData = do b <- body- either (\e -> raise $ stringError $ "jsonData - no parse: " ++ e ++ ". Data was:" ++ BL.unpack b) return $ A.eitherDecode b+ when (b == "") $ do+ let htmlError = "jsonData - No data was provided."+ raiseStatus status400 $ stringError htmlError+ case A.eitherDecode b of+ Left err -> do+ let htmlError = "jsonData - malformed."+ `mappend` " Data was: " `mappend` BL.unpack b+ `mappend` " Error was: " `mappend` err+ raiseStatus status400 $ stringError htmlError+ Right value -> case A.fromJSON value of+ A.Error err -> do+ let htmlError = "jsonData - failed parse."+ `mappend` " Data was: " `mappend` BL.unpack b `mappend` "."+ `mappend` " Error was: " `mappend` err+ raiseStatus status422 $ stringError htmlError+ A.Success a -> do+ return a -- | Get a parameter. First looks in captures, then form data, then query parameters. --
Web/Scotty/Internal/Types.hs view
@@ -16,16 +16,13 @@ import Control.Monad.Error.Class import qualified Control.Monad.Fail as Fail import Control.Monad.Reader-import Control.Monad.State+import Control.Monad.State.Strict import Control.Monad.Trans.Control (MonadBaseControl, StM, liftBaseWith, restoreM, ComposeSt, defaultLiftBaseWith, defaultRestoreM, MonadTransControl, StT, liftWith, restoreT) import Control.Monad.Trans.Except import qualified Data.ByteString as BS import Data.ByteString.Lazy.Char8 (ByteString) import Data.Default.Class (Default, def)-#if !(MIN_VERSION_base(4,8,0))-import Data.Monoid (mempty)-#endif import Data.String (IsString(..)) import Data.Text.Lazy (Text, pack) import Data.Typeable (Typeable)@@ -37,6 +34,9 @@ import Network.Wai.Handler.Warp (Settings, defaultSettings) import Network.Wai.Parse (FileInfo) +import Prelude ()+import Prelude.Compat+ --------------------- Options ----------------------- data Options = Options { verbose :: Int -- ^ 0 = silent, 1(def) = startup banner , settings :: Settings -- ^ Warp 'Settings'@@ -79,10 +79,11 @@ ------------------ Scotty Errors ---------------------data ActionError e = Redirect Text- | Next- | Finish- | ActionError e+data ActionError e+ = Redirect Text+ | Next+ | Finish+ | ActionError Status e -- | In order to use a custom exception type (aside from 'Text'), you must -- define an instance of 'ScottyError' for that type.@@ -95,11 +96,11 @@ showError = id instance ScottyError e => ScottyError (ActionError e) where- stringError = ActionError . stringError+ stringError = ActionError status500 . stringError showError (Redirect url) = url showError Next = pack "Next" showError Finish = pack "Finish"- showError (ActionError e) = showError e+ showError (ActionError _ e) = showError e type ErrorHandler e m = Maybe (e -> ActionT e m ()) @@ -196,6 +197,58 @@ type StM (ActionT e m) a = ComposeSt (ActionT e) m a liftBaseWith = defaultLiftBaseWith restoreM = defaultRestoreM++instance (MonadReader r m, ScottyError e) => MonadReader r (ActionT e m) where+ {-# INLINE ask #-}+ ask = lift ask+ {-# INLINE local #-}+ local f = ActionT . mapExceptT (mapReaderT (mapStateT $ local f)) . runAM++instance (MonadState s m, ScottyError e) => MonadState s (ActionT e m) where+ {-# INLINE get #-}+ get = lift get+ {-# INLINE put #-}+ put = lift . put++instance (Semigroup a) => Semigroup (ScottyT e m a) where+ x <> y = (<>) <$> x <*> y++instance+ ( Monoid a+#if !(MIN_VERSION_base(4,11,0))+ , Semigroup a+#endif+#if !(MIN_VERSION_base(4,8,0))+ , Functor m+#endif+ ) => Monoid (ScottyT e m a) where+ mempty = return mempty+#if !(MIN_VERSION_base(4,11,0))+ mappend = (<>)+#endif++instance+ ( Monad m+#if !(MIN_VERSION_base(4,8,0))+ , Functor m+#endif+ , Semigroup a+ ) => Semigroup (ActionT e m a) where+ x <> y = (<>) <$> x <*> y++instance+ ( Monad m, ScottyError e, Monoid a+#if !(MIN_VERSION_base(4,11,0))+ , Semigroup a+#endif+#if !(MIN_VERSION_base(4,8,0))+ , Functor m+#endif+ ) => Monoid (ActionT e m a) where+ mempty = return mempty+#if !(MIN_VERSION_base(4,11,0))+ mappend = (<>)+#endif ------------------ Scotty Routes -------------------- data RoutePattern = Capture Text
Web/Scotty/Route.hs view
@@ -14,9 +14,6 @@ import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as BL import Data.Maybe (fromMaybe, isJust)-#if !(MIN_VERSION_base(4,8,0))-import Data.Monoid (mconcat)-#endif import Data.String (fromString) import qualified Data.Text.Lazy as T import qualified Data.Text as TS@@ -28,6 +25,9 @@ #endif import qualified Network.Wai.Parse as Parse hiding (parseRequestBody) +import Prelude ()+import Prelude.Compat+ import qualified Text.Regex as Regex import Web.Scotty.Action@@ -109,7 +109,7 @@ matchRoute (Literal pat) req | pat == path req = Just [] | otherwise = Nothing matchRoute (Function fun) req = fun req-matchRoute (Capture pat) req = go (T.split (=='/') pat) (T.split (=='/') $ path req) []+matchRoute (Capture pat) req = go (T.split (=='/') pat) (compress $ T.split (=='/') $ path req) [] where go [] [] prs = Just prs -- request string and pattern match! go [] r prs | T.null (mconcat r) = Just prs -- in case request has trailing slashes | otherwise = Nothing -- request string is longer than pattern@@ -119,6 +119,9 @@ | T.null p = Nothing -- p is null, but r is not, fail | T.head p == ':' = go ps rs $ (T.tail p, r) : prs -- p is a capture, add to params | otherwise = Nothing -- both literals, but unequal, fail+ compress ("":rest@("":_)) = compress rest+ compress (x:xs) = x : compress xs+ compress [] = [] -- Pretend we are at the top level. path :: Request -> T.Text
Web/Scotty/Trans.hs view
@@ -30,7 +30,7 @@ -- definition, as they completely replace the current 'Response' body. , text, html, file, json, stream, raw -- ** Exceptions- , raise, rescue, next, finish, defaultHandler, ScottyError(..), liftAndCatchIO+ , raise, raiseStatus, rescue, next, finish, defaultHandler, ScottyError(..), liftAndCatchIO -- * Parsing Parameters , Param, Parsable(..), readEither -- * Types@@ -42,7 +42,7 @@ import Blaze.ByteString.Builder (fromByteString) import Control.Monad (when)-import Control.Monad.State (execState, modify)+import Control.Monad.State.Strict (execState, modify) import Control.Monad.IO.Class import Data.Default.Class (def)
Web/Scotty/Util.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} module Web.Scotty.Util ( lazyTextToStrictByteString , strictByteStringToLazyText
+ bench/Main.hs view
@@ -0,0 +1,54 @@+{-# language+ OverloadedStrings+ , GeneralizedNewtypeDeriving+ #-}++module Main (main) where++import Control.Monad+import Data.Default.Class (def)+import Data.Functor.Identity+import Data.Text (Text)+import Lucid.Base+import Lucid.Html5+import Web.Scotty+import Web.Scotty.Internal.Types+import qualified Control.Monad.State.Lazy as SL+import qualified Control.Monad.State.Strict as SS+import qualified Data.ByteString.Lazy as BL++import Weigh++main :: IO ()+main = do+ mainWith $ do+ setColumns [Case,Allocated,GCs,Live,Check,Max,MaxOS]+ setFormat Markdown+ io "ScottyM Strict" BL.putStr+ (SS.evalState (runS $ renderBST htmlScotty) def)+ io "ScottyM Lazy" BL.putStr+ (SL.evalState (runScottyLazy $ renderBST htmlScottyLazy) def)+ io "Identity" BL.putStr+ (runIdentity $ renderBST htmlIdentity)++htmlTest :: Monad m => HtmlT m ()+htmlTest = replicateM_ 2 $ div_ $ do+ replicateM_ 1000 $ div_ $ do+ replicateM_ 10000 $ div_ "test"++htmlIdentity :: HtmlT Identity ()+htmlIdentity = htmlTest+{-# noinline htmlIdentity #-}++htmlScotty :: HtmlT ScottyM ()+htmlScotty = htmlTest+{-# noinline htmlScotty #-}++htmlScottyLazy :: HtmlT ScottyLazy ()+htmlScottyLazy = htmlTest+{-# noinline htmlScottyLazy #-}++newtype ScottyLazy a = ScottyLazy+ { runScottyLazy:: SL.State (ScottyState Text IO) a }+ deriving (Functor,Applicative,Monad)+
changelog.md view
@@ -1,3 +1,17 @@+## 0.12 [2020.05.16]+* Provide `MonadReader` and `MonadState` instances for `ActionT`.+* Add HTTP Status code as a field to `ActionError`, and add+ a sister function to `raise`, `raiseStatus`. This makes+ throwing a specific error code and exiting much cleaner, and+ avoids the strange defaulting to HTTP 500. This will make internal+ functions easier to implement with the right status codes 'thrown',+ such as `jsonData`.+* Correct http statuses returned by `jsonData` (#228).+* Better error message when no data is provided to `jsonData` (#226).+* Add `Semigroup` and `Monoid` instances for `ActionT` and `ScottyT`+* ScottyT: Use strict StateT instead of lazy+* Handle adjacent slashes in the request path as one (thanks @SkyWriter)+ ## 0.11.5 [2019.09.07] * Allow building the test suite with `hspec-wai-0.10`.
+ examples/LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2012-2017 Andrew Farmer+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * 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.++ * Neither the name of Andrew Farmer nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT+OWNER 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.
examples/basic.hs view
@@ -7,14 +7,14 @@ import Control.Monad import Control.Monad.Trans-import Data.Monoid import System.Random (newStdGen, randomRs) import Network.HTTP.Types (status302) import Data.Text.Lazy.Encoding (decodeUtf8) import Data.String (fromString)-import Prelude+import Prelude ()+import Prelude.Compat main :: IO () main = scotty 3000 $ do
+ examples/bodyecho.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE OverloadedStrings #-}+module Main (main) where++import Web.Scotty++import Control.Monad.IO.Class (liftIO)+import qualified Blaze.ByteString.Builder as B+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL+import qualified Data.Text.Lazy as T++main :: IO ()+main = scotty 3000 $ do+ post "/echo" $ do+ rd <- bodyReader+ stream $ ioCopy rd $ return ()++ post "/count" $ do+ wb <- body -- this must happen before first 'rd'+ rd <- bodyReader+ let step acc = do + chunk <- rd+ putStrLn "got a chunk"+ let len = BS.length chunk+ if len > 0 + then step $ acc + len+ else return acc+ len <- liftIO $ step 0+ text $ T.pack $ "uploaded " ++ show len ++ " bytes, wb len is " ++ show (BSL.length wb)+++ioCopy :: IO BS.ByteString -> IO () -> (B.Builder -> IO ()) -> IO () -> IO ()+ioCopy reader close write flush = step >> flush where+ step = do chunk <- reader+ if (BS.length chunk > 0) + then (write $ B.insertByteString chunk) >> step+ else close
examples/exceptions.hs view
@@ -3,13 +3,13 @@ import Control.Monad.IO.Class -import Data.Monoid import Data.String (fromString) import Network.HTTP.Types import Network.Wai.Middleware.RequestLogger -import Prelude+import Prelude ()+import Prelude.Compat import System.Random
examples/globalstate.hs view
@@ -9,7 +9,6 @@ -- embedded into any MonadIO monad. module Main (main) where -import Control.Applicative import Control.Concurrent.STM import Control.Monad.Reader @@ -19,7 +18,8 @@ import Network.Wai.Middleware.RequestLogger -import Prelude+import Prelude ()+import Prelude.Compat import Web.Scotty.Trans
+ examples/reader.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}++{-+ An example of embedding a custom monad into Scotty's transformer+ stack, using ReaderT to provide access to a global state.+-}+module Main where++import Control.Monad.Reader (MonadIO, MonadReader, ReaderT, asks, lift, runReaderT)+import Data.Default.Class (def)+import Data.Text.Lazy (Text, pack)+import Prelude ()+import Prelude.Compat+import Web.Scotty.Trans (ScottyT, get, scottyOptsT, text)++data Config = Config+ { environment :: String+ } deriving (Eq, Read, Show)++newtype ConfigM a = ConfigM+ { runConfigM :: ReaderT Config IO a+ } deriving (Applicative, Functor, Monad, MonadIO, MonadReader Config)++application :: ScottyT Text ConfigM ()+application = do+ get "/" $ do+ e <- lift $ asks environment+ text $ pack $ show e++main :: IO ()+main = scottyOptsT def runIO application where+ runIO :: ConfigM a -> IO a+ runIO m = runReaderT (runConfigM m) config++ config :: Config+ config = Config+ { environment = "Development"+ }
examples/upload.hs view
@@ -4,7 +4,6 @@ import Web.Scotty import Control.Monad.IO.Class-import Data.Monoid import Network.Wai.Middleware.RequestLogger import Network.Wai.Middleware.Static@@ -17,7 +16,8 @@ import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Char8 as BS import System.FilePath ((</>))-import Prelude+import Prelude ()+import Prelude.Compat main :: IO () main = scotty 3000 $ do
+ examples/uploads/.keep view
examples/urlshortener.hs view
@@ -6,13 +6,13 @@ import Control.Concurrent.MVar import Control.Monad.IO.Class import qualified Data.Map as M-import Data.Monoid import qualified Data.Text.Lazy as T import Network.Wai.Middleware.RequestLogger import Network.Wai.Middleware.Static -import Prelude+import Prelude ()+import Prelude.Compat import qualified Text.Blaze.Html5 as H import Text.Blaze.Html5.Attributes
scotty.cabal view
@@ -1,5 +1,5 @@ Name: scotty-Version: 0.11.5+Version: 0.11.6 Synopsis: Haskell web framework inspired by Ruby's Sinatra, using WAI and Warp Homepage: https://github.com/scotty-web/scotty Bug-reports: https://github.com/scotty-web/scotty/issues@@ -51,21 +51,17 @@ , GHC == 8.2.2 , GHC == 8.4.4 , GHC == 8.6.5- , GHC == 8.8.1+ , GHC == 8.8.3+ , GHC == 8.10.1 Extra-source-files: README.md changelog.md examples/404.html- examples/basic.hs- examples/cookies.hs- examples/exceptions.hs- examples/globalstate.hs- examples/gzip.hs- examples/options.hs- examples/upload.hs- examples/urlshortener.hs+ examples/LICENSE+ examples/*.hs examples/static/jquery.js examples/static/jquery-json.js+ examples/uploads/.keep Library Exposed-modules: Web.Scotty@@ -75,27 +71,28 @@ Web.Scotty.Route Web.Scotty.Util default-language: Haskell2010- build-depends: aeson >= 0.6.2.1 && < 1.5,- base >= 4.6 && < 5,- blaze-builder >= 0.3.3.0 && < 0.5,- bytestring >= 0.10.0.2 && < 0.11,- case-insensitive >= 1.0.0.1 && < 1.3,- data-default-class >= 0.0.1 && < 0.2,- exceptions >= 0.7 && < 0.11,+ build-depends: aeson >= 0.6.2.1 && < 1.5,+ base >= 4.6 && < 5,+ base-compat-batteries >= 0.10 && < 0.12,+ blaze-builder >= 0.3.3.0 && < 0.5,+ bytestring >= 0.10.0.2 && < 0.11,+ case-insensitive >= 1.0.0.1 && < 1.3,+ data-default-class >= 0.0.1 && < 0.2,+ exceptions >= 0.7 && < 0.11, fail,- http-types >= 0.8.2 && < 0.13,- monad-control >= 1.0.0.3 && < 1.1,- mtl >= 2.1.2 && < 2.3,- nats >= 0.1 && < 2,- network >= 2.6.0.2 && < 3.2,- regex-compat >= 0.95.1 && < 0.96,- text >= 0.11.3.1 && < 1.3,- transformers >= 0.3.0.0 && < 0.6,- transformers-base >= 0.4.1 && < 0.5,- transformers-compat >= 0.4 && < 0.7,- wai >= 3.0.0 && < 3.3,- wai-extra >= 3.0.0 && < 3.1,- warp >= 3.0.13 && < 3.4+ http-types >= 0.9.1 && < 0.13,+ monad-control >= 1.0.0.3 && < 1.1,+ mtl >= 2.1.2 && < 2.3,+ nats >= 0.1 && < 2,+ network >= 2.6.0.2 && < 3.2,+ regex-compat >= 0.95.1 && < 0.96,+ text >= 0.11.3.1 && < 1.3,+ transformers >= 0.3.0.0 && < 0.6,+ transformers-base >= 0.4.1 && < 0.5,+ transformers-compat >= 0.4 && < 0.7,+ wai >= 3.0.0 && < 3.3,+ wai-extra >= 3.0.0 && < 3.1,+ warp >= 3.0.13 && < 3.4 GHC-options: -Wall -fno-warn-orphans @@ -120,6 +117,22 @@ wai build-tool-depends: hspec-discover:hspec-discover == 2.* GHC-options: -Wall -threaded -fno-warn-orphans++benchmark weigh+ main-is: Main.hs+ type: exitcode-stdio-1.0+ default-language: Haskell2010+ hs-source-dirs: bench+ build-depends: base,+ scotty,+ lucid,+ bytestring,+ mtl,+ text,+ transformers,+ data-default-class,+ weigh == 0.0.16+ GHC-options: -Wall -O2 -threaded source-repository head type: git
test/Web/ScottySpec.hs view
@@ -48,6 +48,10 @@ it ("adds route for " ++ method ++ " requests") $ do makeRequest "/scotty" `shouldRespondWith` 200 + withApp (route "/scotty" $ html "") $ do+ it ("properly handles extra slash routes for " ++ method ++ " requests") $ do+ makeRequest "//scotty" `shouldRespondWith` 200+ describe "addroute" $ do forM_ availableMethods $ \method -> do withApp (addroute method "/scotty" $ html "") $ do