servant-elm 0.1.0.0 → 0.1.0.1
raw patch · 5 files changed
+201/−3 lines, 5 files
Files
- CHANGELOG.md +8/−0
- README.md +125/−0
- servant-elm.cabal +4/−2
- src/Servant/Elm.hs +22/−0
- src/Servant/Elm/Generate.hs +42/−1
+ CHANGELOG.md view
@@ -0,0 +1,8 @@+0.1.0.1+-------+* Convenience re-exports from Elm and Data.Proxy.+* Add Haddoc documentation.++0.1+---+* Initial release.
+ README.md view
@@ -0,0 +1,125 @@+# Servant Elm++[](https://travis-ci.org/mattjbray/servant-elm)++Generate Elm functions to query your Servant API!++Elm type generation coutesy of [krisajenkins/elm-export](https://github.com/krisajenkins/elm-export).++## Example++Let's get some boring language pragmas and imports out of the way.++```haskell+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}++import Data.Proxy (Proxy(Proxy))+import Elm (Spec(Spec), ToElmType, specsToDir)+import GHC.Generics (Generic)+import Servant.API ((:>), Capture, Get, JSON)+import Servant.Elm (defElmImports, generateElmForAPI)+```++We have some Haskell-defined types and our Servant API.++```haskell+data Book = Book+ { name :: String+ } deriving (Generic)++instance ToElmType Book++type BooksApi = "books" :> Capture "bookId" Int :> Get '[JSON] Book+```++Now we can generate Elm functions to query the API:++```haskell+spec :: Spec+spec = Spec ["Generated", "MyApi"]+ (defElmImports+ : generateElmForAPI (Proxy :: Proxy BooksApi))++main :: IO ()+main = specsToDir [spec] "my-elm-dir"+```++Let's save this as `example.hs` and run it:++```+$ stack runghc example.hs+Writing: my-elm-dir/Generated/MyApi.elm+$+```++Here's what was generated:++```elm+module Generated.MyApi where++import Json.Decode exposing ((:=))+import Json.Decode.Extra exposing ((|:))+import Json.Encode+import Http+import String+import Task+++type alias Book =+ { name : String+ }++decodeBook : Json.Decode.Decoder Book+decodeBook =+ Json.Decode.succeed Book+ |: ("name" := Json.Decode.string)++getBooksBy : Int -> Task.Task Http.Error (Book)+getBooksBy bookId =+ let+ request =+ { verb =+ "GET"+ , headers =+ [("Content-Type", "application/json")]+ , url =+ "/" ++ "books"+ ++ "/" ++ (bookId |> toString |> Http.uriEncode)+ , body =+ Http.empty+ }+ in+ Http.fromJson+ decodeBook+ (Http.send Http.defaultSettings request)+```++See [`examples`](examples) for a complete usage example, or take a look at+[mattjbray/servant-elm-example-app](https://github.com/mattjbray/servant-elm-example-app)+for an example project using this library.++## Development++```+$ git clone https://github.com/mattjbray/servant-elm.git+$ cd servant-elm+$ stack build+$ stack test+```++## TODO++Servant API coverage:++* MatrixFlag / MatrixParam / MatrixParams+* Header (request)+* Headers (response)+* Delete / Patch / Put / Raw+* Vault / RemoteHost / IsSecure++Other:++* Option to not use elm-export: generate functions that take a decoder and+ String arguments.
servant-elm.cabal view
@@ -1,5 +1,5 @@ name: servant-elm-version: 0.1.0.0+version: 0.1.0.1 synopsis: Automatically derive Elm functions to query servant webservices. description: Please see README.md homepage: http://github.com/mattjbray/servant-elm#readme@@ -10,7 +10,9 @@ copyright: 2015-2016 Matt Bray category: Web build-type: Simple--- extra-source-files:+extra-source-files:+ CHANGELOG.md+ README.md cabal-version: >=1.10 library
src/Servant/Elm.hs view
@@ -1,11 +1,33 @@+{-|+Basic usage:++> import MyLib (MyServantApiType)+> import Servant.Elm+>+> spec :: Spec+> spec = Spec ["Generated", "MyApi"]+> (defElmImports : generateElmForAPI (Proxy :: Proxy MyServantApiType))+>+> main :: IO ()+> main = specsToDir [spec] "my-elm-dir"+-} module Servant.Elm ( generateElmForAPI , generateElmForAPIWith , ElmOptions(..) , defElmOptions , defElmImports+ -- * Convenience re-exports from the "Elm" module+ , Spec(Spec)+ , ToElmType+ , specsToDir+ -- * Convenience re-exports from "Data.Proxy"+ , Proxy(Proxy) ) where import Servant.Elm.Generate (ElmOptions (..), defElmImports, defElmOptions, generateElmForAPI, generateElmForAPIWith)++import Data.Proxy (Proxy(Proxy))+import Elm (Spec(Spec), ToElmType, specsToDir)
src/Servant/Elm/Generate.hs view
@@ -11,15 +11,42 @@ SegmentType (..), camelCase) +{-|+Options to configure how code is generated.+-} data ElmOptions = ElmOptions- { urlPrefix :: String }+ { {- | The protocol, host and any path prefix to be used as the base for all+ requests. + Example: @"https://mydomain.com/api/v1"@+ -}+ urlPrefix :: String } ++{-|+The default options for generating Elm code.++[@urlPrefix@] (An empty string)+-} defElmOptions :: ElmOptions defElmOptions = ElmOptions { urlPrefix = "" } +{-|+Default imports required by generated Elm code.++You probably want to include this at the top of your generated Elm module.++The default required imports are:++> import Json.Decode exposing ((:=))+> import Json.Decode.Extra exposing ((|:))+> import Json.Encode+> import Http+> import String+> import Task+-} defElmImports :: String defElmImports = unlines@@ -32,11 +59,25 @@ ] +{-|+Generate Elm code for the API with default options.++Returns a list of Elm code definitions with everything you need to query your+Servant API from Elm: type definitions, JSON decoders, JSON encoders, and query+functions.++You could spit these out to a file and call them from your Elm code, but you+would be better off creating a 'Spec' with the result and using 'specsToDir',+which handles the module name for you.+-} generateElmForAPI :: (HasElmClient layout) => Proxy layout -> [String] generateElmForAPI = generateElmForAPIWith defElmOptions +{-|+Generate Elm code for the API with custom options.+-} generateElmForAPIWith :: (HasElmClient layout) => ElmOptions -> Proxy layout -> [String] generateElmForAPIWith opts = nub . concatMap (generateElmForRequest opts) . elmClient