git.iko.soy/typeable/servant
on master help sign in
Make Javascript tutorial compile.
commit7aa550aa051d5868a09a2561ae33727409b3146b authorJulian K. Arni <jkarni@gmail.com> dateThu 12 May 2016, 13:20 (10 years ago) parent9e71fde
3 files changed+104−81
doc/tutorial/Javascript.lhs+98−79 doc/tutorial/test/JavascriptSpec.hs+5−2 servant-js/src/Servant/JS.hs+1
doc/tutorial/Javascript.lhs+98−79
@@ -154,33 +154,33 @@ javascript with just a simple function call to `jsForAPI` from
154154`Servant.JS`.
155155
156156``` haskell
157−apiJS :: Text
158−apiJS = jsForAPI api jquery
157+apiJS1 :: Text
158+apiJS1 = jsForAPI api jquery
159159```
160160
161161This `Text` contains 2 Javascript functions, 'getPoint' and 'getBooks':
162162
163163``` javascript
164−var getPoint = function (onSuccess, onError)
165− {
166− $.ajax(
167− { url: '/point'
168− , success: onSuccess
169− , error: onError
170− , method: 'GET'
171− });
172−};
173164
165+var getPoint = function(onSuccess, onError)
166+{
167+ $.ajax(
168+ { url: '/point'
169+ , success: onSuccess
170+ , error: onError
171+ , type: 'GET'
172+ });
173+}
174174
175−var getBooks = function (q, onSuccess, onError)
176− {
177− $.ajax(
178− { url: '/books' + '?q=' + encodeURIComponent(q)
179− , success: onSuccess
180− , error: onError
181− , method: 'GET'
182− });
183−};
175+var getBooks = function(q, onSuccess, onError)
176+{
177+ $.ajax(
178+ { url: '/books' + '?q=' + encodeURIComponent(q)
179+ , success: onSuccess
180+ , error: onError
181+ , type: 'GET'
182+ });
183+}
184184```
185185
186186We created a directory `static` that contains two static files: `index.html`,
@@ -192,7 +192,7 @@ write the generated javascript into a file:
192192``` haskell
193193writeJSFiles :: IO ()
194194writeJSFiles = do
195− T.writeFile "static/api.js" apiJS
195+ T.writeFile "static/api.js" apiJS1
196196 jq <- T.readFile =<< Language.Javascript.JQuery.file
197197 T.writeFile "static/jq.js" jq
198198```
@@ -220,7 +220,7 @@ The `CommonGeneratorOptions` will let you define different behaviors to
220220change how functions are generated. Here is the definition of currently
221221available options:
222222
223−```haskell
223+```haskell ignore
224224data CommonGeneratorOptions = CommonGeneratorOptions
225225 {
226226 -- | function generating function names
@@ -249,8 +249,8 @@ Use the same code as before but simply replace the previous `apiJS` with
249249the following one:
250250
251251``` haskell
252−apiJS :: String
253−apiJS = jsForAPI api vanillaJS
252+apiJS2 :: Text
253+apiJS2 = jsForAPI api vanillaJS
254254```
255255
256256The rest is *completely* unchanged.
@@ -259,41 +259,50 @@ The output file is a bit different, but it has the same parameters,
259259
260260``` javascript
261261
262−var getPoint = function (onSuccess, onError)
263−{
264− var xhr = new XMLHttpRequest();
265− xhr.open('GET', '/point', true);
266262
267− xhr.onreadystatechange = function (e) {
263+var getPoint = function(onSuccess, onError)
264+{
265+ var xhr = new XMLHttpRequest();
266+ xhr.open('GET', '/point', true);
267+ xhr.setRequestHeader(\"Accept\",\"application/json\");
268+ xhr.onreadystatechange = function (e) {
268269 if (xhr.readyState == 4) {
270+ if (xhr.status == 204 || xhr.status == 205) {
271+ onSuccess();
272+ } else if (xhr.status >= 200 && xhr.status < 300) {
273+ var value = JSON.parse(xhr.responseText);
274+ onSuccess(value);
275+ } else {
269276 var value = JSON.parse(xhr.responseText);
270− if (xhr.status == 200 || xhr.status == 201) {
271− onSuccess(value);
272− } else {
273− onError(value);
274− }
275− }
277+ onError(value);
278+ }
276279 }
277− xhr.send(null);
278−};
280+ }
281+ xhr.send(null);
282+}
279283
280−var getBooks = function (q, onSuccess, onError)
284+var getBooks = function(q, onSuccess, onError)
281285{
282− var xhr = new XMLHttpRequest();
283− xhr.open('GET', '/books' + '?q=' + encodeURIComponent(q), true);
284−
285− xhr.onreadystatechange = function (e) {
286+ var xhr = new XMLHttpRequest();
287+ xhr.open('GET', '/books' + '?q=' + encodeURIComponent(q), true);
288+ xhr.setRequestHeader(\"Accept\",\"application/json\");
289+ xhr.onreadystatechange = function (e) {
286290 if (xhr.readyState == 4) {
291+ if (xhr.status == 204 || xhr.status == 205) {
292+ onSuccess();
293+ } else if (xhr.status >= 200 && xhr.status < 300) {
287294 var value = JSON.parse(xhr.responseText);
288− if (xhr.status == 200 || xhr.status == 201) {
289− onSuccess(value);
290− } else {
291− onError(value);
292− }
293− }
295+ onSuccess(value);
296+ } else {
297+ var value = JSON.parse(xhr.responseText);
298+ onError(value);
299+ }
294300 }
295− xhr.send(null);
296−};
301+ }
302+ xhr.send(null);
303+}
304+
305+
297306```
298307
299308And that's all, your web service can of course be accessible from those
@@ -309,8 +318,8 @@ Use the same code as before but simply replace the previous `apiJS` with
309318the following one:
310319
311320``` haskell
312−apiJS :: String
313−apiJS = jsForAPI api $ axios defAxiosOptions
321+apiJS3 :: Text
322+apiJS3 = jsForAPI api $ axios defAxiosOptions
314323```
315324
316325The rest is *completely* unchanged.
@@ -319,19 +328,23 @@ The output file is a bit different,
319328
320329``` javascript
321330
322−var getPoint = function ()
331+
332+var getPoint = function()
323333{
324− return axios({ url: '/point'
325− , method: 'get'
326− });
327−};
334+ return axios({ url: '/point'
335+ , method: 'get'
336+ });
337+}
338+
328339
329−var getBooks = function (q)
340+
341+var getBooks = function(q)
330342{
331− return axios({ url: '/books' + '?q=' + encodeURIComponent(q)
332− , method: 'get'
333− });
334−};
343+ return axios({ url: '/books' + '?q=' + encodeURIComponent(q)
344+ , method: 'get'
345+ });
346+}
347+
335348```
336349
337350**Caution:** In order to support the promise style of the API, there are no onSuccess
@@ -366,8 +379,8 @@ generate top level functions.
366379The difference is that `angular` Generator always takes an argument.
367380
368381``` haskell
369−apiJS :: String
370−apiJS = jsForAPI api $ angular defAngularOptions
382+apiJS4 :: Text
383+apiJS4 = jsForAPI api $ angular defAngularOptions
371384```
372385
373386The generated code will be a bit different than previous generators. An extra
@@ -379,21 +392,25 @@ nor onError callback functions.
379392
380393``` javascript
381394
395+
382396var getPoint = function($http)
383397{
384398 return $http(
385− { url: '/counter'
386− , method: 'GET'
387− });
399+ { url: '/point'
400+ , method: 'GET'
401+ });
388402}
389403
404+
405+
390406var getBooks = function($http, q)
391407{
392408 return $http(
393− { url: '/books' + '?q=' + encodeURIComponent(q), true);
409+ { url: '/books' + '?q=' + encodeURIComponent(q)
394410 , method: 'GET'
395411 });
396412}
413+
397414```
398415
399416You can then build your controllers easily
@@ -440,8 +457,8 @@ app.service('MyService', function($http) {
440457To do so, you just have to use an alternate generator.
441458
442459``` haskell
443−apiJS :: String
444−apiJS = jsForAPI api $ angularService defAngularOptions
460+apiJS5 :: Text
461+apiJS5 = jsForAPI api $ angularService defAngularOptions
445462```
446463
447464Again, it is possible to customize some portions with the options.
@@ -468,32 +485,34 @@ Servant comes with three name builders included:
468485Keeping the JQuery as an example, let's see the impact:
469486
470487``` haskell
471−apiJS :: String
472−apiJS = jsForAPI api $ jqueryWith defCommonGeneratorOptions { functionNameBuilder= snakeCase }
488+apiJS6 :: Text
489+apiJS6 = jsForAPI api $ jqueryWith defCommonGeneratorOptions { functionNameBuilder= snakeCase }
473490```
474491
475−This `String` contains 2 Javascript functions:
492+This `Text` contains 2 Javascript functions:
476493
477494``` javascript
478495
479−var getPoint = function (onSuccess, onError)
496+
497+var get_point = function(onSuccess, onError)
480498{
481499 $.ajax(
482500 { url: '/point'
483501 , success: onSuccess
484502 , error: onError
485− , method: 'GET'
503+ , type: 'GET'
486504 });
487−};
505+}
488506
489−var getBooks = function (q, onSuccess, onError)
507+var get_books = function(q, onSuccess, onError)
490508{
491509 $.ajax(
492510 { url: '/books' + '?q=' + encodeURIComponent(q)
493511 , success: onSuccess
494512 , error: onError
495− , method: 'GET'
513+ , type: 'GET'
496514 });
497−};
515+}
516+
498517```
499518
doc/tutorial/test/JavascriptSpec.hs+5−2
@@ -15,7 +15,10 @@ spec = do
1515 describe "apiJS" $ do
1616 it "is contained verbatim in Javascript.lhs" $ do
1717 code <- readFile "Javascript.lhs"
18− cs apiJS `shouldSatisfy` (`isInfixOf` code)
18+ cs apiJS1 `shouldSatisfy` (`isInfixOf` code)
19+ cs apiJS3 `shouldSatisfy` (`isInfixOf` code)
20+ cs apiJS4 `shouldSatisfy` (`isInfixOf` code)
21+ cs apiJS6 `shouldSatisfy` (`isInfixOf` code)
1922
2023 describe "writeJSFiles" $ do
2124 it "[not a test] write apiJS to static/api.js" $ do
@@ -24,7 +27,7 @@ spec = do
2427 describe "app" $ with (return app) $ do
2528 context "/api.js" $ do
2629 it "delivers apiJS" $ do
27− get "/api.js" `shouldRespondWith` (fromString (cs apiJS))
30+ get "/api.js" `shouldRespondWith` (fromString (cs apiJS1))
2831
2932 context "/" $ do
3033 it "delivers something" $ do
servant-js/src/Servant/JS.hs+1
@@ -112,6 +112,7 @@ module Servant.JS
112112 , javascript
113113 , NoTypes
114114 , GenerateList(..)
115+ , FunctionName(..)
115116 ) where
116117
117118import Prelude hiding (writeFile)