on master help sign in
Contributing
commit98b6e8512859f5783e9e97266419ac090485aadc authorJulian K. Arni <jkarni@gmail.com> dateMon 17 Aug 2015, 23:50 (11 years ago) parent1a27e46
12 files changed+180−84
.stylish-haskell.yaml+77 HLint.hs+6 README.md+46−7 servant-client/src/Servant/Common/Req.hs+1−1 servant-js/src/Servant/JS/Internal.hs+6−6 servant-js/src/Servant/JS/JQuery.hs+4−4 servant-js/src/Servant/JS/Vanilla.hs+6−6 servant-js/test/Servant/JSSpec.hs+10−10 servant-mock/src/Servant/Mock.hs+18−18 servant-server/src/Servant/Server/Internal.hs+3−5 servant/default.nix−24 servant/test/Servant/API/ContentTypesSpec.hs+3−3
.stylish-haskell.yamlnew file+77
@@ -0,0 +1,77 @@
1+# stylish-haskell configuration file
2+# ==================================
3+
4+# The stylish-haskell tool is mainly configured by specifying steps. These steps
5+# are a list, so they have an order, and one specific step may appear more than
6+# once (if needed). Each file is processed by these steps in the given order.
7+steps:
8+ # Convert some ASCII sequences to their Unicode equivalents. This is disabled
9+ # by default.
10+ # - unicode_syntax:
11+ # # In order to make this work, we also need to insert the UnicodeSyntax
12+ # # language pragma. If this flag is set to true, we insert it when it's
13+ # # not already present. You may want to disable it if you configure
14+ # # language extensions using some other method than pragmas. Default:
15+ # # true.
16+ # add_language_pragma: true
17+
18+ # Import cleanup
19+ - imports:
20+ # There are different ways we can align names and lists.
21+ #
22+ # - global: Align the import names and import list throughout the entire
23+ # file.
24+ #
25+ # - file: Like global, but don't add padding when there are no qualified
26+ # imports in the file.
27+ #
28+ # - group: Only align the imports per group (a group is formed by adjacent
29+ # import lines).
30+ #
31+ # - none: Do not perform any alignment.
32+ #
33+ # Default: global.
34+ align: global
35+
36+ # Language pragmas
37+ - language_pragmas:
38+ # We can generate different styles of language pragma lists.
39+ #
40+ # - vertical: Vertical-spaced language pragmas, one per line.
41+ #
42+ # - compact: A more compact style.
43+ #
44+ # - compact_line: Similar to compact, but wrap each line with
45+ # `{-#LANGUAGE #-}'.
46+ #
47+ # Default: vertical.
48+ style: vertical
49+
50+ # stylish-haskell can detect redundancy of some language pragmas. If this
51+ # is set to true, it will remove those redundant pragmas. Default: true.
52+ remove_redundant: true
53+
54+ # Align the types in record declarations
55+ - records: {}
56+
57+ # Replace tabs by spaces. This is disabled by default.
58+ # - tabs:
59+ # # Number of spaces to use for each tab. Default: 8, as specified by the
60+ # # Haskell report.
61+ # spaces: 8
62+
63+ # Remove trailing whitespace
64+ - trailing_whitespace: {}
65+
66+# A common setting is the number of columns (parts of) code will be wrapped
67+# to. Different steps take this into account. Default: 80.
68+columns: 80
69+
70+# Sometimes, language extensions are specified in a cabal file or from the
71+# command line instead of using language pragmas in the file. stylish-haskell
72+# needs to be aware of these, so it can parse the file correctly.
73+#
74+# No language extensions are enabled by default.
75+language_extensions:
76+ - TemplateHaskell
77+ - QuasiQuotes
HLint.hsnew file+6
@@ -0,0 +1,6 @@
1+import "hint" HLint.Default
2+
3+ignore "Redundant do"
4+ignore "Parse error"
5+ignore "Use list comprehension"
6+ignore "Use liftM"
README.md+46−7
@@ -5,17 +5,56 @@
55
66![servant](https://raw.githubusercontent.com/haskell-servant/servant/master/servant.png)
77
8−These libraries provides a family of combinators to define webservices and automatically generate the documentation and client-side querying functions for each endpoint.
8+These libraries provides a family of combinators to define webservices and
9+automatically generate the documentation and client-side querying functions for
10+each endpoint.
911
10−In order to minimize the dependencies depending on your needs, we provide these features under different packages.
12+In order to minimize the dependencies depending on your needs, we provide these
13+features under different packages.
1114
1215- `servant`, which contains everything you need to *declare* a webservice API.
13−- `servant-server`, which lets you *implement* an HTTP server with handlers for each endpoint of an API.
14−- `servant-client`, which lets you derive automatically Haskell functions that let you query each endpoint of a `servant` webservice.
16+- `servant-server`, which lets you *implement* an HTTP server with handlers for
17+each endpoint of an API.
18+- `servant-client`, which lets you derive automatically Haskell functions that
19+let you query each endpoint of a `servant` webservice.
1520- `servant-docs`, which lets you generate API docs for your webservice.
16−- `servant-js`, which lets you derive Javascript functions (using vanilla JS ajax requests, angular or jquery) to query your API's endpoints, in the same spirit as `servant-client`.
17−- `servant-blaze` and `servant-lucid` provide easy HTML rendering of your data as an `HTML` content-type "combinator".
21+- `servant-js`, which lets you derive Javascript functions (using vanilla JS
22+ajax requests, angular or jquery) to query your API's endpoints, in the same
23+spirit as `servant-client`.
24+- `servant-blaze` and `servant-lucid` provide easy HTML rendering of your data
25+as an `HTML` content-type "combinator".
1826
1927## Tutorial
2028
21−We have a [tutorial](http://haskell-servant.github.io/tutorial) guide that introduces the core types and features of servant. After this article, you should be able to write your first servant webservices, learning the rest from the haddocks' examples.
29+We have a [tutorial](http://haskell-servant.github.io/tutorial) guide that
30+introduces the core types and features of servant. After this article, you
31+should be able to write your first servant webservices, learning the rest from
32+the haddocks' examples.
33+
34+## Contributing
35+
36+Contributions are very welcome! To hack on the github version, clone the
37+repository. You can use `cabal`:
38+
39+```shell
40+./scripts/start-sandbox.sh # Initialize the sandbox and add-source the packages
41+./scripts/test-all.sh # Run all the tests
42+```
43+
44+`stack`:
45+
46+```shell
47+stack build # Install and build packages
48+stack test # Run all the tests
49+```
50+
51+Or `nix`:
52+```shell
53+./scripts/update-nix-files.sh # Get up-to-date shell.nix files
54+```
55+
56+Though we aren't sticklers for style, the `.stylish-haskel.yaml` and `HLint.hs`
57+files in the repository provide a good baseline for consistency.
58+
59+Please include a description of the changes in your PR in the `CHANGELOG.md` of
60+the packages you've changed. And of course, write tests!
servant-client/src/Servant/Common/Req.hs+1−1
@@ -99,7 +99,7 @@ setRQBody b t req = req { reqBody = Just (b, t) }
9999
100100reqToRequest :: (Functor m, MonadThrow m) => Req -> BaseUrl -> m Request
101101reqToRequest req (BaseUrl reqScheme reqHost reqPort) =
102− fmap (setheaders . setAccept . setrqb . setQS ) $ parseUrl url
102+ setheaders . setAccept . setrqb . setQS <$> parseUrl url
103103
104104 where url = show $ nullURI { uriScheme = case reqScheme of
105105 Http -> "http:"
servant-js/src/Servant/JS/Internal.hs+6−6
@@ -28,7 +28,7 @@ import Servant.API
2828-- | this structure is used by JavaScriptGenerator implementations to let you
2929-- customize the output
3030data CommonGeneratorOptions = CommonGeneratorOptions
31− {
31+ {
3232 functionNameBuilder :: FunctionName -> String -- ^ function generating function names
3333 , requestBody :: String -- ^ name used when a user want to send the request body (to let you redefine it)
3434 , successCallback :: String -- ^ name of the callback parameter when the request was successful
@@ -63,12 +63,12 @@ defCommonGeneratorOptions = CommonGeneratorOptions
6363-- | Function name builder that simply concat each part together
6464concatCase :: FunctionName -> String
6565concatCase = concat
66−
66+
6767-- | Function name builder using the snake_case convention.
6868-- each part is separated by a single underscore character.
6969snakeCase :: FunctionName -> String
7070snakeCase = intercalate "_"
71−
71+
7272-- | Function name builder using the CamelCase convention.
7373-- each part begins with an upper case character.
7474camelCase :: FunctionName -> String
@@ -78,7 +78,7 @@ camelCase (p:ps) = concat $ p : camelCase' ps
7878 camelCase' (r:rs) = capitalize r : camelCase' rs
7979 capitalize [] = []
8080 capitalize (x:xs) = toUpper x : xs
81−
81+
8282type Arg = String
8383
8484-- A 'JavascriptGenerator' just takes the data found in the API type
@@ -141,8 +141,8 @@ toValidFunctionName (x:xs) = [setFirstChar x] <> filter remainder xs
141141 setFirstChar c = if firstChar c
142142 then c
143143 else '_'
144− firstChar c = (prefixOK c) || (or . map (Set.member c) $ firstLetterOK)
145− remainder c = (prefixOK c) || (or . map (Set.member c) $ remainderOK)
144+ firstChar c = prefixOK c || any (Set.member c) firstLetterOK
145+ remainder c = prefixOK c || any (Set.member c) remainderOK
146146 -- Valid prefixes
147147 prefixOK c = c `elem` ['$','_']
148148 -- Unicode character sets
servant-js/src/Servant/JS/JQuery.hs+4−4
@@ -9,12 +9,12 @@ import Data.Monoid
99-- to make the AJAX calls. Uses 'defCommonGeneratorOptions'
1010-- for the generator options.
1111jquery :: JavaScriptGenerator
12−jquery = concat . map generateJQueryJS
12+jquery = concatMap generateJQueryJS
1313
1414-- | Generate javascript functions that use the /jQuery/ library
1515-- to make the AJAX calls. Lets you specify your own 'CommonGeneratorOptions'.
1616jqueryWith :: CommonGeneratorOptions -> JavaScriptGenerator
17−jqueryWith opts = concat . map (generateJQueryJSWith opts)
17+jqueryWith opts = concatMap (generateJQueryJSWith opts)
1818
1919-- | js codegen using JQuery using default options
2020generateJQueryJS :: AjaxReq -> String
@@ -53,7 +53,7 @@ generateJQueryJSWith opts req = "\n" <>
5353 body = if req ^. reqBody
5454 then [requestBody opts]
5555 else []
56−
56+
5757 onSuccess = successCallback opts
5858 onError = errorCallback opts
5959
@@ -77,7 +77,7 @@ generateJQueryJSWith opts req = "\n" <>
7777 then "var "
7878 else (moduleName opts) <> "."
7979 fname = namespace <> (functionNameBuilder opts $ req ^. funcName)
80−
80+
8181 method = req ^. reqMethod
8282 url = if url' == "'" then "'/'" else url'
8383 url' = "'"
servant-js/src/Servant/JS/Vanilla.hs+6−6
@@ -9,13 +9,13 @@ import Data.Monoid
99-- to your API, using /XMLHttpRequest/. Uses 'defCommonGeneratorOptions'
1010-- for the 'CommonGeneratorOptions'.
1111vanillaJS :: JavaScriptGenerator
12−vanillaJS = concat . map generateVanillaJS
12+vanillaJS = concatMap generateVanillaJS
1313
1414-- | Generate vanilla javascript functions to make AJAX requests
1515-- to your API, using /XMLHttpRequest/. Lets you specify your
1616-- own options.
1717vanillaJSWith :: CommonGeneratorOptions -> JavaScriptGenerator
18−vanillaJSWith opts = concat . map (generateVanillaJSWith opts)
18+vanillaJSWith opts = concatMap (generateVanillaJSWith opts)
1919
2020-- | js codegen using XmlHttpRequest using default generation options
2121generateVanillaJS :: AjaxReq -> String
@@ -56,11 +56,11 @@ generateVanillaJSWith opts req = "\n" <>
5656 hs = req ^. reqHeaders
5757
5858 queryparams = req ^.. reqUrl.queryStr.traverse
59−
59+
6060 body = if req ^. reqBody
6161 then [requestBody opts]
6262 else []
63−
63+
6464 onSuccess = successCallback opts
6565 onError = errorCallback opts
6666
@@ -68,7 +68,7 @@ generateVanillaJSWith opts req = "\n" <>
6868 if req ^. reqBody
6969 then "JSON.stringify(body)\n"
7070 else "null"
71−
71+
7272
7373 reqheaders =
7474 if null hs
@@ -84,7 +84,7 @@ generateVanillaJSWith opts req = "\n" <>
8484 then "var "
8585 else (moduleName opts) <> "."
8686 fname = namespace <> (functionNameBuilder opts $ req ^. funcName)
87−
87+
8888 method = req ^. reqMethod
8989 url = if url' == "'" then "'/'" else url'
9090 url' = "'"
servant-js/test/Servant/JSSpec.hs+10−10
@@ -65,7 +65,7 @@ customOptions = defCommonGeneratorOptions
6565 { successCallback = "okCallback"
6666 , errorCallback = "errorCallback"
6767 }
68−
68+
6969spec :: Spec
7070spec = describe "Servant.JQuery" $ do
7171 generateJSSpec Vanilla JS.generateVanillaJS
@@ -76,12 +76,12 @@ spec = describe "Servant.JQuery" $ do
7676 generateJSSpec AngularCustom (NG.generateAngularJSWith NG.defAngularOptions customOptions)
7777 generateJSSpec Axios (AX.generateAxiosJS AX.defAxiosOptions)
7878 generateJSSpec AxiosCustom (AX.generateAxiosJSWith (AX.defAxiosOptions { withCredentials = True }) customOptions)
79−
79+
8080 angularSpec Angular
8181 axiosSpec
8282 --angularSpec AngularCustom
8383
84−axiosSpec :: Spec
84+axiosSpec :: Spec
8585axiosSpec = describe specLabel $ do
8686 it "should add withCredentials when needed" $ do
8787 let jsText = genJS withCredOpts $ listFromAPI (Proxy :: Proxy TestAPI)
@@ -102,20 +102,20 @@ axiosSpec = describe specLabel $ do
102102 cookieOpts = AX.defAxiosOptions { AX.xsrfCookieName = Just "MyXSRFcookie" }
103103 headerOpts = AX.defAxiosOptions { AX.xsrfHeaderName = Just "MyXSRFheader" }
104104 genJS :: AxiosOptions -> [AjaxReq] -> String
105− genJS opts req = concat $ map (AX.generateAxiosJS opts) req
105+ genJS opts req = concatMap (AX.generateAxiosJS opts) req
106106
107−angularSpec :: TestNames -> Spec
107+angularSpec :: TestNames -> Spec
108108angularSpec test = describe specLabel $ do
109109 it "should implement a service globally" $ do
110110 let jsText = genJS $ listFromAPI (Proxy :: Proxy TestAPI)
111111 output jsText
112112 jsText `shouldContain` (".service('" ++ testName ++ "'")
113−
113+
114114 it "should depend on $http service globally" $ do
115115 let jsText = genJS $ listFromAPI (Proxy :: Proxy TestAPI)
116116 output jsText
117117 jsText `shouldContain` ("('" ++ testName ++ "', function($http) {")
118−
118+
119119 it "should not depend on $http service in handlers" $ do
120120 let jsText = genJS $ listFromAPI (Proxy :: Proxy TestAPI)
121121 output jsText
@@ -126,11 +126,11 @@ angularSpec test = describe specLabel $ do
126126 testName = "MyService"
127127 ngOpts = NG.defAngularOptions { NG.serviceName = testName }
128128 genJS req = NG.angularService ngOpts req
129−
129+
130130generateJSSpec :: TestNames -> (AjaxReq -> String) -> Spec
131131generateJSSpec n gen = describe specLabel $ do
132132 it "should generate valid javascript" $ do
133− let s = jsForAPI (Proxy :: Proxy TestAPI) (concat . map gen)
133+ let s = jsForAPI (Proxy :: Proxy TestAPI) (concatMap gen)
134134 parseFromString s `shouldSatisfy` isRight
135135
136136 it "should use non-empty function names" $ do
@@ -167,7 +167,7 @@ generateJSSpec n gen = describe specLabel $ do
167167 jsText `shouldContain` (header n "X-WhatsForDinner" $ "\"I would like \" + headerXWhatsForDinner + \" with a cherry on top.\"")
168168
169169 it "can generate the whole javascript code string at once with jsForAPI" $ do
170− let jsStr = jsForAPI (Proxy :: Proxy TestAPI) (concat . map gen)
170+ let jsStr = jsForAPI (Proxy :: Proxy TestAPI) (concatMap gen)
171171 parseFromString jsStr `shouldSatisfy` isRight
172172 where
173173 specLabel = "generateJS(" ++ (show n) ++ ")"
servant-mock/src/Servant/Mock.hs+18−18
@@ -1,10 +1,10 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE TypeFamilies #-}
4−{-# LANGUAGE TypeOperators #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
75{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE TypeOperators #-}
88-- |
99-- Module : Servant.Mock
1010-- Copyright : 2015 Alp Mestanogullari
@@ -51,18 +51,18 @@
5151module Servant.Mock ( HasMock(..) ) where
5252
5353#if !MIN_VERSION_base(4,8,0)
54−import Control.Applicative
54+import Control.Applicative
5555#endif
56−import Control.Monad.IO.Class
57−import Data.ByteString.Lazy.Char8 (pack)
58−import Data.Proxy
59−import GHC.TypeLits
60−import Network.HTTP.Types.Status
61−import Network.Wai
62−import Servant
63−import Servant.API.ContentTypes
64−import Test.QuickCheck.Arbitrary (Arbitrary(..), vector)
65−import Test.QuickCheck.Gen (Gen, generate)
56+import Control.Monad.IO.Class
57+import Data.ByteString.Lazy.Char8 (pack)
58+import Data.Proxy
59+import GHC.TypeLits
60+import Network.HTTP.Types.Status
61+import Network.Wai
62+import Servant
63+import Servant.API.ContentTypes
64+import Test.QuickCheck.Arbitrary (Arbitrary (..), vector)
65+import Test.QuickCheck.Gen (Gen, generate)
6666
6767-- | 'HasMock' defines an interpretation of API types
6868-- than turns them into random-response-generating
@@ -169,7 +169,7 @@ instance HasMock Raw where
169169 bdy <- genBody
170170 respond $ responseLBS status200 [] bdy
171171
172− where genBody = fmap pack $ generate (vector 100 :: Gen [Char])
172+ where genBody = pack <$> generate (vector 100 :: Gen [Char])
173173
174174mockArbitrary :: (MonadIO m, Arbitrary a) => m a
175175mockArbitrary = liftIO (generate arbitrary)
servant-server/src/Servant/Server/Internal.hs+3−5
@@ -137,9 +137,7 @@ processMethodRouter handleA status method headers request = case handleA of
137137 Nothing -> failWith UnsupportedMediaType
138138 Just (contentT, body) -> succeedWith $ responseLBS status hdrs bdy
139139 where
140− bdy = case allowedMethodHead method request of
141− True -> ""
142− False -> body
140+ bdy = if allowedMethodHead method request then "" else body
143141 hdrs = (hContentType, cs contentT) : (fromMaybe [] headers)
144142
145143methodRouter :: (AllCTRender ctypes a)
@@ -512,7 +510,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
512510 -- named "foo" or "foo[]" and call fromText on the
513511 -- corresponding values
514512 parameters = filter looksLikeParam querytext
515− values = catMaybes $ map (convert . snd) parameters
513+ values = mapMaybe (convert . snd) parameters
516514 in route (Proxy :: Proxy sublayout) (feedTo subserver values)
517515 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
518516 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
@@ -625,7 +623,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
625623 -- named "foo" or "foo[]" and call fromText on the
626624 -- corresponding values
627625 parameters = filter looksLikeParam matrixtext
628− values = catMaybes $ map (convert . snd) parameters
626+ values = mapMaybe (convert . snd) parameters
629627 route (Proxy :: Proxy sublayout) (feedTo subserver values)
630628 _ -> route (Proxy :: Proxy sublayout) (feedTo subserver [])
631629 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
servant/default.nix−24
@@ -1,24 +0,0 @@
1−{ mkDerivation, aeson, attoparsec, base, bytestring
2−, bytestring-conversion, case-insensitive, directory, doctest
3−, filemanip, filepath, hspec, http-media, http-types, network-uri
4−, parsec, QuickCheck, quickcheck-instances, stdenv
5−, string-conversions, text, url, vault
6−}:
7−mkDerivation {
8− pname = "servant";
9− version = "0.5";
10− src = ./.;
11− buildDepends = [
12− aeson attoparsec base bytestring bytestring-conversion
13− case-insensitive http-media http-types network-uri
14− string-conversions text vault
15− ];
16− testDepends = [
17− aeson attoparsec base bytestring directory doctest filemanip
18− filepath hspec parsec QuickCheck quickcheck-instances
19− string-conversions text url
20− ];
21− homepage = "http://haskell-servant.github.io/";
22− description = "A family of combinators for defining webservices APIs";
23− license = stdenv.lib.licenses.bsd3;
24−}
servant/test/Servant/API/ContentTypesSpec.hs+3−3
@@ -57,15 +57,15 @@ spec = describe "Servant.API.ContentTypes" $ do
5757 let p = Proxy :: Proxy FormUrlEncoded
5858
5959 it "has mimeUnrender reverse mimeRender" $ do
60− property $ \x -> all (/= mempty) x
60+ property $ \x -> mempty `notElem` x
6161 ==> mimeUnrender p (mimeRender p x) == Right (x::[(TextS.Text,TextS.Text)])
6262
6363 it "has mimeUnrender reverse exportParams (Network.URL)" $ do
64− property $ \x -> all (/= mempty) x
64+ property $ \x -> mempty `notElem` x
6565 ==> (mimeUnrender p . cs . exportParams . map (cs *** cs) $ x) == Right (x::[(TextS.Text,TextS.Text)])
6666
6767 it "has importParams (Network.URL) reverse mimeRender" $ do
68− property $ \x -> all (/= mempty) x
68+ property $ \x -> mempty `notElem` x
6969 ==> (fmap (map (cs *** cs)) . importParams . cs . mimeRender p $ x) == Just (x::[(TextS.Text,TextS.Text)])
7070
7171 describe "The PlainText Content-Type type" $ do