lackey 2.0.0.3 → 2.0.0.4
raw patch · 5 files changed
+296/−297 lines, 5 filesdep ~basedep ~textPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, text
API changes (from Hackage documentation)
Files
- LICENSE.markdown +1/−1
- README.markdown +1/−1
- lackey.cabal +9/−12
- source/library/Lackey.hs +106/−100
- source/test-suite/Main.hs +179/−183
LICENSE.markdown view
@@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Taylor Fausak+Copyright (c) 2023 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
README.markdown view
@@ -1,6 +1,6 @@ # [Lackey][] -[](https://github.com/tfausak/lackey/actions/new)+[](https://github.com/tfausak/lackey/actions/workflows/workflow.yaml) [](https://hackage.haskell.org/package/lackey) [](https://www.stackage.org/package/lackey)
lackey.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.2 name: lackey-version: 2.0.0.3+version: 2.0.0.4 synopsis: Generate Ruby clients from Servant APIs. description: Lackey generates Ruby clients from Servant APIs.@@ -26,29 +26,26 @@ common library build-depends:- , base >= 4.13.0 && < 4.18+ , base >= 4.15.0 && < 4.18 , servant-foreign >= 0.15 && < 0.16- , text >= 1.2.4 && < 1.3 || >= 2.0 && < 2.1+ , text >= 1.2.5 && < 1.3 || >= 2.0 && < 2.1 default-language: Haskell2010 ghc-options: -Weverything -Wno-all-missed-specialisations -Wno-implicit-prelude -Wno-missing-exported-signatures+ -Wno-missing-safe-haskell-mode+ -Wno-prepositive-qualified-module -Wno-unsafe - 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 + if flag(pedantic)+ ghc-options: -Werror+ common executable import: library @@ -64,7 +61,7 @@ exposed-modules: Lackey hs-source-dirs: source/library -test-suite test+test-suite lackey-test-suite import: executable build-depends:
source/library/Lackey.hs view
@@ -2,8 +2,9 @@ {-# LANGUAGE OverloadedStrings #-} module Lackey- ( rubyForAPI- ) where+ ( rubyForAPI,+ )+where import qualified Data.Char as Char import Data.Function ((&))@@ -46,48 +47,53 @@ request & Servant._reqHeaders & Maybe.mapMaybe- (\h -> case h of+ ( \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+ 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+ & 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]+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 ",",+ ")" ]- & concat- & Maybe.catMaybes- & Text.intercalate ","- , ")"- ] requestMethod :: Servant.Req Request -> Text.Text requestMethod request =@@ -95,43 +101,41 @@ 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+ 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 "/"+ 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 <> "\""+ & 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)- , ["}"]- ]+ [ ["{"],+ request & getHeaders & fmap (\x -> "\"" <> x <> "\"=>" <> underscore x),+ ["}"]+ ] & concat & Text.concat @@ -139,43 +143,45 @@ 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- , ")"- ]+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"- ]+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 ::+ ( 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 ::+ ( Servant.HasForeign Language Request api,+ Servant.GenerateList Request (Servant.Foreign Request api)+ ) =>+ Proxy.Proxy api ->+ Text.Text rubyForAPI api = api & requestsForAPI & renderRequests
source/test-suite/Main.hs view
@@ -14,243 +14,239 @@ . Hspec.describe "Lackey" . Hspec.describe "rubyForAPI" $ do-- Hspec.it "supports delete requests" $ do- let- api =+ 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+ 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 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 =+ 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+ 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 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 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+ 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+ 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+ 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+ 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+ 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+ 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+ 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+ 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+ 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+ 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+ 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+ Lackey.rubyForAPI api+ `Hspec.shouldBe` ruby "get" ",body" "get" "" "" True - Hspec.it "supports request headers" $ do- let- api =- Proxy.Proxy :: Proxy.Proxy+ 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+ 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+ 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+ Lackey.rubyForAPI api+ `Hspec.shouldBe` ruby "get" "" "get" "" "" False - Hspec.it "puts the body param after captures" $ do- let- api =- Proxy.Proxy :: Proxy.Proxy+ 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+ 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+ 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+ 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+ 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+ Lackey.rubyForAPI api+ `Hspec.shouldBe` ruby "get_A!" "" "get" "A!" "" False - Hspec.it "sanitizes captures" $ do- let- api =- Proxy.Proxy :: Proxy.Proxy+ 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+ 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+ 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+ 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+ 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+ 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+ 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+ Lackey.rubyForAPI api+ `Hspec.shouldBe` ruby "get" ",a_: nil" "get" "?A!=#{a_}" "" False - Hspec.it "sanitizes headers" $ do- let- api =- Proxy.Proxy :: Proxy.Proxy+ 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+ 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"- ]- )+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"+ ]+ )