rest-gen 0.12 → 0.13
raw patch · 18 files changed
+313/−122 lines, 18 files
Files
- CHANGELOG.md +11/−1
- rest-gen.cabal +10/−8
- src/Rest/Gen.hs +13/−10
- src/Rest/Gen/Base/ActionInfo.hs +45/−14
- src/Rest/Gen/Base/ActionInfo/Ident.hs +5/−3
- src/Rest/Gen/Base/ApiTree.hs +23/−2
- src/Rest/Gen/Base/JSON.hs +4/−4
- src/Rest/Gen/Base/JSON/Pretty.hs +1/−1
- src/Rest/Gen/Base/Link.hs +11/−1
- src/Rest/Gen/Base/XML.hs +5/−1
- src/Rest/Gen/Config.hs +21/−4
- src/Rest/Gen/Docs/Generate.hs +26/−15
- src/Rest/Gen/Docs/Happstack.hs +3/−1
- src/Rest/Gen/Haskell/Generate.hs +51/−45
- src/Rest/Gen/JavaScript/Generate.hs +6/−5
- src/Rest/Gen/Ruby/Generate.hs +7/−6
- src/Rest/Gen/Types.hs +60/−0
- src/Rest/Gen/Utils.hs +11/−1
CHANGELOG.md view
@@ -1,6 +1,16 @@ # Changelog -### 0.12+## 0.13++Breaking changes:+* Un-exposes internal modules so we don't have to major bump on every change.+* `gen` Now accepts AST-like types instead of just strings to make it more obvious how to use it, see types in `Rest.Gen.Types`++Bugfixes:+* Make sure Identifiers are always imported when needed. This is a further improvement on the bugfix in rest-gen-0.11.+* rest-gen-0.12 did not always take arguments in generated methods into account, so the renamed qualification has been reverted for now.++## 0.12 * Haskell: Module rewrites such as `Data.Text.Internal` -> `Data.Text` now produces qualified imports `import qualified Data.Text as Data.Text` instead of `import qualified Data.Text as Data.Text.Lazy`. This prevents building against different versions of the same package that may have moved the internal module (as is the case with `text`) from generating different clients.
rest-gen.cabal view
@@ -1,5 +1,5 @@ name: rest-gen-version: 0.12+version: 0.13 description: Documentation and client generation from rest definition. synopsis: Documentation and client generation from rest definition. maintainer: code@silk.co@@ -27,9 +27,17 @@ library ghc-options: -Wall hs-source-dirs: src- other-modules: Paths_rest_gen exposed-modules: Rest.Gen+ Rest.Gen.Config+ Rest.Gen.Docs.Generate+ Rest.Gen.Docs.Happstack+ Rest.Gen.Haskell.Generate+ Rest.Gen.JavaScript.Generate+ Rest.Gen.Ruby.Generate+ Rest.Gen.Types+ other-modules:+ Paths_rest_gen Rest.Gen.Base Rest.Gen.Base.ActionInfo Rest.Gen.Base.ActionInfo.Ident@@ -38,12 +46,6 @@ Rest.Gen.Base.JSON.Pretty Rest.Gen.Base.Link Rest.Gen.Base.XML- Rest.Gen.Config- Rest.Gen.Docs.Generate- Rest.Gen.Docs.Happstack- Rest.Gen.Haskell.Generate- Rest.Gen.JavaScript.Generate- Rest.Gen.Ruby.Generate Rest.Gen.Utils build-depends: base >= 4.5 && < 4.8
src/Rest/Gen.hs view
@@ -1,23 +1,26 @@-module Rest.Gen where+module Rest.Gen+ ( generate+ ) where import Data.Char-import Data.Label import Data.Foldable+import Data.Label import Data.Maybe import System.Directory import System.Exit import System.Process -import Rest.Api (withVersion, Api, Some1 (..))+import Rest.Api (Api, Some1 (..), withVersion) import Rest.Gen.Config-import Rest.Gen.Docs.Generate (writeDocs, DocsContext (DocsContext))+import Rest.Gen.Docs.Generate (DocsContext (DocsContext), writeDocs)+import Rest.Gen.Haskell.Generate (HaskellContext (HaskellContext), mkHsApi) import Rest.Gen.JavaScript.Generate (mkJsApi)-import Rest.Gen.Haskell.Generate (mkHsApi, HaskellContext (HaskellContext)) import Rest.Gen.Ruby.Generate (mkRbApi)+import Rest.Gen.Types import Rest.Gen.Utils -generate :: Config -> String -> Api m -> [String] -> [String] -> [(String, String)] -> IO ()+generate :: Config -> String -> Api m -> [ModuleName] -> [Import] -> [(ModuleName, ModuleName)] -> IO () generate config name api sources imports rewrites = withVersion (get apiVersion config) api (putStrLn "Could not find api version" >> exitFailure) $ \ver (Some1 r) -> case get action config of@@ -27,18 +30,18 @@ let context = DocsContext root ver (fromMaybe "./templates" (getSourceLocation config)) writeDocs context r loc exitSuccess- Just MakeJS -> mkJsApi (moduleName ++ "Api") (get apiPrivate config) ver r >>= toTarget config- Just MakeRb -> mkRbApi (moduleName ++ "Api") (get apiPrivate config) ver r >>= toTarget config+ Just MakeJS -> mkJsApi (overModuleName (++ "Api") moduleName) (get apiPrivate config) ver r >>= toTarget config+ Just MakeRb -> mkRbApi (overModuleName (++ "Api") moduleName) (get apiPrivate config) ver r >>= toTarget config Just MakeHS -> do loc <- getTargetDir config "./client" setupTargetDir config loc- let context = HaskellContext ver loc (packageName ++ "-client") (get apiPrivate config) sources imports rewrites [moduleName, "Client"]+ let context = HaskellContext ver loc (packageName ++ "-client") (get apiPrivate config) sources imports rewrites [unModuleName moduleName, "Client"] mkHsApi context r exitSuccess Nothing -> return () where packageName = map toLower name- moduleName = upFirst packageName+ moduleName = ModuleName $ upFirst packageName getTargetDir :: Config -> String -> IO String getTargetDir config str =
src/Rest/Gen/Base/ActionInfo.hs view
@@ -3,7 +3,25 @@ , GADTs , ScopedTypeVariables #-}-module Rest.Gen.Base.ActionInfo where+module Rest.Gen.Base.ActionInfo+ ( Accessor+ , ActionInfo (..)+ , ActionType (..)+ , DataDescription (..)+ , DataType (..)+ , ResourceId+ , accessLink+ , accessors+ , chooseType+ , isAccessor+ , listGetterActionInfo+ , mkActionDescription+ , namedActionInfo+ , resourceToAccessors+ , resourceToActionInfo+ , selectActionInfo+ , singleActionInfo+ ) where import Prelude hiding (id, (.)) @@ -15,26 +33,29 @@ import Data.Maybe import Data.Proxy import Data.Typeable+-- TODO Remove CPP #if __GLASGOW_HASKELL__ < 704 import Data.List.Split #endif-import Rest.Gen.Base.ActionInfo.Ident (Ident (Ident), description)-import Rest.Info-import qualified Data.JSON.Schema as J-import qualified Data.Label.Total as L-import qualified Rest.Gen.Base.JSON as J-import qualified Rest.Gen.Base.XML as X+import qualified Data.JSON.Schema as J+import qualified Data.Label.Total as L import Rest.Dictionary (Error (..), Input (..), Output (..), Param (..)) import Rest.Driver.Routing (mkListHandler, mkMultiHandler)-import Rest.Gen.Base.Link import Rest.Handler+import Rest.Info import Rest.Resource hiding (description) import Rest.Schema- import qualified Rest.Dictionary as Dict import qualified Rest.Resource as Rest +import Rest.Gen.Base.ActionInfo.Ident (Ident (Ident))+import Rest.Gen.Base.Link+import Rest.Gen.Types+import qualified Rest.Gen.Base.ActionInfo.Ident as Ident+import qualified Rest.Gen.Base.JSON as J+import qualified Rest.Gen.Base.XML as X+ -------------------- -- * The types describing a resource's actions. @@ -89,7 +110,7 @@ -------------------- -- * Traverse a resource's Schema and Handlers to create a [ActionInfo]. -resourceToActionInfo :: Resource m s sid mid aid -> [ActionInfo]+resourceToActionInfo :: forall m s sid mid aid. Resource m s sid mid aid -> [ActionInfo] resourceToActionInfo r = case schema r of Schema mTopLevel step -> foldMap (topLevelActionInfo r) mTopLevel@@ -107,7 +128,7 @@ where f ("", x) = par x f (pth, x) = LAction pth : par x- par = maybe [] (return . LParam . description)+ par = maybe [] (return . LParam . Ident.description) accessors :: Step sid mid aid -> [Accessor] accessors (Named hs) = mapMaybe (uncurry accessorsNamed) hs@@ -237,7 +258,7 @@ where dirPart = if pth /= "" then [LAction pth] else []- identPart = maybe [] ((:[]) . LParam . description) id_+ identPart = maybe [] ((:[]) . LParam . Ident.description) id_ --------------------@@ -370,8 +391,18 @@ idIdent (Id idnt _) = actionIdent idnt actionIdent :: forall a. Dict.Ident a -> Ident-actionIdent Dict.StringId = Ident "string" "String" []-actionIdent Dict.ReadId = Ident (describe proxy_) (typeString proxy_) (modString proxy_)+actionIdent Dict.StringId+ = Ident+ { Ident.description = "string"+ , Ident.haskellType = "String"+ , Ident.haskellModules = []+ }+actionIdent Dict.ReadId+ = Ident+ { Ident.description = describe proxy_+ , Ident.haskellType = typeString proxy_+ , Ident.haskellModules = map ModuleName $ modString proxy_+ } where proxy_ :: Proxy a proxy_ = Proxy
src/Rest/Gen/Base/ActionInfo/Ident.hs view
@@ -1,7 +1,9 @@ module Rest.Gen.Base.ActionInfo.Ident (Ident (..)) where +import Rest.Gen.Types+ data Ident = Ident- { description :: String- , haskellType :: String- , haskellModule :: [String]+ { description :: String+ , haskellType :: String+ , haskellModules :: [ModuleName] } deriving (Show, Eq)
src/Rest/Gen/Base/ApiTree.hs view
@@ -1,5 +1,26 @@-{-# LANGUAGE GADTs #-}-module Rest.Gen.Base.ApiTree where+module Rest.Gen.Base.ApiTree+ ( ApiAction (..)+ , ApiResource (..)+ , allResourceIds+ , allSubResourceIds+ , allSubResources+ , allSubTrees+ , allTrees+ , apiResources+ , apiSubtrees+ , apiTree'+ , cleanName+ , defaultTree+ , foldTree+ , foldTreeChildren+ , hasAccessor+ , mkFuncParts+ , noPrivate+ , resIdents+ , sortTree+ , subResourceIds+ , subResourceNames+ ) where import Data.Char import Data.Function
src/Rest/Gen/Base/JSON.hs view
@@ -1,14 +1,14 @@ {-# LANGUAGE OverloadedStrings #-}--module Rest.Gen.Base.JSON where+module Rest.Gen.Base.JSON (showExample) where import Data.Aeson ((.=)) import Data.JSON.Schema import Data.Text (pack)-import Rest.Gen.Base.JSON.Pretty import Text.PrettyPrint.HughesPJ-import qualified Data.Aeson as A+import qualified Data.Aeson as A import qualified Data.Vector as V++import Rest.Gen.Base.JSON.Pretty showExample :: Schema -> String showExample = render . pp_value . showExample'
src/Rest/Gen/Base/JSON/Pretty.hs view
@@ -1,4 +1,4 @@-module Rest.Gen.Base.JSON.Pretty where+module Rest.Gen.Base.JSON.Pretty (pp_value) where import Control.Arrow (first) import Data.Aeson.Types
src/Rest/Gen/Base/Link.hs view
@@ -1,4 +1,14 @@-module Rest.Gen.Base.Link where+module Rest.Gen.Base.Link+ ( Link+ , LinkItem (..)+ , flattenLast+ , flattenLastResource+ , flattenLink+ , getLinkIds+ , hasParam+ , itemString+ , setLinkIds+ ) where -- | Data structure representing Api links data LinkItem =
src/Rest/Gen/Base/XML.hs view
@@ -1,4 +1,8 @@-module Rest.Gen.Base.XML (getXmlSchema, showSchema, showExample) where+module Rest.Gen.Base.XML+ ( getXmlSchema+ , showSchema+ , showExample+ ) where import Data.List import Text.XML.HXT.Arrow.Pickle
src/Rest/Gen/Config.hs view
@@ -1,8 +1,25 @@-{-# LANGUAGE TemplateHaskell, TypeOperators #-}-module Rest.Gen.Config where+{-# LANGUAGE+ TemplateHaskell+ , TypeOperators+ #-}+module Rest.Gen.Config+ ( Action (..)+ , Location (..) -import Prelude hiding ((.), id)+ , Config+ , action+ , source+ , target+ , apiVersion+ , apiPrivate + , defaultConfig+ , parseLocation+ , options+ ) where++import Prelude hiding (id, (.))+ import Control.Category import Data.Label import System.Console.GetOpt@@ -18,7 +35,7 @@ , _apiPrivate :: Bool } -$(mkLabels [''Config])+mkLabels [''Config] defaultConfig :: Config defaultConfig = Config
src/Rest/Gen/Docs/Generate.hs view
@@ -1,15 +1,26 @@-{-# LANGUAGE GADTs- , TupleSections- , ScopedTypeVariables- , OverlappingInstances- , TemplateHaskell- , TypeFamilies- , EmptyDataDecls- , MultiParamTypeClasses- #-}-module Rest.Gen.Docs.Generate where+{-# LANGUAGE+ EmptyDataDecls+ , GADTs+ , MultiParamTypeClasses+ , OverlappingInstances+ , ScopedTypeVariables+ , TemplateHaskell+ , TupleSections+ , TypeFamilies+ #-}+module Rest.Gen.Docs.Generate+ ( DocsContext (..)+ , cdiv+ , cls+ , mkAllResources+ , mkSingleResource+ , resourcesInfo+ , row+ , subResourcesInfo+ , writeDocs+ ) where -import Prelude hiding (head, id, div, span)+import Prelude hiding (div, head, id, span) import qualified Prelude as P import Control.Monad hiding (forM_)@@ -19,16 +30,16 @@ import Data.String import System.Directory import System.FilePath+import System.Log.Logger import Text.Blaze.Html-import Text.Blaze.Html.Renderer.String import Text.Blaze.Html5 hiding (map)-import Text.Blaze.Html5.Attributes hiding (title, method, span)+import Text.Blaze.Html5.Attributes hiding (method, span, title)+import Text.Blaze.Html.Renderer.String import Text.StringTemplate -import Rest.Api (Version, Router)+import Rest.Api (Router, Version) import Rest.Gen.Base import Rest.Gen.Utils-import System.Log.Logger -- | Information about the context in which a resource is contained data DocsContext = DocsContext
src/Rest/Gen/Docs/Happstack.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE FlexibleContexts #-}-module Rest.Gen.Docs.Happstack where+module Rest.Gen.Docs.Happstack+ ( apiDocsHandler+ ) where import Control.Monad import Control.Monad.Trans
src/Rest/Gen/Haskell/Generate.hs view
@@ -2,21 +2,23 @@ DoAndIfThenElse , TemplateHaskell #-}-module Rest.Gen.Haskell.Generate where+module Rest.Gen.Haskell.Generate+ ( HaskellContext (..)+ , mkHsApi+ ) where import Control.Applicative-import Control.Arrow (first)+import Control.Arrow (first, second) import Control.Category import Control.Monad-import Data.Label (mkLabels, modify, set)+import Data.Label (modify, set)+import Data.Label.Derive (mkLabelsNamed) import Data.List-import Data.List.Split import Data.Maybe import Prelude hiding (id, (.)) import Safe import System.Directory import System.FilePath- import qualified Distribution.ModuleName as Cabal import qualified Distribution.Package as Cabal import qualified Distribution.PackageDescription as Cabal@@ -31,11 +33,11 @@ import Rest.Api (Router, Version) import Rest.Gen.Base+import Rest.Gen.Types import Rest.Gen.Utils- import qualified Rest.Gen.Base.ActionInfo.Ident as Ident -$(mkLabels[''Cabal.GenericPackageDescription, ''Cabal.CondTree, ''Cabal.Library])+mkLabelsNamed ("_" ++) [''Cabal.GenericPackageDescription, ''Cabal.CondTree, ''Cabal.Library] data HaskellContext = HaskellContext@@ -43,9 +45,9 @@ , targetPath :: String , wrapperName :: String , includePrivate :: Bool- , sources :: [String]- , imports :: [String]- , rewrites :: [(String, String)]+ , sources :: [ModuleName]+ , imports :: [Import]+ , rewrites :: [(ModuleName, ModuleName)] , namespace :: [String] } @@ -65,7 +67,7 @@ writeCabalFile cabalFile gpkg where cabalFile = targetPath ctx </> wrapperName ctx ++ ".cabal"- modules = map Cabal.fromString (sources ctx)+ modules = map (Cabal.fromString . unModuleName) (sources ctx) ++ map (Cabal.fromString . qualModName . (namespace ctx ++)) (allSubResourceIds tree) writeCabalFile :: FilePath -> Cabal.GenericPackageDescription -> IO ()@@ -73,7 +75,7 @@ where emptyField = (/= "\"\" ") . takeWhile (/= ':') . reverse updateExposedModules :: [Cabal.ModuleName] -> Cabal.GenericPackageDescription -> Cabal.GenericPackageDescription-updateExposedModules modules = modify lCondLibrary (Just . maybe (mkCondLibrary modules) (set (lExposedModules . lCondTreeData) modules))+updateExposedModules modules = modify _condLibrary (Just . maybe (mkCondLibrary modules) (set (_exposedModules . _condTreeData) modules)) mkGenericPackageDescription :: String -> [Cabal.ModuleName] -> Cabal.GenericPackageDescription mkGenericPackageDescription name modules = Cabal.GenericPackageDescription pkg [] (Just (mkCondLibrary modules)) [] [] []@@ -101,32 +103,45 @@ showCode $ "{-# LANGUAGE OverloadedStrings #-}\n{-# OPTIONS_GHC -fno-warn-unused-imports #-}\n{- Warning!! This is automatically generated code, do not modify! -}" <-> hsModule (qualModName $ namespace ctx ++ resId node)- [ mkImports ctx node rewrittenMods+ [ mkImports ctx node mods , idData node , mkStack funcs ] where- (funcs, mods) = unzip $ map (mkFunction (rewrites ctx) (apiVersion ctx) $ resName node) $ resItems node- rewrittenMods = rewriteModules (rewrites ctx) $ nub $ concat mods+ (funcs, mods) = second (nub . concat) . unzip . map (mkFunction (apiVersion ctx) . resName $ node) $ resItems node -mkImports :: HaskellContext -> ApiResource -> [String] -> Code-mkImports ctx node datImp =- mkStack- [ code "import Rest.Client.Internal"- , extraImports- , parentImports- , dataImports- ]+mkImports :: HaskellContext -> ApiResource -> [ModuleName] -> Code+mkImports ctx node datImp+ = mkStack+ . map (rewriteImport $ rewrites ctx)+ $ [Import UnQualified (ModuleName "Rest.Client.Internal") Nothing Nothing]+ ++ extraImports+ ++ parentImports+ ++ dataImports+ ++ idImports where- extraImports = mkStack . map ("import " <+>) $ imports ctx- parentImports = mkStack . map mkImport . tail . inits . resParents $ node- dataImports = mkStack . map ("import qualified " <+>) $ datImp- mkImport p = "import qualified" <++> qualModName (namespace ctx ++ p) <++> "as" <++> modName (last p)+ extraImports = imports ctx+ parentImports = map mkImport . tail . inits . resParents $ node+ dataImports = map qualImp datImp+ idImports = concat . mapMaybe (return . map qualImp . Ident.haskellModules <=< snd) . resAccessors $ node+ -- We need the `as' name to be explicit here even though it's the same, see comment below.+ qualImp v = Import Qualified v (Just v) Nothing+ mkImport p = Import Qualified (ModuleName . qualModName $ namespace ctx ++ p) (Just . ModuleName . modName . last $ p) Nothing+ rewriteImport :: [(ModuleName, ModuleName)] -> Import -> Import+ rewriteImport rws i = case i of+ -- We don't rewrite the `as` part of the import so if you have a+ -- rewrite ("Data.Text.Internal.Lazy", "Data.Text.Lazy") the+ -- import will become `import qualified Data.Text as+ -- Data.Text.Internal.Lazy', this is because mkFunction produces+ -- types through strings and doesn't take rewrites into+ -- account. mkFunction should be changed to do this.+ Import q m mas l -> Import q (look m) mas l+ where+ look m = lookupJustDef m m rws -mkFunction :: [(String,String)] -> Version -> String -> ApiAction -> (Code, [String])-mkFunction rewrits ver res (ApiAction _ lnk ai) =- let mInp = fmap (inputInfo rewrits) $ chooseType $ inputs ai- identMod = maybe [] Ident.haskellModule (ident ai)+mkFunction :: Version -> String -> ApiAction -> (Code, [ModuleName])+mkFunction ver res (ApiAction _ lnk ai) =+ let mInp = fmap inputInfo $ chooseType $ inputs ai defaultErrorConversion = if fmap dataType (chooseType (outputs ai)) == Just JSON then "fromJSON" else "fromXML" (oMod, oType, oCType, oFunc) = maybe ([], "()", "text/plain", "(const ())") outputInfo $ chooseType $ outputs ai (eMod, eType, eFunc) = headDef (([], "()", defaultErrorConversion))@@ -163,7 +178,7 @@ ] $ "liftM (parseResult" <++> eFunc <++> oFunc <+> ") . doRequest $ request" ]- , eMod ++ oMod ++ identMod ++ maybe [] (\(m,_,_,_) -> m) mInp+ , map ModuleName $ eMod ++ oMod ++ maybe [] (\(m,_,_,_) -> m) mInp ) linkToURL :: String -> Link -> (Code, [String])@@ -229,15 +244,6 @@ by = if target /= [] && (isJust (ident ai) || actionType ai == UpdateMany) then ["by"] else [] get = if isAccessor ai then [] else ["get"] --- | Rewrites multiple flattened module names-rewriteModules :: [(String, String)] -> [String] -> [String]-rewriteModules _ [] = []-rewriteModules rw (v : vs) = maybe v (\m -> m ++ " as " ++ m) (lookup v rw) : rewriteModules rw vs---- | Rewrites a single module name-rewriteModule :: [(String, String)] -> [String] -> [String]-rewriteModule rewrits m = maybe m (splitOn ".") (lookup (intercalate "." $ m) rewrits)- hsName :: [String] -> String hsName [] = "" hsName (x : xs) = clean $ downFirst x ++ concatMap upFirst xs@@ -260,12 +266,12 @@ dataName :: String -> String dataName = modName -inputInfo :: [(String,String)] -> DataDescription -> ([String], String, String, String)-inputInfo rewrits ds =+inputInfo :: DataDescription -> ([String], String, String, String)+inputInfo ds = case dataType ds of String -> ([], "String", "text/plain", "fromString")- XML -> (rewriteModule rewrits (haskellModule ds), haskellType ds, "text/xml", "toXML")- JSON -> (rewriteModule rewrits (haskellModule ds), haskellType ds, "text/json", "toJSON")+ XML -> (haskellModule ds, haskellType ds, "text/xml", "toXML")+ JSON -> (haskellModule ds, haskellType ds, "text/json", "toJSON") File -> ([], "ByteString", "application/octet-stream", "id") Other -> ([], "ByteString", "text/plain", "id")
src/Rest/Gen/JavaScript/Generate.hs view
@@ -8,19 +8,20 @@ import Data.Maybe import Text.StringTemplate -import Rest.Api (Version, Router)+import Rest.Api (Router, Version) import Rest.Gen.Base+import Rest.Gen.Types import Rest.Gen.Utils -mkJsApi :: String -> Bool -> Version -> Router m s -> IO String+mkJsApi :: ModuleName -> Bool -> Version -> Router m s -> IO String mkJsApi ns priv ver r = do prelude <- liftM (render . setManyAttrib attrs . newSTMP) (readContent "Javascript/base.js") let cod = showCode $ mkStack- [ ns ++ ".prototype.version" .=. string (show ver)- , mkJsCode ns priv r+ [ unModuleName ns ++ ".prototype.version" .=. string (show ver)+ , mkJsCode (unModuleName ns) priv r ] return $ prelude ++ cod- where attrs = [("apinamespace", ns), ("dollar", "$")]+ where attrs = [("apinamespace", unModuleName ns), ("dollar", "$")] mkJsCode :: String -> Bool -> Router m s -> Code mkJsCode ns priv = mkJs ns . sortTree . (if priv then id else noPrivate) . apiSubtrees
src/Rest/Gen/Ruby/Generate.hs view
@@ -1,21 +1,22 @@ {-# LANGUAGE ScopedTypeVariables #-} module Rest.Gen.Ruby.Generate (mkRbApi) where -import Code.Build-import Code.Build.Ruby- import Data.Char import Data.List import Data.Maybe -import Rest.Api (Version, Router)+import Code.Build+import Code.Build.Ruby++import Rest.Api (Router, Version) import Rest.Gen.Base+import Rest.Gen.Types import Rest.Gen.Utils -mkRbApi :: String -> Bool -> Version -> Router m s -> IO String+mkRbApi :: ModuleName -> Bool -> Version -> Router m s -> IO String mkRbApi ns priv ver r = do prelude <- readContent "Ruby/base.rb"- let cod = showCode . mkRb ns ver . sortTree . (if priv then id else noPrivate) . apiSubtrees $ r+ let cod = showCode . mkRb (unModuleName ns) ver . sortTree . (if priv then id else noPrivate) . apiSubtrees $ r return $ cod ++ prelude mkRb :: String -> Version -> ApiResource -> Code
+ src/Rest/Gen/Types.hs view
@@ -0,0 +1,60 @@+module Rest.Gen.Types+ ( ModuleName (..)+ , overModuleName+ , Import (..)+ , Qualification (..)+ , QName (..)+ , Name (..)+ ) where++import Data.List++import Code.Build++newtype ModuleName = ModuleName { unModuleName :: String }+ deriving (Eq, Show)++instance Codeable ModuleName where+ code = code . unModuleName++overModuleName :: (String -> String) -> ModuleName -> ModuleName+overModuleName f = ModuleName . f . unModuleName++newtype Name = Name { unName :: String }++instance Codeable Name where+ code = code . unName++data QName+ = Qual ModuleName Name+ | UnQual Name++instance Codeable QName where+ code (UnQual n) = code n+ code (Qual m n) = code m <+> "." <+> code n++data Qualification = Qualified | UnQualified++instance Codeable Qualification where+ code Qualified = code "qualified"+ code UnQualified = code ""++data Import+ = Import Qualification ModuleName (Maybe ModuleName) (Maybe [QName])++instance Codeable Import where+ code i = case i of+ Import q m mas ids+ -> "import"+ <++> q+ <++> m+ <++> qualAs+ <++> maybe (code "") (\v -> "(" <+> impList v <+> ")") ids+ where+ qualAs = case mas of+ Just as+ | as == m -> code ""+ | otherwise -> "as" <++> as+ Nothing -> code ""+ impList :: [QName] -> Code+ impList = foldl' (<++>) (code "") . intersperse (code ",") . map code
src/Rest/Gen/Utils.hs view
@@ -1,4 +1,14 @@-module Rest.Gen.Utils where+module Rest.Gen.Utils+ ( readContent+ , copyContent+ , groupByFirst+ , fst3+ , snd3+ , thd3+ , upFirst+ , downFirst+ , mapHead+ ) where import Data.Char import Data.List.Split