packages feed

rest-gen 0.13.1.2 → 0.14

raw patch · 17 files changed

+1004/−934 lines, 17 filesdep +haskell-src-extsdep +uniplatedep −attoparsecdep −bytestringdep −containersdep ~json-schemadep ~rest-corePVP ok

version bump matches the API change (PVP)

Dependencies added: haskell-src-exts, uniplate

Dependencies removed: attoparsec, bytestring, containers, generic-aeson, happstack-server, mtl, regular, rest-types, utf8-string

Dependency ranges changed: json-schema, rest-core

API changes (from Hackage documentation)

- Rest.Gen.Docs.Generate: DocsContext :: String -> Version -> String -> DocsContext
- Rest.Gen.Docs.Generate: cdiv :: String -> Html -> Html
- Rest.Gen.Docs.Generate: cls :: String -> Attribute
- Rest.Gen.Docs.Generate: contextVersion :: DocsContext -> Version
- Rest.Gen.Docs.Generate: data DocsContext
- Rest.Gen.Docs.Generate: instance [overlap ok] Eq DocsContext
- Rest.Gen.Docs.Generate: instance [overlap ok] Show DocsContext
- Rest.Gen.Docs.Generate: mkAllResources :: DocsContext -> ApiResource -> IO String
- Rest.Gen.Docs.Generate: mkSingleResource :: DocsContext -> ApiResource -> IO String
- Rest.Gen.Docs.Generate: resourcesInfo :: DocsContext -> ApiResource -> Html
- Rest.Gen.Docs.Generate: rootUrl :: DocsContext -> String
- Rest.Gen.Docs.Generate: row :: Html -> Html
- Rest.Gen.Docs.Generate: subResourcesInfo :: DocsContext -> ApiResource -> Html
- Rest.Gen.Docs.Generate: templates :: DocsContext -> String
- Rest.Gen.Docs.Generate: writeDocs :: DocsContext -> Router m s -> String -> IO ()
- Rest.Gen.Docs.Happstack: apiDocsHandler :: (ServerMonad m, MonadPlus m, FilterMonad Response m, MonadIO m) => String -> String -> Api a -> m Response
- Rest.Gen.Haskell.Generate: HaskellContext :: Version -> String -> String -> Bool -> [ModuleName] -> [Import] -> [(ModuleName, ModuleName)] -> [String] -> HaskellContext
- Rest.Gen.Haskell.Generate: apiVersion :: HaskellContext -> Version
- Rest.Gen.Haskell.Generate: data HaskellContext
- Rest.Gen.Haskell.Generate: imports :: HaskellContext -> [Import]
- Rest.Gen.Haskell.Generate: includePrivate :: HaskellContext -> Bool
- Rest.Gen.Haskell.Generate: mkHsApi :: HaskellContext -> Router m s -> IO ()
- Rest.Gen.Haskell.Generate: namespace :: HaskellContext -> [String]
- Rest.Gen.Haskell.Generate: rewrites :: HaskellContext -> [(ModuleName, ModuleName)]
- Rest.Gen.Haskell.Generate: sources :: HaskellContext -> [ModuleName]
- Rest.Gen.Haskell.Generate: targetPath :: HaskellContext -> String
- Rest.Gen.Haskell.Generate: wrapperName :: HaskellContext -> String
- Rest.Gen.JavaScript.Generate: mkJsApi :: ModuleName -> Bool -> Version -> Router m s -> IO String
- Rest.Gen.Ruby.Generate: mkRbApi :: ModuleName -> Bool -> Version -> Router m s -> IO String
- Rest.Gen.Types: Import :: Qualification -> ModuleName -> (Maybe ModuleName) -> (Maybe [QName]) -> Import
- Rest.Gen.Types: Name :: String -> Name
- Rest.Gen.Types: Qual :: ModuleName -> Name -> QName
- Rest.Gen.Types: Qualified :: Qualification
- Rest.Gen.Types: UnQual :: Name -> QName
- Rest.Gen.Types: UnQualified :: Qualification
- Rest.Gen.Types: data Import
- Rest.Gen.Types: data QName
- Rest.Gen.Types: data Qualification
- Rest.Gen.Types: instance Codeable Import
- Rest.Gen.Types: instance Codeable ModuleName
- Rest.Gen.Types: instance Codeable Name
- Rest.Gen.Types: instance Codeable QName
- Rest.Gen.Types: instance Codeable Qualification
- Rest.Gen.Types: instance Eq Import
- Rest.Gen.Types: instance Eq ModuleName
- Rest.Gen.Types: instance Eq Name
- Rest.Gen.Types: instance Eq QName
- Rest.Gen.Types: instance Eq Qualification
- Rest.Gen.Types: instance Show Import
- Rest.Gen.Types: instance Show ModuleName
- Rest.Gen.Types: instance Show Name
- Rest.Gen.Types: instance Show QName
- Rest.Gen.Types: instance Show Qualification
- Rest.Gen.Types: newtype Name
- Rest.Gen.Types: unName :: Name -> String
+ Rest.Gen.Base: ActionInfo :: Maybe Ident -> Bool -> ActionType -> ActionTarget -> String -> RequestMethod -> [DataDescription] -> [DataDescription] -> [DataDescription] -> [String] -> Bool -> Link -> ActionInfo
+ Rest.Gen.Base: ApiAction :: ResourceId -> Link -> ActionInfo -> ApiAction
+ Rest.Gen.Base: Create :: ActionType
+ Rest.Gen.Base: DataDescription :: DataType -> String -> String -> String -> Maybe Type -> [ModuleName] -> DataDescription
+ Rest.Gen.Base: Delete :: ActionType
+ Rest.Gen.Base: DeleteMany :: ActionType
+ Rest.Gen.Base: File :: DataType
+ Rest.Gen.Base: Ident :: String -> Type -> [ModuleName] -> Ident
+ Rest.Gen.Base: JSON :: DataType
+ Rest.Gen.Base: LAccess :: [Link] -> LinkItem
+ Rest.Gen.Base: LAction :: String -> LinkItem
+ Rest.Gen.Base: LParam :: String -> LinkItem
+ Rest.Gen.Base: LResource :: String -> LinkItem
+ Rest.Gen.Base: List :: ActionType
+ Rest.Gen.Base: Modify :: ActionType
+ Rest.Gen.Base: Other :: DataType
+ Rest.Gen.Base: Retrieve :: ActionType
+ Rest.Gen.Base: String :: DataType
+ Rest.Gen.Base: TreeItem :: String -> ResourceId -> ResourceId -> Link -> [Accessor] -> Bool -> [ApiAction] -> String -> [ApiResource] -> ApiResource
+ Rest.Gen.Base: Update :: ActionType
+ Rest.Gen.Base: UpdateMany :: ActionType
+ Rest.Gen.Base: XML :: DataType
+ Rest.Gen.Base: accessLink :: [Accessor] -> Link
+ Rest.Gen.Base: accessors :: Step sid mid aid -> [Accessor]
+ Rest.Gen.Base: actionTarget :: ActionInfo -> ActionTarget
+ Rest.Gen.Base: actionType :: ActionInfo -> ActionType
+ Rest.Gen.Base: allResourceIds :: ApiResource -> [ResourceId]
+ Rest.Gen.Base: allSubResourceIds :: ApiResource -> [ResourceId]
+ Rest.Gen.Base: allSubResources :: ApiResource -> [ApiResource]
+ Rest.Gen.Base: allSubTrees :: ApiResource -> [ApiResource]
+ Rest.Gen.Base: allTrees :: ApiResource -> [ApiResource]
+ Rest.Gen.Base: apiResources :: ApiResource -> [ResourceId]
+ Rest.Gen.Base: apiSubtrees :: Router m s -> ApiResource
+ Rest.Gen.Base: apiTree' :: ResourceId -> Link -> Router m s -> ApiResource
+ Rest.Gen.Base: chooseType :: [DataDescription] -> Maybe DataDescription
+ Rest.Gen.Base: cleanName :: String -> [String]
+ Rest.Gen.Base: data ActionInfo
+ Rest.Gen.Base: data ActionType
+ Rest.Gen.Base: data ApiAction
+ Rest.Gen.Base: data ApiResource
+ Rest.Gen.Base: data DataDescription
+ Rest.Gen.Base: data DataType
+ Rest.Gen.Base: data Ident
+ Rest.Gen.Base: data LinkItem
+ Rest.Gen.Base: dataExample :: DataDescription -> String
+ Rest.Gen.Base: dataSchema :: DataDescription -> String
+ Rest.Gen.Base: dataType :: DataDescription -> DataType
+ Rest.Gen.Base: dataTypeDesc :: DataDescription -> String
+ Rest.Gen.Base: defaultTree :: ApiResource
+ Rest.Gen.Base: description :: Ident -> String
+ Rest.Gen.Base: errors :: ActionInfo -> [DataDescription]
+ Rest.Gen.Base: flattenLast :: Link -> [Link]
+ Rest.Gen.Base: flattenLastResource :: Link -> [Link]
+ Rest.Gen.Base: flattenLink :: Link -> [Link]
+ Rest.Gen.Base: foldTree :: (ApiResource -> [a] -> a) -> ApiResource -> a
+ Rest.Gen.Base: foldTreeChildren :: ([a] -> a) -> (ApiResource -> [a] -> a) -> ApiResource -> a
+ Rest.Gen.Base: getLinkIds :: Link -> [(String, [(String, String)])]
+ Rest.Gen.Base: hasAccessor :: ApiResource -> Bool
+ Rest.Gen.Base: hasParam :: LinkItem -> Bool
+ Rest.Gen.Base: haskellModules :: Ident -> [ModuleName]
+ Rest.Gen.Base: haskellType :: Ident -> Type
+ Rest.Gen.Base: https :: ActionInfo -> Bool
+ Rest.Gen.Base: ident :: ActionInfo -> Maybe Ident
+ Rest.Gen.Base: inputs :: ActionInfo -> [DataDescription]
+ Rest.Gen.Base: isAccessor :: ActionInfo -> Bool
+ Rest.Gen.Base: itemInfo :: ApiAction -> ActionInfo
+ Rest.Gen.Base: itemLink :: ApiAction -> Link
+ Rest.Gen.Base: itemResource :: ApiAction -> ResourceId
+ Rest.Gen.Base: itemString :: LinkItem -> String
+ Rest.Gen.Base: link :: ActionInfo -> Link
+ Rest.Gen.Base: listGetterActionInfo :: Resource m s sid mid aid -> String -> Getter mid -> [ActionInfo]
+ Rest.Gen.Base: method :: ActionInfo -> RequestMethod
+ Rest.Gen.Base: mkActionDescription :: String -> ActionInfo -> String
+ Rest.Gen.Base: mkFuncParts :: ApiAction -> [String]
+ Rest.Gen.Base: namedActionInfo :: Resource m s sid mid aid -> String -> Endpoint sid mid aid -> [ActionInfo]
+ Rest.Gen.Base: noPrivate :: ApiResource -> ApiResource
+ Rest.Gen.Base: outputs :: ActionInfo -> [DataDescription]
+ Rest.Gen.Base: params :: ActionInfo -> [String]
+ Rest.Gen.Base: postAction :: ActionInfo -> Bool
+ Rest.Gen.Base: resAccessors :: ApiResource -> [Accessor]
+ Rest.Gen.Base: resDescription :: ApiResource -> String
+ Rest.Gen.Base: resDir :: ActionInfo -> String
+ Rest.Gen.Base: resId :: ApiResource -> ResourceId
+ Rest.Gen.Base: resIdents :: ApiResource -> [Link]
+ Rest.Gen.Base: resItems :: ApiResource -> [ApiAction]
+ Rest.Gen.Base: resLink :: ApiResource -> Link
+ Rest.Gen.Base: resName :: ApiResource -> String
+ Rest.Gen.Base: resParents :: ApiResource -> ResourceId
+ Rest.Gen.Base: resPrivate :: ApiResource -> Bool
+ Rest.Gen.Base: resourceToAccessors :: Resource m s sid mid aid -> [Accessor]
+ Rest.Gen.Base: resourceToActionInfo :: Resource m s sid mid aid -> [ActionInfo]
+ Rest.Gen.Base: selectActionInfo :: Link -> String -> Handler m -> ActionInfo
+ Rest.Gen.Base: setLinkIds :: Link -> [String] -> String
+ Rest.Gen.Base: singleActionInfo :: Resource m s sid mid aid -> Maybe (Id sid) -> String -> [ActionInfo]
+ Rest.Gen.Base: sortTree :: ApiResource -> ApiResource
+ Rest.Gen.Base: subResourceIds :: ApiResource -> [ResourceId]
+ Rest.Gen.Base: subResourceNames :: ApiResource -> [String]
+ Rest.Gen.Base: subResources :: ApiResource -> [ApiResource]
+ Rest.Gen.Base: type Accessor = (String, Maybe Ident)
+ Rest.Gen.Base: type Link = [LinkItem]
+ Rest.Gen.Base: type ResourceId = [String]
+ Rest.Gen.Config: configFromArgs :: String -> IO Config
+ Rest.Gen.Docs: DocsContext :: String -> Version -> String -> DocsContext
+ Rest.Gen.Docs: cdiv :: String -> Html -> Html
+ Rest.Gen.Docs: cls :: String -> Attribute
+ Rest.Gen.Docs: contextVersion :: DocsContext -> Version
+ Rest.Gen.Docs: data DocsContext
+ Rest.Gen.Docs: instance [overlap ok] Eq DocsContext
+ Rest.Gen.Docs: instance [overlap ok] Show DocsContext
+ Rest.Gen.Docs: mkAllResources :: DocsContext -> ApiResource -> IO String
+ Rest.Gen.Docs: mkSingleResource :: DocsContext -> ApiResource -> IO String
+ Rest.Gen.Docs: resourcesInfo :: DocsContext -> ApiResource -> Html
+ Rest.Gen.Docs: rootUrl :: DocsContext -> String
+ Rest.Gen.Docs: row :: Html -> Html
+ Rest.Gen.Docs: subResourcesInfo :: DocsContext -> ApiResource -> Html
+ Rest.Gen.Docs: templates :: DocsContext -> String
+ Rest.Gen.Docs: writeDocs :: DocsContext -> Router m s -> String -> IO ()
+ Rest.Gen.Haskell: HaskellContext :: Version -> String -> String -> Bool -> [ModuleName] -> [ImportDecl] -> [(ModuleName, ModuleName)] -> [String] -> HaskellContext
+ Rest.Gen.Haskell: apiVersion :: HaskellContext -> Version
+ Rest.Gen.Haskell: data HaskellContext
+ Rest.Gen.Haskell: imports :: HaskellContext -> [ImportDecl]
+ Rest.Gen.Haskell: includePrivate :: HaskellContext -> Bool
+ Rest.Gen.Haskell: instance Eq ErrorInfo
+ Rest.Gen.Haskell: instance Eq Info
+ Rest.Gen.Haskell: instance Show ErrorInfo
+ Rest.Gen.Haskell: instance Show Info
+ Rest.Gen.Haskell: mkHsApi :: HaskellContext -> Router m s -> IO ()
+ Rest.Gen.Haskell: namespace :: HaskellContext -> [String]
+ Rest.Gen.Haskell: rewrites :: HaskellContext -> [(ModuleName, ModuleName)]
+ Rest.Gen.Haskell: sources :: HaskellContext -> [ModuleName]
+ Rest.Gen.Haskell: targetPath :: HaskellContext -> String
+ Rest.Gen.Haskell: wrapperName :: HaskellContext -> String
+ Rest.Gen.JavaScript: mkJsApi :: ModuleName -> Bool -> Version -> Router m s -> IO String
+ Rest.Gen.Ruby: mkRbApi :: ModuleName -> Bool -> Version -> Router m s -> IO String
+ Rest.Gen.Types: ImportDecl :: SrcLoc -> ModuleName -> Bool -> Bool -> Maybe String -> Maybe ModuleName -> Maybe (Bool, [ImportSpec]) -> ImportDecl
+ Rest.Gen.Types: data ImportDecl :: *
+ Rest.Gen.Types: haskellByteStringType :: Type
+ Rest.Gen.Types: haskellSimpleType :: String -> Type
+ Rest.Gen.Types: haskellStringType :: Type
+ Rest.Gen.Types: haskellUnitType :: Type
+ Rest.Gen.Types: importAs :: ImportDecl -> Maybe ModuleName
+ Rest.Gen.Types: importLoc :: ImportDecl -> SrcLoc
+ Rest.Gen.Types: importModule :: ImportDecl -> ModuleName
+ Rest.Gen.Types: importPkg :: ImportDecl -> Maybe String
+ Rest.Gen.Types: importQualified :: ImportDecl -> Bool
+ Rest.Gen.Types: importSpecs :: ImportDecl -> Maybe (Bool, [ImportSpec])
+ Rest.Gen.Types: importSrc :: ImportDecl -> Bool
+ Rest.Gen.Types: namedImport :: String -> ImportDecl
+ Rest.Gen.Types: noLoc :: SrcLoc
+ Rest.Gen.Types: qualImport :: String -> ImportDecl
- Rest.Gen: generate :: Config -> String -> Api m -> [ModuleName] -> [Import] -> [(ModuleName, ModuleName)] -> IO ()
+ Rest.Gen: generate :: Config -> String -> Api m -> [ModuleName] -> [ImportDecl] -> [(ModuleName, ModuleName)] -> IO ()
- Rest.Gen.Types: newtype ModuleName
+ Rest.Gen.Types: newtype ModuleName :: *

Files

CHANGELOG.md view
@@ -1,5 +1,15 @@ # Changelog +## 0.14++* Abstracted generated code into rest-client, you should regenerate haskell clients+* haskell code generation is now done using `haskell-src-exts`+* When using module name rewrites their qualification are now also rewritten.+* Add `hs-source-dirs` and `build-depends` when generating cabal files+* Moved `Rest.Gen.Docs.Happstack` to `rest-happstack:Rest.Driver.Happstack.Docs`+* Expose `Rest.Gen.Base`+* Flattened module hierarchy, `Rest.Gen.Haskell.Generate` is now `Rest.Gen.Haskell` etc.+ #### 0.13.1.2  * Use `json-schema 0.5.*` and add `showExample` cases for `Map` and `Any`
rest-gen.cabal view
@@ -1,5 +1,5 @@ name:                rest-gen-version:             0.13.1.2+version:             0.14 description:         Documentation and client generation from rest definition. synopsis:            Documentation and client generation from rest definition. maintainer:          code@silk.co@@ -29,16 +29,15 @@   hs-source-dirs:    src   exposed-modules:     Rest.Gen+    Rest.Gen.Base     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.Docs+    Rest.Gen.Haskell+    Rest.Gen.JavaScript+    Rest.Gen.Ruby     Rest.Gen.Types   other-modules:     Paths_rest_gen-    Rest.Gen.Base     Rest.Gen.Base.ActionInfo     Rest.Gen.Base.ActionInfo.Ident     Rest.Gen.Base.ApiTree@@ -52,33 +51,26 @@     , Cabal >= 1.18 && < 1.22     , HStringTemplate >= 0.6 && < 0.8     , aeson >= 0.7 && < 0.8-    , attoparsec >= 0.10 && < 0.13     , blaze-html >= 0.5 && < 0.8-    , bytestring >= 0.9 && < 0.11     , code-builder == 0.1.*-    , containers >= 0.4 && < 0.6     , directory >= 1.1 && < 1.3     , fclabels >= 1.0.4 && < 2.1     , filepath >= 1.2 && < 1.4-    , generic-aeson == 0.1.*-    , happstack-server >= 7.0.5 && < 7.4     , hashable >= 1.1 && < 1.3+    , haskell-src-exts >= 1.15.0 && < 1.16.0     , hslogger >= 1.1 && < 1.3     , hxt >= 9.2 && < 9.4-    , json-schema == 0.5.*-    , mtl >= 2.0 && < 2.3+    , json-schema == 0.6.*     , pretty >= 1.0 && < 1.2     , process >= 1.0 && < 1.3-    , regular == 0.3.*-    , rest-core >= 0.29 && < 0.31-    , rest-types == 1.10.*+    , rest-core == 0.31.*     , safe >= 0.2 && < 0.4     , scientific >= 0.3.2 && < 0.4     , split >= 0.1 && < 0.3     , tagged >= 0.2 && < 0.8     , text >= 0.11 && < 1.2+    , uniplate >= 1.6.12 && < 1.7     , unordered-containers == 0.2.*-    , utf8-string == 0.3.*     , vector == 0.10.*  test-suite rest-gen-tests@@ -89,7 +81,7 @@   build-depends:       base >= 4.5 && < 4.8     , HUnit == 1.2.*-    , rest-core >= 0.29 && < 0.31+    , rest-core == 0.31.*     , rest-gen     , test-framework == 0.8.*     , test-framework-hunit == 0.3.*
src/Rest/Gen.hs view
@@ -9,18 +9,19 @@ import System.Directory import System.Exit import System.Process+import qualified Language.Haskell.Exts.Syntax as H  import Rest.Api (Api, Some1 (..), withVersion)  import Rest.Gen.Config-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.Ruby.Generate (mkRbApi)+import Rest.Gen.Docs (DocsContext (DocsContext), writeDocs)+import Rest.Gen.Haskell (HaskellContext (HaskellContext), mkHsApi)+import Rest.Gen.JavaScript (mkJsApi)+import Rest.Gen.Ruby (mkRbApi) import Rest.Gen.Types import Rest.Gen.Utils -generate :: Config -> String -> Api m -> [ModuleName] -> [Import] -> [(ModuleName, ModuleName)] -> IO ()+generate :: Config -> String -> Api m -> [H.ModuleName] -> [H.ImportDecl] -> [(H.ModuleName, H.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@@ -41,7 +42,7 @@        Nothing              -> return ()   where     packageName = map toLower name-    moduleName  = ModuleName $ upFirst packageName+    moduleName  = H.ModuleName $ upFirst packageName  getTargetDir :: Config -> String -> IO String getTargetDir config str =
src/Rest/Gen/Base/ActionInfo.hs view
@@ -39,6 +39,8 @@ #endif import qualified Data.JSON.Schema as J import qualified Data.Label.Total as L+import qualified Language.Haskell.Exts.Parser as H+import qualified Language.Haskell.Exts.Syntax as H  import Rest.Dictionary (Error (..), Input (..), Output (..), Param (..)) import Rest.Driver.Routing (mkListHandler, mkMultiHandler)@@ -48,10 +50,10 @@ import Rest.Schema import qualified Rest.Dictionary as Dict import qualified Rest.Resource   as Rest+import Rest.Gen.Types  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@@ -92,16 +94,16 @@  -- | Description of input/output data data DataDescription = DataDescription-  { dataType      :: DataType-  , dataTypeDesc  :: String-  , dataSchema    :: String-  , dataExample   :: String-  , haskellType   :: String-  , haskellModule :: [String]+  { dataType       :: DataType+  , dataTypeDesc   :: String+  , dataSchema     :: String+  , dataExample    :: String+  , haskellType    :: Maybe H.Type+  , haskellModules :: [H.ModuleName]   } deriving (Show, Eq)  defaultDescription :: DataDescription-defaultDescription = DataDescription Other "" "" "" "" []+defaultDescription = DataDescription Other "" "" "" Nothing []  chooseType :: [DataDescription] -> Maybe DataDescription chooseType []         = Nothing@@ -292,22 +294,22 @@                                                      , dataTypeDesc = "XML"                                                      , dataSchema   = X.showSchema  . X.getXmlSchema $ d                                                      , dataExample  = X.showExample . X.getXmlSchema $ d-                                                     , haskellType  = typeString d-                                                     , haskellModule = modString d+                                                     , haskellType  = Just $ toHaskellType d+                                                     , haskellModules = modString d                                                      }         handlerInput _ XmlTextI = defaultDescription { dataType     = XML                                                      , dataTypeDesc = "XML"-                                                     , haskellType  = "String"+                                                     , haskellType  = Just haskellStringType                                                      }         handlerInput _ RawXmlI  = defaultDescription { dataType     = XML                                                      , dataTypeDesc = "XML"-                                                     , haskellType  = "String"+                                                     , haskellType  = Just haskellStringType                                                      }         handlerInput d JsonI    = defaultDescription { dataType     = JSON                                                      , dataTypeDesc = "JSON"                                                      , dataExample  = J.showExample . J.schema $ d-                                                     , haskellType  = typeString d-                                                     , haskellModule = modString d+                                                     , haskellType  = Just $ toHaskellType d+                                                     , haskellModules = modString d                                                      }         handlerInput _ FileI    = defaultDescription { dataType     = File                                                      , dataTypeDesc = "File"@@ -324,18 +326,18 @@                                                       , dataTypeDesc  = "XML"                                                       , dataSchema    = X.showSchema  . X.getXmlSchema $ d                                                       , dataExample   = X.showExample . X.getXmlSchema $ d-                                                      , haskellType   = typeString d-                                                      , haskellModule = modString d+                                                      , haskellType   = Just $ toHaskellType d+                                                      , haskellModules = modString d                                                       }         handlerOutput _ RawXmlO  = defaultDescription { dataType     = XML                                                       , dataTypeDesc = "XML"-                                                      , haskellType  = "String"+                                                      , haskellType  = Just haskellStringType                                                       }         handlerOutput d JsonO    = defaultDescription { dataType      = JSON                                                       , dataTypeDesc  = "JSON"                                                       , dataExample   = J.showExample . J.schema $ d-                                                      , haskellType   = typeString d-                                                      , haskellModule = modString d+                                                      , haskellType   = Just $ toHaskellType d+                                                      , haskellModules = modString d                                                       }         handlerOutput _ FileO    = defaultDescription { dataType      = File                                                       , dataTypeDesc  = "File"@@ -349,15 +351,16 @@                                                     , dataTypeDesc  = "XML"                                                     , dataSchema    = X.showSchema  . X.getXmlSchema $ d                                                     , dataExample   = X.showExample . X.getXmlSchema $ d-                                                    , haskellType   = typeString d-                                                    , haskellModule = modString d+                                                    , haskellType   = Just $ toHaskellType d+                                                    , haskellModules = modString d                                                     }         handleError d JsonE    = defaultDescription { dataType      = JSON                                                     , dataTypeDesc  = "JSON"                                                     , dataExample   = J.showExample . J.schema $ d-                                                    , haskellType   = typeString d-                                                    , haskellModule = modString d+                                                    , haskellType   = Just $ toHaskellType d+                                                    , haskellModules = modString d                                                     }+ #if __GLASGOW_HASKELL__ >= 704 typeString :: forall a. Typeable a => Proxy a -> String typeString _ = typeString' . typeOf $ (undefined :: a)@@ -371,20 +374,32 @@                         (tyConName tyCon)                         (concatMap (\t -> " (" ++ typeString' t ++ ")") subs) -modString :: forall a. Typeable a => Proxy a -> [String]-modString _ = filter (\v -> v /= "" && take 4 v /= "GHC.") . modString' . typeOf $ (undefined :: a)+modString :: forall a. Typeable a => Proxy a -> [H.ModuleName]+modString _ = map H.ModuleName . filter (\v -> v /= "" && take 4 v /= "GHC.") . modString' . typeOf $ (undefined :: a)   where modString' tr =           let (tyCon, subs) = splitTyConApp tr           in  tyConModule tyCon : concatMap modString' subs++toHaskellType :: forall a. Typeable a => Proxy a -> H.Type+toHaskellType ty =+  case H.parseType (typeString ty) of+    H.ParseOk parsedType -> parsedType+    H.ParseFailed _loc msg -> error msg #else typeString :: Typeable a => a -> String typeString = show . typeOf -modString :: Typeable a => a -> [String]-modString = filter (/= "") . modString' . typeOf+modString :: Typeable a => a -> [ModuleName]+modString = map ModuleName . filter (/= "") . modString' . typeOf   where modString' tr =           let (tyCon, subs) = splitTyConApp tr           in (intercalate "." . init . splitOn "." . tyConString $ tyCon) : concatMap modString' subs++toHaskellType :: Typeable a => a -> H.Type+toHaskellType ty =+  case H.parseType (typeString ty) of+    H.ParseOk parsedType -> parsedType+    H.ParseFailed _loc msg -> error msg #endif  idIdent :: Id id -> Ident@@ -394,14 +409,14 @@ actionIdent Dict.StringId   = Ident     { Ident.description    = "string"-    , Ident.haskellType    = "String"+    , Ident.haskellType    = haskellStringType     , Ident.haskellModules = []     } actionIdent Dict.ReadId   = Ident     { Ident.description    = describe proxy_-    , Ident.haskellType    =  typeString proxy_-    , Ident.haskellModules = map ModuleName $ modString proxy_+    , Ident.haskellType    = toHaskellType proxy_+    , Ident.haskellModules = modString proxy_     }   where     proxy_ :: Proxy a
src/Rest/Gen/Base/ActionInfo/Ident.hs view
@@ -1,9 +1,9 @@ module Rest.Gen.Base.ActionInfo.Ident (Ident (..)) where -import Rest.Gen.Types+import qualified Language.Haskell.Exts.Syntax as H  data Ident = Ident   { description    :: String-  , haskellType    :: String-  , haskellModules :: [ModuleName]+  , haskellType    :: H.Type+  , haskellModules :: [H.ModuleName]   } deriving (Show, Eq)
src/Rest/Gen/Base/JSON.hs view
@@ -3,7 +3,7 @@  import Data.Aeson ((.=)) import Data.JSON.Schema-import Data.Text (pack)+import Data.Maybe import Text.PrettyPrint.HughesPJ import qualified Data.Aeson  as A import qualified Data.Vector as V@@ -14,14 +14,21 @@ showExample = render . pp_value . showExample'   where     showExample' s = case s of-      Choice []     -> A.Null -- Cannot create zero value-      Choice (x:_)  -> showExample' x-      Object fs     -> A.object $ map (\f -> pack (key f) .= showExample' (content f)) fs-      Map v         -> A.object ["<key>" .= showExample' v]-      Tuple vs      -> A.Array $ V.fromList $ map showExample' vs-      Array l _ _ v -> A.Array $ V.fromList $ replicate (l `max` 1) (showExample' v)-      Value _ _     -> A.String "value"-      Boolean       -> A.Bool True-      Number l _    -> A.Number (fromIntegral l)-      Null          -> A.Null-      Any           -> A.String "<value>"+      Choice []    -> A.Null -- Cannot create zero value+      Choice (x:_) -> showExample' x+      Object fs    -> A.object $ map (\f -> key f .= showExample' (content f)) fs+      Map v        -> A.object ["<key>" .= showExample' v]+      Tuple vs     -> A.Array $ V.fromList $ map showExample' vs+      Array l _ v  -> A.Array $ V.fromList $ replicate (lengthBoundExample l `max` 1) (showExample' v)+      Value _      -> A.String "value"+      Boolean      -> A.Bool True+      Number b     -> A.Number . boundExample $ b+      Null         -> A.Null+      Any          -> A.String "<value>"+      Constant v   -> v++boundExample :: Num a => Bound -> a+boundExample b = fromIntegral $ fromMaybe 0 (maybe (lower b) Just (upper b))++lengthBoundExample :: Num a => LengthBound -> a+lengthBoundExample b = fromIntegral $ fromMaybe 0 (maybe (lowerLength b) Just (upperLength b))
src/Rest/Gen/Config.hs view
@@ -16,13 +16,16 @@   , defaultConfig   , parseLocation   , options+  , configFromArgs   ) where  import Prelude hiding (id, (.)) +import Control.Applicative import Control.Category import Data.Label import System.Console.GetOpt+import System.Environment (getArgs)  data Action   = MakeDocs String | MakeJS | MakeRb | MakeHS data Location = Default | Stream | Location String@@ -61,3 +64,15 @@   , Option ['v'] ["version"]       (ReqArg (set (apiVersion . parent)) "VERSION") "The version of the API under generation. Default latest."   , Option ['p'] ["hide-private"]  (NoArg  (set (apiPrivate . parent) False)) "Generate API for the public, hiding private resources. Not default."   ]++configFromArgs :: String -> IO Config+configFromArgs name = do+  let header = "Usage: " ++ name ++ " [OPTIONS...], with the following options:"+  args <- getArgs+  fst <$> processArgs defaultConfig (options id) header args++processArgs :: a -> [OptDescr (a -> a)] -> String -> [String] -> IO (a, [String])+processArgs defConfig opts header args =+    case getOpt Permute opts args of+        (oargs, nonopts, []    ) -> return (foldl (flip ($)) defConfig oargs, nonopts)+        (_    , _      , errors) -> ioError $ userError $ concat errors ++ usageInfo header opts
+ src/Rest/Gen/Docs.hs view
@@ -0,0 +1,212 @@+{-# LANGUAGE+    EmptyDataDecls+  , GADTs+  , MultiParamTypeClasses+  , OverlappingInstances+  , ScopedTypeVariables+  , TemplateHaskell+  , TupleSections+  , TypeFamilies+  #-}+module Rest.Gen.Docs+  ( DocsContext (..)+  , cdiv+  , cls+  , mkAllResources+  , mkSingleResource+  , resourcesInfo+  , row+  , subResourcesInfo+  , writeDocs+  ) where++import Prelude hiding (div, head, id, span)+import qualified Prelude as P++import Control.Monad hiding (forM_)+import Data.Function (on)+import Data.Hashable (hash)+import Data.List hiding (head, span)+import Data.String+import System.Directory+import System.FilePath+import System.Log.Logger+import Text.Blaze.Html+import Text.Blaze.Html5 hiding (map)+import Text.Blaze.Html5.Attributes hiding (method, span, title)+import Text.Blaze.Html.Renderer.String+import Text.StringTemplate++import Rest.Api (Router, Version)+import Rest.Gen.Base+import Rest.Gen.Utils++-- | Information about the context in which a resource is contained+data DocsContext = DocsContext+  { rootUrl        :: String+  , contextVersion :: Version+  , templates      :: String+  } deriving (Eq, Show)++writeDocs :: DocsContext -> Router m s -> String -> IO ()+writeDocs context router loc =+  do createDirectoryIfMissing True loc+     let tree = apiSubtrees router+     mkAllResources context tree >>= writeFile (loc </> "index.html")+     mapM_ (writeSingleResource context loc) $ allSubResources tree++writeSingleResource :: DocsContext -> String -> ApiResource -> IO ()+writeSingleResource ctx loc r =+  do let dr = loc </> intercalate "/" (resId r)+     createDirectoryIfMissing True dr+     mkSingleResource ctx r >>= writeFile (dr </> "index.html")++mkAllResources :: DocsContext -> ApiResource -> IO String+mkAllResources ctx tree =+  do tmpls <- directoryGroup (templates ctx)+     tmpl  <- maybe (errorM "Doc generation" "Couldn't find template api-docs-all" >> return (newSTMP "")) return $+                     getStringTemplate "api-docs-all" tmpls+     return $ render+            $ setManyAttrib+                   [ ("listing"  , map (renderHtml . (\v -> resourceLinkAnchor v (resourceDisp v))) . sort . allSubResourceIds $ tree) ]+            $ setManyAttrib+                   [ ("resources", renderHtml $ subResourcesInfo ctx tree )+                   , ("version",   show $ contextVersion ctx  )+                   ]+                   tmpl++mkSingleResource :: DocsContext -> ApiResource -> IO String+mkSingleResource ctx tree =+  do tmpls <- directoryGroup (templates ctx)+     tmpl  <- maybe (errorM "Doc Generation" "Couldn't find template api-docs-resource" >> return (newSTMP "")) return $+                     getStringTemplate "api-docs-resource" tmpls+     return $ render+            $ setManyAttrib+                   [ ("subresources", map (renderHtml . (\v -> resourceLinkRemote (rootUrl ctx) v (resourceDisp v))) $ allResourceIds tree)+                   , ("resource"    , resId tree)+                   , ("parents"     , map (renderHtml . (\v -> resourceLinkRemote (rootUrl ctx) v (toHtml $ last v))) $ tail $ inits (resId tree))+                   , ("identifiers" , map renderHtml $ resourceIdentifiers (resLink tree) (resIdents tree))+                   ]+            $ setManyAttrib+                   [ ("name"        , resName tree)+                   , ("urls"        , renderHtml $ resourceTable tree)+                   , ("description" , resDescription tree)+                   , ("version"     , show $ contextVersion ctx)+                   ]+                   tmpl++-- | Helper functions for generating HTML+cls :: String -> Attribute+cls = class_ . toValue++cdiv :: String -> Html -> Html+cdiv s = div ! cls s++row :: Html -> Html+row = cdiv "row"++-- | Recursively generate information for a resource structure+resourcesInfo :: DocsContext -> ApiResource -> Html+resourcesInfo ctx = foldTree $ (\it -> sequence_ . (resourceInfo ctx it :) )++subResourcesInfo :: DocsContext -> ApiResource -> Html+subResourcesInfo ctx = foldTreeChildren sequence_ $ (\it -> sequence_ . (resourceInfo ctx it :) )++-- | Generate information for one resource+resourceInfo :: DocsContext -> ApiResource -> Html+resourceInfo ctx it = section $+  do resourceAnchor (resId it)+     row $ cdiv "span16 page-header resource-title" $ h1 $ resourceLinkRemote (rootUrl ctx) (resId it) $ resourceDisp (resId it)+     row $+      do cdiv "span10" $+           do h2 $ toHtml "Description"+              p $ toHtml $ resDescription it+         cdiv "span6" $+           do h2 $ toHtml "Identifiers"+              p $ sequence_ $ intersperse br $ resourceIdentifiers (resLink it) (resIdents it)+     br+     resourceTable it++resourceIdentifiers :: Link -> [Link] -> [Html]+resourceIdentifiers lnk lnks =+  case lnks of+    [] -> [toHtml "No identifiers"]+    ls -> map (linkHtml . (lnk ++)) $ ls++resourceTable :: ApiResource -> Html+resourceTable it =+  let urlInfo = groupByFirst . concatMap (\ai -> map (,itemInfo ai) $ flattenLast $ itemLink ai) $ resItems it+  in table ! cls "bordered-table resource-table" $+      do thead $ mapM_ (\v -> th ! cls v $ toHtml v) ["URL", "Method", "Description", "Input", "Output", "Errors", "Parameters"]+         tbody $ flip mapM_ (zip [(1 :: Int)..] urlInfo) $ \(n, (url, ais)) ->+          do tr ! cls ("stripe-" ++ show (n `mod` 2) ++ " url-main-row") $ mapM_ td $+                     [ linkHtml $ url+                     , toHtml $ show $ method $ P.head ais+                     , toHtml $ mkActionDescription (resName it) $ P.head ais+                     , dataDescriptions "None"  $ inputs $ P.head ais+                     , dataDescriptions "None" $ outputs $ P.head ais+                     , dataDescriptions "None" $ errors $ P.head ais+                     , toHtml $ if null (params (P.head ais)) then "None" else intercalate ", " $ params $ P.head ais+                     ]+             flip mapM_ (tail ais) $ \ai ->+                tr ! cls ("stripe-" ++ show (n `mod` 2) ++ " url-data-row") $ mapM_ td $+                     [ return ()+                     , toHtml $ show $ method ai+                     , toHtml $ mkActionDescription (resName it) ai+                     , dataDescriptions "None"  (inputs ai)+                     , dataDescriptions "None" (outputs ai)+                     , dataDescriptions "None" (errors ai)+                     , toHtml $ if null (params ai) then "None" else intercalate ", " $ params ai+                     ]++-- | Generate information for input/output data structure+dataDescriptions :: String -> [DataDescription] -> Html+dataDescriptions s []    = toHtml s+dataDescriptions _ descs =+  table ! cls "data-description" $+    do tr $ flip mapM_ descs $ \desc -> td $ toHtml $ dataTypeDesc desc+       tr $ flip mapM_ descs $ \desc -> td $+        do when (dataSchema desc /= "") $ mkCode (typeLang (dataType desc)) "Schema" $ dataSchema desc+           when (dataExample desc /= "") $ mkCode (typeLang (dataType desc)) "Example" $ dataExample desc+  where typeLang XML  = "xml"+        typeLang JSON = "js"+        typeLang _    = ""++-- | Helper function for setting the right attributes to make something collapsible.+-- The javascript prt can be found in docs.js++mkCode :: String -> String -> String -> Html+mkCode lng cap cd =+  let eid = "idv" ++ show (hash cd)+  in do div ! cls "modal hide fade code" ! id (toValue eid) $+          do cdiv "modal-header" $+              do a ! href (toValue "#") ! cls "close" $ toHtml "x"+                 h3 $ toHtml cap+             cdiv "modal-body" $ pre ! cls ("prettyprint lang-" ++ lng) $ toHtml $ cd+        button ! cls "btn open-modal"+               ! customAttribute (fromString "data-controls-modal") (toValue eid)+               ! customAttribute (fromString "data-backdrop") (toValue "true")+               ! customAttribute (fromString "data-keyboard") (toValue "true")+               $ toHtml cap++resourceId :: ResourceId -> String+resourceId = intercalate "."++resourceDisp :: ResourceId -> Html+resourceDisp = toHtml . intercalate "/"++resourceLinkAnchor :: ResourceId -> Html -> Html+resourceLinkAnchor rid = a ! cls "resource-link" ! href (toValue $ "#" ++ resourceId rid)++resourceLinkRemote :: String -> ResourceId -> Html -> Html+resourceLinkRemote rUrl rid = a ! cls "resource-link" ! href (toValue $ rUrl ++ intercalate "/" rid)++resourceAnchor :: ResourceId -> Html+resourceAnchor rid = a ! name (toValue $ resourceId rid) $ return ()++linkHtml :: Link -> Html+linkHtml =  mapM_ linkItem+  where linkItem (LParam idf)   = toHtml ("/<" ++ idf ++ ">")+        linkItem (LAccess lnks) = span ! class_ (toValue "link-block") $ sequence_ $ intersperse br+                                   $ map linkHtml $ reverse $ sortBy (compare `on` length) lnks+        linkItem x              = toHtml ("/" ++ itemString x)
− src/Rest/Gen/Docs/Generate.hs
@@ -1,212 +0,0 @@-{-# 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 (div, head, id, span)-import qualified Prelude as P--import Control.Monad hiding (forM_)-import Data.Function (on)-import Data.Hashable (hash)-import Data.List hiding (head, span)-import Data.String-import System.Directory-import System.FilePath-import System.Log.Logger-import Text.Blaze.Html-import Text.Blaze.Html5 hiding (map)-import Text.Blaze.Html5.Attributes hiding (method, span, title)-import Text.Blaze.Html.Renderer.String-import Text.StringTemplate--import Rest.Api (Router, Version)-import Rest.Gen.Base-import Rest.Gen.Utils---- | Information about the context in which a resource is contained-data DocsContext = DocsContext-  { rootUrl        :: String-  , contextVersion :: Version-  , templates      :: String-  } deriving (Eq, Show)--writeDocs :: DocsContext -> Router m s -> String -> IO ()-writeDocs context router loc =-  do createDirectoryIfMissing True loc-     let tree = apiSubtrees router-     mkAllResources context tree >>= writeFile (loc </> "index.html")-     mapM_ (writeSingleResource context loc) $ allSubResources tree--writeSingleResource :: DocsContext -> String -> ApiResource -> IO ()-writeSingleResource ctx loc r =-  do let dr = loc </> intercalate "/" (resId r)-     createDirectoryIfMissing True dr-     mkSingleResource ctx r >>= writeFile (dr </> "index.html")--mkAllResources :: DocsContext -> ApiResource -> IO String-mkAllResources ctx tree =-  do tmpls <- directoryGroup (templates ctx)-     tmpl  <- maybe (errorM "Doc generation" "Couldn't find template api-docs-all" >> return (newSTMP "")) return $-                     getStringTemplate "api-docs-all" tmpls-     return $ render-            $ setManyAttrib-                   [ ("listing"  , map (renderHtml . (\v -> resourceLinkAnchor v (resourceDisp v))) . sort . allSubResourceIds $ tree) ]-            $ setManyAttrib-                   [ ("resources", renderHtml $ subResourcesInfo ctx tree )-                   , ("version",   show $ contextVersion ctx  )-                   ]-                   tmpl--mkSingleResource :: DocsContext -> ApiResource -> IO String-mkSingleResource ctx tree =-  do tmpls <- directoryGroup (templates ctx)-     tmpl  <- maybe (errorM "Doc Generation" "Couldn't find template api-docs-resource" >> return (newSTMP "")) return $-                     getStringTemplate "api-docs-resource" tmpls-     return $ render-            $ setManyAttrib-                   [ ("subresources", map (renderHtml . (\v -> resourceLinkRemote (rootUrl ctx) v (resourceDisp v))) $ allResourceIds tree)-                   , ("resource"    , resId tree)-                   , ("parents"     , map (renderHtml . (\v -> resourceLinkRemote (rootUrl ctx) v (toHtml $ last v))) $ tail $ inits (resId tree))-                   , ("identifiers" , map renderHtml $ resourceIdentifiers (resLink tree) (resIdents tree))-                   ]-            $ setManyAttrib-                   [ ("name"        , resName tree)-                   , ("urls"        , renderHtml $ resourceTable tree)-                   , ("description" , resDescription tree)-                   , ("version"     , show $ contextVersion ctx)-                   ]-                   tmpl---- | Helper functions for generating HTML-cls :: String -> Attribute-cls = class_ . toValue--cdiv :: String -> Html -> Html-cdiv s = div ! cls s--row :: Html -> Html-row = cdiv "row"---- | Recursively generate information for a resource structure-resourcesInfo :: DocsContext -> ApiResource -> Html-resourcesInfo ctx = foldTree $ (\it -> sequence_ . (resourceInfo ctx it :) )--subResourcesInfo :: DocsContext -> ApiResource -> Html-subResourcesInfo ctx = foldTreeChildren sequence_ $ (\it -> sequence_ . (resourceInfo ctx it :) )---- | Generate information for one resource-resourceInfo :: DocsContext -> ApiResource -> Html-resourceInfo ctx it = section $-  do resourceAnchor (resId it)-     row $ cdiv "span16 page-header resource-title" $ h1 $ resourceLinkRemote (rootUrl ctx) (resId it) $ resourceDisp (resId it)-     row $-      do cdiv "span10" $-           do h2 $ toHtml "Description"-              p $ toHtml $ resDescription it-         cdiv "span6" $-           do h2 $ toHtml "Identifiers"-              p $ sequence_ $ intersperse br $ resourceIdentifiers (resLink it) (resIdents it)-     br-     resourceTable it--resourceIdentifiers :: Link -> [Link] -> [Html]-resourceIdentifiers lnk lnks =-  case lnks of-    [] -> [toHtml "No identifiers"]-    ls -> map (linkHtml . (lnk ++)) $ ls--resourceTable :: ApiResource -> Html-resourceTable it =-  let urlInfo = groupByFirst . concatMap (\ai -> map (,itemInfo ai) $ flattenLast $ itemLink ai) $ resItems it-  in table ! cls "bordered-table resource-table" $-      do thead $ mapM_ (\v -> th ! cls v $ toHtml v) ["URL", "Method", "Description", "Input", "Output", "Errors", "Parameters"]-         tbody $ flip mapM_ (zip [(1 :: Int)..] urlInfo) $ \(n, (url, ais)) ->-          do tr ! cls ("stripe-" ++ show (n `mod` 2) ++ " url-main-row") $ mapM_ td $-                     [ linkHtml $ url-                     , toHtml $ show $ method $ P.head ais-                     , toHtml $ mkActionDescription (resName it) $ P.head ais-                     , dataDescriptions "None"  $ inputs $ P.head ais-                     , dataDescriptions "None" $ outputs $ P.head ais-                     , dataDescriptions "None" $ errors $ P.head ais-                     , toHtml $ if null (params (P.head ais)) then "None" else intercalate ", " $ params $ P.head ais-                     ]-             flip mapM_ (tail ais) $ \ai ->-                tr ! cls ("stripe-" ++ show (n `mod` 2) ++ " url-data-row") $ mapM_ td $-                     [ return ()-                     , toHtml $ show $ method ai-                     , toHtml $ mkActionDescription (resName it) ai-                     , dataDescriptions "None"  (inputs ai)-                     , dataDescriptions "None" (outputs ai)-                     , dataDescriptions "None" (errors ai)-                     , toHtml $ if null (params ai) then "None" else intercalate ", " $ params ai-                     ]---- | Generate information for input/output data structure-dataDescriptions :: String -> [DataDescription] -> Html-dataDescriptions s []    = toHtml s-dataDescriptions _ descs =-  table ! cls "data-description" $-    do tr $ flip mapM_ descs $ \desc -> td $ toHtml $ dataTypeDesc desc-       tr $ flip mapM_ descs $ \desc -> td $-        do when (dataSchema desc /= "") $ mkCode (typeLang (dataType desc)) "Schema" $ dataSchema desc-           when (dataExample desc /= "") $ mkCode (typeLang (dataType desc)) "Example" $ dataExample desc-  where typeLang XML  = "xml"-        typeLang JSON = "js"-        typeLang _    = ""---- | Helper function for setting the right attributes to make something collapsible.--- The javascript prt can be found in docs.js--mkCode :: String -> String -> String -> Html-mkCode lng cap cd =-  let eid = "idv" ++ show (hash cd)-  in do div ! cls "modal hide fade code" ! id (toValue eid) $-          do cdiv "modal-header" $-              do a ! href (toValue "#") ! cls "close" $ toHtml "x"-                 h3 $ toHtml cap-             cdiv "modal-body" $ pre ! cls ("prettyprint lang-" ++ lng) $ toHtml $ cd-        button ! cls "btn open-modal"-               ! customAttribute (fromString "data-controls-modal") (toValue eid)-               ! customAttribute (fromString "data-backdrop") (toValue "true")-               ! customAttribute (fromString "data-keyboard") (toValue "true")-               $ toHtml cap--resourceId :: ResourceId -> String-resourceId = intercalate "."--resourceDisp :: ResourceId -> Html-resourceDisp = toHtml . intercalate "/"--resourceLinkAnchor :: ResourceId -> Html -> Html-resourceLinkAnchor rid = a ! cls "resource-link" ! href (toValue $ "#" ++ resourceId rid)--resourceLinkRemote :: String -> ResourceId -> Html -> Html-resourceLinkRemote rUrl rid = a ! cls "resource-link" ! href (toValue $ rUrl ++ intercalate "/" rid)--resourceAnchor :: ResourceId -> Html-resourceAnchor rid = a ! name (toValue $ resourceId rid) $ return ()--linkHtml :: Link -> Html-linkHtml =  mapM_ linkItem-  where linkItem (LParam idf)   = toHtml ("/<" ++ idf ++ ">")-        linkItem (LAccess lnks) = span ! class_ (toValue "link-block") $ sequence_ $ intersperse br-                                   $ map linkHtml $ reverse $ sortBy (compare `on` length) lnks-        linkItem x              = toHtml ("/" ++ itemString x)
− src/Rest/Gen/Docs/Happstack.hs
@@ -1,41 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-module Rest.Gen.Docs.Happstack-  ( apiDocsHandler-  ) where--import Control.Monad-import Control.Monad.Trans-import Happstack.Server-import Rest.Api-import Rest.Gen.Base-import Rest.Gen.Docs.Generate---- | Web interface for documentation-apiDocsHandler :: (ServerMonad m, MonadPlus m, FilterMonad Response m, MonadIO m) => String -> String -> Api a -> m Response-apiDocsHandler rootURL tmpls api =-  let mkCtx v ct = DocsContext (rootURL ++ ct ++ "/") v tmpls-      serve ctx = serveDocs ctx . sortTree . noPrivate . (\(Some1 r) -> apiSubtrees r)-  in path $ \i -> withVersion i api mzero $ \v -> serve (mkCtx v i)--serveDocs :: (ServerMonad m, MonadPlus m, FilterMonad Response m, MonadIO m) => DocsContext -> ApiResource -> m Response-serveDocs ctx tree =-  msum $-    [ nullDir >> allDocsHandler ctx tree-    , docHandlers ctx tree-    ]---allDocsHandler :: (ServerMonad m, MonadPlus m, FilterMonad Response m, MonadIO m) => DocsContext -> ApiResource -> m Response-allDocsHandler ctx tree =-  do pg <- liftIO $ mkAllResources ctx tree-     setHeaderM "Content-Type" "text/html"-     return $ toResponse pg--docHandlers :: (ServerMonad m, MonadPlus m, FilterMonad Response m, MonadIO m) => DocsContext -> ApiResource -> m Response-docHandlers ctx = foldTreeChildren msum $ \it subs ->-  dir (resName it) $ msum $-       [ nullDir >> do pg <- liftIO $ mkSingleResource ctx it-                       setHeaderM "Content-Type" "text/html"-                       return $ toResponse pg-       ]-    ++ subs
+ src/Rest/Gen/Haskell.hs view
@@ -0,0 +1,374 @@+{-# LANGUAGE+    DoAndIfThenElse+  , PatternGuards+  , TemplateHaskell+  #-}+module Rest.Gen.Haskell+  ( HaskellContext (..)+  , mkHsApi+  ) where++import Control.Applicative+import Control.Arrow (first, second)+import Control.Category+import Control.Monad+import Data.Label (modify, set)+import Data.Label.Derive (mkLabelsNamed)+import Data.List+import Data.Maybe+import Prelude hiding (id, (.))+import Safe+import System.Directory+import System.FilePath+import qualified Data.Generics.Uniplate.Data                 as U+import qualified Distribution.ModuleName                     as Cabal+import qualified Distribution.Package                        as Cabal+import qualified Distribution.PackageDescription             as Cabal+import qualified Distribution.PackageDescription.Parse       as Cabal+import qualified Distribution.PackageDescription.PrettyPrint as Cabal+import qualified Distribution.Simple.Utils                   as Cabal+import qualified Distribution.Verbosity                      as Cabal+import qualified Distribution.Version                        as Cabal+import qualified Language.Haskell.Exts.Pretty                as H+import qualified Language.Haskell.Exts.Syntax                as H++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++mkLabelsNamed ("_" ++) [''Cabal.GenericPackageDescription, ''Cabal.CondTree, ''Cabal.Library]++data HaskellContext =+  HaskellContext+    { apiVersion     :: Version+    , targetPath     :: String+    , wrapperName    :: String+    , includePrivate :: Bool+    , sources        :: [H.ModuleName]+    , imports        :: [H.ImportDecl]+    , rewrites       :: [(H.ModuleName, H.ModuleName)]+    , namespace      :: [String]+    }++mkHsApi :: HaskellContext -> Router m s -> IO ()+mkHsApi ctx r =+  do let tree = sortTree . (if includePrivate ctx then id else noPrivate) . apiSubtrees $ r+     mkCabalFile ctx tree+     mapM_ (writeRes ctx) $ allSubTrees tree++mkCabalFile :: HaskellContext -> ApiResource -> IO ()+mkCabalFile ctx tree =+  do cabalExists <- doesFileExist cabalFile+     gpkg <-+       if cabalExists+       then updateExposedModules modules <$> Cabal.readPackageDescription Cabal.normal cabalFile+       else return (mkGenericPackageDescription (wrapperName ctx) modules)+     writeCabalFile cabalFile gpkg+  where+    cabalFile = targetPath ctx </> wrapperName ctx ++ ".cabal"+    modules   = map (Cabal.fromString . unModuleName) (sources ctx)+             ++ map (Cabal.fromString . qualModName . (namespace ctx ++)) (allSubResourceIds tree)++writeCabalFile :: FilePath -> Cabal.GenericPackageDescription -> IO ()+writeCabalFile path = Cabal.writeUTF8File path . unlines . filter emptyField . lines . Cabal.showGenericPackageDescription+  where emptyField = (/= "\"\" ") . takeWhile (/= ':') . reverse++updateExposedModules :: [Cabal.ModuleName] -> Cabal.GenericPackageDescription -> Cabal.GenericPackageDescription+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)) [] [] []+  where+    pkg = Cabal.emptyPackageDescription+      { Cabal.package        = Cabal.PackageIdentifier (Cabal.PackageName name) (Cabal.Version [0, 1] [])+      , Cabal.buildType      = Just Cabal.Simple+      , Cabal.specVersionRaw = Right (Cabal.orLaterVersion (Cabal.Version [1, 8] []))+      }++mkCondLibrary :: [Cabal.ModuleName] -> Cabal.CondTree Cabal.ConfVar [Cabal.Dependency] Cabal.Library+mkCondLibrary modules = Cabal.CondNode+  { Cabal.condTreeData        = Cabal.Library modules True Cabal.emptyBuildInfo { Cabal.hsSourceDirs = ["src"] }+  , Cabal.condTreeConstraints =+     [ Cabal.Dependency (Cabal.PackageName "base")        (Cabal.withinVersion $ Cabal.Version [4]     [])+     , Cabal.Dependency (Cabal.PackageName "rest-types")  (Cabal.withinVersion $ Cabal.Version [1, 10] [])+     , Cabal.Dependency (Cabal.PackageName "rest-client") (Cabal.withinVersion $ Cabal.Version [0,  4] [])+     ]+  , Cabal.condTreeComponents  = []+  }++writeRes :: HaskellContext -> ApiResource -> IO ()+writeRes ctx node =+  do createDirectoryIfMissing True (targetPath ctx </> "src" </> modPath (namespace ctx ++ resParents node))+     writeFile (targetPath ctx </> "src" </> modPath (namespace ctx ++ resId node) ++ ".hs") (mkRes ctx node)++mkRes :: HaskellContext -> ApiResource -> String+mkRes ctx node = H.prettyPrint $ buildHaskellModule ctx node pragmas Nothing+  where+    pragmas = [ H.LanguagePragma noLoc [H.Ident "OverloadedStrings"],+                H.OptionsPragma noLoc (Just H.GHC) "-fno-warn-unused-imports"]+    _warningText = "Warning!! This is automatically generated code, do not modify!"++buildHaskellModule :: HaskellContext -> ApiResource ->+                      [H.ModulePragma] -> Maybe H.WarningText ->+                      H.Module+buildHaskellModule ctx node pragmas warningText =+  rewriteModuleNames (rewrites ctx) $+     H.Module noLoc name pragmas warningText exportSpecs importDecls decls+  where+    name = H.ModuleName $ qualModName $ namespace ctx ++ resId node+    exportSpecs = Nothing+    importDecls = nub $ namedImport "Rest.Client.Internal"+                      : extraImports+                     ++ parentImports+                     ++ dataImports+                     ++ idImports+    decls = idData node ++ concat funcs++    extraImports = imports ctx+    parentImports = map mkImport . tail . inits . resParents $ node+    dataImports = map (qualImport . unModuleName) datImp+    idImports = concat . mapMaybe (return . map (qualImport . unModuleName) . Ident.haskellModules <=< snd) . resAccessors $ node++    (funcs, datImp) = second (nub . concat) . unzip . map (mkFunction (apiVersion ctx) . resName $ node) $ resItems node+    mkImport p = (namedImport importName) { H.importQualified = True,+                                            H.importAs = importAs' }+      where importName = qualModName $ namespace ctx ++ p+            importAs' = fmap (H.ModuleName . modName) . lastMay $ p++rewriteModuleNames :: [(H.ModuleName, H.ModuleName)] -> H.Module -> H.Module+rewriteModuleNames rews = U.transformBi $ \m -> lookupJustDef m m rews++noBinds :: H.Binds+noBinds = H.BDecls []++use :: H.Name -> H.Exp+use = H.Var . H.UnQual++useMQual :: (Maybe H.ModuleName) -> H.Name -> H.Exp+useMQual Nothing = use+useMQual (Just qual) = H.Var . (H.Qual $ qual)++mkFunction :: Version -> String -> ApiAction -> ([H.Decl], [H.ModuleName])+mkFunction ver res (ApiAction _ lnk ai) =+  ([H.TypeSig noLoc [funName] fType,+    H.FunBind [H.Match noLoc funName fParams Nothing rhs noBinds]],+   errorInfoModules errorI ++ infoModules output ++ maybe [] infoModules mInp)+     where+       funName = mkHsName ai+       fParams = map H.PVar $ lPars+                           ++ maybe [] ((:[]) . hsName . cleanName . description) (ident ai)+                           ++ maybe [] (const [input]) mInp+                           ++ (if null (params ai) then [] else [pList])+       (lUrl, lPars) = linkToURL res lnk+       mInp    = fmap inputInfo . chooseType . inputs $ ai+       fType   = H.TyForall Nothing [H.ClassA (H.UnQual cls) [m]] $ fTypify tyParts+         where cls = H.Ident "ApiStateC"+               m = H.TyVar $ H.Ident "m"+               fTypify :: [H.Type] -> H.Type+               fTypify [] = error "Rest.Gen.Haskell.mkFunction.fTypify - expects at least one type"+               fTypify [ty1] = ty1+               fTypify [ty1, ty2] = H.TyFun ty1 ty2+               fTypify (ty1 : tys) = H.TyFun ty1 (fTypify tys)+               tyParts = map qualIdent lPars+                         ++ maybe [] (return . Ident.haskellType) (ident ai)+                         ++ inp+                         ++ (if null (params ai) then []+                             else [H.TyList (H.TyTuple H.Boxed [haskellStringType,+                                                                haskellStringType])])+                         ++ [H.TyApp m (H.TyApp+                                         (H.TyApp+                                           (H.TyCon $ H.UnQual (H.Ident "ApiResponse"))+                                           (maybe haskellUnitType id (errorInfoType errorI)))+                                         (maybe haskellUnitType id (infoType output)))]+               qualIdent (H.Ident s)+                 | s == res = H.TyCon $ H.UnQual tyIdent+                 | otherwise = H.TyCon $ H.Qual (H.ModuleName $ modName s) tyIdent+               qualIdent H.Symbol{} = error "Rest.Gen.Haskell.mkFunction.qualIdent - not expecting a Symbol"+               inp | Just i <- mInp,+                     Just i' <- infoType i = [i']+                   | otherwise = []+       input = H.Ident "input"+       pList = H.Ident "pList"+       rhs = H.UnGuardedRhs $ H.Let binds expr+         where binds = H.BDecls [rHeadersBind, requestBind]+               rHeadersBind =+                 H.PatBind noLoc (H.PVar rHeaders) Nothing+                    (H.UnGuardedRhs $ H.List [H.Tuple H.Boxed [use hAccept, H.Lit $ H.String $ infoContentType output],+                                              H.Tuple H.Boxed [use hContentType, H.Lit $ H.String $ maybe "text/plain" infoContentType mInp]])+                              noBinds++               rHeaders     = H.Ident "rHeaders"+               hAccept      = H.Ident "hAccept"+               hContentType = H.Ident "hContentType"+               doRequest    = H.Ident "doRequest"++               requestBind =+                 H.PatBind noLoc (H.PVar request) Nothing+                    (H.UnGuardedRhs $+                      appLast+                        (H.App+                          (H.App+                            (H.App+                              (H.App (H.App (use makeReq) (H.Lit $ H.String $ show $ method ai))+                                     (H.Lit $ H.String ve))+                              url)+                            (if null (params ai) then (H.List []) else (use pList)))+                          (use rHeaders))) noBinds+               appLast e+                 | Just i <- mInp = H.App e (H.App (use $ H.Ident $ infoFunc i) (use input))+                 | otherwise = H.App e (H.Lit $ H.String "")+               makeReq = H.Ident "makeReq"+               request = H.Ident "request"++               expr = H.App (H.App (H.App (use doRequest)+                                          (use $ H.Ident $ errorInfoFunc errorI))+                                          (use $ H.Ident $ infoFunc output)) (use request)++       (ve, url) = ("v" ++ show ver, lUrl)+       errorI = headDef (ErrorInfo [] (Just haskellUnitType) defaultErrorConversion)+                . map errorInfo+                . mapMaybe (\v -> find ((v ==) . dataType) $ errors ai)+                $ maybeToList (dataType <$> chooseType (outputs ai)) ++ [XML, JSON]+       output = maybe (Info [] (Just haskellUnitType) "text/plain" "(const ())") outputInfo . chooseType . outputs $ ai+       defaultErrorConversion = if fmap dataType (chooseType (outputs ai)) == Just JSON then "fromJSON" else "fromXML"++linkToURL :: String -> Link -> (H.Exp, [H.Name])+linkToURL res lnk = first H.List $ urlParts res lnk ([], [])++urlParts :: String -> Link -> ([H.Exp], [H.Name]) -> ([H.Exp], [H.Name])+urlParts res lnk ac@(rlnk, pars) =+  case lnk of+    [] -> ac+    (LResource r : a@(LAccess _) : xs)+      | not (hasParam a) -> urlParts res xs (rlnk ++ [H.List [H.Lit $ H.String r]], pars)+      | otherwise -> urlParts res xs (rlnk', pars ++ [H.Ident r])+           where rlnk' = rlnk ++ (H.List [H.Lit $ H.String $ r] : tailed)+                 tailed = [H.App (useMQual qual $ H.Ident "readId")+                                 (use $ hsName (cleanName r))]+                   where qual | r == res = Nothing+                              | otherwise = Just $ H.ModuleName $ modName r+    (LParam p : xs) -> urlParts res xs (rlnk ++ [H.List [H.App (use $ H.Ident "showUrl")+                                                          (use $ hsName (cleanName p))]], pars)+    (i : xs) -> urlParts res xs (rlnk ++ [H.List [H.Lit $ H.String $ itemString i]], pars)++idData :: ApiResource -> [H.Decl]+idData node =+  case resAccessors node of+    [] -> []+    [(_pth, Nothing)] -> []+    [(pth, Just i)] ->+      let pp xs | null pth = xs+                | otherwise = H.Lit (H.String pth) : xs+      in [ H.TypeDecl noLoc tyIdent [] (Ident.haskellType i),+           H.TypeSig noLoc [funName] fType,+           H.FunBind [ H.Match noLoc funName [H.PVar x] Nothing+                       (H.UnGuardedRhs $ H.List $ pp [ showURLx ]) noBinds] ]+    ls ->+      let ctor (pth,mi) =+            H.QualConDecl noLoc [] [] (H.ConDecl (H.Ident (dataName pth)) $ maybe [] f mi)+              where f ty = [H.UnBangedTy $ Ident.haskellType ty]+          fun (pth, mi) = [+                           H.FunBind [H.Match noLoc funName fparams Nothing rhs noBinds]]+            where (fparams, rhs) =+                    case mi of+                      Nothing ->+                        ([H.PVar $ H.Ident (dataName pth)],+                         (H.UnGuardedRhs $ H.List [H.Lit (H.String pth)]))+                      Just{}  ->  -- Pattern match with data constructor+                        ([H.PParen $ H.PApp (H.UnQual $ H.Ident (dataName pth)) [H.PVar x]],+                         (H.UnGuardedRhs $ H.List [H.Lit $ H.String pth, showURLx]))+      in [ H.DataDecl noLoc H.DataType [] tyIdent [] (map ctor ls) []+         , H.TypeSig noLoc [funName] fType+         ] ++ concatMap fun ls+    where+      x        = H.Ident "x"+      fType    = H.TyFun (H.TyCon $ H.UnQual tyIdent) (H.TyList haskellStringType)+      funName  = H.Ident "readId"+      showURLx = H.App (H.Var $ H.UnQual $ H.Ident "showUrl") (H.Var $ H.UnQual $ x)++tyIdent :: H.Name+tyIdent = H.Ident "Identifier"++mkHsName :: ActionInfo -> H.Name+mkHsName ai = hsName $ concatMap cleanName parts+  where+      parts = case actionType ai of+                Retrieve   -> let nm = get ++ by ++ target+                              in if null nm then ["access"] else nm+                Create     -> ["create"] ++ by ++ target+                -- Should be delete, but delete is a JS keyword and causes problems in collect.+                Delete     -> ["remove"] ++ by ++ target+                DeleteMany -> ["removeMany"] ++ by ++ target+                List       -> ["list"]   ++ by ++ target+                Update     -> ["save"]   ++ by ++ target+                UpdateMany -> ["saveMany"] ++ by ++ target+                Modify   -> if resDir ai == "" then ["do"] else [resDir ai]++      target = if resDir ai == "" then maybe [] ((:[]) . description) (ident ai) else [resDir ai]+      by     = if target /= [] && (isJust (ident ai) || actionType ai == UpdateMany) then ["by"] else []+      get    = if isAccessor ai then [] else ["get"]++hsName :: [String] -> H.Name+hsName []       = H.Ident ""+hsName (x : xs) = H.Ident $ clean $ downFirst x ++ concatMap upFirst xs+  where+    clean s = if s `elem` reservedNames then s ++ "_" else s+    reservedNames =+      ["as","case","class","data","instance","default","deriving","do"+      ,"foreign","if","then","else","import","infix","infixl","infixr","let"+      ,"in","module","newtype","of","qualified","type","where"]++qualModName :: ResourceId -> String+qualModName = intercalate "." . map modName++modPath :: ResourceId -> String+modPath = intercalate "/" . map modName++dataName :: String -> String+dataName = modName++modName :: String -> String+modName = concatMap upFirst . cleanName++data Info = Info+  { infoModules     :: [H.ModuleName]+  , infoType        :: Maybe H.Type+  , infoContentType :: String+  , infoFunc        :: String+  } deriving (Eq, Show)++inputInfo :: DataDescription -> Info+inputInfo ds =+  case dataType ds of+    String -> Info []                  (Just haskellStringType)     "text/plain"               "fromString"+    XML    -> Info (haskellModules ds) (haskellType ds)             "text/xml"                 "toXML"+    JSON   -> Info (haskellModules ds) (haskellType ds)             "text/json"                "toJSON"+    File   -> Info []                  (Just haskellByteStringType) "application/octet-stream" "id"+    Other  -> Info []                  (Just haskellByteStringType) "text/plain"               "id"++outputInfo :: DataDescription -> Info+outputInfo ds =+  case dataType ds of+    String -> Info []                  (Just haskellStringType)     "text/plain" "toString"+    XML    -> Info (haskellModules ds) (haskellType ds)             "text/xml"   "fromXML"+    JSON   -> Info (haskellModules ds) (haskellType ds)             "text/json"  "fromJSON"+    File   -> Info []                  (Just haskellByteStringType) "*"          "id"+    Other  -> Info []                  (Just haskellByteStringType)"text/plain" "id"++data ErrorInfo = ErrorInfo+  { errorInfoModules :: [H.ModuleName]+  , errorInfoType    :: (Maybe H.Type)+  , errorInfoFunc    :: String+  } deriving (Eq, Show)++errorInfo :: DataDescription -> ErrorInfo+errorInfo ds =+  case dataType ds of+    String -> ErrorInfo (haskellModules ds) (haskellType ds) "fromXML"+    XML    -> ErrorInfo (haskellModules ds) (haskellType ds) "fromXML"+    JSON   -> ErrorInfo (haskellModules ds) (haskellType ds) "fromJSON"+    File   -> ErrorInfo (haskellModules ds) (haskellType ds) "fromXML"+    Other  -> ErrorInfo (haskellModules ds) (haskellType ds) "fromXML"
− src/Rest/Gen/Haskell/Generate.hs
@@ -1,293 +0,0 @@-{-# LANGUAGE-    DoAndIfThenElse-  , TemplateHaskell-  #-}-module Rest.Gen.Haskell.Generate-  ( HaskellContext (..)-  , mkHsApi-  ) where--import Control.Applicative-import Control.Arrow (first, second)-import Control.Category-import Control.Monad-import Data.Label (modify, set)-import Data.Label.Derive (mkLabelsNamed)-import Data.List-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-import qualified Distribution.PackageDescription.Parse       as Cabal-import qualified Distribution.PackageDescription.PrettyPrint as Cabal-import qualified Distribution.Simple.Utils                   as Cabal-import qualified Distribution.Verbosity                      as Cabal-import qualified Distribution.Version                        as Cabal--import Code.Build-import Code.Build.Haskell-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--mkLabelsNamed ("_" ++) [''Cabal.GenericPackageDescription, ''Cabal.CondTree, ''Cabal.Library]--data HaskellContext =-  HaskellContext-    { apiVersion     :: Version-    , targetPath     :: String-    , wrapperName    :: String-    , includePrivate :: Bool-    , sources        :: [ModuleName]-    , imports        :: [Import]-    , rewrites       :: [(ModuleName, ModuleName)]-    , namespace      :: [String]-    }--mkHsApi :: HaskellContext -> Router m s -> IO ()-mkHsApi ctx r =-  do let tree = sortTree . (if includePrivate ctx then id else noPrivate) . apiSubtrees $ r-     mkCabalFile ctx tree-     mapM_ (writeRes ctx) $ allSubTrees tree--mkCabalFile :: HaskellContext -> ApiResource -> IO ()-mkCabalFile ctx tree =-  do cabalExists <- doesFileExist cabalFile-     gpkg <--       if cabalExists-       then updateExposedModules modules <$> Cabal.readPackageDescription Cabal.normal cabalFile-       else return (mkGenericPackageDescription (wrapperName ctx) modules)-     writeCabalFile cabalFile gpkg-  where-    cabalFile = targetPath ctx </> wrapperName ctx ++ ".cabal"-    modules   = map (Cabal.fromString . unModuleName) (sources ctx)-             ++ map (Cabal.fromString . qualModName . (namespace ctx ++)) (allSubResourceIds tree)--writeCabalFile :: FilePath -> Cabal.GenericPackageDescription -> IO ()-writeCabalFile path = Cabal.writeUTF8File path . unlines . filter emptyField . lines . Cabal.showGenericPackageDescription-  where emptyField = (/= "\"\" ") . takeWhile (/= ':') . reverse--updateExposedModules :: [Cabal.ModuleName] -> Cabal.GenericPackageDescription -> Cabal.GenericPackageDescription-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)) [] [] []-  where-    pkg = Cabal.emptyPackageDescription-      { Cabal.package        = Cabal.PackageIdentifier (Cabal.PackageName name) (Cabal.Version [0, 1] [])-      , Cabal.buildType      = Just Cabal.Simple-      , Cabal.specVersionRaw = Right (Cabal.orLaterVersion (Cabal.Version [1, 8] []))-      }--mkCondLibrary :: [Cabal.ModuleName] -> Cabal.CondTree Cabal.ConfVar [Cabal.Dependency] Cabal.Library-mkCondLibrary modules = Cabal.CondNode-  { Cabal.condTreeData        = Cabal.Library modules True Cabal.emptyBuildInfo-  , Cabal.condTreeConstraints = []-  , Cabal.condTreeComponents  = []-  }--writeRes :: HaskellContext -> ApiResource -> IO ()-writeRes ctx node =-  do createDirectoryIfMissing True (targetPath ctx </> "src" </> modPath (namespace ctx ++ resParents node))-     writeFile (targetPath ctx </> "src" </> modPath (namespace ctx ++ resId node) ++ ".hs") (mkRes ctx node)--mkRes :: HaskellContext -> ApiResource -> String-mkRes ctx node =-  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 mods-       , idData node-       , mkStack funcs-       ]-  where-    (funcs, mods) = second (nub . concat) . unzip . map (mkFunction (apiVersion ctx) . resName $ node) $ resItems node--mkImports :: HaskellContext -> ApiResource -> [ModuleName] -> Code-mkImports ctx node datImp-  = mkStack . nub . map (rewriteImport $ rewrites ctx)-  $    [Import UnQualified (ModuleName "Rest.Client.Internal") Nothing Nothing]-    ++ extraImports-    ++ parentImports-    ++ dataImports-    ++ idImports-  where-    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 :: 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))-                           . map errorInfo-                           . catMaybes-                           . map (\v -> find ((v ==) . dataType) $ errors ai)-                           $ maybeToList (fmap dataType $ chooseType (outputs ai)) ++ [XML, JSON]-      (lUrl, lPars) = linkToURL res lnk-      url      = string ("v" <+> show ver <+> "/") <++> "++" <++> lUrl-      fParams  = map (hsName . cleanName) lPars-              ++ maybe [] ((:[]) . hsName . cleanName . description) (ident ai)-              ++ maybe [] (const ["input"]) mInp-              ++ (if null (params ai) then [] else ["pList"])-      fType    = "ApiStateC m => "-              ++ (hsType $ map (\p -> (if p == res then "" else modName p ++ ".") ++ "Identifier") lPars-                       ++ maybe [] (return . Ident.haskellType) (ident ai)-                       ++ maybe [] (\(_,v,_,_) -> [v]) mInp-                       ++ (if null (params ai) then [] else ["[(String, String)]"])-                       ++ ["m (ApiResponse (" ++ eType ++ ") (" ++ oType ++ "))"])-  in ( mkStack $-        [ function (mkHsName ai) fType-        , hsDecl (mkHsName ai) fParams $-           hsLet-              [ "rHeaders" .=. hsArray [ hsTuple [code "hAccept", string oCType]-                                       , hsTuple [code "hContentType", string (maybe "text/plain" (\(_,_,v,_) -> v) mInp)]-                                       ]-              , "request" .=. "ApiRequest"-                                  <++> string (show (method ai))-                                  <++> parenthesis url-                                  <++> (if null (params ai) then "[]" else "pList")-                                  <++> "rHeaders"-                                  <++> "$"-                                  <++> maybe "\"\"" ((++ " input") . (\(_,_,_,v) -> v)) mInp-              ]-              $ "liftM (parseResult" <++> eFunc <++> oFunc <+> ") . doRequest $ request"-        ]-     , map ModuleName $ eMod ++ oMod ++ maybe [] (\(m,_,_,_) -> m) mInp-     )--linkToURL :: String -> Link -> (Code, [String])-linkToURL res lnk = first (\v -> "intercalate" <++> string "/" <++> parenthesis ("map encode $ concat" <++> hsArray v)) $ urlParts res lnk ([], [])--urlParts :: String -> Link -> ([Code], [String]) -> ([Code], [String])-urlParts res lnk ac@(rlnk, pars) =-  case lnk of-    [] -> ac-    (LResource r : a@(LAccess _) : xs) | not (hasParam a) -> urlParts res xs (rlnk ++ [hsArray [string r]], pars)-                                       | otherwise ->-      urlParts res xs-            ( rlnk ++ [ hsArray [string r]-                      , (if r == res then noCode else modName r <+> ".") <+> "readId" <++> hsName (cleanName r)-                      ]-            , pars ++ [r]-            )-    (LParam p : xs) -> urlParts res xs (rlnk ++ [hsArray ["showUrl" <++> hsName (cleanName p)]], pars)-    (i : xs) -> urlParts res xs (rlnk ++ [hsArray [string $ itemString i]], pars)--idData :: ApiResource -> Code-idData node =-  case resAccessors node of-    []  -> noCode-    [(pth,mi)] -> maybe noCode-           (\i -> mkStack-            [ code "type Identifier = " <++> Ident.haskellType i-            , function "readId" "Identifier -> [String]"-            , hsDecl "readId" ["x"] (hsArray $ if pth /= ""-                                               then [string pth, code "showUrl x"]-                                               else [code "showUrl x"]-                                    )-            ]-           )-           mi-    ls  -> mkStack $-            [ hsData "Identifier" $ map (\(pth,mi) -> dataName pth ++ maybe "" (\x -> " (" ++ Ident.haskellType x ++ ")") mi) ls-            , function "readId" "Identifier -> [String]"-            , mkStack $-                map (\(pth,mi) ->-                        if isJust mi-                          then hsDecl "readId" ["(" ++ dataName pth ++ " x" ++ ")"] $ hsArray [string pth, code "showUrl x"]-                          else hsDecl "readId" [dataName pth] $ hsArray [string pth]-                    ) ls-            ]--mkHsName :: ActionInfo -> String-mkHsName ai = hsName $ concatMap cleanName parts-  where-      parts = case actionType ai of-                Retrieve   -> let nm = get ++ by ++ target-                              in if null nm then ["access"] else nm-                Create     -> ["create"] ++ by ++ target-                -- Should be delete, but delete is a JS keyword and causes problems in collect.-                Delete     -> ["remove"] ++ by ++ target-                DeleteMany -> ["removeMany"] ++ by ++ target-                List       -> ["list"]   ++ by ++ target-                Update     -> ["save"]   ++ by ++ target-                UpdateMany -> ["saveMany"] ++ by ++ target-                Modify   -> if resDir ai == "" then ["do"] else [resDir ai]--      target = if resDir ai == "" then maybe [] ((:[]) . description) (ident ai) else [resDir ai]-      by     = if target /= [] && (isJust (ident ai) || actionType ai == UpdateMany) then ["by"] else []-      get    = if isAccessor ai then [] else ["get"]--hsName :: [String] -> String-hsName []       = ""-hsName (x : xs) = clean $ downFirst x ++ concatMap upFirst xs-  where-    clean s = if s `elem` reservedNames then s ++ "_" else s-    reservedNames =-      ["as","case","class","data","instance","default","deriving","do"-      ,"foreign","if","then","else","import","infix","infixl","infixr","let"-      ,"in","module","newtype","of","qualified","type","where"]--qualModName :: ResourceId -> String-qualModName = intercalate "." . map modName--modPath :: ResourceId -> String-modPath = intercalate "/" . map modName--modName :: String -> String-modName = concatMap upFirst . cleanName--dataName :: String -> String-dataName = modName--inputInfo :: DataDescription -> ([String], String, String, String)-inputInfo ds =-  case dataType ds of-    String -> ([], "String", "text/plain", "fromString")-    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")--outputInfo :: DataDescription -> ([String], String, String, String)-outputInfo ds =-  case dataType ds of-    String -> ([], "String", "text/plain", "toString")-    XML    -> (haskellModule ds, haskellType ds, "text/xml", "fromXML")-    JSON   -> (haskellModule ds, haskellType ds, "text/json", "fromJSON")-    File   -> ([], "ByteString", "*", "id")-    Other  -> ([], "ByteString", "text/plain", "id")--errorInfo :: DataDescription -> ([String], String, String)-errorInfo ds =-  case dataType ds of-    String -> (haskellModule ds, haskellType ds, "fromXML")-    XML    -> (haskellModule ds, haskellType ds, "fromXML")-    JSON   -> (haskellModule ds, haskellType ds, "fromJSON")-    File   -> (haskellModule ds, haskellType ds, "fromXML")-    Other  -> (haskellModule ds, haskellType ds, "fromXML")
+ src/Rest/Gen/JavaScript.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE ScopedTypeVariables #-}+module Rest.Gen.JavaScript (mkJsApi) where++import Code.Build+import Code.Build.JavaScript++import Control.Monad+import Data.Maybe+import Text.StringTemplate+import qualified Language.Haskell.Exts.Syntax as H++import Rest.Api (Router, Version)+import Rest.Gen.Base+import Rest.Gen.Types+import Rest.Gen.Utils++mkJsApi :: H.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+                [ unModuleName ns ++ ".prototype.version" .=. string (show ver)+                , mkJsCode (unModuleName ns) priv r+                ]+     return $ prelude ++ cod+  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++mkJs :: String -> ApiResource -> Code+mkJs ns = foldTreeChildren mkStack (\i ls -> mkStack $ mkRes ns i : ls)++mkRes :: String -> ApiResource -> Code+mkRes ns node = mkStack $+  [ if hasAccessor node+      then resourceLoc ns node .=. ns ++ ".makeSilkConstructor()"+      else resourceLoc ns node .=. jsObject []+  , resourceLoc ns node ++ ".apiObjectType" .=. string "resourceDir"+  , mkAccessFuncs ns node+  , mkPreFuncs ns node+  , mkPostFuncs ns node+  ]++mkPreFuncs :: String -> ApiResource -> Code+mkPreFuncs ns node =+  let items = filter ((\i -> not $ postAction i || isAccessor i) . itemInfo) $ resItems node+  in mkFunctions (resourceLoc ns node ++ ".") (mkFunction ns) items++mkAccessFuncs :: String -> ApiResource -> Code+mkAccessFuncs ns node =+  let items = filter ((\i -> not (postAction i) && isAccessor i) . itemInfo) $ resItems node+  in mkFunctions (resourceLoc ns node ++ ".") (mkAccessor ns) items++mkPostFuncs :: String -> ApiResource -> Code+mkPostFuncs ns node =+  let items = filter (postAction . itemInfo) $ resItems node+  in mkFunctions (resourceLoc ns node ++ ".prototype.") (mkFunction ns) items++mkFunctions :: String -> (ApiAction -> Code) -> [ApiAction] -> Code+mkFunctions loc maker = mkStack . map (\item -> loc ++ mkJsName item .=. maker item)++mkAccessor :: String -> ApiAction -> Code+mkAccessor ns node@(ApiAction _ _ ai) =+  let fParams  = maybeToList mIdent+      urlPart  = (if resDir ai == "" then "" else resDir ai ++ "/")+              ++ maybe "" (\i -> "' + encodeURIComponent(" ++ i ++ ") + '/") mIdent+      mIdent   = fmap (jsId . cleanName . description) $ ident ai+  in function fParams $+      [ var "accessor" $ new "this" . code $ "this.contextUrl + '" ++ urlPart ++ "'"+      , "accessor.get" .=. mkFunction ns node+      , ret "accessor"+      ]++mkFunction :: String -> ApiAction -> Code+mkFunction ns (ApiAction _ _ ai) =+  let fParams  = maybeToList mIdent+              ++ maybeToList (fmap fst3 mInp)+              ++ ["success", "error", "params", "callOpts"]+      mInp     = fmap mkType . chooseType $ inputs ai+      mOut     = fmap mkType . chooseType $ outputs ai+      urlPart  = (if isAccessor ai then const "" else id) $+                 (if resDir ai == "" then "" else resDir ai ++ "/")+              ++ maybe "" (\i -> "' + encodeURIComponent(" ++ i ++ ") + '/") mIdent+      mIdent   = (if isAccessor ai then const Nothing else id) $ fmap (jsId . cleanName . description) $ ident ai+  in function fParams $ ret $+        proc (ns ++ "." ++ "ajaxCall")+          [ string (method ai)+          , code $ (if (https ai) then "this.secureContextUrl" else "this.contextUrl") ++ " + '" ++ urlPart ++ "'"+          , code "params"+          , code "success"+          , code "error"+          , string $ maybe "text/plain" snd3 mInp+          , string $ maybe "text" fst3 mOut+          , maybe (code "undefined") (\(p, _, f) -> f (code p)) mInp+          , code "callOpts"+          ]++resourceLoc :: String -> ApiResource -> String+resourceLoc ns = ((ns ++ ".prototype.") ++) . locFromLink . resLink+  where locFromLink (LResource i1 : LAccess [] : LResource i2 : xs) = jsDir (cleanName i1) ++ "." ++ locFromLink (LResource i2 : xs)+        locFromLink (LResource i : xs) = case locFromLink xs of+                                          [] -> jsDir $ cleanName i+                                          ls -> jsDir (cleanName i) ++ ".prototype." ++ ls+        locFromLink (_ : xs) = locFromLink xs+        locFromLink [] = ""++mkJsName :: ApiAction -> String+mkJsName item =+  case mkFuncParts item of+    []       -> ""+    (x : xs) -> x ++ concatMap upFirst xs++jsDir :: [String] -> String+jsDir = concatMap upFirst++jsId :: [String] -> String+jsId []       = ""+jsId (x : xs) = x ++ concatMap upFirst xs++mkType :: DataDescription -> (String, String, Code -> Code)+mkType ds =+  case dataType ds of+    String -> ("text", "text/plain", id)+    XML    -> ("xml" , "text/xml", id)+    JSON   -> ("json", "text/json", call "JSON.stringify")+    File   -> ("file", "application/octet-stream", id)+    Other  -> ("text", "text/plain", id)
− src/Rest/Gen/JavaScript/Generate.hs
@@ -1,126 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}-module Rest.Gen.JavaScript.Generate (mkJsApi) where--import Code.Build-import Code.Build.JavaScript--import Control.Monad-import Data.Maybe-import Text.StringTemplate--import Rest.Api (Router, Version)-import Rest.Gen.Base-import Rest.Gen.Types-import Rest.Gen.Utils--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-                [ unModuleName ns ++ ".prototype.version" .=. string (show ver)-                , mkJsCode (unModuleName ns) priv r-                ]-     return $ prelude ++ cod-  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--mkJs :: String -> ApiResource -> Code-mkJs ns = foldTreeChildren mkStack (\i ls -> mkStack $ mkRes ns i : ls)--mkRes :: String -> ApiResource -> Code-mkRes ns node = mkStack $-  [ if hasAccessor node-      then resourceLoc ns node .=. ns ++ ".makeSilkConstructor()"-      else resourceLoc ns node .=. jsObject []-  , resourceLoc ns node ++ ".apiObjectType" .=. string "resourceDir"-  , mkAccessFuncs ns node-  , mkPreFuncs ns node-  , mkPostFuncs ns node-  ]--mkPreFuncs :: String -> ApiResource -> Code-mkPreFuncs ns node =-  let items = filter ((\i -> not $ postAction i || isAccessor i) . itemInfo) $ resItems node-  in mkFunctions (resourceLoc ns node ++ ".") (mkFunction ns) items--mkAccessFuncs :: String -> ApiResource -> Code-mkAccessFuncs ns node =-  let items = filter ((\i -> not (postAction i) && isAccessor i) . itemInfo) $ resItems node-  in mkFunctions (resourceLoc ns node ++ ".") (mkAccessor ns) items--mkPostFuncs :: String -> ApiResource -> Code-mkPostFuncs ns node =-  let items = filter (postAction . itemInfo) $ resItems node-  in mkFunctions (resourceLoc ns node ++ ".prototype.") (mkFunction ns) items--mkFunctions :: String -> (ApiAction -> Code) -> [ApiAction] -> Code-mkFunctions loc maker = mkStack . map (\item -> loc ++ mkJsName item .=. maker item)--mkAccessor :: String -> ApiAction -> Code-mkAccessor ns node@(ApiAction _ _ ai) =-  let fParams  = maybeToList mIdent-      urlPart  = (if resDir ai == "" then "" else resDir ai ++ "/")-              ++ maybe "" (\i -> "' + encodeURIComponent(" ++ i ++ ") + '/") mIdent-      mIdent   = fmap (jsId . cleanName . description) $ ident ai-  in function fParams $-      [ var "accessor" $ new "this" . code $ "this.contextUrl + '" ++ urlPart ++ "'"-      , "accessor.get" .=. mkFunction ns node-      , ret "accessor"-      ]--mkFunction :: String -> ApiAction -> Code-mkFunction ns (ApiAction _ _ ai) =-  let fParams  = maybeToList mIdent-              ++ maybeToList (fmap fst3 mInp)-              ++ ["success", "error", "params", "callOpts"]-      mInp     = fmap mkType . chooseType $ inputs ai-      mOut     = fmap mkType . chooseType $ outputs ai-      urlPart  = (if isAccessor ai then const "" else id) $-                 (if resDir ai == "" then "" else resDir ai ++ "/")-              ++ maybe "" (\i -> "' + encodeURIComponent(" ++ i ++ ") + '/") mIdent-      mIdent   = (if isAccessor ai then const Nothing else id) $ fmap (jsId . cleanName . description) $ ident ai-  in function fParams $ ret $-        proc (ns ++ "." ++ "ajaxCall")-          [ string (method ai)-          , code $ (if (https ai) then "this.secureContextUrl" else "this.contextUrl") ++ " + '" ++ urlPart ++ "'"-          , code "params"-          , code "success"-          , code "error"-          , string $ maybe "text/plain" snd3 mInp-          , string $ maybe "text" fst3 mOut-          , maybe (code "undefined") (\(p, _, f) -> f (code p)) mInp-          , code "callOpts"-          ]--resourceLoc :: String -> ApiResource -> String-resourceLoc ns = ((ns ++ ".prototype.") ++) . locFromLink . resLink-  where locFromLink (LResource i1 : LAccess [] : LResource i2 : xs) = jsDir (cleanName i1) ++ "." ++ locFromLink (LResource i2 : xs)-        locFromLink (LResource i : xs) = case locFromLink xs of-                                          [] -> jsDir $ cleanName i-                                          ls -> jsDir (cleanName i) ++ ".prototype." ++ ls-        locFromLink (_ : xs) = locFromLink xs-        locFromLink [] = ""--mkJsName :: ApiAction -> String-mkJsName item =-  case mkFuncParts item of-    []       -> ""-    (x : xs) -> x ++ concatMap upFirst xs--jsDir :: [String] -> String-jsDir = concatMap upFirst--jsId :: [String] -> String-jsId []       = ""-jsId (x : xs) = x ++ concatMap upFirst xs--mkType :: DataDescription -> (String, String, Code -> Code)-mkType ds =-  case dataType ds of-    String -> ("text", "text/plain", id)-    XML    -> ("xml" , "text/xml", id)-    JSON   -> ("json", "text/json", call "JSON.stringify")-    File   -> ("file", "application/octet-stream", id)-    Other  -> ("text", "text/plain", id)
+ src/Rest/Gen/Ruby.hs view
@@ -0,0 +1,144 @@+{-# LANGUAGE ScopedTypeVariables #-}+module Rest.Gen.Ruby (mkRbApi) where++import Data.Char+import Data.List+import Data.Maybe++import Code.Build+import Code.Build.Ruby++import qualified Language.Haskell.Exts.Syntax as H++import Rest.Api (Router, Version)+import Rest.Gen.Base+import Rest.Gen.Types+import Rest.Gen.Utils++mkRbApi :: H.ModuleName -> Bool -> Version -> Router m s -> IO String+mkRbApi ns priv ver r =+  do prelude <- readContent "Ruby/base.rb"+     let cod = showCode . mkRb (unModuleName ns) ver . sortTree . (if priv then id else noPrivate) . apiSubtrees $ r+     return $ cod ++ prelude++mkRb :: String -> Version -> ApiResource -> Code+mkRb ns ver node =+  rbModule ns $ mkStack+        [ "@version" .=. string (show ver)+        , apiConstructor ver node+        , foldTreeChildren mkStack (\i ls -> mkStack $ mkRes i : ls) node+        ]++apiConstructor :: Version -> ApiResource -> Code+apiConstructor ver node =+  rbClass "BaseApi" $+    [ function "initialize" ["resUrl"] $ mkStack $+        [ "@url" .=. ("resUrl + '/v" ++ show ver ++ "/'")+        , "@api" .=. "self"+        ]+    , mkStack . map resGetter . subResources $ node+    ]++mkRes :: ApiResource -> Code+mkRes node = mkResAcc node <-> mkResObj node++mkResAcc :: ApiResource -> Code+mkResAcc node =+  rbClass (accessorName $ resId node)+    [ accInitializer+    , mkPreFuncs node+    , mkStack . map resGetter . filter (not . hasAccessor) . subResources $ node+    ]++mkResObj :: ApiResource -> Code+mkResObj node =+  rbClass (className $ resId node)+    [ objInitializer+    , if hasAccessor node then get else noCode+    , mkPostFuncs node+    , mkStack . map resGetter . subResources $ node+    ]++mkPostFuncs :: ApiResource -> Code+mkPostFuncs node = mkStack . map mkFunction . filter (postAction . itemInfo) . resItems $ node++mkPreFuncs :: ApiResource -> Code+mkPreFuncs node =+  let (acs, funcs) = partition (isAccessor . itemInfo) . filter ((\i -> not $ postAction i) . itemInfo) $ resItems node+  in mkStack (map mkAccessor acs) <-> mkStack (map mkFunction funcs)++mkAccessor :: ApiAction -> Code+mkAccessor node@(ApiAction rid _ ai) =+  let fParams  = maybeToList mIdent+      urlPart  = (if resDir ai == "" then "" else resDir ai ++ "/")+              ++ maybe "" (\i -> "' + " ++ i ++ " + '/") mIdent+      datType  = maybe ":data" ((':':) . fst3 . mkType) $ chooseType $ outputs ai+      mIdent   = fmap (rbName . cleanName . description) $ ident ai+  in function (rbName $ mkFuncParts node) fParams $ ret $+        new (className rid) ["@url + '" ++ urlPart ++ "'", "@api", datType]++mkFunction :: ApiAction -> Code+mkFunction node@(ApiAction _ _ ai) =+  let fParams   = maybeToList mIdent+              ++ maybeToList (fmap fst3 mInp)+              ++ ["params = {}", "headers = {}"]+      mInp     = fmap mkType . chooseType $ inputs ai+      mOut     = fmap mkType . chooseType $ outputs ai+      urlPart  = (if resDir ai == "" then "" else resDir ai ++ "/")+              ++ maybe "" (\i -> "' + " ++ i ++ " + '/") mIdent+      mIdent   = fmap (rbName . cleanName . description) $ ident ai+  in function (rbName $ mkFuncParts node) fParams $+        call ("internalSilkRequest")+          [ code "@api"+          , code $ ':' : map toLower (show $ method ai)+          , code $ "@url + '" ++ urlPart ++ "'"+          , code "params"+          , string $ maybe "text/plain" snd3 mInp+          , code $ maybe ":data" ((':':) . fst3) mOut+          , maybe (code "nil") (\(p, _, f) -> f (code p)) mInp+          , code "headers"+          ]++accInitializer :: Code+accInitializer =+  function "initialize" ["resUrl", "myApi"]+     [ "@url"      .=. "resUrl"+     , "@api"      .=. "myApi"+     ]++objInitializer :: Code+objInitializer =+  function "initialize" ["resUrl", "myApi", "retData = :data"]+     [ "@url"      .=. "resUrl"+     , "@dataType" .=. "retData"+     , "@api"      .=. "myApi"+     ]++resGetter :: ApiResource -> Code+resGetter node =+  function (className [resName node]) [] $+    ret $ new (accessorName $ resId node) ["@url + '" ++ resName node ++ "/'", "@api"]++get :: Code+get =+  function "get" ["params = {}", "headers = {}"] $+    call "internalSilkRequest" ["@api", ":get", "@url", "params", "'text/plain'", "@dataType", "headers"]++rbName :: [String] -> String+rbName []       = ""+rbName (x : xs) = downFirst x ++ concatMap upFirst xs++className :: ResourceId -> String+className = concatMap upFirst . concatMap cleanName++accessorName :: ResourceId -> String+accessorName = concatMap upFirst . ("Access":) . concatMap cleanName++mkType :: DataDescription -> (String, String, Code -> Code)+mkType ds =+  case dataType ds of+    String -> ("data", "text/plain", id)+    XML    -> ("xml" , "text/xml", (<+> ".to_s"))+    JSON   -> ("json", "text/json", call "mkJson")+    File   -> ("file", "application/octet-stream", id)+    Other  -> ("data", "text/plain", id)
− src/Rest/Gen/Ruby/Generate.hs
@@ -1,142 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}-module Rest.Gen.Ruby.Generate (mkRbApi) where--import Data.Char-import Data.List-import Data.Maybe--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 :: ModuleName -> Bool -> Version -> Router m s -> IO String-mkRbApi ns priv ver r =-  do prelude <- readContent "Ruby/base.rb"-     let cod = showCode . mkRb (unModuleName ns) ver . sortTree . (if priv then id else noPrivate) . apiSubtrees $ r-     return $ cod ++ prelude--mkRb :: String -> Version -> ApiResource -> Code-mkRb ns ver node =-  rbModule ns $ mkStack-        [ "@version" .=. string (show ver)-        , apiConstructor ver node-        , foldTreeChildren mkStack (\i ls -> mkStack $ mkRes i : ls) node-        ]--apiConstructor :: Version -> ApiResource -> Code-apiConstructor ver node =-  rbClass "BaseApi" $-    [ function "initialize" ["resUrl"] $ mkStack $-        [ "@url" .=. ("resUrl + '/v" ++ show ver ++ "/'")-        , "@api" .=. "self"-        ]-    , mkStack . map resGetter . subResources $ node-    ]--mkRes :: ApiResource -> Code-mkRes node = mkResAcc node <-> mkResObj node--mkResAcc :: ApiResource -> Code-mkResAcc node =-  rbClass (accessorName $ resId node)-    [ accInitializer-    , mkPreFuncs node-    , mkStack . map resGetter . filter (not . hasAccessor) . subResources $ node-    ]--mkResObj :: ApiResource -> Code-mkResObj node =-  rbClass (className $ resId node)-    [ objInitializer-    , if hasAccessor node then get else noCode-    , mkPostFuncs node-    , mkStack . map resGetter . subResources $ node-    ]--mkPostFuncs :: ApiResource -> Code-mkPostFuncs node = mkStack . map mkFunction . filter (postAction . itemInfo) . resItems $ node--mkPreFuncs :: ApiResource -> Code-mkPreFuncs node =-  let (acs, funcs) = partition (isAccessor . itemInfo) . filter ((\i -> not $ postAction i) . itemInfo) $ resItems node-  in mkStack (map mkAccessor acs) <-> mkStack (map mkFunction funcs)--mkAccessor :: ApiAction -> Code-mkAccessor node@(ApiAction rid _ ai) =-  let fParams  = maybeToList mIdent-      urlPart  = (if resDir ai == "" then "" else resDir ai ++ "/")-              ++ maybe "" (\i -> "' + " ++ i ++ " + '/") mIdent-      datType  = maybe ":data" ((':':) . fst3 . mkType) $ chooseType $ outputs ai-      mIdent   = fmap (rbName . cleanName . description) $ ident ai-  in function (rbName $ mkFuncParts node) fParams $ ret $-        new (className rid) ["@url + '" ++ urlPart ++ "'", "@api", datType]--mkFunction :: ApiAction -> Code-mkFunction node@(ApiAction _ _ ai) =-  let fParams   = maybeToList mIdent-              ++ maybeToList (fmap fst3 mInp)-              ++ ["params = {}", "headers = {}"]-      mInp     = fmap mkType . chooseType $ inputs ai-      mOut     = fmap mkType . chooseType $ outputs ai-      urlPart  = (if resDir ai == "" then "" else resDir ai ++ "/")-              ++ maybe "" (\i -> "' + " ++ i ++ " + '/") mIdent-      mIdent   = fmap (rbName . cleanName . description) $ ident ai-  in function (rbName $ mkFuncParts node) fParams $-        call ("internalSilkRequest")-          [ code "@api"-          , code $ ':' : map toLower (show $ method ai)-          , code $ "@url + '" ++ urlPart ++ "'"-          , code "params"-          , string $ maybe "text/plain" snd3 mInp-          , code $ maybe ":data" ((':':) . fst3) mOut-          , maybe (code "nil") (\(p, _, f) -> f (code p)) mInp-          , code "headers"-          ]--accInitializer :: Code-accInitializer =-  function "initialize" ["resUrl", "myApi"]-     [ "@url"      .=. "resUrl"-     , "@api"      .=. "myApi"-     ]--objInitializer :: Code-objInitializer =-  function "initialize" ["resUrl", "myApi", "retData = :data"]-     [ "@url"      .=. "resUrl"-     , "@dataType" .=. "retData"-     , "@api"      .=. "myApi"-     ]--resGetter :: ApiResource -> Code-resGetter node =-  function (className [resName node]) [] $-    ret $ new (accessorName $ resId node) ["@url + '" ++ resName node ++ "/'", "@api"]--get :: Code-get =-  function "get" ["params = {}", "headers = {}"] $-    call "internalSilkRequest" ["@api", ":get", "@url", "params", "'text/plain'", "@dataType", "headers"]--rbName :: [String] -> String-rbName []       = ""-rbName (x : xs) = downFirst x ++ concatMap upFirst xs--className :: ResourceId -> String-className = concatMap upFirst . concatMap cleanName--accessorName :: ResourceId -> String-accessorName = concatMap upFirst . ("Access":) . concatMap cleanName--mkType :: DataDescription -> (String, String, Code -> Code)-mkType ds =-  case dataType ds of-    String -> ("data", "text/plain", id)-    XML    -> ("xml" , "text/xml", (<+> ".to_s"))-    JSON   -> ("json", "text/json", call "mkJson")-    File   -> ("file", "application/octet-stream", id)-    Other  -> ("data", "text/plain", id)
src/Rest/Gen/Types.hs view
@@ -1,64 +1,51 @@ module Rest.Gen.Types-  ( ModuleName (..)+  ( unModuleName   , overModuleName-  , Import (..)-  , Qualification (..)-  , QName (..)-  , Name (..)+  , namedImport+  , qualImport+  , haskellStringType+  , haskellByteStringType+  , haskellUnitType+  , haskellSimpleType+  , noLoc+  , ModuleName (..)+  , ImportDecl (..)   ) where -import Data.List--import Code.Build--newtype ModuleName = ModuleName { unModuleName :: String }-  deriving (Eq, Show)+import Language.Haskell.Exts.SrcLoc (noLoc)+import Language.Haskell.Exts.Syntax (ImportDecl (..), ModuleName (..), Name (..), QName (..), SpecialCon (..), Type (..)) -instance Codeable ModuleName where-  code = code . unModuleName+unModuleName :: ModuleName -> String+unModuleName (ModuleName name) = name  overModuleName :: (String -> String) -> ModuleName -> ModuleName overModuleName f = ModuleName . f . unModuleName -newtype Name = Name { unName :: String }-  deriving (Eq, Show)--instance Codeable Name where-  code = code . unName--data QName-  = Qual ModuleName Name-  | UnQual Name-  deriving (Eq, Show)+-- | Create a simple named basic import, to be updated with other fields+--   as needed.+namedImport :: String -> ImportDecl+namedImport name = ImportDecl+  { importLoc       = noLoc+  , importQualified = False+  , importModule    = ModuleName name+  , importSrc       = False+  , importPkg       = Nothing+  , importAs        = Nothing+  , importSpecs     = Nothing+  } -instance Codeable QName where-  code (UnQual n) = code n-  code (Qual m n) = code m <+> "." <+> code n+-- | Qualified import with given name+qualImport :: String -> ImportDecl+qualImport name = (namedImport name) { importQualified = True } -data Qualification = Qualified | UnQualified-  deriving (Eq, Show)+haskellStringType :: Type+haskellStringType = haskellSimpleType "String" -instance Codeable Qualification where-  code Qualified   = code "qualified"-  code UnQualified = code ""+haskellByteStringType :: Type+haskellByteStringType = haskellSimpleType "ByteString" -data Import-  = Import Qualification ModuleName (Maybe ModuleName) (Maybe [QName])-  deriving (Eq, Show)+haskellSimpleType :: String -> Type+haskellSimpleType = TyCon . UnQual . Ident -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+haskellUnitType :: Type+haskellUnitType = TyCon (Special UnitCon)