@@ -4,56 +4,140 @@
44{-# LANGUAGE FlexibleInstances #-}
55-----------------------------------------------------------------------------
66-- |
7−-- Module : Servant.JQuery
7+-- Module : Servant.JS
88-- License : BSD3
99-- Maintainer : Alp Mestanogullari <alpmestan@gmail.com>
1010-- Stability : experimental
1111-- Portability : non-portable
12+--
13+-- Generating Javascript code to query your APIs using vanilla Javascript,
14+-- Angular.js or JQuery.
15+--
16+-- Using this package is very simple. Say you have this API type around:
17+--
18+-- > type API = "users" :> Get '[JSON] [Users]
19+-- > :<|> "messages" :> Get '[JSON] [Message]
20+--
21+-- All you need to do to generate the Javascript code is to write a 'Proxy'
22+-- for this API type:
23+--
24+-- > api :: Proxy API
25+-- > api = Proxy
26+--
27+-- And pick one of the generators:
28+--
29+-- - 'vanillaJS' and 'vanillaJSWith' generate functions that use
30+-- /XMLHttpRequest/ to query your endpoints. The former just calls
31+-- the latter with default code-generation options.
32+-- - 'jquery' and 'jqueryWith' follow the same pattern except that they
33+-- generate functions that use /jQuery/'s AJAX functions.
34+-- - 'angular' and 'angularWith' do the same but use /Angular.js/'s $http
35+-- service. In addition, we provide 'angularService' and 'angularServiceWith'
36+-- which produce functions under an Angular service that your controlers
37+-- can depend on to query the API.
38+--
39+-- Let's keep it simple and produce vanilla Javascript code with the default options.
40+--
41+-- @
42+-- jsCode :: String
43+-- jsCode = 'jsForAPI' api 'vanillaJS'
44+-- @
45+--
46+-- That's it! If you want to write that code to a file:
47+--
48+-- @
49+-- writeJSCode :: IO ()
50+-- writeJSCode = 'writeJSForAPI' api 'vanillaJS' "./my_api.js"
51+-- @
52+--
53+-- If you want to customize the rendering options, take a look
54+-- at 'CommonGeneratorOptions' which are generic options common to all the
55+-- generators. the /xxxWith/ variants all take 'CommonGeneratorOptions' whereas
56+-- the /xxx/ versions use 'defCommonGeneratorOptions'. Once you have some custom
57+--
58+-- > myOptions :: 'CommonGeneratorOptions'
59+--
60+-- All you need to do to use it is to use 'vanillaJSWith' and pass it @myOptions@.
61+--
62+-- @
63+-- jsCodeWithMyOptions :: String
64+-- jsCodeWithMyOptions = 'jsForAPI' api ('vanillaJSWith' myOptions)
65+-- @
66+--
67+-- Follow the same pattern for any other generator.
68+--
69+-- /Note/: The Angular generators take an additional type of options,
70+-- namely 'AngularOptions', to let you tweak aspects of the code generation
71+-- that are specific to /Angular.js/.
1272module Servant.JS
13− ( javascript
14− , generateJS
15− , jsForAPI
16− , listFromAPI
17− , printJS
18− , module Servant.JS.Internal
19− , GenerateCode(..)
20− , CommonGeneratorOptions(..)
73+ ( -- * Generating javascript code from an API type
74+ jsForAPI
75+ , writeJSForAPI
76+ , JavaScriptGenerator
77+
78+ , -- * Options common to all generators
79+ CommonGeneratorOptions(..)
80+ , defCommonGeneratorOptions
81+
82+ , -- * Vanilla Javascript code generation
83+ vanillaJS
84+ , vanillaJSWith
85+
86+ , -- * JQuery code generation
87+ jquery
88+ , jqueryWith
89+
90+ , -- * Angular.js code generation
91+ angular
92+ , angularWith
93+ , angularService
94+ , angularServiceWith
95+ , AngularOptions(..)
96+ , defAngularOptions
97+
98+ , -- * Misc.
99+ listFromAPI
100+ , javascript
101+ , HasJS(..)
102+ , GenerateList(..)
103+ , AjaxReq
21104 ) where
22105
23106import Data.Proxy
24107import Servant.API
108+import Servant.JS.Angular
25109import Servant.JS.Internal
110+import Servant.JS.JQuery
111+import Servant.JS.Vanilla
26112
113+-- | Generate the data necessary to generate javascript code
114+-- for all the endpoints of an API, as ':<|>'-separated values
115+-- of type 'AjaxReq'.
27116javascript :: HasJS layout => Proxy layout -> JS layout
28117javascript p = javascriptFor p defReq
29118
30−printJS :: AjaxReq -> JavaScriptGenerator -> IO ()
31−printJS req gen = putStrLn (generateJS req gen)
32−
33−generateJS :: AjaxReq -> JavaScriptGenerator -> String
34−generateJS req gen = gen $ req
35−
36−-- | Utility class used by 'jsForAPI' which will
37−-- directly hand you all the Javascript code
38−-- instead of handing you a ':<|>'-separated list
39−-- of 'AjaxReq' like 'javascript' and then having to
40−-- use 'generateJS' on each 'AjaxReq'.
41−class GenerateCode reqs where
42− jsFor :: reqs -> JavaScriptGenerator -> String
43−
44−instance GenerateCode AjaxReq where
45− jsFor = generateJS
46−
47−instance GenerateCode rest => GenerateCode (AjaxReq :<|> rest) where
48− jsFor (req :<|> rest) gen = jsFor req gen ++ jsFor rest gen
49−
50119-- | Directly generate all the javascript functions for your API
51120-- from a 'Proxy' for your API type. You can then write it to
52121-- a file or integrate it in a page, for example.
53−jsForAPI :: (HasJS api, GenerateCode (JS api)) => Proxy api
54− -> JavaScriptGenerator -> String
55−jsForAPI p = jsFor (javascript p)
122+jsForAPI :: (HasJS api, GenerateList (JS api))
123+ => Proxy api -- ^ proxy for your API type
124+ -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
125+ -> String -- ^ a string that you can embed in your pages or write to a file
126+jsForAPI p gen = gen (listFromAPI p)
127+
128+-- | Directly generate all the javascript functions for your API
129+-- from a 'Proxy' for your API type using the given generator
130+-- and write the resulting code to a file at the given path.
131+writeJSForAPI :: (HasJS api, GenerateList (JS api))
132+ => Proxy api -- ^ proxy for your API type
133+ -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
134+ -> FilePath -- ^ path to the file you want to write the resulting javascript code into
135+ -> IO ()
136+writeJSForAPI p gen fp = writeFile fp (jsForAPI p gen)
56137
138+-- | Utility class used by 'jsForAPI' which computes
139+-- the data needed to generate a function for each endpoint
140+-- and hands it all back in a list.
57141class GenerateList reqs where
58142 generateList :: reqs -> [AjaxReq]
59143
@@ -63,5 +147,7 @@ instance GenerateList AjaxReq where
63147instance GenerateList rest => GenerateList (AjaxReq :<|> rest) where
64148 generateList (r :<|> rest) = r : generateList rest
65149
150+-- | Generate the necessary data for JS codegen as a list, each 'AjaxReq'
151+-- describing one endpoint from your API type.
66152listFromAPI :: (HasJS api, GenerateList (JS api)) => Proxy api -> [AjaxReq]
67153listFromAPI p = generateList (javascript p)