git.fiddlerwoaroof.com
Browse code

Add docs

Ed Langley authored on 29/08/2019 04:23:14
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,284 @@
1
+#+TITLE: TODO backend implementation using CL and fukamachi/ningle
2
+
3
+* todo API
4
+
5
+  We use a fairly simple structure for our "database": a fset map (a
6
+  clojure-inspired persistent data structure) and a handful of
7
+  interface functions that wrap it. In this code, this fset map is
8
+  referenced as =*todo*=, but this is a detail hidden behind the API.
9
+  
10
+** List-level APIS
11
+   
12
+   These are functions for getting the todo list and clearing it.
13
+
14
+   #+NAME: todolist-manipulation
15
+   #+BEGIN_SRC lisp :tangle no
16
+     (defun todos ()
17
+       (gmap:gmap :seq
18
+                  (lambda (_ b)
19
+                    (declare (ignore _))
20
+                    b)
21
+                  (:map *todos*)))
22
+
23
+     (defun clear-todos ()
24
+       (setf *todos*
25
+             (fset:empty-map)))
26
+   #+END_SRC
27
+  
28
+** Getting/Replacing a todo
29
+   
30
+   This uses lisp's [[http://www.lispworks.com/documentation/HyperSpec/Body/05_a.htm][generalized references]] to abstract away the
31
+   storage details of the todos. We also provide a =delete-todo=
32
+   function for removing a todo from the list.
33
+   
34
+   #+NAME: todo-accessor
35
+   #+BEGIN_SRC lisp :tangle no
36
+     (defun todo (id)
37
+       (let ((todo (fset:@ *todos* id)))
38
+         todo))
39
+
40
+     (defun (setf todo) (new-value id)
41
+       (setf (fset:@ *todos* id)
42
+             new-value))
43
+
44
+     (defun delete-todo (id)
45
+       (setf *todos*
46
+             (fset:less *todos* id)))
47
+   #+END_SRC
48
+   
49
+** Adding and modifying todos
50
+
51
+   =new-todo= is fairly trivial. It's main feature is that it has to
52
+   make sure the =completed= and =url= keys are set to the appropriate
53
+   values. Completed isn't a lisp boolean, so it serializes to JSON
54
+   properly.
55
+
56
+   #+NAME: new-todo
57
+   #+BEGIN_SRC lisp :tangle no
58
+     (defun new-todo (value)
59
+       (let ((id (next-id)))
60
+         (setf (todo id)
61
+               (alexandria:alist-hash-table
62
+                (rutilsx.threading:->>
63
+                 value
64
+                 (acons "completed" 'yason:false)
65
+                 (acons "url" (format nil "http://localhost:5000/todo/~d" id)))
66
+                :test 'equal))))
67
+   #+END_SRC
68
+
69
+   =update-todo= just merges the input from the frontend into the
70
+   relevant todo and then makes sure that the =completed= key is a
71
+   yason-compatible boolean.
72
+
73
+   #+NAME: update-todo
74
+   #+BEGIN_SRC lisp :tangle no
75
+     (defun update-todo (id v)
76
+       (setf (todo id)
77
+             (serapeum:merge-tables (or (todo id)
78
+                                        (make-hash-table :test 'equal))
79
+                                    (data-lens.lenses:over *completed-lens*
80
+                                                           'bool-to-yason
81
+                                                           (alexandria:alist-hash-table
82
+                                                            v
83
+                                                            :test 'equal)))))
84
+   #+END_SRC
85
+
86
+** Examples
87
+  
88
+   #+BEGIN_SRC lisp :tangle no :noweb yes :exports both :results verbatim
89
+     <<example-setup>>
90
+     (with-fresh-todos ()
91
+       (new-todo '(("title" . "get groceries")))
92
+       (new-todo '(("title" . "write-better-documentation")))
93
+       (fset:convert 'list (todos)))
94
+   #+END_SRC
95
+  
96
+   #+RESULTS:
97
+   : (#<hash-table "url": "http://localhost:5000/todo/22",
98
+   :               "title": "get groceries",
99
+   :               "completed": YASON:FALSE>
100
+   :  #<hash-table "url": "http://localhost:5000/todo/23",
101
+   :               "title": "write-better-documentation",
102
+   :               "completed": YASON:FALSE>)
103
+  
104
+* Source
105
+** model.lisp source code
106
+
107
+   #+BEGIN_SRC lisp :tangle model.lisp :noweb yes :comments noweb
108
+     <<package-include>>
109
+     <<model-utils>>
110
+
111
+     (defvar *todos* (fset:empty-map))
112
+
113
+     <<todolist-manipulation>>
114
+
115
+     <<todo-accessor>>
116
+
117
+     <<new-todo>>
118
+
119
+     <<update-todo>>
120
+
121
+     (defmacro with-fresh-todos (() &body body)
122
+       `(let ((*todos* (fset:empty-map)))
123
+          ,@body))
124
+   #+END_SRC
125
+
126
+** routing.lisp source 
127
+
128
+   #+BEGIN_SRC lisp :tangle routing.lisp :noweb yes
129
+   <<package-include>>
130
+   (defmacro defroutes (app &body routes)
131
+     "Define a set of routes for given paths. the ROUTES parameter expects this format:
132
+      ((\"/path/to/{route}\" :method :POST) route-callback) the AS-ROUTE macro helps one
133
+      avoid binding function values to the route for flexibility."
134
+     (alexandria:once-only (app)
135
+       `(progn
136
+          ,@(loop for ((target &key method) callback) in routes
137
+                  collect `(setf (ningle:route ,app ,target :method ,(or method :GET)) ,callback)))))
138
+
139
+
140
+   ;; routing
141
+   (defun success (value)
142
+     (list 200 nil value))
143
+
144
+   (defmacro handler ((&optional (sym (gensym "PARAMS"))) &body body)
145
+     `(lambda (,sym)
146
+        (declare (ignorable ,sym))
147
+        (success
148
+         (fwoar.lack.json.middleware:wrap-result
149
+          (progn ,@body)))))
150
+
151
+   (defun get-id (params)
152
+     (parse-integer (serapeum:assocdr :id params)))
153
+
154
+   (defun setup-routes (app)
155
+     (defroutes app
156
+       (("/" :method :GET)            (handler () (todos)))
157
+       (("/" :method :POST)           (handler (v) (new-todo v)))
158
+       (("/" :method :DELETE)         (handler () (clear-todos)))
159
+       (("/todo/:id" :method :GET)    (handler (v) (todo (get-id v))))
160
+       (("/todo/:id" :method :DELETE) (handler (v)
161
+                                        (delete-todo (get-id v))
162
+                                        nil))
163
+       (("/todo/:id" :method :PATCH)  (handler (v)
164
+                                        (update-todo (get-id v) 
165
+                                                     (remove :id v :key #'car))))))
166
+   #+END_SRC
167
+
168
+** main.lisp source 
169
+
170
+   #+BEGIN_SRC lisp :tangle main.lisp :noweb yes
171
+   <<package-include>>
172
+   ;;; entrypoint
173
+   (defun setup ()
174
+     (let ((app (make-instance 'ningle:<app>)))
175
+       (prog1 app (setup-routes app))))
176
+
177
+   (defvar *handler*)
178
+
179
+   (defun is-running ()
180
+     (and (boundp '*handler*)
181
+          ,*handler*))
182
+
183
+   (defun ensure-started (&rest r &key port)
184
+     (declare (ignore port))
185
+     (let ((app (setup)))
186
+       (values app
187
+               (setf *handler*
188
+                     (if (not (is-running))
189
+                         (apply 'clack:clackup
190
+                                (lack.builder:builder
191
+                                 :accesslog
192
+                                 'fwoar.lack.cors.middleware:cors-middleware
193
+                                 'fwoar.lack.json.middleware:json-middleware
194
+                                 app)
195
+                                r)
196
+                         ,*handler*)))))
197
+
198
+   (defun stop ()
199
+     (if (is-running)
200
+         (progn
201
+           (clack:stop *handler*)
202
+           (makunbound '*handler*)
203
+           nil)
204
+         nil))
205
+   #+END_SRC
206
+
207
+   #+NAME: package-include
208
+   #+BEGIN_SRC lisp :tangle no :exports none
209
+   (in-package :fwoar.todo)
210
+
211
+   #+END_SRC
212
+
213
+   #+NAME: model-utils
214
+   #+BEGIN_SRC lisp :tangle no :exports none
215
+   (defparameter *cur-id* 0)
216
+   (defun next-id ()
217
+     (incf *cur-id*))
218
+
219
+   (defparameter *completed-lens*
220
+     (data-lens.lenses:make-hash-table-lens "completed"))
221
+
222
+   (defun bool-to-yason (bool)
223
+     (if bool
224
+         'yason:true
225
+         'yason:false))
226
+   #+END_SRC
227
+
228
+   #+NAME: example-setup
229
+   #+BEGIN_SRC lisp :tangle no :noweb yes :exports none
230
+   <<package-include>>
231
+   (load "pprint-setup")
232
+
233
+   #+END_SRC
234
+
235
+#+HTML_HEAD: <style>
236
+#+HTML_HEAD: :root {
237
+#+HTML_HEAD:     --zenburn-fg-plus-2: #ffffef;
238
+#+HTML_HEAD:     --zenburn-fg-plus-1: #f5f5d6;
239
+#+HTML_HEAD:     --zenburn-fg: #dcdccc;
240
+#+HTML_HEAD:     --zenburn-bg: #3f3f3f;
241
+#+HTML_HEAD:     --zenburn-bg-plus-1: #4f4f4f;
242
+#+HTML_HEAD:     --zenburn-bg-plus-2: #5f5f5f;
243
+#+HTML_HEAD:     --zenburn-blue: #8cd0d3;
244
+#+HTML_HEAD: }
245
+#+HTML_HEAD: 
246
+#+HTML_HEAD: * {box-sizing: border-box;}
247
+#+HTML_HEAD: 
248
+#+HTML_HEAD: body {
249
+#+HTML_HEAD:     font-size: 1.2rem;
250
+#+HTML_HEAD:     width: 75rem;
251
+#+HTML_HEAD:     margin: 0 0 0 25rem;
252
+#+HTML_HEAD:     background: var(--zenburn-bg);
253
+#+HTML_HEAD:     color: var(--zenburn-fg);
254
+#+HTML_HEAD: }
255
+#+HTML_HEAD: 
256
+#+HTML_HEAD: a {color: var(--zenburn-blue);}
257
+#+HTML_HEAD: 
258
+#+HTML_HEAD: h1, h2, h3, h4, h5, h6 {margin: 0;}
259
+#+HTML_HEAD: 
260
+#+HTML_HEAD: pre {margin: 0; box-shadow: none; border-width: 0.5em;}
261
+#+HTML_HEAD: 
262
+#+HTML_HEAD: pre.example {
263
+#+HTML_HEAD:     background-color: var(--zenburn-bg-plus-2);
264
+#+HTML_HEAD:     color: var(--zenburn-fg-plus-2);
265
+#+HTML_HEAD:     border: none;
266
+#+HTML_HEAD:     padding-left: 4em;
267
+#+HTML_HEAD: }
268
+#+HTML_HEAD: 
269
+#+HTML_HEAD: pre.src {
270
+#+HTML_HEAD:     background-color: var(--zenburn-bg-plus-1);
271
+#+HTML_HEAD:     border-color: var(--zenburn-bg-plus-2);
272
+#+HTML_HEAD:     color: var(--zenburn-fg-plus-1);
273
+#+HTML_HEAD: }
274
+#+HTML_HEAD: 
275
+#+HTML_HEAD: pre.src::before {
276
+#+HTML_HEAD:     background-color: var(--zenburn-bg-plus-1);
277
+#+HTML_HEAD:     border-color: var(--zenburn-bg-plus-2);
278
+#+HTML_HEAD:     color: var(--zenburn-fg-plus-1);
279
+#+HTML_HEAD: }
280
+#+HTML_HEAD: </style>
281
+
282
+# Local Variables:
283
+# after-save-hook: (lambda nil (when (org-html-export-to-html) (rename-file "README.html" "docs/index.html" t)))
284
+# End:
0 285
new file mode 100644
... ...
@@ -0,0 +1,623 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
5
+<head>
6
+<!-- 2019-08-28 Wed 23:22 -->
7
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
8
+<meta name="viewport" content="width=device-width, initial-scale=1" />
9
+<title>TODO backend implementation using CL and fukamachi/ningle</title>
10
+<meta name="generator" content="Org mode" />
11
+<meta name="author" content="Langley" />
12
+<style type="text/css">
13
+ <!--/*--><![CDATA[/*><!--*/
14
+  .title  { text-align: center;
15
+             margin-bottom: .2em; }
16
+  .subtitle { text-align: center;
17
+              font-size: medium;
18
+              font-weight: bold;
19
+              margin-top:0; }
20
+  .todo   { font-family: monospace; color: red; }
21
+  .done   { font-family: monospace; color: green; }
22
+  .priority { font-family: monospace; color: orange; }
23
+  .tag    { background-color: #eee; font-family: monospace;
24
+            padding: 2px; font-size: 80%; font-weight: normal; }
25
+  .timestamp { color: #bebebe; }
26
+  .timestamp-kwd { color: #5f9ea0; }
27
+  .org-right  { margin-left: auto; margin-right: 0px;  text-align: right; }
28
+  .org-left   { margin-left: 0px;  margin-right: auto; text-align: left; }
29
+  .org-center { margin-left: auto; margin-right: auto; text-align: center; }
30
+  .underline { text-decoration: underline; }
31
+  #postamble p, #preamble p { font-size: 90%; margin: .2em; }
32
+  p.verse { margin-left: 3%; }
33
+  pre {
34
+    border: 1px solid #ccc;
35
+    box-shadow: 3px 3px 3px #eee;
36
+    padding: 8pt;
37
+    font-family: monospace;
38
+    overflow: auto;
39
+    margin: 1.2em;
40
+  }
41
+  pre.src {
42
+    position: relative;
43
+    overflow: visible;
44
+    padding-top: 1.2em;
45
+  }
46
+  pre.src:before {
47
+    display: none;
48
+    position: absolute;
49
+    background-color: white;
50
+    top: -10px;
51
+    right: 10px;
52
+    padding: 3px;
53
+    border: 1px solid black;
54
+  }
55
+  pre.src:hover:before { display: inline;}
56
+  /* Languages per Org manual */
57
+  pre.src-asymptote:before { content: 'Asymptote'; }
58
+  pre.src-awk:before { content: 'Awk'; }
59
+  pre.src-C:before { content: 'C'; }
60
+  /* pre.src-C++ doesn't work in CSS */
61
+  pre.src-clojure:before { content: 'Clojure'; }
62
+  pre.src-css:before { content: 'CSS'; }
63
+  pre.src-D:before { content: 'D'; }
64
+  pre.src-ditaa:before { content: 'ditaa'; }
65
+  pre.src-dot:before { content: 'Graphviz'; }
66
+  pre.src-calc:before { content: 'Emacs Calc'; }
67
+  pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
68
+  pre.src-fortran:before { content: 'Fortran'; }
69
+  pre.src-gnuplot:before { content: 'gnuplot'; }
70
+  pre.src-haskell:before { content: 'Haskell'; }
71
+  pre.src-hledger:before { content: 'hledger'; }
72
+  pre.src-java:before { content: 'Java'; }
73
+  pre.src-js:before { content: 'Javascript'; }
74
+  pre.src-latex:before { content: 'LaTeX'; }
75
+  pre.src-ledger:before { content: 'Ledger'; }
76
+  pre.src-lisp:before { content: 'Lisp'; }
77
+  pre.src-lilypond:before { content: 'Lilypond'; }
78
+  pre.src-lua:before { content: 'Lua'; }
79
+  pre.src-matlab:before { content: 'MATLAB'; }
80
+  pre.src-mscgen:before { content: 'Mscgen'; }
81
+  pre.src-ocaml:before { content: 'Objective Caml'; }
82
+  pre.src-octave:before { content: 'Octave'; }
83
+  pre.src-org:before { content: 'Org mode'; }
84
+  pre.src-oz:before { content: 'OZ'; }
85
+  pre.src-plantuml:before { content: 'Plantuml'; }
86
+  pre.src-processing:before { content: 'Processing.js'; }
87
+  pre.src-python:before { content: 'Python'; }
88
+  pre.src-R:before { content: 'R'; }
89
+  pre.src-ruby:before { content: 'Ruby'; }
90
+  pre.src-sass:before { content: 'Sass'; }
91
+  pre.src-scheme:before { content: 'Scheme'; }
92
+  pre.src-screen:before { content: 'Gnu Screen'; }
93
+  pre.src-sed:before { content: 'Sed'; }
94
+  pre.src-sh:before { content: 'shell'; }
95
+  pre.src-sql:before { content: 'SQL'; }
96
+  pre.src-sqlite:before { content: 'SQLite'; }
97
+  /* additional languages in org.el's org-babel-load-languages alist */
98
+  pre.src-forth:before { content: 'Forth'; }
99
+  pre.src-io:before { content: 'IO'; }
100
+  pre.src-J:before { content: 'J'; }
101
+  pre.src-makefile:before { content: 'Makefile'; }
102
+  pre.src-maxima:before { content: 'Maxima'; }
103
+  pre.src-perl:before { content: 'Perl'; }
104
+  pre.src-picolisp:before { content: 'Pico Lisp'; }
105
+  pre.src-scala:before { content: 'Scala'; }
106
+  pre.src-shell:before { content: 'Shell Script'; }
107
+  pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
108
+  /* additional language identifiers per "defun org-babel-execute"
109
+       in ob-*.el */
110
+  pre.src-cpp:before  { content: 'C++'; }
111
+  pre.src-abc:before  { content: 'ABC'; }
112
+  pre.src-coq:before  { content: 'Coq'; }
113
+  pre.src-groovy:before  { content: 'Groovy'; }
114
+  /* additional language identifiers from org-babel-shell-names in
115
+     ob-shell.el: ob-shell is the only babel language using a lambda to put
116
+     the execution function name together. */
117
+  pre.src-bash:before  { content: 'bash'; }
118
+  pre.src-csh:before  { content: 'csh'; }
119
+  pre.src-ash:before  { content: 'ash'; }
120
+  pre.src-dash:before  { content: 'dash'; }
121
+  pre.src-ksh:before  { content: 'ksh'; }
122
+  pre.src-mksh:before  { content: 'mksh'; }
123
+  pre.src-posh:before  { content: 'posh'; }
124
+  /* Additional Emacs modes also supported by the LaTeX listings package */
125
+  pre.src-ada:before { content: 'Ada'; }
126
+  pre.src-asm:before { content: 'Assembler'; }
127
+  pre.src-caml:before { content: 'Caml'; }
128
+  pre.src-delphi:before { content: 'Delphi'; }
129
+  pre.src-html:before { content: 'HTML'; }
130
+  pre.src-idl:before { content: 'IDL'; }
131
+  pre.src-mercury:before { content: 'Mercury'; }
132
+  pre.src-metapost:before { content: 'MetaPost'; }
133
+  pre.src-modula-2:before { content: 'Modula-2'; }
134
+  pre.src-pascal:before { content: 'Pascal'; }
135
+  pre.src-ps:before { content: 'PostScript'; }
136
+  pre.src-prolog:before { content: 'Prolog'; }
137
+  pre.src-simula:before { content: 'Simula'; }
138
+  pre.src-tcl:before { content: 'tcl'; }
139
+  pre.src-tex:before { content: 'TeX'; }
140
+  pre.src-plain-tex:before { content: 'Plain TeX'; }
141
+  pre.src-verilog:before { content: 'Verilog'; }
142
+  pre.src-vhdl:before { content: 'VHDL'; }
143
+  pre.src-xml:before { content: 'XML'; }
144
+  pre.src-nxml:before { content: 'XML'; }
145
+  /* add a generic configuration mode; LaTeX export needs an additional
146
+     (add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
147
+  pre.src-conf:before { content: 'Configuration File'; }
148
+
149
+  table { border-collapse:collapse; }
150
+  caption.t-above { caption-side: top; }
151
+  caption.t-bottom { caption-side: bottom; }
152
+  td, th { vertical-align:top;  }
153
+  th.org-right  { text-align: center;  }
154
+  th.org-left   { text-align: center;   }
155
+  th.org-center { text-align: center; }
156
+  td.org-right  { text-align: right;  }
157
+  td.org-left   { text-align: left;   }
158
+  td.org-center { text-align: center; }
159
+  dt { font-weight: bold; }
160
+  .footpara { display: inline; }
161
+  .footdef  { margin-bottom: 1em; }
162
+  .figure { padding: 1em; }
163
+  .figure p { text-align: center; }
164
+  .equation-container {
165
+    display: table;
166
+    text-align: center;
167
+    width: 100%;
168
+  }
169
+  .equation {
170
+    vertical-align: middle;
171
+  }
172
+  .equation-label {
173
+    display: table-cell;
174
+    text-align: right;
175
+    vertical-align: middle;
176
+  }
177
+  .inlinetask {
178
+    padding: 10px;
179
+    border: 2px solid gray;
180
+    margin: 10px;
181
+    background: #ffffcc;
182
+  }
183
+  #org-div-home-and-up
184
+   { text-align: right; font-size: 70%; white-space: nowrap; }
185
+  textarea { overflow-x: auto; }
186
+  .linenr { font-size: smaller }
187
+  .code-highlighted { background-color: #ffff00; }
188
+  .org-info-js_info-navigation { border-style: none; }
189
+  #org-info-js_console-label
190
+    { font-size: 10px; font-weight: bold; white-space: nowrap; }
191
+  .org-info-js_search-highlight
192
+    { background-color: #ffff00; color: #000000; font-weight: bold; }
193
+  .org-svg { width: 90%; }
194
+  /*]]>*/-->
195
+</style>
196
+<style>
197
+:root {
198
+--zenburn-fg-plus-2: #ffffef;
199
+--zenburn-fg-plus-1: #f5f5d6;
200
+--zenburn-fg: #dcdccc;
201
+--zenburn-bg: #3f3f3f;
202
+--zenburn-bg-plus-1: #4f4f4f;
203
+--zenburn-bg-plus-2: #5f5f5f;
204
+--zenburn-blue: #8cd0d3;
205
+}
206
+* {box-sizing: border-box;}
207
+body {
208
+font-size: 1.2rem;
209
+width: 75rem;
210
+margin: 0 0 0 25rem;
211
+background: var(--zenburn-bg);
212
+color: var(--zenburn-fg);
213
+}
214
+a {color: var(--zenburn-blue);}
215
+h1, h2, h3, h4, h5, h6 {margin: 0;}
216
+pre {margin: 0; box-shadow: none; border-width: 0.5em;}
217
+pre.example {
218
+background-color: var(--zenburn-bg-plus-2);
219
+color: var(--zenburn-fg-plus-2);
220
+border: none;
221
+padding-left: 4em;
222
+}
223
+pre.src {
224
+background-color: var(--zenburn-bg-plus-1);
225
+border-color: var(--zenburn-bg-plus-2);
226
+color: var(--zenburn-fg-plus-1);
227
+}
228
+pre.src::before {
229
+background-color: var(--zenburn-bg-plus-1);
230
+border-color: var(--zenburn-bg-plus-2);
231
+color: var(--zenburn-fg-plus-1);
232
+}
233
+</style>
234
+<script type="text/javascript">
235
+/*
236
+@licstart  The following is the entire license notice for the
237
+JavaScript code in this tag.
238
+
239
+Copyright (C) 2012-2019 Free Software Foundation, Inc.
240
+
241
+The JavaScript code in this tag is free software: you can
242
+redistribute it and/or modify it under the terms of the GNU
243
+General Public License (GNU GPL) as published by the Free Software
244
+Foundation, either version 3 of the License, or (at your option)
245
+any later version.  The code is distributed WITHOUT ANY WARRANTY;
246
+without even the implied warranty of MERCHANTABILITY or FITNESS
247
+FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
248
+
249
+As additional permission under GNU GPL version 3 section 7, you
250
+may distribute non-source (e.g., minimized or compacted) forms of
251
+that code without the copy of the GNU GPL normally required by
252
+section 4, provided you include this license notice and a URL
253
+through which recipients can access the Corresponding Source.
254
+
255
+
256
+@licend  The above is the entire license notice
257
+for the JavaScript code in this tag.
258
+*/
259
+<!--/*--><![CDATA[/*><!--*/
260
+ function CodeHighlightOn(elem, id)
261
+ {
262
+   var target = document.getElementById(id);
263
+   if(null != target) {
264
+     elem.cacheClassElem = elem.className;
265
+     elem.cacheClassTarget = target.className;
266
+     target.className = "code-highlighted";
267
+     elem.className   = "code-highlighted";
268
+   }
269
+ }
270
+ function CodeHighlightOff(elem, id)
271
+ {
272
+   var target = document.getElementById(id);
273
+   if(elem.cacheClassElem)
274
+     elem.className = elem.cacheClassElem;
275
+   if(elem.cacheClassTarget)
276
+     target.className = elem.cacheClassTarget;
277
+ }
278
+/*]]>*///-->
279
+</script>
280
+</head>
281
+<body>
282
+<div id="content">
283
+<h1 class="title">TODO backend implementation using CL and fukamachi/ningle</h1>
284
+<div id="table-of-contents">
285
+<h2>Table of Contents</h2>
286
+<div id="text-table-of-contents">
287
+<ul>
288
+<li><a href="#org22e38cd">1. todo API</a>
289
+<ul>
290
+<li><a href="#org30c5989">1.1. List-level APIS</a></li>
291
+<li><a href="#org676c15a">1.2. Getting/Replacing a todo</a></li>
292
+<li><a href="#orga9d0c79">1.3. Adding and modifying todos</a></li>
293
+<li><a href="#org245bf6b">1.4. Examples</a></li>
294
+</ul>
295
+</li>
296
+<li><a href="#org2e8a2fb">2. Source</a>
297
+<ul>
298
+<li><a href="#orgd099240">2.1. model.lisp source code</a></li>
299
+<li><a href="#org2e445bb">2.2. routing.lisp source</a></li>
300
+<li><a href="#org4fedef0">2.3. main.lisp source</a></li>
301
+</ul>
302
+</li>
303
+</ul>
304
+</div>
305
+</div>
306
+
307
+<div id="outline-container-org22e38cd" class="outline-2">
308
+<h2 id="org22e38cd"><span class="section-number-2">1</span> todo API</h2>
309
+<div class="outline-text-2" id="text-1">
310
+<p>
311
+We use a fairly simple structure for our "database": a fset map (a
312
+clojure-inspired persistent data structure) and a handful of
313
+interface functions that wrap it. In this code, this fset map is
314
+referenced as <code>*todo*</code>, but this is a detail hidden behind the API.
315
+</p>
316
+</div>
317
+
318
+<div id="outline-container-org30c5989" class="outline-3">
319
+<h3 id="org30c5989"><span class="section-number-3">1.1</span> List-level APIS</h3>
320
+<div class="outline-text-3" id="text-1-1">
321
+<p>
322
+These are functions for getting the todo list and clearing it.
323
+</p>
324
+
325
+<div class="org-src-container">
326
+<pre class="src src-lisp" id="org6199316"><span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">todos</span> <span style="color: #93a8c6;">()</span>
327
+  <span style="color: #93a8c6;">(</span>gmap:gmap <span style="font-weight: bold;">:seq</span>
328
+             <span style="color: #b0b1a3;">(</span><span style="color: #F0DFAF;">lambda</span> <span style="color: #97b098;">(</span>_ b<span style="color: #97b098;">)</span>
329
+               <span style="color: #97b098;">(</span><span style="color: #F0DFAF;">declare</span> <span style="color: #aebed8;">(</span>ignore _<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
330
+               b<span style="color: #b0b1a3;">)</span>
331
+             <span style="color: #b0b1a3;">(</span><span style="font-weight: bold;">:map</span> *todos*<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
332
+
333
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">clear-todos</span> <span style="color: #93a8c6;">()</span>
334
+  <span style="color: #93a8c6;">(</span>setf *todos*
335
+        <span style="color: #b0b1a3;">(</span>fset:empty-map<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
336
+</pre>
337
+</div>
338
+</div>
339
+</div>
340
+
341
+<div id="outline-container-org676c15a" class="outline-3">
342
+<h3 id="org676c15a"><span class="section-number-3">1.2</span> Getting/Replacing a todo</h3>
343
+<div class="outline-text-3" id="text-1-2">
344
+<p>
345
+This uses lisp's <a href="http://www.lispworks.com/documentation/HyperSpec/Body/05_a.htm">generalized references</a> to abstract away the
346
+storage details of the todos. We also provide a <code>delete-todo</code>
347
+function for removing a todo from the list.
348
+</p>
349
+
350
+<div class="org-src-container">
351
+<pre class="src src-lisp" id="org1952c5b"><span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">todo</span> <span style="color: #93a8c6;">(</span>id<span style="color: #93a8c6;">)</span>
352
+  <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">let</span> <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span>todo <span style="color: #aebed8;">(</span>fset:@ *todos* id<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
353
+    todo<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
354
+
355
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #93a8c6;">(</span><span style="color: #8CD0D3;">setf todo</span><span style="color: #93a8c6;">)</span> <span style="color: #93a8c6;">(</span>new-value id<span style="color: #93a8c6;">)</span>
356
+  <span style="color: #93a8c6;">(</span>setf <span style="color: #b0b1a3;">(</span>fset:@ *todos* id<span style="color: #b0b1a3;">)</span>
357
+        new-value<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
358
+
359
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">delete-todo</span> <span style="color: #93a8c6;">(</span>id<span style="color: #93a8c6;">)</span>
360
+  <span style="color: #93a8c6;">(</span>setf *todos*
361
+        <span style="color: #b0b1a3;">(</span>fset:less *todos* id<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
362
+</pre>
363
+</div>
364
+</div>
365
+</div>
366
+
367
+<div id="outline-container-orga9d0c79" class="outline-3">
368
+<h3 id="orga9d0c79"><span class="section-number-3">1.3</span> Adding and modifying todos</h3>
369
+<div class="outline-text-3" id="text-1-3">
370
+<p>
371
+<code>new-todo</code> is fairly trivial. It's main feature is that it has to
372
+make sure the <code>completed</code> and <code>url</code> keys are set to the appropriate
373
+values. Completed isn't a lisp boolean, so it serializes to JSON
374
+properly.
375
+</p>
376
+
377
+<div class="org-src-container">
378
+<pre class="src src-lisp" id="org32926f6"><span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">new-todo</span> <span style="color: #93a8c6;">(</span>value<span style="color: #93a8c6;">)</span>
379
+  <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">let</span> <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span>id <span style="color: #aebed8;">(</span>next-id<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
380
+    <span style="color: #b0b1a3;">(</span>setf <span style="color: #97b098;">(</span>todo id<span style="color: #97b098;">)</span>
381
+          <span style="color: #97b098;">(</span>alexandria:alist-hash-table
382
+           <span style="color: #aebed8;">(</span>rutilsx.threading:-&gt;&gt;
383
+            value
384
+            <span style="color: #b0b0b3;">(</span>acons <span style="color: #D0BF8F;">"completed"</span> 'yason:false<span style="color: #b0b0b3;">)</span>
385
+            <span style="color: #b0b0b3;">(</span>acons <span style="color: #D0BF8F;">"url"</span> <span style="color: #90a890;">(</span>format nil <span style="color: #D0BF8F;">"http://localhost:5000/todo/~d"</span> id<span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span>
386
+           <span style="font-weight: bold;">:test</span> 'equal<span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
387
+</pre>
388
+</div>
389
+
390
+<p>
391
+<code>update-todo</code> just merges the input from the frontend into the
392
+relevant todo and then makes sure that the <code>completed</code> key is a
393
+yason-compatible boolean.
394
+</p>
395
+
396
+<div class="org-src-container">
397
+<pre class="src src-lisp" id="org81fabb9"><span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">update-todo</span> <span style="color: #93a8c6;">(</span>id v<span style="color: #93a8c6;">)</span>
398
+  <span style="color: #93a8c6;">(</span>setf <span style="color: #b0b1a3;">(</span>todo id<span style="color: #b0b1a3;">)</span>
399
+        <span style="color: #b0b1a3;">(</span>serapeum:merge-tables <span style="color: #97b098;">(</span>or <span style="color: #aebed8;">(</span>todo id<span style="color: #aebed8;">)</span>
400
+                                   <span style="color: #aebed8;">(</span>make-hash-table <span style="font-weight: bold;">:test</span> 'equal<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
401
+                               <span style="color: #97b098;">(</span>data-lens.lenses:over *completed-lens*
402
+                                                      'bool-to-yason
403
+                                                      <span style="color: #aebed8;">(</span>alexandria:alist-hash-table
404
+                                                       v
405
+                                                       <span style="font-weight: bold;">:test</span> 'equal<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
406
+</pre>
407
+</div>
408
+</div>
409
+</div>
410
+
411
+<div id="outline-container-org245bf6b" class="outline-3">
412
+<h3 id="org245bf6b"><span class="section-number-3">1.4</span> Examples</h3>
413
+<div class="outline-text-3" id="text-1-4">
414
+<div class="org-src-container">
415
+<pre class="src src-lisp"><span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">in-package</span> <span style="font-weight: bold;">:fwoar.todo</span><span style="color: #8c8c8c;">)</span>
416
+
417
+<span style="color: #8c8c8c;">(</span>load <span style="color: #D0BF8F;">"pprint-setup"</span><span style="color: #8c8c8c;">)</span>
418
+
419
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">with-fresh-todos</span> <span style="color: #93a8c6;">()</span>
420
+  <span style="color: #93a8c6;">(</span>new-todo '<span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span><span style="color: #D0BF8F;">"title"</span> . <span style="color: #D0BF8F;">"get groceries"</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span>
421
+  <span style="color: #93a8c6;">(</span>new-todo '<span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span><span style="color: #D0BF8F;">"title"</span> . <span style="color: #D0BF8F;">"write-better-documentation"</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span>
422
+  <span style="color: #93a8c6;">(</span>fset:convert 'list <span style="color: #b0b1a3;">(</span>todos<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
423
+</pre>
424
+</div>
425
+
426
+<pre class="example">
427
+(#&lt;hash-table "url": "http://localhost:5000/todo/15",
428
+              "title": "get groceries",
429
+              "completed": YASON:FALSE&gt;
430
+ #&lt;hash-table "url": "http://localhost:5000/todo/16",
431
+              "title": "write-better-documentation",
432
+              "completed": YASON:FALSE&gt;)
433
+</pre>
434
+</div>
435
+</div>
436
+</div>
437
+
438
+<div id="outline-container-org2e8a2fb" class="outline-2">
439
+<h2 id="org2e8a2fb"><span class="section-number-2">2</span> Source</h2>
440
+<div class="outline-text-2" id="text-2">
441
+</div>
442
+<div id="outline-container-orgd099240" class="outline-3">
443
+<h3 id="orgd099240"><span class="section-number-3">2.1</span> model.lisp source code</h3>
444
+<div class="outline-text-3" id="text-2-1">
445
+<div class="org-src-container">
446
+<pre class="src src-lisp"><span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">[[file:~/git_repos/lisp-sandbox/todo/README.org::package-include][package-include]]</span>
447
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">in-package</span> <span style="font-weight: bold;">:fwoar.todo</span><span style="color: #8c8c8c;">)</span>
448
+
449
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">package-include ends here</span>
450
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">[[file:~/git_repos/lisp-sandbox/todo/README.org::model-utils][model-utils]]</span>
451
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defparameter</span> <span style="color: #DC8CC3;">*cur-id*</span> 0<span style="color: #8c8c8c;">)</span>
452
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">next-id</span> <span style="color: #93a8c6;">()</span>
453
+  <span style="color: #93a8c6;">(</span>incf *cur-id*<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
454
+
455
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defparameter</span> <span style="color: #DC8CC3;">*completed-lens*</span>
456
+  <span style="color: #93a8c6;">(</span>data-lens.lenses:make-hash-table-lens <span style="color: #D0BF8F;">"completed"</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
457
+
458
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">bool-to-yason</span> <span style="color: #93a8c6;">(</span>bool<span style="color: #93a8c6;">)</span>
459
+  <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">if</span> bool
460
+      'yason:true
461
+      'yason:false<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
462
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">model-utils ends here</span>
463
+
464
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defvar</span> <span style="color: #DC8CC3;">*todos*</span> <span style="color: #93a8c6;">(</span>fset:empty-map<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
465
+
466
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">[[file:~/git_repos/lisp-sandbox/todo/README.org::todolist-manipulation][todolist-manipulation]]</span>
467
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">todos</span> <span style="color: #93a8c6;">()</span>
468
+  <span style="color: #93a8c6;">(</span>gmap:gmap <span style="font-weight: bold;">:seq</span>
469
+             <span style="color: #b0b1a3;">(</span><span style="color: #F0DFAF;">lambda</span> <span style="color: #97b098;">(</span>_ b<span style="color: #97b098;">)</span>
470
+               <span style="color: #97b098;">(</span><span style="color: #F0DFAF;">declare</span> <span style="color: #aebed8;">(</span>ignore _<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
471
+               b<span style="color: #b0b1a3;">)</span>
472
+             <span style="color: #b0b1a3;">(</span><span style="font-weight: bold;">:map</span> *todos*<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
473
+
474
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">clear-todos</span> <span style="color: #93a8c6;">()</span>
475
+  <span style="color: #93a8c6;">(</span>setf *todos*
476
+        <span style="color: #b0b1a3;">(</span>fset:empty-map<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
477
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">todolist-manipulation ends here</span>
478
+
479
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">[[file:~/git_repos/lisp-sandbox/todo/README.org::todo-accessor][todo-accessor]]</span>
480
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">todo</span> <span style="color: #93a8c6;">(</span>id<span style="color: #93a8c6;">)</span>
481
+  <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">let</span> <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span>todo <span style="color: #aebed8;">(</span>fset:@ *todos* id<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
482
+    todo<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
483
+
484
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #93a8c6;">(</span><span style="color: #8CD0D3;">setf todo</span><span style="color: #93a8c6;">)</span> <span style="color: #93a8c6;">(</span>new-value id<span style="color: #93a8c6;">)</span>
485
+  <span style="color: #93a8c6;">(</span>setf <span style="color: #b0b1a3;">(</span>fset:@ *todos* id<span style="color: #b0b1a3;">)</span>
486
+        new-value<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
487
+
488
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">delete-todo</span> <span style="color: #93a8c6;">(</span>id<span style="color: #93a8c6;">)</span>
489
+  <span style="color: #93a8c6;">(</span>setf *todos*
490
+        <span style="color: #b0b1a3;">(</span>fset:less *todos* id<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
491
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">todo-accessor ends here</span>
492
+
493
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">[[file:~/git_repos/lisp-sandbox/todo/README.org::new-todo][new-todo]]</span>
494
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">new-todo</span> <span style="color: #93a8c6;">(</span>value<span style="color: #93a8c6;">)</span>
495
+  <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">let</span> <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span>id <span style="color: #aebed8;">(</span>next-id<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
496
+    <span style="color: #b0b1a3;">(</span>setf <span style="color: #97b098;">(</span>todo id<span style="color: #97b098;">)</span>
497
+          <span style="color: #97b098;">(</span>alexandria:alist-hash-table
498
+           <span style="color: #aebed8;">(</span>rutilsx.threading:-&gt;&gt;
499
+            value
500
+            <span style="color: #b0b0b3;">(</span>acons <span style="color: #D0BF8F;">"completed"</span> 'yason:false<span style="color: #b0b0b3;">)</span>
501
+            <span style="color: #b0b0b3;">(</span>acons <span style="color: #D0BF8F;">"url"</span> <span style="color: #90a890;">(</span>format nil <span style="color: #D0BF8F;">"http://localhost:5000/todo/~d"</span> id<span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span>
502
+           <span style="font-weight: bold;">:test</span> 'equal<span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
503
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">new-todo ends here</span>
504
+
505
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">[[file:~/git_repos/lisp-sandbox/todo/README.org::update-todo][update-todo]]</span>
506
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">update-todo</span> <span style="color: #93a8c6;">(</span>id v<span style="color: #93a8c6;">)</span>
507
+  <span style="color: #93a8c6;">(</span>setf <span style="color: #b0b1a3;">(</span>todo id<span style="color: #b0b1a3;">)</span>
508
+        <span style="color: #b0b1a3;">(</span>serapeum:merge-tables <span style="color: #97b098;">(</span>or <span style="color: #aebed8;">(</span>todo id<span style="color: #aebed8;">)</span>
509
+                                   <span style="color: #aebed8;">(</span>make-hash-table <span style="font-weight: bold;">:test</span> 'equal<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
510
+                               <span style="color: #97b098;">(</span>data-lens.lenses:over *completed-lens*
511
+                                                      'bool-to-yason
512
+                                                      <span style="color: #aebed8;">(</span>alexandria:alist-hash-table
513
+                                                       v
514
+                                                       <span style="font-weight: bold;">:test</span> 'equal<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
515
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">update-todo ends here</span>
516
+
517
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defmacro</span> <span style="color: #8CD0D3;">with-fresh-todos</span> <span style="color: #93a8c6;">(</span><span style="color: #b0b1a3;">()</span> <span style="color: #CC9393;">&amp;body</span> body<span style="color: #93a8c6;">)</span>
518
+  `<span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">let</span> <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span>*todos* <span style="color: #aebed8;">(</span>fset:empty-map<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
519
+     ,@body<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
520
+</pre>
521
+</div>
522
+</div>
523
+</div>
524
+
525
+<div id="outline-container-org2e445bb" class="outline-3">
526
+<h3 id="org2e445bb"><span class="section-number-3">2.2</span> routing.lisp source</h3>
527
+<div class="outline-text-3" id="text-2-2">
528
+<div class="org-src-container">
529
+<pre class="src src-lisp"><span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">in-package</span> <span style="font-weight: bold;">:fwoar.todo</span><span style="color: #8c8c8c;">)</span>
530
+
531
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defmacro</span> <span style="color: #8CD0D3;">defroutes</span> <span style="color: #93a8c6;">(</span>app <span style="color: #CC9393;">&amp;body</span> routes<span style="color: #93a8c6;">)</span>
532
+  <span style="color: #D0BF8F;">"Define a set of routes for given paths. the ROUTES parameter expects this format:</span>
533
+<span style="color: #D0BF8F;">   ((\"/path/to/{route}\" :method :POST) route-callback) the AS-ROUTE macro helps one</span>
534
+<span style="color: #D0BF8F;">   avoid binding function values to the route for flexibility."</span>
535
+  <span style="color: #93a8c6;">(</span>alexandria:once-only <span style="color: #b0b1a3;">(</span>app<span style="color: #b0b1a3;">)</span>
536
+    `<span style="color: #b0b1a3;">(</span><span style="color: #F0DFAF;">progn</span>
537
+       ,@<span style="color: #97b098;">(</span><span style="color: #F0DFAF;">loop</span> for <span style="color: #aebed8;">(</span><span style="color: #b0b0b3;">(</span>target <span style="color: #CC9393;">&amp;key</span> method<span style="color: #b0b0b3;">)</span> callback<span style="color: #aebed8;">)</span> in routes
538
+               collect `<span style="color: #aebed8;">(</span>setf <span style="color: #b0b0b3;">(</span>ningle:route ,app ,target <span style="font-weight: bold;">:method</span> ,<span style="color: #90a890;">(</span>or method <span style="font-weight: bold;">:GET</span><span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span> ,callback<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
539
+
540
+
541
+<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">routing</span>
542
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">success</span> <span style="color: #93a8c6;">(</span>value<span style="color: #93a8c6;">)</span>
543
+  <span style="color: #93a8c6;">(</span>list 200 nil value<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
544
+
545
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defmacro</span> <span style="color: #8CD0D3;">handler</span> <span style="color: #93a8c6;">(</span><span style="color: #b0b1a3;">(</span><span style="color: #CC9393;">&amp;optional</span> <span style="color: #97b098;">(</span>sym <span style="color: #aebed8;">(</span>gensym <span style="color: #D0BF8F;">"PARAMS"</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span> <span style="color: #CC9393;">&amp;body</span> body<span style="color: #93a8c6;">)</span>
546
+  `<span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">lambda</span> <span style="color: #b0b1a3;">(</span>,sym<span style="color: #b0b1a3;">)</span>
547
+     <span style="color: #b0b1a3;">(</span><span style="color: #F0DFAF;">declare</span> <span style="color: #97b098;">(</span>ignorable ,sym<span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
548
+     <span style="color: #b0b1a3;">(</span>success
549
+      <span style="color: #97b098;">(</span>fwoar.lack.json.middleware:wrap-result
550
+       <span style="color: #aebed8;">(</span><span style="color: #F0DFAF;">progn</span> ,@body<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
551
+
552
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">get-id</span> <span style="color: #93a8c6;">(</span>params<span style="color: #93a8c6;">)</span>
553
+  <span style="color: #93a8c6;">(</span>parse-integer <span style="color: #b0b1a3;">(</span>serapeum:assocdr <span style="font-weight: bold;">:id</span> params<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
554
+
555
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">setup-routes</span> <span style="color: #93a8c6;">(</span>app<span style="color: #93a8c6;">)</span>
556
+  <span style="color: #93a8c6;">(</span>defroutes app
557
+    <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span><span style="color: #D0BF8F;">"/"</span> <span style="font-weight: bold;">:method</span> <span style="font-weight: bold;">:GET</span><span style="color: #97b098;">)</span>            <span style="color: #97b098;">(</span>handler <span style="color: #aebed8;">()</span> <span style="color: #aebed8;">(</span>todos<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
558
+    <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span><span style="color: #D0BF8F;">"/"</span> <span style="font-weight: bold;">:method</span> <span style="font-weight: bold;">:POST</span><span style="color: #97b098;">)</span>           <span style="color: #97b098;">(</span>handler <span style="color: #aebed8;">(</span>v<span style="color: #aebed8;">)</span> <span style="color: #aebed8;">(</span>new-todo v<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
559
+    <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span><span style="color: #D0BF8F;">"/"</span> <span style="font-weight: bold;">:method</span> <span style="font-weight: bold;">:DELETE</span><span style="color: #97b098;">)</span>         <span style="color: #97b098;">(</span>handler <span style="color: #aebed8;">()</span> <span style="color: #aebed8;">(</span>clear-todos<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
560
+    <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span><span style="color: #D0BF8F;">"/todo/:id"</span> <span style="font-weight: bold;">:method</span> <span style="font-weight: bold;">:GET</span><span style="color: #97b098;">)</span>    <span style="color: #97b098;">(</span>handler <span style="color: #aebed8;">(</span>v<span style="color: #aebed8;">)</span> <span style="color: #aebed8;">(</span>todo <span style="color: #b0b0b3;">(</span>get-id v<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
561
+    <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span><span style="color: #D0BF8F;">"/todo/:id"</span> <span style="font-weight: bold;">:method</span> <span style="font-weight: bold;">:DELETE</span><span style="color: #97b098;">)</span> <span style="color: #97b098;">(</span>handler <span style="color: #aebed8;">(</span>v<span style="color: #aebed8;">)</span>
562
+                                     <span style="color: #aebed8;">(</span>delete-todo <span style="color: #b0b0b3;">(</span>get-id v<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span>
563
+                                     nil<span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
564
+    <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span><span style="color: #D0BF8F;">"/todo/:id"</span> <span style="font-weight: bold;">:method</span> <span style="font-weight: bold;">:PATCH</span><span style="color: #97b098;">)</span>  <span style="color: #97b098;">(</span>handler <span style="color: #aebed8;">(</span>v<span style="color: #aebed8;">)</span>
565
+                                     <span style="color: #aebed8;">(</span>update-todo <span style="color: #b0b0b3;">(</span>get-id v<span style="color: #b0b0b3;">)</span> 
566
+                                                  <span style="color: #b0b0b3;">(</span>remove <span style="font-weight: bold;">:id</span> v <span style="font-weight: bold;">:key</span> #'car<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
567
+</pre>
568
+</div>
569
+</div>
570
+</div>
571
+
572
+<div id="outline-container-org4fedef0" class="outline-3">
573
+<h3 id="org4fedef0"><span class="section-number-3">2.3</span> main.lisp source</h3>
574
+<div class="outline-text-3" id="text-2-3">
575
+<div class="org-src-container">
576
+<pre class="src src-lisp"><span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">in-package</span> <span style="font-weight: bold;">:fwoar.todo</span><span style="color: #8c8c8c;">)</span>
577
+
578
+<span style="color: #A6A689; background-color: #2B2B2B;">;;; </span><span style="color: #A6A689; background-color: #2B2B2B;">entrypoint</span>
579
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">setup</span> <span style="color: #93a8c6;">()</span>
580
+  <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">let</span> <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span>app <span style="color: #aebed8;">(</span>make-instance 'ningle:&lt;app&gt;<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
581
+    <span style="color: #b0b1a3;">(</span><span style="color: #F0DFAF;">prog1</span> app <span style="color: #97b098;">(</span>setup-routes app<span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
582
+
583
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defvar</span> <span style="color: #DC8CC3;">*handler*</span><span style="color: #8c8c8c;">)</span>
584
+
585
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">is-running</span> <span style="color: #93a8c6;">()</span>
586
+  <span style="color: #93a8c6;">(</span>and <span style="color: #b0b1a3;">(</span>boundp '*handler*<span style="color: #b0b1a3;">)</span>
587
+       *handler*<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
588
+
589
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">ensure-started</span> <span style="color: #93a8c6;">(</span><span style="color: #CC9393;">&amp;rest</span> r <span style="color: #CC9393;">&amp;key</span> port<span style="color: #93a8c6;">)</span>
590
+  <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">declare</span> <span style="color: #b0b1a3;">(</span>ignore port<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span>
591
+  <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">let</span> <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span>app <span style="color: #aebed8;">(</span>setup<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
592
+    <span style="color: #b0b1a3;">(</span>values app
593
+            <span style="color: #97b098;">(</span>setf *handler*
594
+                  <span style="color: #aebed8;">(</span><span style="color: #F0DFAF;">if</span> <span style="color: #b0b0b3;">(</span>not <span style="color: #90a890;">(</span>is-running<span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span>
595
+                      <span style="color: #b0b0b3;">(</span>apply 'clack:clackup
596
+                             <span style="color: #90a890;">(</span>lack.builder:builder
597
+                              <span style="font-weight: bold;">:accesslog</span>
598
+                              'fwoar.lack.cors.middleware:cors-middleware
599
+                              'fwoar.lack.json.middleware:json-middleware
600
+                              app<span style="color: #90a890;">)</span>
601
+                             r<span style="color: #b0b0b3;">)</span>
602
+                      *handler*<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
603
+
604
+<span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">stop</span> <span style="color: #93a8c6;">()</span>
605
+  <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">if</span> <span style="color: #b0b1a3;">(</span>is-running<span style="color: #b0b1a3;">)</span>
606
+      <span style="color: #b0b1a3;">(</span><span style="color: #F0DFAF;">progn</span>
607
+        <span style="color: #97b098;">(</span>clack:stop *handler*<span style="color: #97b098;">)</span>
608
+        <span style="color: #97b098;">(</span>makunbound '*handler*<span style="color: #97b098;">)</span>
609
+        nil<span style="color: #b0b1a3;">)</span>
610
+      nil<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
611
+</pre>
612
+</div>
613
+</div>
614
+</div>
615
+</div>
616
+</div>
617
+<div id="postamble" class="status">
618
+<p class="author">Author: Langley</p>
619
+<p class="date">Created: 2019-08-28 Wed 23:22</p>
620
+<p class="validation"><a href="http://validator.w3.org/check?uri=referer">Validate</a></p>
621
+</div>
622
+</body>
623
+</html>
... ...
@@ -1,8 +1,25 @@
1
+;; [[file:~/git_repos/lisp-sandbox/todo/README.org::*model.lisp%20source%20code][model.lisp source code:1]]
2
+;; [[file:~/git_repos/lisp-sandbox/todo/README.org::package-include][package-include]]
1 3
 (in-package :fwoar.todo)
2 4
 
3
-;; todo "database" api
5
+;; package-include ends here
6
+;; [[file:~/git_repos/lisp-sandbox/todo/README.org::model-utils][model-utils]]
7
+(defparameter *cur-id* 0)
8
+(defun next-id ()
9
+  (incf *cur-id*))
10
+
11
+(defparameter *completed-lens*
12
+  (data-lens.lenses:make-hash-table-lens "completed"))
13
+
14
+(defun bool-to-yason (bool)
15
+  (if bool
16
+      'yason:true
17
+      'yason:false))
18
+;; model-utils ends here
19
+
4 20
 (defvar *todos* (fset:empty-map))
5 21
 
22
+;; [[file:~/git_repos/lisp-sandbox/todo/README.org::todolist-manipulation][todolist-manipulation]]
6 23
 (defun todos ()
7 24
   (gmap:gmap :seq
8 25
              (lambda (_ b)
... ...
@@ -10,23 +27,26 @@
10 27
                b)
11 28
              (:map *todos*)))
12 29
 
30
+(defun clear-todos ()
31
+  (setf *todos*
32
+        (fset:empty-map)))
33
+;; todolist-manipulation ends here
34
+
35
+;; [[file:~/git_repos/lisp-sandbox/todo/README.org::todo-accessor][todo-accessor]]
13 36
 (defun todo (id)
14 37
   (let ((todo (fset:@ *todos* id)))
15 38
     todo))
16 39
 
17 40
 (defun (setf todo) (new-value id)
18
-  (setf *todos*
19
-        (fset:with *todos* id new-value))
20
-  new-value)
41
+  (setf (fset:@ *todos* id)
42
+        new-value))
21 43
 
22 44
 (defun delete-todo (id)
23 45
   (setf *todos*
24 46
         (fset:less *todos* id)))
47
+;; todo-accessor ends here
25 48
 
26
-(defparameter *cur-id* 0)
27
-(defun next-id ()
28
-  (incf *cur-id*))
29
-
49
+;; [[file:~/git_repos/lisp-sandbox/todo/README.org::new-todo][new-todo]]
30 50
 (defun new-todo (value)
31 51
   (let ((id (next-id)))
32 52
     (setf (todo id)
... ...
@@ -36,20 +56,9 @@
36 56
             (acons "completed" 'yason:false)
37 57
             (acons "url" (format nil "http://localhost:5000/todo/~d" id)))
38 58
            :test 'equal))))
59
+;; new-todo ends here
39 60
 
40
-(defun clear-todos ()
41
-  (setf *todos*
42
-        (fset:empty-map)))
43
-
44
-
45
-(defparameter *completed-lens*
46
-  (data-lens.lenses:make-hash-table-lens "completed"))
47
-
48
-(defun bool-to-yason (bool)
49
-  (if bool
50
-      'yason:true
51
-      'yason:false))
52
-
61
+;; [[file:~/git_repos/lisp-sandbox/todo/README.org::update-todo][update-todo]]
53 62
 (defun update-todo (id v)
54 63
   (setf (todo id)
55 64
         (serapeum:merge-tables (or (todo id)
... ...
@@ -59,3 +68,9 @@
59 68
                                                       (alexandria:alist-hash-table
60 69
                                                        v
61 70
                                                        :test 'equal)))))
71
+;; update-todo ends here
72
+
73
+(defmacro with-fresh-todos (() &body body)
74
+  `(let ((*todos* (fset:empty-map)))
75
+     ,@body))
76
+;; model.lisp source code:1 ends here
62 77
new file mode 100644
... ...
@@ -0,0 +1,21 @@
1
+(in-package :cl-user)
2
+(defparameter *old-pprint-dispatch* *print-pprint-dispatch*)
3
+(setf *print-pprint-dispatch*
4
+      (copy-pprint-dispatch *old-pprint-dispatch*))
5
+
6
+(defun pprint-hashtable (s hash-table)
7
+  (pprint-logical-block (s nil)
8
+    (princ "#<hash-table "  s)
9
+    (let ((v (fset:convert 'list (fset:convert 'fset:map hash-table))))
10
+      (when v
11
+        (pprint-logical-block (s v)
12
+          (pprint-indent :block 0 s)
13
+          (loop do
14
+            (destructuring-bind (key . value) (pprint-pop)
15
+              (format s "~s: ~s" key value)
16
+              (pprint-exit-if-list-exhausted)
17
+              (princ ", " s)
18
+              (pprint-newline :mandatory s))))))
19
+    (princ #\> s)))
20
+
21
+(set-pprint-dispatch 'hash-table 'pprint-hashtable)