packages feed

lackey 0.2.0 → 0.3.0

raw patch · 14 files changed

+322/−487 lines, 14 filesdep +servant-foreigndep +textdep ~basedep ~servantdep ~tasty

Dependencies added: servant-foreign, text

Dependency ranges changed: base, servant, tasty

Files

README.md view
@@ -5,46 +5,38 @@  Lackey is a Haskell library for generating Ruby consumers of [Servant][] APIs. --   [Installation](#installation)--   [Usage](#usage)--## Installation--You can install Lackey by adding it to your Cabal file.--```-build-depends:-    lackey ==0.1.*-```--You can also install it manually.--``` sh-cabal update-cabal install 'lackey ==0.1.*'-```--Please see [the change log][] for a detailed list of changes.--## Usage- Use `Lackey.rubyForAPI` to generate a string of Ruby source code for consuming a Servant API. For example:  ``` hs-type API = "things" :> Get '[JSON] [Thing]+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-} -api :: Proxy API-api = Proxy+import qualified Data.Proxy as Proxy+import qualified Data.Text as Text+import qualified Data.Text.IO as Text+import qualified Lackey+import Servant.API -ruby :: String-ruby = rubyForAPI api+type API = "words" :> Get '[JSON] [String]++api :: Proxy.Proxy API+api = Proxy.Proxy++ruby :: Text.Text+ruby = Lackey.rubyForAPI api++main :: IO ()+main = Text.putStrLn ruby+-- def get_words(excon)excon.request(:method=>:get,:path=>"/words",:headers=>{},:body=>nil)end ``` +The generated functions require [Excon][].+ [Lackey]: https://github.com/tfausak/lackey [Version badge]: https://www.stackage.org/package/lackey/badge/nightly?label=version [version]: https://www.stackage.org/package/lackey [Build badge]: https://travis-ci.org/tfausak/lackey.svg?branch=master [build]: https://travis-ci.org/tfausak/lackey-[Servant]: http://haskell-servant.github.io-[the change log]: ./CHANGELOG.md+[Servant]: https://haskell-servant.readthedocs.org/en/stable/+[Excon]: https://rubygems.org/gems/excon
lackey.cabal view
@@ -3,9 +3,9 @@ -- see: https://github.com/sol/hpack  name:           lackey-version:        0.2.0-synopsis:       Generate Ruby consumers of Servant APIs.-description:    Lackey generates Ruby consumers of Servant APIs.+version:        0.3.0+synopsis:       Generate Ruby clients from Servant APIs.+description:    Lackey generates Ruby clients from Servant APIs. category:       Web homepage:       https://github.com/tfausak/lackey#readme bug-reports:    https://github.com/tfausak/lackey/issues@@ -30,18 +30,12 @@       library   ghc-options: -Wall   build-depends:-      base >=4.7 && <4.9-    , servant >=0.4 && <0.6+      base >=4.8 && <4.9+    , servant >=0.5 && <0.7+    , servant-foreign >=0.5 && <0.7+    , text >=1.2 && <1.3   exposed-modules:       Lackey-      Lackey.Internal-      Lackey.Internal.Endpoint-      Lackey.Internal.HasCode-      Lackey.Internal.HasRuby-      Lackey.Internal.Header-      Lackey.Internal.Method-      Lackey.Internal.PathSegment-      Lackey.Internal.QueryItem   default-language: Haskell2010  test-suite lackey-test-suite@@ -49,11 +43,12 @@   main-is: Main.hs   hs-source-dirs:       test-suite-  ghc-options: -Wall+  ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N   build-depends:       base     , lackey     , servant-    , tasty >=0.10 && <0.12+    , tasty >=0.11 && <0.12     , tasty-hspec >=1.1 && <1.2+    , text   default-language: Haskell2010
library/Lackey.hs view
@@ -1,17 +1,186 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} -{- |-    This module allows you to generate Ruby code for APIs defined by Servant.--}-module Lackey-    ( rubyForAPI-    ) where+module Lackey (rubyForAPI) where -import Data.Proxy (Proxy)-import Lackey.Internal+import qualified Data.Char as Char+import Data.Function ((&))+import qualified Data.Maybe as Maybe+import Data.Monoid ((<>))+import qualified Data.Proxy as Proxy+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text+import qualified Servant.Foreign as Servant -{- |-    Generate Ruby code from an API description.--}-rubyForAPI :: (HasCode a, HasRuby (Ruby a)) => Proxy a -> String-rubyForAPI proxy = rubyFor (codeFor proxy defaultEndpoint)+type Language = Servant.NoTypes++languageProxy :: Proxy.Proxy Language+languageProxy = Proxy.Proxy++type Request = ()++requestProxy :: Proxy.Proxy Request+requestProxy = Proxy.Proxy++renderRequests :: [Servant.Req Request] -> Text.Text+renderRequests requests = requests & map renderRequest & Text.intercalate ";"++functionName :: Servant.Req Request -> Text.Text+functionName request = request & Servant._reqFuncName & Servant.snakeCase++hasBody :: Servant.Req Request -> Bool+hasBody request =+    case Servant._reqBody request of+        Nothing -> False+        Just _ -> True++bodyArgument :: Text.Text+bodyArgument = "body"++underscore :: Text.Text -> Text.Text+underscore text =+    text & Text.toLower &+    Text.map+        (\c ->+              if Char.isAlphaNum c+                  then c+                  else '_')++getHeaders :: Servant.Req Request -> [Text.Text]+getHeaders request =+    request & Servant._reqHeaders &+    Maybe.mapMaybe+        (\h ->+              case h of+                  Servant.HeaderArg x -> Just x+                  Servant.ReplaceHeaderArg _ _ -> Nothing) &+    map Servant._argName &+    map Servant.unPathSegment++getURLPieces :: Servant.Req Request -> [Either Text.Text Text.Text]+getURLPieces request =+    let url = request & Servant._reqUrl+        path =+            url & Servant._path & map Servant.unSegment &+            Maybe.mapMaybe+                (\segment ->+                      case segment of+                          Servant.Static _ -> Nothing+                          Servant.Cap arg -> Just arg) &+            map Servant._argName &+            map Servant.unPathSegment+        query =+            url & Servant._queryStr & map Servant._queryArgName &+            map Servant._argName &+            map Servant.unPathSegment+    in map Left path ++ map Right query++functionArguments :: Servant.Req Request -> Text.Text+functionArguments request =+    Text.concat+        [ "("+        , [ [Just "excon"]+          , request & getURLPieces &+            map+                (\piece ->+                      case piece of+                          Left capture -> underscore capture+                          Right param -> underscore param <> ": nil") &+            map Just+          , request & getHeaders & map underscore & map (<> ": nil") & map Just+          , [ if hasBody request+                  then Just bodyArgument+                  else Nothing]] &+          concat &+          Maybe.catMaybes &+          Text.intercalate ","+        , ")"]++requestMethod :: Servant.Req Request -> Text.Text+requestMethod request =+    request & Servant._reqMethod & Text.decodeUtf8 & Text.toLower &+    Text.cons ':'++requestPath :: Servant.Req Request -> Text.Text+requestPath request =+    let path =+            request & Servant._reqUrl & Servant._path & map Servant.unSegment &+            map+                (\x ->+                      case x of+                          Servant.Static y -> Servant.unPathSegment y+                          Servant.Cap y ->+                              let z =+                                      y & Servant._argName &+                                      Servant.unPathSegment &+                                      underscore+                              in "#{" <> z <> "}") &+            Text.intercalate "/"+        query =+            request & Servant._reqUrl & Servant._queryStr &+            map Servant._queryArgName &+            map Servant._argName &+            map Servant.unPathSegment &+            map+                (\x ->+                      x <> "=#{" <> underscore x <> "}") &+            Text.intercalate "&"+        url =+            "/" <> path <>+            (if Text.null query+                 then ""+                 else "?" <> query)+    in "\"" <> url <> "\""++requestHeaders :: Servant.Req Request -> Text.Text+requestHeaders request =+    [ ["{"]+    , request & getHeaders &+      map+          (\x ->+                "\"" <> x <> "\"=>" <> underscore x)+    , ["}"]] &+    concat &+    Text.concat++requestBody :: Servant.Req Request -> Text.Text+requestBody request =+    if hasBody request+        then bodyArgument+        else "nil"++functionBody :: Servant.Req Request -> Text.Text+functionBody request =+    Text.concat+        [ "excon.request("+        , ":method=>"+        , requestMethod request+        , ","+        , ":path=>"+        , requestPath request+        , ","+        , ":headers=>"+        , requestHeaders request+        , ","+        , ":body=>"+        , requestBody request+        , ")"]++renderRequest :: Servant.Req Request -> Text.Text+renderRequest request =+    Text.concat+        [ "def "+        , functionName request+        , functionArguments request+        , functionBody request+        , "end"]++requestsForAPI+    :: (Servant.HasForeign Language Request api, Servant.GenerateList Request (Servant.Foreign Request api))+    => Proxy.Proxy api -> [Servant.Req Request]+requestsForAPI api = api & Servant.listFromAPI languageProxy requestProxy++rubyForAPI+    :: (Servant.HasForeign Language Request api, Servant.GenerateList Request (Servant.Foreign Request api))+    => Proxy.Proxy api -> Text.Text+rubyForAPI api = api & requestsForAPI & renderRequests
− library/Lackey/Internal.hs
@@ -1,19 +0,0 @@-{- |-    This module is for internal use only and should be considered private. If-    you need anything exported from this module, please contact the maintainer.--}-module Lackey.Internal-    ( module Lackey.Internal.Endpoint-    , module Lackey.Internal.HasCode-    , module Lackey.Internal.HasRuby-    , module Lackey.Internal.Method-    , module Lackey.Internal.PathSegment-    , module Lackey.Internal.QueryItem-    ) where--import Lackey.Internal.Endpoint-import Lackey.Internal.HasCode-import Lackey.Internal.HasRuby-import Lackey.Internal.Method-import Lackey.Internal.PathSegment-import Lackey.Internal.QueryItem
− library/Lackey/Internal/Endpoint.hs
@@ -1,23 +0,0 @@-module Lackey.Internal.Endpoint where--import Lackey.Internal.Header (Header)-import Lackey.Internal.Method (Method (Get))-import Lackey.Internal.PathSegment (PathSegment)-import Lackey.Internal.QueryItem (QueryItem)--data Endpoint = Endpoint-    { endpointMethod :: Method-    , endpointPathSegments :: [PathSegment]-    , endpointQueryItems :: [QueryItem]-    , endpointHeaders :: [Header]-    , endpointHasBody :: Bool-    } deriving (Eq, Ord, Read, Show)--defaultEndpoint :: Endpoint-defaultEndpoint = Endpoint-    { endpointMethod = Get-    , endpointPathSegments = []-    , endpointQueryItems = []-    , endpointHeaders = []-    , endpointHasBody = False-    }
− library/Lackey/Internal/HasCode.hs
@@ -1,112 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}--module Lackey.Internal.HasCode where--import Data.Proxy (Proxy (Proxy))-import GHC.TypeLits (KnownSymbol, symbolVal)-import Lackey.Internal.Endpoint-import Lackey.Internal.Header-import Lackey.Internal.Method-import Lackey.Internal.PathSegment-import Lackey.Internal.QueryItem-import Servant.API ((:>), (:<|>)((:<|>)))--import qualified Servant.API as S--class HasCode a where-    type Ruby a-    codeFor :: Proxy a -> Endpoint -> Ruby a--instance HasCode S.Raw where-    type Ruby S.Raw = ()-    codeFor _ _ = ()--instance (HasCode a, HasCode b) => HasCode (a :<|> b) where-    type Ruby (a :<|> b) = Ruby a :<|> Ruby b-    codeFor _ e = codeFor a e :<|> codeFor b e where-        a = Proxy :: Proxy a-        b = Proxy :: Proxy b--instance (HasCode c) => HasCode (S.ReqBody a b :> c) where-    type Ruby (S.ReqBody a b :> c) = Ruby c-    codeFor _ e = codeFor c (e { endpointHasBody = True}) where-        c = Proxy :: Proxy c---- Methods--instance HasCode (S.Delete a b) where-    type Ruby (S.Delete a b) = Endpoint-    codeFor _ e = e { endpointMethod = Delete }--instance HasCode (S.Get a b) where-    type Ruby (S.Get a b) = Endpoint-    codeFor _ e = e { endpointMethod = Get }--instance HasCode (S.Patch a b) where-    type Ruby (S.Patch a b) = Endpoint-    codeFor _ e = e { endpointMethod = Patch }--instance HasCode (S.Post a b) where-    type Ruby (S.Post a b) = Endpoint-    codeFor _ e = e { endpointMethod = Post }--instance HasCode (S.Put a b) where-    type Ruby (S.Put a b) = Endpoint-    codeFor _ e = e { endpointMethod = Put }---- Path segments--instance (KnownSymbol s, HasCode a) => HasCode (s :> a) where-    type Ruby (s :> a) = Ruby a-    codeFor _ e = codeFor a (e { endpointPathSegments = segments }) where-        a = Proxy :: Proxy a-        segments = endpointPathSegments e ++ [segment]-        segment = PathLiteral (symbolVal s)-        s = Proxy :: Proxy s--instance (KnownSymbol s, HasCode b) => HasCode (S.Capture s a :> b) where-    type Ruby (S.Capture s a :> b) = Ruby b-    codeFor _ e = codeFor b (e { endpointPathSegments = segments }) where-        b = Proxy :: Proxy b-        segments = endpointPathSegments e ++ [segment]-        segment = PathCapture (symbolVal s)-        s = Proxy :: Proxy s---- Query items--instance (KnownSymbol s, HasCode a) => HasCode (S.QueryFlag s :> a) where-    type Ruby (S.QueryFlag s :> a) = Ruby a-    codeFor _ e = codeFor a (e { endpointQueryItems = items }) where-        a = Proxy :: Proxy a-        items = endpointQueryItems e ++ [item]-        item = QueryFlag (symbolVal s)-        s = Proxy :: Proxy s--instance (KnownSymbol s, HasCode b) => HasCode (S.QueryParam s a :> b) where-    type Ruby (S.QueryParam s a :> b) = Ruby b-    codeFor _ e = codeFor b (e { endpointQueryItems = items }) where-        b = Proxy :: Proxy b-        items = endpointQueryItems e ++ [item]-        item = QueryParam (symbolVal s)-        s = Proxy :: Proxy s--instance (KnownSymbol s, HasCode b) => HasCode (S.QueryParams s a :> b) where-    type Ruby (S.QueryParams s a :> b) = Ruby b-    codeFor _ e = codeFor b (e { endpointQueryItems = items }) where-        b = Proxy :: Proxy b-        items = endpointQueryItems e ++ [item]-        item = QueryParams (symbolVal s)-        s = Proxy :: Proxy s---- Headers--instance (KnownSymbol s, HasCode b) => HasCode (S.Header s a :> b) where-    type Ruby (S.Header s a :> b) = Ruby b-    codeFor  _ e = codeFor b (e { endpointHeaders = headers }) where-        b = Proxy :: Proxy b-        headers = endpointHeaders e ++ [header]-        header = Header (symbolVal s)-        s = Proxy :: Proxy s
− library/Lackey/Internal/HasRuby.hs
@@ -1,137 +0,0 @@-{-# LANGUAGE TypeOperators #-}--module Lackey.Internal.HasRuby where--import Lackey.Internal.Endpoint-import Lackey.Internal.Header-import Lackey.Internal.PathSegment-import Lackey.Internal.QueryItem-import Servant.API ((:<|>)(..))--import qualified Data.Char as Char-import qualified Data.List as List-import qualified Data.Maybe as Maybe--infixl 1 &-(&) :: a -> (a -> b) -> b-x & f = f x--class HasRuby a where-    rubyFor :: a -> String--instance HasRuby () where-    rubyFor _ = ""--instance HasRuby Endpoint where-    rubyFor endpoint = "\-        \def " ++ renderName endpoint ++ "(" ++ renderParams endpoint ++ ")\n\-        \  excon.request(\n\-        \    method: :" ++ renderMethod endpoint ++ ",\n\-        \    path: \"" ++ renderPath endpoint ++ "\",\n\-        \    headers: {" ++ renderHeaders endpoint ++ "},\n\-        \    body: " ++ (if endpointHasBody endpoint then "body" else "nil") ++ "\n\-        \  )\n\-        \end\-    \"--instance (HasRuby a, HasRuby b) => HasRuby (a :<|> b) where-    rubyFor (x :<|> y) =-        let rx = rubyFor x-            ry = rubyFor y-            s = if null rx || null ry then "" else "\n\n"-        in  concat [rx, s, ry]--renderName :: Endpoint -> String-renderName endpoint =-    let method = renderMethod endpoint--        renderPathSegment (PathLiteral literal) = literal-        renderPathSegment (PathCapture capture) = capture-        pathSegments =-            let segments = endpointPathSegments endpoint-                renderedSegments = map renderPathSegment segments-            in  if null segments-                then "index" : renderedSegments-                else renderedSegments-        path = List.intercalate "_" pathSegments--        renderQueryItem (QueryFlag flag) = flag-        renderQueryItem (QueryParam param) = param-        renderQueryItem (QueryParams params) = params-        queryItems = map renderQueryItem (endpointQueryItems endpoint)-        query = if null queryItems-            then ""-            else "_" ++ List.intercalate "_" queryItems--        renderHeader (Header x) = x-        headers = map renderHeader (endpointHeaders endpoint)-        header = if null headers-            then ""-            else "_" ++ List.intercalate "_" headers--    in  sanitize (method ++ "_" ++ path ++ query ++ header)--renderParams :: Endpoint -> String-renderParams endpoint =-    let renderPathSegment (PathCapture capture) = Just (sanitize capture)-        renderPathSegment _ = Nothing-        pathSegments-            = endpoint-            & endpointPathSegments-            & map renderPathSegment-            & Maybe.catMaybes--        renderQueryItem (QueryFlag flag) = Just (sanitize flag ++ ": false")-        renderQueryItem (QueryParam param) = Just (sanitize param ++ ": nil")-        renderQueryItem (QueryParams params) = Just (sanitize params ++ ": []")-        queryItems-            = endpoint-            & endpointQueryItems-            & Maybe.mapMaybe renderQueryItem--        renderHeader (Header header) = sanitize header ++ ": nil"-        headers = endpoint & endpointHeaders & map renderHeader--        body = ["body" | endpointHasBody endpoint]--    in  List.intercalate ", "-        (concat [["excon"], pathSegments, body, queryItems, headers])--renderMethod :: Endpoint -> String-renderMethod endpoint = endpoint & endpointMethod & show & map Char.toLower--renderPath :: Endpoint -> String-renderPath endpoint =-    let renderPathSegment (PathLiteral literal) = '/' : literal-        renderPathSegment (PathCapture capture) = concat ["/#{", sanitize capture, "}"]-        pathSegments =-            let segments = endpointPathSegments endpoint-                renderedSegments = concatMap renderPathSegment segments-            in  case segments of-                [] -> '/' : renderedSegments-                _ -> renderedSegments--        renderQueryItem (QueryFlag flag) = concat ["#{\"&", flag, "\" if ", sanitize flag, "}"]-        renderQueryItem (QueryParam param) = concat ["&", param, "=#{", sanitize param, "}"]-        renderQueryItem (QueryParams params) = concat ["#{", sanitize params, ".map { |x| \"&", params, "[]=#{x}\" }.join}"]-        queryItems = case endpointQueryItems endpoint of-            [] -> ""-            items -> '?' : concatMap renderQueryItem items--    in pathSegments ++ queryItems--renderHeaders :: Endpoint -> String-renderHeaders endpoint =-    let headers = endpointHeaders endpoint-    in  if null headers-        then ""-        else-            let y = headers-                    & map (\ (Header x) -> concat ["\"", x, "\" => ", sanitize x])-                    & List.intercalate ", "-            in  " " ++ y ++ " "--sanitize :: String -> String-sanitize xs-    = xs-    & map (\ x -> if Char.isAlphaNum x then Char.toLower x else '_')
− library/Lackey/Internal/Header.hs
@@ -1,4 +0,0 @@-module Lackey.Internal.Header where--newtype Header = Header String-    deriving (Eq, Ord, Read, Show)
− library/Lackey/Internal/Method.hs
@@ -1,9 +0,0 @@-module Lackey.Internal.Method where--data Method-    = Delete-    | Get-    | Patch-    | Post-    | Put-    deriving (Bounded, Enum, Eq, Ord, Read, Show)
− library/Lackey/Internal/PathSegment.hs
@@ -1,6 +0,0 @@-module Lackey.Internal.PathSegment where--data PathSegment-    = PathLiteral String-    | PathCapture String-    deriving (Eq, Ord, Read, Show)
− library/Lackey/Internal/QueryItem.hs
@@ -1,7 +0,0 @@-module Lackey.Internal.QueryItem where--data QueryItem-    = QueryFlag String-    | QueryParam String-    | QueryParams String-    deriving (Eq, Ord, Read, Show)
package.yaml view
@@ -1,31 +1,37 @@ category: Web-description: Lackey generates Ruby consumers of Servant APIs.+description: Lackey generates Ruby clients from Servant APIs. extra-source-files: - CHANGELOG.md - LICENSE.md - package.yaml - README.md - stack.yaml-ghc-options:-- -Wall+ghc-options: -Wall github: tfausak/lackey library:   dependencies:-  - base >=4.7 && <4.9-  - servant >=0.4 && <0.6+  - base >=4.8 && <4.9+  - servant >=0.5 && <0.7+  - servant-foreign >=0.5 && <0.7+  - text >=1.2 && <1.3   source-dirs: library license: MIT maintainer: Taylor Fausak name: lackey-synopsis: Generate Ruby consumers of Servant APIs.+synopsis: Generate Ruby clients from Servant APIs. tests:   lackey-test-suite:     dependencies:     - base     - lackey     - servant-    - tasty >=0.10 && <0.12+    - tasty >=0.11 && <0.12     - tasty-hspec >=1.1 && <1.2+    - text+    ghc-options:+    - -rtsopts+    - -threaded+    - -with-rtsopts=-N     source-dirs: test-suite     main: Main.hs-version: '0.2.0'+version: '0.3.0'
stack.yaml view
@@ -1,5 +1,6 @@ extra-deps:-- servant-0.5+- servant-0.6.1+- servant-foreign-0.6.1 install-ghc: true require-stack-version: ! '>=1.0.4'-resolver: lts-5.8+resolver: nightly-2016-04-07
test-suite/Main.hs view
@@ -1,152 +1,141 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeOperators #-} -module Main where--import Data.Proxy-import Lackey-import Servant.API-import Test.Tasty+import Data.Monoid ((<>))+import qualified Data.Proxy as Proxy+import qualified Data.Text as Text+import qualified Lackey+import qualified Servant.API as Servant+import qualified Test.Tasty as Tasty import Test.Tasty.Hspec  main :: IO ()-main = defaultMain =<< testSpec "Lackey" spec---- Since every generated function has the same structure, it can be abstracted--- away behind this function.-ruby :: String -> String -> String -> String -> String -> String -> String-ruby name params method path headers body = "\-    \def " ++ name ++ "(excon" ++ params ++ ")\n\-    \  excon.request(\n\-    \    method: :" ++ method ++ ",\n\-    \    path: \"/" ++ path ++ "\",\n\-    \    headers: {" ++ headers ++ "},\n\-    \    body: " ++ body ++ "\n\-    \  )\n\-    \end\-\"+main = do+    test <- testSpec "Lackey" spec+    Tasty.defaultMain test  spec :: Spec-spec = do+spec = parallel $ do     describe "rubyForAPI" $ do         it "supports delete requests" $ do-            let api = Proxy :: Proxy (Delete '[] ())-            rubyForAPI api `shouldBe`-                ruby "delete_index" "" "delete" "" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Delete '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "delete" "" "delete" "" "" False          it "supports get requests" $ do-            let api = Proxy :: Proxy (Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index" "" "get" "" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" "" "get" "" "" False          it "supports patch requests" $ do-            let api = Proxy :: Proxy (Patch '[] ())-            rubyForAPI api `shouldBe`-                ruby "patch_index" "" "patch" "" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Patch '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "patch" "" "patch" "" "" False          it "supports post requests" $ do-            let api = Proxy :: Proxy (Post '[] ())-            rubyForAPI api `shouldBe`-                ruby "post_index" "" "post" "" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Post '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "post" "" "post" "" "" False          it "supports put requests" $ do-            let api = Proxy :: Proxy (Put '[] ())-            rubyForAPI api `shouldBe`-                ruby "put_index" "" "put" "" "" "nil"--        it "supports path components" $ do-            let api = Proxy :: Proxy ("resource" :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_resource" "" "get" "resource" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Put '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "put" "" "put" "" "" False          it "supports alternatives" $ do-            let api = Proxy :: Proxy (Get '[] () :<|> Delete '[] ())-            rubyForAPI api `shouldBe` concat-                [ ruby "get_index" "" "get" "" "" "nil"-                , "\n\n"-                , ruby "delete_index" "" "delete" "" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Get '[Servant.JSON] () Servant.:<|> Servant.Delete '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe` Text.concat+                [ ruby "get" "" "get" "" "" False+                , ";"+                , ruby "delete" "" "delete" "" "" False                 ]          it "supports captures" $ do-            let api = Proxy :: Proxy (Capture "id" () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_id" ", id" "get" "#{id}" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Capture "id" () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get_by_id" ",id" "get" "#{id}" "" False          it "supports query flags" $ do-            let api = Proxy :: Proxy (QueryFlag "flag" :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index_flag" ", flag: false" "get" "?#{\"&flag\" if flag}" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryFlag "flag" Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",flag: nil" "get" "?flag=#{flag}" "" False          it "supports query params" $ do-            let api = Proxy :: Proxy (QueryParam "param" () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index_param" ", param: nil" "get" "?&param=#{param}" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryParam "param" () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",param: nil" "get" "?param=#{param}" "" False          it "supports multiple query params" $ do-            let api = Proxy :: Proxy (QueryParams "params" () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index_params" ", params: []" "get" "?#{params.map { |x| \"&params[]=#{x}\" }.join}" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryParams "params" () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",params: nil" "get" "?params=#{params}" "" False          it "supports request bodies" $ do-            let api = Proxy :: Proxy (ReqBody '[] () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index" ", body" "get" "" "" "body"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.ReqBody '[Servant.JSON] () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",body" "get" "" "" True          it "supports request headers" $ do-            let api = Proxy :: Proxy (Header "cookie" () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index_cookie" ", cookie: nil" "get" "" " \"cookie\" => cookie " "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Header "cookie" () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",cookie: nil" "get" "" "\"cookie\"=>cookie" False          it "supports response headers" $ do-            let api = Proxy :: Proxy (Get '[] (Headers '[] ()))-            rubyForAPI api `shouldBe`-                ruby "get_index" "" "get" "" "" "nil"--        it "supports raw" $ do-            let api = Proxy :: Proxy Raw-            rubyForAPI api `shouldBe` ""--        it "supports raw as part of a larger API" $ do-            let api = Proxy :: Proxy (Get '[] () :<|> Raw)-            rubyForAPI api `shouldBe`-                ruby "get_index" "" "get" "" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Get '[Servant.JSON] (Servant.Headers '[] ()))+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" "" "get" "" "" False          it "puts the body param after captures" $ do-            let api = Proxy :: Proxy (Capture "segment" () :> ReqBody '[] () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_segment" ", segment, body" "get" "#{segment}" "" "body"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Capture "segment" () Servant.:> Servant.ReqBody '[Servant.JSON] () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get_by_segment" ",segment,body" "get" "#{segment}" "" True -        it "puts the body param before query params" $ do-            let api = Proxy :: Proxy (QueryFlag "flag" :> ReqBody '[] () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index_flag" ", body, flag: false" "get" "?#{\"&flag\" if flag}" "" "body"+        it "puts the body param after query params" $ do+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryFlag "flag" Servant.:> Servant.ReqBody '[Servant.JSON] () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",flag: nil,body" "get" "?flag=#{flag}" "" True          it "sanitizes path segments" $ do-            let api = Proxy :: Proxy ("A!" :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_a_" "" "get" "A!" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy ("A!" Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get_A!" "" "get" "A!" "" False          it "sanitizes captures" $ do-            let api = Proxy :: Proxy (Capture "A!" () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_a_" ", a_" "get" "#{a_}" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Capture "A!" () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get_by_A!" ",a_" "get" "#{a_}" "" False          it "sanitizes query flags" $ do-            let api = Proxy :: Proxy (QueryFlag "A!" :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index_a_" ", a_: false" "get" "?#{\"&A!\" if a_}" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryFlag "A!" Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False          it "sanitizes query params" $ do-            let api = Proxy :: Proxy (QueryParam "A!" () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index_a_" ", a_: nil" "get" "?&A!=#{a_}" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryParam "A!" () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False          it "sanitizes multiple query params" $ do-            let api = Proxy :: Proxy (QueryParams "A!" () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index_a_" ", a_: []" "get" "?#{a_.map { |x| \"&A![]=#{x}\" }.join}" "" "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryParams "A!" () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False          it "sanitizes headers" $ do-            let api = Proxy :: Proxy (Header "A!" () :> Get '[] ())-            rubyForAPI api `shouldBe`-                ruby "get_index_a_" ", a_: nil" "get" "" " \"A!\" => a_ " "nil"+            let api = Proxy.Proxy :: Proxy.Proxy (Servant.Header "A!" () Servant.:> Servant.Get '[Servant.JSON] ())+            Lackey.rubyForAPI api `shouldBe`+                ruby "get" ",a_: nil" "get" "" "\"A!\"=>a_" False++-- Since every generated function has the same structure, it can be abstracted+-- away behind this function.+ruby :: Text.Text -> Text.Text -> Text.Text -> Text.Text -> Text.Text -> Bool -> Text.Text+ruby name params method path headers body = "\+    \def " <> name <> "(excon" <> params <> ")\+      \excon.request(\+        \:method=>:" <> method <> ",\+        \:path=>\"/" <> path <> "\",\+        \:headers=>{" <> headers <> "},\+        \:body=>" <> (if body then "body" else "nil") <> "\+      \)\+    \end\+\"