diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.markdown
@@ -0,0 +1,4 @@
+# Change log
+
+Lackey follows the [Package Versioning Policy](https://pvp.haskell.org).
+You can find release notes [on GitHub](https://github.com/tfausak/lackey/releases).
diff --git a/LICENSE.markdown b/LICENSE.markdown
--- a/LICENSE.markdown
+++ b/LICENSE.markdown
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2021 Taylor Fausak
+Copyright (c) 2022 Taylor Fausak
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/lackey.cabal b/lackey.cabal
--- a/lackey.cabal
+++ b/lackey.cabal
@@ -1,7 +1,7 @@
-cabal-version: >= 1.10
+cabal-version: 2.2
 
 name: lackey
-version: 1.0.16
+version: 2.0.0.0
 
 synopsis: Generate Ruby clients from Servant APIs.
 description: Lackey generates Ruby clients from Servant APIs.
@@ -9,7 +9,7 @@
 bug-reports: https://github.com/tfausak/lackey/issues
 build-type: Simple
 category: Web
-extra-source-files: README.markdown
+extra-source-files: CHANGELOG.markdown README.markdown
 homepage: https://github.com/tfausak/lackey#readme
 license-file: LICENSE.markdown
 license: MIT
@@ -19,35 +19,57 @@
   location: https://github.com/tfausak/lackey
   type: git
 
-library
+flag pedantic
+  default: False
+  description: Enables @-Werror@, which turns warnings into errors.
+  manual: True
+
+common library
   build-depends:
-    base >= 4.13.0 && < 4.16
-    , servant >= 0.16.2 && < 0.19
+    , base >= 4.13.0 && < 4.17
     , servant-foreign >= 0.15 && < 0.16
     , text >= 1.2.4 && < 1.3
   default-language: Haskell2010
-  exposed-modules: Lackey
   ghc-options:
     -Weverything
+    -Wno-all-missed-specialisations
     -Wno-implicit-prelude
     -Wno-missing-exported-signatures
     -Wno-unsafe
-  hs-source-dirs: src/lib
 
+  if flag(pedantic)
+    ghc-options: -Werror
+
   if impl(ghc >= 8.10)
     ghc-options:
       -Wno-missing-safe-haskell-mode
       -Wno-prepositive-qualified-module
 
+  if impl(ghc >= 9.2)
+    ghc-options:
+      -Wno-missing-kind-signatures
+
+common executable
+  import: library
+
+  build-depends: lackey
+  ghc-options:
+    -rtsopts
+    -threaded
+    -Wno-unused-packages
+
+library
+  import: library
+
+  exposed-modules: Lackey
+  hs-source-dirs: source/library
+
 test-suite test
+  import: executable
+
   build-depends:
-    base -any
     , hspec >= 2.7.6 && < 2.10
-    , lackey -any
-    , servant -any
-    , servant-foreign -any
-    , text -any
-  default-language: Haskell2010
-  hs-source-dirs: src/test
+    , servant >= 0.16.2 && < 0.19
+  hs-source-dirs: source/test-suite
   main-is: Main.hs
   type: exitcode-stdio-1.0
diff --git a/source/library/Lackey.hs b/source/library/Lackey.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Lackey.hs
@@ -0,0 +1,181 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Lackey
+  ( rubyForAPI
+  ) where
+
+import qualified Data.Char as Char
+import Data.Function ((&))
+import qualified Data.Maybe as Maybe
+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
+
+type Language = Servant.NoTypes
+
+languageProxy :: Proxy.Proxy Language
+languageProxy = Proxy.Proxy
+
+type Request = Servant.NoContent
+
+requestProxy :: Proxy.Proxy Request
+requestProxy = Proxy.Proxy
+
+renderRequests :: [Servant.Req Request] -> Text.Text
+renderRequests requests = requests & fmap 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
+        )
+    & fmap (Servant.unPathSegment . Servant._argName)
+
+getURLPieces :: Servant.Req Request -> [Either Text.Text Text.Text]
+getURLPieces request =
+  let
+    url = request & Servant._reqUrl
+    path =
+      url
+        & Servant._path
+        & Maybe.mapMaybe
+            ((\segment -> case segment of
+               Servant.Static _ -> Nothing
+               Servant.Cap arg -> Just arg
+             )
+            . Servant.unSegment
+            )
+        & fmap (Servant.unPathSegment . Servant._argName)
+    query = url & Servant._queryStr & fmap
+      (Servant.unPathSegment . Servant._argName . Servant._queryArgName)
+  in fmap Left path <> fmap Right query
+
+functionArguments :: Servant.Req Request -> Text.Text
+functionArguments request = Text.concat
+  [ "("
+  , [ [Just "excon"]
+    , request & getURLPieces & fmap
+      (\piece -> Just $ case piece of
+        Left capture -> underscore capture
+        Right param -> underscore param <> ": nil"
+      )
+    , request & getHeaders & fmap (Just . (<> ": nil") . underscore)
+    , [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
+        & fmap
+            ((\x -> case x of
+               Servant.Static y -> Servant.unPathSegment y
+               Servant.Cap y ->
+                 let
+                   z =
+                     y & Servant._argName & Servant.unPathSegment & underscore
+                 in "#{" <> z <> "}"
+             )
+            . Servant.unSegment
+            )
+        & Text.intercalate "/"
+    query =
+      request
+        & Servant._reqUrl
+        & Servant._queryStr
+        & fmap
+            ((\x -> x <> "=#{" <> underscore x <> "}")
+            . Servant.unPathSegment
+            . Servant._argName
+            . Servant._queryArgName
+            )
+        & Text.intercalate "&"
+    url = "/" <> path <> (if Text.null query then "" else "?" <> query)
+  in "\"" <> url <> "\""
+
+requestHeaders :: Servant.Req Request -> Text.Text
+requestHeaders request =
+  [ ["{"]
+    , request & getHeaders & fmap (\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
diff --git a/source/test-suite/Main.hs b/source/test-suite/Main.hs
new file mode 100644
--- /dev/null
+++ b/source/test-suite/Main.hs
@@ -0,0 +1,256 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeOperators #-}
+
+import qualified Data.Proxy as Proxy
+import qualified Data.Text as Text
+import qualified Lackey
+import qualified Servant.API as Servant
+import qualified Test.Hspec as Hspec
+
+main :: IO ()
+main =
+  Hspec.hspec
+    . Hspec.parallel
+    . Hspec.describe "Lackey"
+    . Hspec.describe "rubyForAPI"
+    $ do
+
+        Hspec.it "supports delete requests" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy (Servant.Delete '[Servant.JSON] ())
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "delete" "" "delete" "" "" False
+
+        Hspec.it "supports get requests" $ do
+          let
+            api = Proxy.Proxy :: Proxy.Proxy (Servant.Get '[Servant.JSON] ())
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "get" "" "get" "" "" False
+
+        Hspec.it "supports patch requests" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy (Servant.Patch '[Servant.JSON] ())
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "patch" "" "patch" "" "" False
+
+        Hspec.it "supports post requests" $ do
+          let
+            api = Proxy.Proxy :: Proxy.Proxy (Servant.Post '[Servant.JSON] ())
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "post" "" "post" "" "" False
+
+        Hspec.it "supports put requests" $ do
+          let
+            api = Proxy.Proxy :: Proxy.Proxy (Servant.Put '[Servant.JSON] ())
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "put" "" "put" "" "" False
+
+        Hspec.it "supports alternatives" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.Get '[Servant.JSON] () Servant.:<|> Servant.Delete '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api `Hspec.shouldBe` Text.concat
+            [ ruby "get" "" "get" "" "" False
+            , Text.singleton ';'
+            , ruby "delete" "" "delete" "" "" False
+            ]
+
+        Hspec.it "supports captures" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.Capture "id" () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "get_by_id" ",id" "get" "#{id}" "" False
+
+        Hspec.it "supports query flags" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.QueryFlag "flag" Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby
+                               "get"
+                               ",flag: nil"
+                               "get"
+                               "?flag=#{flag}"
+                               ""
+                               False
+
+        Hspec.it "supports query params" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.QueryParam "param" () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby
+                               "get"
+                               ",param: nil"
+                               "get"
+                               "?param=#{param}"
+                               ""
+                               False
+
+        Hspec.it "supports multiple query params" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.QueryParams "params" () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby
+                               "get"
+                               ",params: nil"
+                               "get"
+                               "?params=#{params}"
+                               ""
+                               False
+
+        Hspec.it "supports request bodies" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.ReqBody '[Servant.JSON] () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "get" ",body" "get" "" "" True
+
+        Hspec.it "supports request headers" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.Header "cookie" () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby
+                               "get"
+                               ",cookie: nil"
+                               "get"
+                               ""
+                               "\"cookie\"=>cookie"
+                               False
+
+        Hspec.it "supports response headers" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  (Servant.Get '[Servant.JSON] (Servant.Headers '[] ()))
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "get" "" "get" "" "" False
+
+        Hspec.it "puts the body param after captures" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.Capture "segment" () Servant.:> Servant.ReqBody '[Servant.JSON] () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby
+                               "get_by_segment"
+                               ",segment,body"
+                               "get"
+                               "#{segment}"
+                               ""
+                               True
+
+        Hspec.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
+            `Hspec.shouldBe` ruby
+                               "get"
+                               ",flag: nil,body"
+                               "get"
+                               "?flag=#{flag}"
+                               ""
+                               True
+
+        Hspec.it "sanitizes path segments" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ("A!" Servant.:> Servant.Get '[Servant.JSON] ())
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "get_A!" "" "get" "A!" "" False
+
+        Hspec.it "sanitizes captures" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.Capture "A!" () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "get_by_A!" ",a_" "get" "#{a_}" "" False
+
+        Hspec.it "sanitizes query flags" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.QueryFlag "A!" Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False
+
+        Hspec.it "sanitizes query params" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.QueryParam "A!" () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False
+
+        Hspec.it "sanitizes multiple query params" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.QueryParams "A!" () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.shouldBe` ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False
+
+        Hspec.it "sanitizes headers" $ do
+          let
+            api =
+              Proxy.Proxy :: Proxy.Proxy
+                  ( Servant.Header "A!" () Servant.:> Servant.Get '[Servant.JSON] ()
+                  )
+          Lackey.rubyForAPI api
+            `Hspec.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 :: String -> String -> String -> String -> String -> Bool -> Text.Text
+ruby name params method path headers body = Text.pack
+  (concat
+    [ "def "
+    , name
+    , "(excon"
+    , params
+    , ")"
+    , "excon.request("
+    , ":method=>:"
+    , method
+    , ","
+    , ":path=>\"/"
+    , path
+    , "\","
+    , ":headers=>{"
+    , headers
+    , "},"
+    , ":body=>"
+    , if body then "body" else "nil"
+    , ")"
+    , "end"
+    ]
+  )
diff --git a/src/lib/Lackey.hs b/src/lib/Lackey.hs
deleted file mode 100644
--- a/src/lib/Lackey.hs
+++ /dev/null
@@ -1,191 +0,0 @@
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE OverloadedStrings #-}
-
-module Lackey
-  ( rubyForAPI
-  ) where
-
-import qualified Data.Char as Char
-import Data.Function ((&))
-import qualified Data.Maybe as Maybe
-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
-
-type Language = Servant.NoTypes
-
-languageProxy :: Proxy.Proxy Language
-languageProxy = Proxy.Proxy
-
-type Request = Servant.NoContent
-
-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
diff --git a/src/test/Main.hs b/src/test/Main.hs
deleted file mode 100644
--- a/src/test/Main.hs
+++ /dev/null
@@ -1,133 +0,0 @@
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE TypeOperators #-}
-
-import qualified Data.Proxy as Proxy
-import qualified Data.Text as Text
-import qualified Lackey
-import qualified Servant.API as Servant
-import qualified Test.Hspec as Hspec
-
-main :: IO ()
-main = Hspec.hspec . Hspec.parallel . Hspec.describe "Lackey" . Hspec.describe "rubyForAPI" $ do
-
-  Hspec.it "supports delete requests" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Delete '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "delete" "" "delete" "" "" False
-
-  Hspec.it "supports get requests" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" "" "get" "" "" False
-
-  Hspec.it "supports patch requests" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Patch '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "patch" "" "patch" "" "" False
-
-  Hspec.it "supports post requests" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Post '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "post" "" "post" "" "" False
-
-  Hspec.it "supports put requests" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Put '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "put" "" "put" "" "" False
-
-  Hspec.it "supports alternatives" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Get '[Servant.JSON] () Servant.:<|> Servant.Delete '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe` Text.concat
-      [ ruby "get" "" "get" "" "" False
-      , Text.singleton ';'
-      , ruby "delete" "" "delete" "" "" False
-      ]
-
-  Hspec.it "supports captures" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Capture "id" () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get_by_id" ",id" "get" "#{id}" "" False
-
-  Hspec.it "supports query flags" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryFlag "flag" Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" ",flag: nil" "get" "?flag=#{flag}" "" False
-
-  Hspec.it "supports query params" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryParam "param" () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" ",param: nil" "get" "?param=#{param}" "" False
-
-  Hspec.it "supports multiple query params" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryParams "params" () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" ",params: nil" "get" "?params=#{params}" "" False
-
-  Hspec.it "supports request bodies" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.ReqBody '[Servant.JSON] () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" ",body" "get" "" "" True
-
-  Hspec.it "supports request headers" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Header "cookie" () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" ",cookie: nil" "get" "" "\"cookie\"=>cookie" False
-
-  Hspec.it "supports response headers" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Get '[Servant.JSON] (Servant.Headers '[] ()))
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" "" "get" "" "" False
-
-  Hspec.it "puts the body param after captures" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Capture "segment" () Servant.:> Servant.ReqBody '[Servant.JSON] () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get_by_segment" ",segment,body" "get" "#{segment}" "" True
-
-  Hspec.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 `Hspec.shouldBe`
-      ruby "get" ",flag: nil,body" "get" "?flag=#{flag}" "" True
-
-  Hspec.it "sanitizes path segments" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy ("A!" Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get_A!" "" "get" "A!" "" False
-
-  Hspec.it "sanitizes captures" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Capture "A!" () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get_by_A!" ",a_" "get" "#{a_}" "" False
-
-  Hspec.it "sanitizes query flags" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryFlag "A!" Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False
-
-  Hspec.it "sanitizes query params" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryParam "A!" () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False
-
-  Hspec.it "sanitizes multiple query params" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.QueryParams "A!" () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.shouldBe`
-      ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False
-
-  Hspec.it "sanitizes headers" $ do
-    let api = Proxy.Proxy :: Proxy.Proxy (Servant.Header "A!" () Servant.:> Servant.Get '[Servant.JSON] ())
-    Lackey.rubyForAPI api `Hspec.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 :: String -> String -> String -> String -> String -> Bool -> Text.Text
-ruby name params method path headers body = Text.pack (concat
-  [ "def ", name, "(excon", params, ")"
-  , "excon.request("
-  , ":method=>:", method, ","
-  , ":path=>\"/", path, "\","
-  , ":headers=>{", headers, "},"
-  , ":body=>", if body then "body" else "nil"
-  , ")"
-  , "end"
-  ])
