twain 2.0.1.0 → 2.1.0.0
raw patch · 8 files changed
+170/−44 lines, 8 filesdep +hspecdep +hspec-discoverdep +hspec-waidep ~aesondep ~basedep ~bytestringsetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: hspec, hspec-discover, hspec-wai, twain
Dependency ranges changed: aeson, base, bytestring, text, time
API changes (from Hackage documentation)
+ Web.Twain: css :: ByteString -> Response
Files
- README.md +10/−4
- Setup.hs +0/−2
- src/Web/Twain.hs +23/−24
- src/Web/Twain/Internal.hs +1/−1
- src/Web/Twain/Types.hs +1/−2
- test/Spec.hs +1/−0
- test/TwainSpec.hs +102/−0
- twain.cabal +32/−11
README.md view
@@ -12,15 +12,21 @@ - Helpers for redirects, headers, status codes, and errors. ```haskell+{-# language OverloadedStrings #-}+ import Network.Wai.Handler.Warp (run) import Web.Twain main :: IO () main = do- run 8080- $ get "/" index- $ post "/echo/:name" echoName- $ notFound missing+ run 8080 $+ foldr ($) (notFound missing) routes++routes :: [Middleware]+routes =+ [ get "/" index+ , post "/echo/:name" echoName+ ] index :: ResponderM a index = send $ html "Hello World!"
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
src/Web/Twain.hs view
@@ -6,15 +6,19 @@ -- - Helpers for redirects, headers, status codes, and errors. -- -- @+-- {-# language OverloadedStrings #-}+-- -- import Network.Wai.Handler.Warp (run) -- import Web.Twain -- -- main :: IO () -- main = do--- run 8080--- $ get "/" index--- $ post "/echo/:name" echo--- $ notFound missing+-- run 8080 $+-- foldr ($)+-- (notFound missing)+-- [ get "/" index+-- , post "/echo/:name" echo+-- ] -- -- index :: ResponderM a -- index = send $ html "Hello World!"@@ -62,6 +66,7 @@ html, json, xml,+ css, raw, status, withHeader,@@ -296,41 +301,35 @@ -- -- Sets the Content-Type and Content-Length headers. text :: Text -> Response-text body =- let lbs = BL.fromStrict (encodeUtf8 body)- typ = (hContentType, "text/plain; charset=utf-8")- len = (hContentLength, Char8.pack (show (BL.length lbs)))- in raw status200 [typ, len] lbs+text =+ raw status200 [(hContentType, "text/plain; charset=utf-8")] . BL.fromStrict . encodeUtf8 -- | Construct an HTML response. -- -- Sets the Content-Type and Content-Length headers. html :: BL.ByteString -> Response-html body =- let lbs = body- typ = (hContentType, "text/html; charset=utf-8")- len = (hContentLength, Char8.pack (show (BL.length lbs)))- in raw status200 [typ, len] lbs+html =+ raw status200 [(hContentType, "text/html; charset=utf-8")] -- | Construct a JSON response using `ToJSON`. -- -- Sets the Content-Type and Content-Length headers. json :: ToJSON a => a -> Response-json val =- let lbs = JSON.encode val- typ = (hContentType, "application/json; charset=utf-8")- len = (hContentLength, Char8.pack (show (BL.length lbs)))- in raw status200 [typ, len] lbs+json =+ raw status200 [(hContentType, "application/json; charset=utf-8")] . JSON.encode +-- | Construct a CSS response.+--+-- Sets the Content-Type and Content-Length headers.+css :: BL.ByteString -> Response+css =+ raw status200 [(hContentType, "text/css; charset=utf-8")]+ -- | Construct an XML response. -- -- Sets the Content-Type and Content-Length headers. xml :: BL.ByteString -> Response-xml body =- let lbs = body- typ = (hContentType, "application/xml; charset=utf-8")- len = (hContentLength, Char8.pack (show (BL.length lbs)))- in raw status200 [typ, len] lbs+xml = raw status200 [(hContentType, "application/xml; charset=utf-8")] -- | Construct a raw response from a lazy `ByteString`. --
src/Web/Twain/Internal.hs view
@@ -16,7 +16,7 @@ import qualified Data.Vault.Lazy as V import Data.Word (Word64) import Network.HTTP.Types (Method, hCookie, mkStatus, status204, status400, status413, status500)-import Network.HTTP2 (ErrorCodeId (..), HTTP2Error (..))+import Network.HTTP2.Frame (ErrorCodeId (..), HTTP2Error (..)) import Network.Wai (Application, Middleware, Request (..), lazyRequestBody, queryString, requestHeaders, requestMethod, responseLBS) import Network.Wai.Parse (File, ParseRequestBodyOptions, lbsBackEnd, noLimitParseRequestBodyOptions, parseRequestBodyEx) import Network.Wai.Request (RequestSizeException (..), requestSizeCheck)
src/Web/Twain/Types.hs view
@@ -52,11 +52,10 @@ fmap f (ResponderM g) = ResponderM $ \r -> mapRight (\(a, b) -> (f a, b)) `fmap` g r instance Applicative ResponderM where- pure = return+ pure a = ResponderM $ \r -> pure (Right (a, r)) (<*>) = ap instance Monad ResponderM where- return a = ResponderM $ \r -> return (Right (a, r)) (ResponderM act) >>= fn = ResponderM $ \r -> do eres <- act r case eres of
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test/TwainSpec.hs view
@@ -0,0 +1,102 @@+module TwainSpec where++import Web.Twain+import Control.Monad (when)+import Control.Monad.IO.Class (liftIO)+import Data.String (fromString)+import Test.Hspec+import qualified Test.Hspec.Wai as Test++-- * Tests++spec :: Spec+spec = Test.with (pure app) $ do+ describe "GET" $ do+ describe "GET /" $ do+ it "responds with 200 & html 'hello world'" $+ Test.shouldRespondWith+ (Test.get "/")+ "Hello World!"+ { Test.matchStatus = 200+ , Test.matchHeaders = ["Content-Type" Test.<:> "text/html; charset=utf-8"]+ }++ describe "GET /json" $ do+ it "responds with Content-Type application/json" $+ Test.shouldRespondWith+ (Test.get "/json")+ "[true]"+ { Test.matchStatus = 200+ , Test.matchHeaders = ["Content-Type" Test.<:> "application/json; charset=utf-8"]+ }++ describe "GET /css" $ do+ it "responds with Content-Type text/css" $+ Test.shouldRespondWith+ (Test.get "/css")+ (fromString style)+ { Test.matchStatus = 200+ , Test.matchHeaders = ["Content-Type" Test.<:> "text/css; charset=utf-8"]+ }++ describe "GET /notfound" $ do+ it "responds with 404 / 'Not found...'" $+ Test.shouldRespondWith+ (Test.get "/notfound")+ "Not found..." { Test.matchStatus = 404 }++ describe "POST" $ do+ describe "POST /echo/James" $ do+ it "Without body - responds with 200 & 'Hello, James'" $+ Test.shouldRespondWith+ (Test.post "/echo/James" "")+ "Hello, James" { Test.matchStatus = 200 }++ it "With body - responds with 200 & 'Hello, Jameson'" $+ Test.shouldRespondWith+ (Test.postHtmlForm "/echo/James" [("bodyname", "Jameson")])+ "Hello, Jameson" { Test.matchStatus = 200 }++ describe "POST /echo/" $ do+ it "responds with 404 & 'Person not specified...' (next works correctly)" $+ Test.shouldRespondWith+ (Test.postHtmlForm "/echo/" [])+ "Person not specified..." { Test.matchStatus = 404 }++-- * App++app :: Application+app = foldr ($) (notFound missing) routes++routes :: [Middleware]+routes =+ [ get "/" index+ , get "/json" myjson+ , get "/css" (send $ css $ fromString style)+ , post "/echo/:name" echo+ , post "/echo/:name" missingPerson+ ]++index :: ResponderM a+index = send $ html "Hello World!"++myjson :: ResponderM a+myjson = send $ json [True]++style :: String+style = "body { width: 900px; margin: auto; }"++echo :: ResponderM a+echo = do+ name <- fmap (unwords . words) $ paramMaybe "bodyname" >>= maybe (param "name") pure+ when (null name) $ next+ send $ html $ "Hello, " <> fromString name++missing :: ResponderM a+missing = send $ html "Not found..."++missingPerson :: ResponderM a+missingPerson =+ send $+ raw status404 [("Content-Type", "text/html; charset=utf-8")] $+ "Person not specified..."
twain.cabal view
@@ -1,11 +1,11 @@-cabal-version: 1.12+cabal-version: 2.2 -- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack name: twain-version: 2.0.1.0+version: 2.1.0.0 synopsis: Tiny web application framework for WAI. description: Twain is tiny web application framework for WAI. It provides routing, parameter parsing, and an either-like monad for composing responses. category: Web@@ -14,7 +14,7 @@ author: Alex Mingoia maintainer: alex@alexmingoia.com copyright: 2021 Alexander C. Mingoia-license: BSD3+license: BSD-3-Clause license-file: LICENSE build-type: Simple extra-source-files:@@ -27,27 +27,48 @@ library exposed-modules:- Web.Twain Web.Twain.Types+ Web.Twain+ Web.Twain.Types other-modules:- Web.Twain.Internal+ Web.Twain.Internal hs-source-dirs:- src+ src default-extensions:- OverloadedStrings+ OverloadedStrings build-depends:- aeson >=1.4 && <1.7+ aeson >=2 && <3 , base >=4.7 && <5- , bytestring ==0.10.*+ , bytestring >=0.10 && < 0.12 , case-insensitive ==1.2.* , cookie >=0.4 && <0.6 , either ==5.0.* , exceptions ==0.10.* , http-types ==0.12.* , http2 >=1.0.0 && <3.3.0- , text >=1.2.3 && <1.3- , time >=1.8 && <1.9.9+ , text >=1.2.3 && <3+ , time >=1.8 && <2 , transformers >=0.5.6 && <0.6 , vault ==0.3.* , wai ==3.2.* , wai-extra >=3.0 && <3.2+ default-language: Haskell2010++test-suite twain-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ other-modules:+ TwainSpec+ default-extensions:+ OverloadedStrings+ build-depends:+ base+ , hspec+ , hspec-discover+ , hspec-wai+ , twain+ ghc-options:+ -threaded -rtsopts -with-rtsopts=-N+ build-tool-depends:+ hspec-discover:hspec-discover default-language: Haskell2010