scotty 0.9.0 → 0.9.1
raw patch · 9 files changed
+76/−44 lines, 9 filesdep +hspecdep −hspec2dep ~aesondep ~basedep ~blaze-builderPVP ok
version bump matches the API change (PVP)
Dependencies added: hspec
Dependencies removed: hspec2
Dependency ranges changed: aeson, base, blaze-builder, hspec-wai, monad-control
API changes (from Hackage documentation)
Files
- Web/Scotty.hs +3/−3
- Web/Scotty/Action.hs +34/−21
- Web/Scotty/Internal/Types.hs +7/−7
- Web/Scotty/Route.hs +4/−4
- Web/Scotty/Trans.hs +1/−1
- Web/Scotty/Util.hs +11/−3
- changelog.md +10/−0
- examples/basic.hs +1/−0
- scotty.cabal +5/−5
Web/Scotty.hs view
@@ -171,12 +171,12 @@ setHeader = Trans.setHeader -- | Set the body of the response to the given 'Text' value. Also sets \"Content-Type\"--- header to \"text/plain; charset=utf-8\".+-- header to \"text/plain; charset=utf-8\" if it has not already been set. text :: Text -> ActionM () text = Trans.text -- | Set the body of the response to the given 'Text' value. Also sets \"Content-Type\"--- header to \"text/html; charset=utf-8\".+-- header to \"text/html; charset=utf-8\" if it has not already been set. html :: Text -> ActionM () html = Trans.html @@ -186,7 +186,7 @@ file = Trans.file -- | Set the body of the response to the JSON encoding of the given value. Also sets \"Content-Type\"--- header to \"application/json; charset=utf-8\".+-- header to \"application/json; charset=utf-8\" if it has not already been set. json :: ToJSON a => a -> ActionM () json = Trans.json
Web/Scotty/Action.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE CPP, OverloadedStrings, RankNTypes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-} module Web.Scotty.Action ( addHeader , body@@ -28,7 +30,7 @@ , runAction ) where -import Blaze.ByteString.Builder (fromLazyByteString)+import Blaze.ByteString.Builder (fromLazyByteString) #if MIN_VERSION_mtl(2,2,1) import Control.Monad.Except@@ -36,17 +38,17 @@ import Control.Monad.Error #endif import Control.Monad.Reader-import qualified Control.Monad.State as MS+import qualified Control.Monad.State as MS -import qualified Data.Aeson as A-import qualified Data.ByteString.Char8 as B+import qualified Data.Aeson as A+import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as BL-import qualified Data.CaseInsensitive as CI-import Data.Default (def)-import Data.Monoid (mconcat)-import qualified Data.Text as ST-import qualified Data.Text.Lazy as T-import Data.Text.Lazy.Encoding (encodeUtf8)+import qualified Data.CaseInsensitive as CI+import Data.Default (def)+import Data.Monoid (mconcat)+import qualified Data.Text as ST+import qualified Data.Text.Lazy as T+import Data.Text.Lazy.Encoding (encodeUtf8) import Network.HTTP.Types import Network.Wai@@ -151,7 +153,7 @@ jsonData :: (A.FromJSON a, ScottyError e, Monad m) => ActionT e m a jsonData = do b <- body- maybe (raise $ stringError $ "jsonData - no parse: " ++ BL.unpack b) return $ A.decode b+ either (\e -> raise $ stringError $ "jsonData - no parse: " ++ e ++ ". Data was:" ++ BL.unpack b) return $ A.eitherDecode b -- | Get a parameter. First looks in captures, then form data, then query parameters. --@@ -186,6 +188,7 @@ instance Parsable T.Text where parseParam = Right instance Parsable ST.Text where parseParam = Right . T.toStrict instance Parsable B.ByteString where parseParam = Right . lazyTextToStrictByteString+instance Parsable BL.ByteString where parseParam = Right . encodeUtf8 -- | Overrides default 'parseParamList' to parse String. instance Parsable Char where parseParam t = case T.unpack t of@@ -215,7 +218,7 @@ -- | Useful for creating 'Parsable' instances for things that already implement 'Read'. Ex: -- -- > instance Parsable Int where parseParam = readEither-readEither :: (Read a) => T.Text -> Either T.Text a+readEither :: Read a => T.Text -> Either T.Text a readEither t = case [ x | (x,"") <- reads (T.unpack t) ] of [x] -> Right x [] -> Left "readEither: no parse"@@ -225,27 +228,37 @@ status :: (ScottyError e, Monad m) => Status -> ActionT e m () status = ActionT . MS.modify . setStatus +-- Not exported, but useful in the functions below.+changeHeader :: (ScottyError e, Monad m)+ => (CI.CI B.ByteString -> B.ByteString -> [(HeaderName, B.ByteString)] -> [(HeaderName, B.ByteString)])+ -> T.Text -> T.Text -> ActionT e m ()+changeHeader f k = ActionT+ . MS.modify+ . setHeaderWith+ . f (CI.mk $ lazyTextToStrictByteString k)+ . lazyTextToStrictByteString+ -- | Add to the response headers. Header names are case-insensitive. addHeader :: (ScottyError e, Monad m) => T.Text -> T.Text -> ActionT e m ()-addHeader k v = ActionT . MS.modify $ setHeaderWith $ add (CI.mk $ lazyTextToStrictByteString k) (lazyTextToStrictByteString v)+addHeader = changeHeader add -- | Set one of the response headers. Will override any previously set value for that header. -- Header names are case-insensitive. setHeader :: (ScottyError e, Monad m) => T.Text -> T.Text -> ActionT e m ()-setHeader k v = ActionT . MS.modify $ setHeaderWith $ replace (CI.mk $ lazyTextToStrictByteString k) (lazyTextToStrictByteString v)+setHeader = changeHeader replace -- | Set the body of the response to the given 'T.Text' value. Also sets \"Content-Type\"--- header to \"text/plain; charset=utf-8\".+-- header to \"text/plain; charset=utf-8\" if it has not already been set. text :: (ScottyError e, Monad m) => T.Text -> ActionT e m () text t = do- setHeader "Content-Type" "text/plain; charset=utf-8"+ changeHeader addIfNotPresent "Content-Type" "text/plain; charset=utf-8" raw $ encodeUtf8 t -- | Set the body of the response to the given 'T.Text' value. Also sets \"Content-Type\"--- header to \"text/html; charset=utf-8\".+-- header to \"text/html; charset=utf-8\" if it has not already been set. html :: (ScottyError e, Monad m) => T.Text -> ActionT e m () html t = do- setHeader "Content-Type" "text/html; charset=utf-8"+ changeHeader addIfNotPresent "Content-Type" "text/html; charset=utf-8" raw $ encodeUtf8 t -- | Send a file as the response. Doesn't set the \"Content-Type\" header, so you probably@@ -254,10 +267,10 @@ file = ActionT . MS.modify . setContent . ContentFile -- | Set the body of the response to the JSON encoding of the given value. Also sets \"Content-Type\"--- header to \"application/json; charset=utf-8\".+-- header to \"application/json; charset=utf-8\" if it has not already been set. json :: (A.ToJSON a, ScottyError e, Monad m) => a -> ActionT e m () json v = do- setHeader "Content-Type" "application/json; charset=utf-8"+ changeHeader addIfNotPresent "Content-Type" "application/json; charset=utf-8" raw $ A.encode v -- | Set the body of the response to a Source. Doesn't set the
Web/Scotty/Internal/Types.hs view
@@ -150,21 +150,21 @@ instance ScottyError e => MonadTransControl (ActionT e) where #if MIN_VERSION_mtl(2,2,1)- newtype StT (ActionT e) a = StAction {unStAction :: StT (StateT ScottyResponse) (StT (ReaderT ActionEnv) (StT (ExceptT (ActionError e)) a))}+ type StT (ActionT e) a = StT (StateT ScottyResponse) (StT (ReaderT ActionEnv) (StT (ExceptT (ActionError e)) a)) #else- newtype StT (ActionT e) a = StAction {unStAction :: StT (StateT ScottyResponse) (StT (ReaderT ActionEnv) (StT (ErrorT (ActionError e)) a))}+ type StT (ActionT e) a = StT (StateT ScottyResponse) (StT (ReaderT ActionEnv) (StT (ErrorT (ActionError e)) a)) #endif liftWith = \f -> ActionT $ liftWith $ \run -> liftWith $ \run' -> liftWith $ \run'' ->- f $ liftM StAction . run'' . run' . run . runAM- restoreT = ActionT . restoreT . restoreT . restoreT . liftM unStAction+ f $ run'' . run' . run . runAM+ restoreT = ActionT . restoreT . restoreT . restoreT instance (ScottyError e, MonadBaseControl b m) => MonadBaseControl b (ActionT e m) where- newtype StM (ActionT e m) a = STMAction {unStMActionT :: ComposeSt (ActionT e) m a}- liftBaseWith = defaultLiftBaseWith STMAction- restoreM = defaultRestoreM unStMActionT+ type StM (ActionT e m) a = ComposeSt (ActionT e) m a+ liftBaseWith = defaultLiftBaseWith+ restoreM = defaultRestoreM ------------------ Scotty Routes -------------------- data RoutePattern = Capture Text
Web/Scotty/Route.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, FlexibleContexts, FlexibleInstances, LambdaCase,+{-# LANGUAGE CPP, FlexibleContexts, FlexibleInstances, OverloadedStrings, RankNTypes, ScopedTypeVariables #-} module Web.Scotty.Route ( get, post, put, delete, patch, addroute, matchAny, notFound,@@ -124,9 +124,9 @@ Just rbt -> do mvar <- liftIO $ newMVar bl -- MVar is a bit of a hack so we don't have to inline -- large portions of Network.Wai.Parse- let provider = takeMVar mvar >>= \case- [] -> putMVar mvar [] >> return B.empty- (b:bs) -> putMVar mvar bs >> return b+ let provider = modifyMVar mvar $ \bsold -> case bsold of+ [] -> return ([], B.empty)+ (b:bs) -> return (bs, b) liftIO $ Parse.sinkRequestBody s rbt provider mkEnv :: forall m. MonadIO m => Request -> [Param] -> m ActionEnv
Web/Scotty/Trans.hs view
@@ -89,7 +89,7 @@ -> n Application scottyAppT runM runActionToIO defs = do s <- runM $ execStateT (runS defs) def- let rapp = \ req callback -> runActionToIO (foldl (flip ($)) notFoundApp (routes s) req) >>= callback+ let rapp req callback = runActionToIO (foldl (flip ($)) notFoundApp (routes s) req) >>= callback return $ foldl (flip ($)) rapp (middlewares s) notFoundApp :: Monad m => Scotty.Application m
Web/Scotty/Util.hs view
@@ -7,6 +7,7 @@ , mkResponse , replace , add+ , addIfNotPresent ) where import Network.Wai@@ -46,8 +47,15 @@ h = srHeaders sr -- Note: we assume headers are not sensitive to order here (RFC 2616 specifies they are not)-replace :: (Eq a) => a -> b -> [(a,b)] -> [(a,b)]-replace k v m = add k v $ filter ((/= k) . fst) m+replace :: Eq a => a -> b -> [(a,b)] -> [(a,b)]+replace k v = add k v . filter ((/= k) . fst) -add :: (Eq a) => a -> b -> [(a,b)] -> [(a,b)]+add :: Eq a => a -> b -> [(a,b)] -> [(a,b)] add k v m = (k,v):m++addIfNotPresent :: Eq a => a -> b -> [(a,b)] -> [(a,b)]+addIfNotPresent k v = go+ where go [] = [(k,v)]+ go l@((x,y):r)+ | x == k = l+ | otherwise = (x,y) : go r
changelog.md view
@@ -1,3 +1,13 @@+## 0.9.1++* `Parsable` instance for lazy bytestrings++* `jsonData` now returns decoding error message++* text/html/json only set Content-Type header when not already set++* Bump `monad-control` to 1.0.0.0+ ## 0.9.0 * Add `charset=utf-8` to `Content-Type` for `text`, `html` and `json`
examples/basic.hs view
@@ -63,6 +63,7 @@ -- Files are streamed directly to the client. get "/404" $ file "404.html" + -- You can stop execution of this action and keep pattern matching routes. get "/random" $ do next redirect "http://www.we-never-go-here.com"
scotty.cabal view
@@ -1,5 +1,5 @@ Name: scotty-Version: 0.9.0+Version: 0.9.1 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@@ -76,9 +76,9 @@ data-default >= 0.5.3 && < 0.6, http-types >= 0.8.2 && < 0.9, mtl >= 2.1.2 && < 2.3,- monad-control >= 0.3.2.3 && < 0.4,+ monad-control >= 1.0.0.0 && < 1.1, regex-compat >= 0.95.1 && < 0.96,- text >= 0.11.3.1 && < 1.2,+ text >= 0.11.3.1 && < 1.3, transformers >= 0.3.0.0 && < 0.5, transformers-base >= 0.4.1 && < 0.5, wai >= 3.0.0 && < 3.1,@@ -98,8 +98,8 @@ http-types, lifted-base, wai,- hspec2,- hspec-wai >= 0.3.0,+ hspec == 2.*,+ hspec-wai >= 0.5, scotty GHC-options: -Wall -fno-warn-orphans