git.fiddlerwoaroof.com
docs/index.html
4c3588fc
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
 <head>
55a11d3d
 <!-- 2019-09-25 Wed 21:02 -->
4c3588fc
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1" />
 <title>TODO backend implementation using CL and fukamachi/ningle</title>
 <meta name="generator" content="Org mode" />
 <meta name="author" content="Langley" />
 <style type="text/css">
  <!--/*--><![CDATA[/*><!--*/
   .title  { text-align: center;
              margin-bottom: .2em; }
   .subtitle { text-align: center;
               font-size: medium;
               font-weight: bold;
               margin-top:0; }
   .todo   { font-family: monospace; color: red; }
   .done   { font-family: monospace; color: green; }
   .priority { font-family: monospace; color: orange; }
   .tag    { background-color: #eee; font-family: monospace;
             padding: 2px; font-size: 80%; font-weight: normal; }
   .timestamp { color: #bebebe; }
   .timestamp-kwd { color: #5f9ea0; }
   .org-right  { margin-left: auto; margin-right: 0px;  text-align: right; }
   .org-left   { margin-left: 0px;  margin-right: auto; text-align: left; }
   .org-center { margin-left: auto; margin-right: auto; text-align: center; }
   .underline { text-decoration: underline; }
   #postamble p, #preamble p { font-size: 90%; margin: .2em; }
   p.verse { margin-left: 3%; }
   pre {
     border: 1px solid #ccc;
     box-shadow: 3px 3px 3px #eee;
     padding: 8pt;
     font-family: monospace;
     overflow: auto;
     margin: 1.2em;
   }
   pre.src {
     position: relative;
     overflow: visible;
     padding-top: 1.2em;
   }
   pre.src:before {
     display: none;
     position: absolute;
     background-color: white;
     top: -10px;
     right: 10px;
     padding: 3px;
     border: 1px solid black;
   }
   pre.src:hover:before { display: inline;}
   /* Languages per Org manual */
   pre.src-asymptote:before { content: 'Asymptote'; }
   pre.src-awk:before { content: 'Awk'; }
   pre.src-C:before { content: 'C'; }
   /* pre.src-C++ doesn't work in CSS */
   pre.src-clojure:before { content: 'Clojure'; }
   pre.src-css:before { content: 'CSS'; }
   pre.src-D:before { content: 'D'; }
   pre.src-ditaa:before { content: 'ditaa'; }
   pre.src-dot:before { content: 'Graphviz'; }
   pre.src-calc:before { content: 'Emacs Calc'; }
   pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
   pre.src-fortran:before { content: 'Fortran'; }
   pre.src-gnuplot:before { content: 'gnuplot'; }
   pre.src-haskell:before { content: 'Haskell'; }
   pre.src-hledger:before { content: 'hledger'; }
   pre.src-java:before { content: 'Java'; }
   pre.src-js:before { content: 'Javascript'; }
   pre.src-latex:before { content: 'LaTeX'; }
   pre.src-ledger:before { content: 'Ledger'; }
   pre.src-lisp:before { content: 'Lisp'; }
   pre.src-lilypond:before { content: 'Lilypond'; }
   pre.src-lua:before { content: 'Lua'; }
   pre.src-matlab:before { content: 'MATLAB'; }
   pre.src-mscgen:before { content: 'Mscgen'; }
   pre.src-ocaml:before { content: 'Objective Caml'; }
   pre.src-octave:before { content: 'Octave'; }
   pre.src-org:before { content: 'Org mode'; }
   pre.src-oz:before { content: 'OZ'; }
   pre.src-plantuml:before { content: 'Plantuml'; }
   pre.src-processing:before { content: 'Processing.js'; }
   pre.src-python:before { content: 'Python'; }
   pre.src-R:before { content: 'R'; }
   pre.src-ruby:before { content: 'Ruby'; }
   pre.src-sass:before { content: 'Sass'; }
   pre.src-scheme:before { content: 'Scheme'; }
   pre.src-screen:before { content: 'Gnu Screen'; }
   pre.src-sed:before { content: 'Sed'; }
   pre.src-sh:before { content: 'shell'; }
   pre.src-sql:before { content: 'SQL'; }
   pre.src-sqlite:before { content: 'SQLite'; }
   /* additional languages in org.el's org-babel-load-languages alist */
   pre.src-forth:before { content: 'Forth'; }
   pre.src-io:before { content: 'IO'; }
   pre.src-J:before { content: 'J'; }
   pre.src-makefile:before { content: 'Makefile'; }
   pre.src-maxima:before { content: 'Maxima'; }
   pre.src-perl:before { content: 'Perl'; }
   pre.src-picolisp:before { content: 'Pico Lisp'; }
   pre.src-scala:before { content: 'Scala'; }
   pre.src-shell:before { content: 'Shell Script'; }
   pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
   /* additional language identifiers per "defun org-babel-execute"
        in ob-*.el */
   pre.src-cpp:before  { content: 'C++'; }
   pre.src-abc:before  { content: 'ABC'; }
   pre.src-coq:before  { content: 'Coq'; }
   pre.src-groovy:before  { content: 'Groovy'; }
   /* additional language identifiers from org-babel-shell-names in
      ob-shell.el: ob-shell is the only babel language using a lambda to put
      the execution function name together. */
   pre.src-bash:before  { content: 'bash'; }
   pre.src-csh:before  { content: 'csh'; }
   pre.src-ash:before  { content: 'ash'; }
   pre.src-dash:before  { content: 'dash'; }
   pre.src-ksh:before  { content: 'ksh'; }
   pre.src-mksh:before  { content: 'mksh'; }
   pre.src-posh:before  { content: 'posh'; }
   /* Additional Emacs modes also supported by the LaTeX listings package */
   pre.src-ada:before { content: 'Ada'; }
   pre.src-asm:before { content: 'Assembler'; }
   pre.src-caml:before { content: 'Caml'; }
   pre.src-delphi:before { content: 'Delphi'; }
   pre.src-html:before { content: 'HTML'; }
   pre.src-idl:before { content: 'IDL'; }
   pre.src-mercury:before { content: 'Mercury'; }
   pre.src-metapost:before { content: 'MetaPost'; }
   pre.src-modula-2:before { content: 'Modula-2'; }
   pre.src-pascal:before { content: 'Pascal'; }
   pre.src-ps:before { content: 'PostScript'; }
   pre.src-prolog:before { content: 'Prolog'; }
   pre.src-simula:before { content: 'Simula'; }
   pre.src-tcl:before { content: 'tcl'; }
   pre.src-tex:before { content: 'TeX'; }
   pre.src-plain-tex:before { content: 'Plain TeX'; }
   pre.src-verilog:before { content: 'Verilog'; }
   pre.src-vhdl:before { content: 'VHDL'; }
   pre.src-xml:before { content: 'XML'; }
   pre.src-nxml:before { content: 'XML'; }
   /* add a generic configuration mode; LaTeX export needs an additional
      (add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
   pre.src-conf:before { content: 'Configuration File'; }
 
   table { border-collapse:collapse; }
   caption.t-above { caption-side: top; }
   caption.t-bottom { caption-side: bottom; }
   td, th { vertical-align:top;  }
   th.org-right  { text-align: center;  }
   th.org-left   { text-align: center;   }
   th.org-center { text-align: center; }
   td.org-right  { text-align: right;  }
   td.org-left   { text-align: left;   }
   td.org-center { text-align: center; }
   dt { font-weight: bold; }
   .footpara { display: inline; }
   .footdef  { margin-bottom: 1em; }
   .figure { padding: 1em; }
   .figure p { text-align: center; }
   .equation-container {
     display: table;
     text-align: center;
     width: 100%;
   }
   .equation {
     vertical-align: middle;
   }
   .equation-label {
     display: table-cell;
     text-align: right;
     vertical-align: middle;
   }
   .inlinetask {
     padding: 10px;
     border: 2px solid gray;
     margin: 10px;
     background: #ffffcc;
   }
   #org-div-home-and-up
    { text-align: right; font-size: 70%; white-space: nowrap; }
   textarea { overflow-x: auto; }
   .linenr { font-size: smaller }
   .code-highlighted { background-color: #ffff00; }
   .org-info-js_info-navigation { border-style: none; }
   #org-info-js_console-label
     { font-size: 10px; font-weight: bold; white-space: nowrap; }
   .org-info-js_search-highlight
     { background-color: #ffff00; color: #000000; font-weight: bold; }
   .org-svg { width: 90%; }
   /*]]>*/-->
 </style>
 <style>
 :root {
 --zenburn-fg-plus-2: #ffffef;
 --zenburn-fg-plus-1: #f5f5d6;
 --zenburn-fg: #dcdccc;
 --zenburn-bg: #3f3f3f;
 --zenburn-bg-plus-1: #4f4f4f;
 --zenburn-bg-plus-2: #5f5f5f;
 --zenburn-blue: #8cd0d3;
 }
291406c5
 #table-of-contents h2 {
 text-align: center;
 padding-top: 3.5em;
 }
 #table-of-contents {
 width: 25rem;
 position: fixed;
 left: 0;
 top: 0;
 height: 100%;
 overflow-y: scroll;
 scrollbar-width: thin;
 }
 #table-of-contents::-webkit-scrollbar { width :6px; }
4c3588fc
 * {box-sizing: border-box;}
 body {
 font-size: 1.2rem;
 width: 75rem;
 margin: 0 0 0 25rem;
 background: var(--zenburn-bg);
 color: var(--zenburn-fg);
e193c726
 font-family: "Alegreya Sans", "Lato", "Roboto", "Open Sans", "Helvetica", sans-serif;
4c3588fc
 }
 a {color: var(--zenburn-blue);}
e193c726
 h1, h2, h3, h4, h5, h6 {margin: 0; margin-top: 1.5em; margin-bottom: 0.5em;}
4c3588fc
 pre {margin: 0; box-shadow: none; border-width: 0.5em;}
 pre.example {
 background-color: var(--zenburn-bg-plus-2);
 color: var(--zenburn-fg-plus-2);
 border: none;
 padding-left: 4em;
 }
 pre.src {
 background-color: var(--zenburn-bg-plus-1);
 border-color: var(--zenburn-bg-plus-2);
 color: var(--zenburn-fg-plus-1);
 }
 pre.src::before {
 background-color: var(--zenburn-bg-plus-1);
 border-color: var(--zenburn-bg-plus-2);
 color: var(--zenburn-fg-plus-1);
 }
 </style>
 <script type="text/javascript">
 /*
 @licstart  The following is the entire license notice for the
 JavaScript code in this tag.
 
 Copyright (C) 2012-2019 Free Software Foundation, Inc.
 
 The JavaScript code in this tag is free software: you can
 redistribute it and/or modify it under the terms of the GNU
 General Public License (GNU GPL) as published by the Free Software
 Foundation, either version 3 of the License, or (at your option)
 any later version.  The code is distributed WITHOUT ANY WARRANTY;
 without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
 
 As additional permission under GNU GPL version 3 section 7, you
 may distribute non-source (e.g., minimized or compacted) forms of
 that code without the copy of the GNU GPL normally required by
 section 4, provided you include this license notice and a URL
 through which recipients can access the Corresponding Source.
 
 
 @licend  The above is the entire license notice
 for the JavaScript code in this tag.
 */
 <!--/*--><![CDATA[/*><!--*/
  function CodeHighlightOn(elem, id)
  {
    var target = document.getElementById(id);
    if(null != target) {
      elem.cacheClassElem = elem.className;
      elem.cacheClassTarget = target.className;
      target.className = "code-highlighted";
      elem.className   = "code-highlighted";
    }
  }
  function CodeHighlightOff(elem, id)
  {
    var target = document.getElementById(id);
    if(elem.cacheClassElem)
      elem.className = elem.cacheClassElem;
    if(elem.cacheClassTarget)
      target.className = elem.cacheClassTarget;
  }
 /*]]>*///-->
 </script>
 </head>
 <body>
 <div id="content">
 <h1 class="title">TODO backend implementation using CL and fukamachi/ningle</h1>
 <div id="table-of-contents">
 <h2>Table of Contents</h2>
 <div id="text-table-of-contents">
 <ul>
55a11d3d
 <li><a href="#org9cdd67c">1. Setup</a></li>
 <li><a href="#org4320cff">2. todo API</a>
4c3588fc
 <ul>
55a11d3d
 <li><a href="#org0212081">2.1. List-level APIs</a></li>
 <li><a href="#org4f066d5">2.2. Getting/Replacing a todo</a></li>
 <li><a href="#orge9f5122">2.3. Adding and modifying todos</a></li>
 <li><a href="#org745149a">2.4. Examples</a></li>
4c3588fc
 </ul>
 </li>
55a11d3d
 <li><a href="#orgc967bea">3. Routing</a>
4c3588fc
 <ul>
55a11d3d
 <li><a href="#org43beaea">3.1. Routing utilities</a></li>
 <li><a href="#org1b71e8e">3.2. todo routes</a></li>
4258e918
 </ul>
 </li>
55a11d3d
 <li><a href="#orga9b0a0d">4. Source</a>
4258e918
 <ul>
55a11d3d
 <li><a href="#org87d2e6f">4.1. model.lisp source code</a></li>
 <li><a href="#org8641414">4.2. routing.lisp source</a></li>
 <li><a href="#org99a4e68">4.3. main.lisp source</a></li>
4c3588fc
 </ul>
 </li>
 </ul>
 </div>
 </div>
 
55a11d3d
 <div id="outline-container-org9cdd67c" class="outline-2">
 <h2 id="org9cdd67c"><span class="section-number-2">1</span> Setup</h2>
4c3588fc
 <div class="outline-text-2" id="text-1">
19b6e2b4
 <ul class="org-ul">
2abb94cb
 <li><code class="src src-sh">git clone https://github.com/fiddlerwoaroof/data-lens.git ~/quicklisp/local-projects/data-lens</code></li>
 <li><code class="src src-sh">git clone https://github.com/fukamachi/lack.git ~/quicklisp/local-projects/lack</code></li>
 </ul>
19b6e2b4
 
2abb94cb
 <div class="org-src-container">
 <pre class="src src-sh">sbcl --eval <span style="color: #D0BF8F;">'(asdf:load-asd (truename "todo-backend.asd"))'</span> 
      --eval <span style="color: #D0BF8F;">'(ql:quickload :todo-backend)'</span> 
      --eval <span style="color: #D0BF8F;">'(fwoar.todo::ensure-started)'</span>
 </pre>
 </div>
19b6e2b4
 
2abb94cb
 <p>
 After this, all the tests <a href="http://www.todobackend.com/specs/index.html?http://localhost:5000">here</a> should pass and the frontend <a href="http://www.todobackend.com/client/index.html?http://localhost:5000">here</a> should work.
 </p>
19b6e2b4
 </div>
 </div>
 
55a11d3d
 <div id="outline-container-org4320cff" class="outline-2">
 <h2 id="org4320cff"><span class="section-number-2">2</span> todo API</h2>
19b6e2b4
 <div class="outline-text-2" id="text-2">
4c3588fc
 <p>
 We use a fairly simple structure for our "database": a fset map (a
 clojure-inspired persistent data structure) and a handful of
 interface functions that wrap it. In this code, this fset map is
 referenced as <code>*todo*</code>, but this is a detail hidden behind the API.
 </p>
 </div>
 
55a11d3d
 <div id="outline-container-org0212081" class="outline-3">
 <h3 id="org0212081"><span class="section-number-3">2.1</span> List-level APIs</h3>
19b6e2b4
 <div class="outline-text-3" id="text-2-1">
4c3588fc
 <p>
6613ae69
 These are functions for getting the todo list and clearing
 it. These are activated by the root route: <code>todos</code> for GET requests
 and <code>clear-todos</code> for DELETE requests.
4c3588fc
 </p>
 
 <div class="org-src-container">
55a11d3d
 <pre class="src src-lisp" id="org96c36b7"><span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">todos</span> <span style="color: #93a8c6;">()</span>
4c3588fc
   <span style="color: #93a8c6;">(</span>gmap:gmap <span style="font-weight: bold;">:seq</span>
              <span style="color: #b0b1a3;">(</span><span style="color: #F0DFAF;">lambda</span> <span style="color: #97b098;">(</span>_ b<span style="color: #97b098;">)</span>
                <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>
                b<span style="color: #b0b1a3;">)</span>
              <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>
 
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">clear-todos</span> <span style="color: #93a8c6;">()</span>
   <span style="color: #93a8c6;">(</span>setf *todos*
         <span style="color: #b0b1a3;">(</span>fset:empty-map<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 </pre>
 </div>
 </div>
 </div>
 
55a11d3d
 <div id="outline-container-org4f066d5" class="outline-3">
 <h3 id="org4f066d5"><span class="section-number-3">2.2</span> Getting/Replacing a todo</h3>
19b6e2b4
 <div class="outline-text-3" id="text-2-2">
4c3588fc
 <p>
 This uses lisp's <a href="http://www.lispworks.com/documentation/HyperSpec/Body/05_a.htm">generalized references</a> to abstract away the
 storage details of the todos. We also provide a <code>delete-todo</code>
6613ae69
 function for removing a todo from the list. <code>todo</code> is what backs
 the GET request for a specific todo by id.
4c3588fc
 </p>
 
 <div class="org-src-container">
55a11d3d
 <pre class="src src-lisp" id="org8282374"><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>
4c3588fc
   <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>
     todo<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 
 <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>
   <span style="color: #93a8c6;">(</span>setf <span style="color: #b0b1a3;">(</span>fset:@ *todos* id<span style="color: #b0b1a3;">)</span>
         new-value<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 
 <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>
   <span style="color: #93a8c6;">(</span>setf *todos*
         <span style="color: #b0b1a3;">(</span>fset:less *todos* id<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 </pre>
 </div>
 </div>
 </div>
 
55a11d3d
 <div id="outline-container-orge9f5122" class="outline-3">
 <h3 id="orge9f5122"><span class="section-number-3">2.3</span> Adding and modifying todos</h3>
19b6e2b4
 <div class="outline-text-3" id="text-2-3">
4c3588fc
 <p>
 <code>new-todo</code> is fairly trivial. It's main feature is that it has to
 make sure the <code>completed</code> and <code>url</code> keys are set to the appropriate
 values. Completed isn't a lisp boolean, so it serializes to JSON
6613ae69
 properly. <code>new-todo</code> backs POST requests to the root endpoint.
4c3588fc
 </p>
 
 <div class="org-src-container">
55a11d3d
 <pre class="src src-lisp" id="orgbeae4f5"><span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defvar</span> <span style="color: #DC8CC3;">*external-host*</span>
4258e918
   <span style="color: #D0BF8F;">"localhost"</span><span style="color: #8c8c8c;">)</span>
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defvar</span> <span style="color: #DC8CC3;">*external-port*</span>
   5000<span style="color: #8c8c8c;">)</span>
 
 <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>
4c3588fc
   <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>
     <span style="color: #b0b1a3;">(</span>setf <span style="color: #97b098;">(</span>todo id<span style="color: #97b098;">)</span>
           <span style="color: #97b098;">(</span>alexandria:alist-hash-table
            <span style="color: #aebed8;">(</span>rutilsx.threading:-&gt;&gt;
             value
             <span style="color: #b0b0b3;">(</span>acons <span style="color: #D0BF8F;">"completed"</span> 'yason:false<span style="color: #b0b0b3;">)</span>
5fd28f99
             <span style="color: #b0b0b3;">(</span>acons <span style="color: #D0BF8F;">"url"</span>
                    <span style="color: #90a890;">(</span>format nil <span style="color: #D0BF8F;">"http://~a:~d/todo/~d"</span>
                            *external-host*
                            *external-port*
                            id<span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span>
4c3588fc
            <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>
 </pre>
 </div>
 
 <p>
 <code>update-todo</code> just merges the input from the frontend into the
 relevant todo and then makes sure that the <code>completed</code> key is a
6613ae69
 yason-compatible boolean. <code>update-todo</code> backs PATCH requests to the
 todo endpoint for a specific ID.
4c3588fc
 </p>
 
 <div class="org-src-container">
55a11d3d
 <pre class="src src-lisp" id="orgc65925d"><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>
5fd28f99
   <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">let*</span> <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span>old-todo <span style="color: #aebed8;">(</span>or <span style="color: #b0b0b3;">(</span>todo id<span style="color: #b0b0b3;">)</span>
                        <span style="color: #b0b0b3;">(</span>make-hash-table <span style="font-weight: bold;">:test</span> 'equal<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
          <span style="color: #97b098;">(</span>in-hash-table <span style="color: #aebed8;">(</span>alexandria:alist-hash-table v <span style="font-weight: bold;">:test</span> 'equal<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
          <span style="color: #97b098;">(</span>update <span style="color: #aebed8;">(</span>data-lens.lenses:over *completed-lens*
                                         'bool-to-yason
                                         in-hash-table<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
     <span style="color: #b0b1a3;">(</span>setf <span style="color: #97b098;">(</span>todo id<span style="color: #97b098;">)</span>
           <span style="color: #97b098;">(</span>serapeum:merge-tables old-todo
                                  update<span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
4c3588fc
 </pre>
 </div>
 </div>
 </div>
 
55a11d3d
 <div id="outline-container-org745149a" class="outline-3">
 <h3 id="org745149a"><span class="section-number-3">2.4</span> Examples</h3>
19b6e2b4
 <div class="outline-text-3" id="text-2-4">
4c3588fc
 <div class="org-src-container">
 <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>
 
 <span style="color: #8c8c8c;">(</span>load <span style="color: #D0BF8F;">"pprint-setup"</span><span style="color: #8c8c8c;">)</span>
 
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">with-fresh-todos</span> <span style="color: #93a8c6;">()</span>
   <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>
   <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>
   <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>
 </pre>
 </div>
 
 <pre class="example">
55a11d3d
 (#&lt;hash-table "url": "http://localhost:5000/todo/1",
4c3588fc
               "title": "get groceries",
               "completed": YASON:FALSE&gt;
55a11d3d
  #&lt;hash-table "url": "http://localhost:5000/todo/2",
4c3588fc
               "title": "write-better-documentation",
               "completed": YASON:FALSE&gt;)
 </pre>
 </div>
 </div>
 </div>
 
55a11d3d
 <div id="outline-container-orgc967bea" class="outline-2">
 <h2 id="orgc967bea"><span class="section-number-2">3</span> Routing</h2>
19b6e2b4
 <div class="outline-text-2" id="text-3">
4c3588fc
 </div>
55a11d3d
 <div id="outline-container-org43beaea" class="outline-3">
 <h3 id="org43beaea"><span class="section-number-3">3.1</span> Routing utilities</h3>
19b6e2b4
 <div class="outline-text-3" id="text-3-1">
4258e918
 <p>
 The core utility here is the <code>defroutes</code> macro.  This takes a
 sequence of endpoint descriptions which contain nested definitions
 for HTTP verbs and expands to ningle's functions for manipulating
 routes.
 </p>
 
7762e7b6
 <div class="org-src-container">
55a11d3d
 <pre class="src src-lisp" id="orgd30e492"><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>
7762e7b6
   <span style="color: #93a8c6;">(</span>alexandria:once-only <span style="color: #b0b1a3;">(</span>app<span style="color: #b0b1a3;">)</span>
     `<span style="color: #b0b1a3;">(</span>setf
       ,@<span style="color: #97b098;">(</span><span style="color: #F0DFAF;">loop</span> for <span style="color: #aebed8;">(</span>target . descriptors<span style="color: #aebed8;">)</span> in routes
               append <span style="color: #aebed8;">(</span><span style="color: #F0DFAF;">loop</span> for <span style="color: #b0b0b3;">(</span>method callback<span style="color: #b0b0b3;">)</span> in descriptors
                            append `<span style="color: #b0b0b3;">(</span><span style="color: #90a890;">(</span>ningle:route ,app ,target
9360d164
                                                   <span style="font-weight: bold;">:method</span> ,method<span style="color: #90a890;">)</span>
7762e7b6
                                     ,callback<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>
 </pre>
 </div>
 
 <p>
 This macro organizes all the HTTP verbs for a given endpoint under
 the path to that endpoint. A more complete version might allow for
 a list of verbs <code>(:GET :POST)</code> in the head of each handler clause.
 </p>
 
4258e918
 <div class="org-src-container">
 <pre class="src src-lisp"><span style="color: #8c8c8c;">(</span>macroexpand-1
  '<span style="color: #93a8c6;">(</span>defroutes app
    <span style="color: #b0b1a3;">(</span><span style="color: #D0BF8F;">"/"</span>
     <span style="color: #97b098;">(</span><span style="font-weight: bold;">:GET</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">()</span> <span style="color: #b0b0b3;">(</span>todos<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
     <span style="color: #97b098;">(</span><span style="font-weight: bold;">:POST</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">(</span>v<span style="color: #b0b0b3;">)</span> <span style="color: #b0b0b3;">(</span>new-todo v<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
     <span style="color: #97b098;">(</span><span style="font-weight: bold;">:DELETE</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">()</span> <span style="color: #b0b0b3;">(</span>clear-todos<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>
 </pre>
 </div>
 
7762e7b6
 
4258e918
 <pre class="example">
9360d164
 (DEFROUTES APP
  ("/" (:GET (HANDLER NIL (TODOS))) (:POST (HANDLER (V) (NEW-TODO V)))
   (:DELETE (HANDLER NIL (CLEAR-TODOS)))))
 NIL
4258e918
 </pre>
 
9360d164
 
7762e7b6
 <p>
 Finally, there are some simple helpers to handle some of the
 boilerplate in a clack webserver.  Of particular interest is the
 <code>handler</code> macro, which (since this is a json-only API) makes sure
 that all the API results get JSON encoded.
 </p>
4258e918
 
7762e7b6
 <div class="org-src-container">
55a11d3d
 <pre class="src src-lisp" id="org4594387"><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>
7762e7b6
   <span style="color: #93a8c6;">(</span>list 200 '<span style="color: #b0b1a3;">(</span><span style="font-weight: bold;">:conent-type</span> <span style="color: #D0BF8F;">"application/json"</span><span style="color: #b0b1a3;">)</span> value<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
4258e918
 
 <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>
   `<span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">lambda</span> <span style="color: #b0b1a3;">(</span>,sym<span style="color: #b0b1a3;">)</span>
      <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>
      <span style="color: #b0b1a3;">(</span>success
       <span style="color: #97b098;">(</span>fwoar.lack.json.middleware:wrap-result
        <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>
 </pre>
 </div>
 </div>
 </div>
7762e7b6
 
55a11d3d
 <div id="outline-container-org1b71e8e" class="outline-3">
 <h3 id="org1b71e8e"><span class="section-number-3">3.2</span> todo routes</h3>
7762e7b6
 <div class="outline-text-3" id="text-3-2">
 <p>
46d4eb2f
 <code>setup-routes</code> binds the endpoints to handlers: <code>"/"</code> to handlers
 that handle the todo lists while <code>"/todo/:id"</code> to handlers that
7762e7b6
 handle individual todos.  The <code>:id</code> indicates that the
 corresponding segment of the path is bound to <code>:id</code> in the param
 alist. <code>get-id</code> handles this, and extracts an integer for the id
 (since we are using successive integers for the todo ids).
 </p>
 
 <div class="org-src-container">
55a11d3d
 <pre class="src src-lisp" id="org148c9f4"><span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">routing</span>
7762e7b6
 <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>
   <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>
 
 <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>
   <span style="color: #93a8c6;">(</span>defroutes app
     <span style="color: #b0b1a3;">(</span><span style="color: #D0BF8F;">"/"</span> <span style="color: #97b098;">(</span><span style="font-weight: bold;">:GET</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">()</span> <span style="color: #b0b0b3;">(</span>todos<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
          <span style="color: #97b098;">(</span><span style="font-weight: bold;">:POST</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">(</span>v<span style="color: #b0b0b3;">)</span> <span style="color: #b0b0b3;">(</span>new-todo v<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
          <span style="color: #97b098;">(</span><span style="font-weight: bold;">:DELETE</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">()</span> <span style="color: #b0b0b3;">(</span>clear-todos<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
     <span style="color: #b0b1a3;">(</span><span style="color: #D0BF8F;">"/todo/:id"</span> <span style="color: #97b098;">(</span><span style="font-weight: bold;">:GET</span>    <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">(</span>v<span style="color: #b0b0b3;">)</span> <span style="color: #b0b0b3;">(</span>todo <span style="color: #90a890;">(</span>get-id v<span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
                  <span style="color: #97b098;">(</span><span style="font-weight: bold;">:DELETE</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">(</span>v<span style="color: #b0b0b3;">)</span>
                             <span style="color: #b0b0b3;">(</span>delete-todo <span style="color: #90a890;">(</span>get-id v<span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span>
                             nil<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
                  <span style="color: #97b098;">(</span><span style="font-weight: bold;">:PATCH</span>  <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">(</span>v<span style="color: #b0b0b3;">)</span>
                             <span style="color: #b0b0b3;">(</span>update-todo <span style="color: #90a890;">(</span>get-id v<span style="color: #90a890;">)</span> 
                                          <span style="color: #90a890;">(</span>remove <span style="font-weight: bold;">:id</span> v <span style="font-weight: bold;">:key</span> #'car<span style="color: #90a890;">)</span><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>
 </pre>
 </div>
 </div>
 </div>
4258e918
 </div>
 
55a11d3d
 <div id="outline-container-orga9b0a0d" class="outline-2">
 <h2 id="orga9b0a0d"><span class="section-number-2">4</span> Source</h2>
4258e918
 <div class="outline-text-2" id="text-4">
 </div>
55a11d3d
 <div id="outline-container-org87d2e6f" class="outline-3">
 <h3 id="org87d2e6f"><span class="section-number-3">4.1</span> model.lisp source code</h3>
4258e918
 <div class="outline-text-3" id="text-4-1">
4c3588fc
 <div class="org-src-container">
 <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>
 <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>
 
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">package-include ends here</span>
 <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>
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defparameter</span> <span style="color: #DC8CC3;">*cur-id*</span> 0<span style="color: #8c8c8c;">)</span>
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">next-id</span> <span style="color: #93a8c6;">()</span>
   <span style="color: #93a8c6;">(</span>incf *cur-id*<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defparameter</span> <span style="color: #DC8CC3;">*completed-lens*</span>
   <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>
 
 <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>
   <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">if</span> bool
       'yason:true
       'yason:false<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">model-utils ends here</span>
 
 <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>
 
 <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>
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">todos</span> <span style="color: #93a8c6;">()</span>
   <span style="color: #93a8c6;">(</span>gmap:gmap <span style="font-weight: bold;">:seq</span>
              <span style="color: #b0b1a3;">(</span><span style="color: #F0DFAF;">lambda</span> <span style="color: #97b098;">(</span>_ b<span style="color: #97b098;">)</span>
                <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>
                b<span style="color: #b0b1a3;">)</span>
              <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>
 
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">clear-todos</span> <span style="color: #93a8c6;">()</span>
   <span style="color: #93a8c6;">(</span>setf *todos*
         <span style="color: #b0b1a3;">(</span>fset:empty-map<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">todolist-manipulation ends here</span>
 
 <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>
 <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>
   <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>
     todo<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 
 <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>
   <span style="color: #93a8c6;">(</span>setf <span style="color: #b0b1a3;">(</span>fset:@ *todos* id<span style="color: #b0b1a3;">)</span>
         new-value<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 
 <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>
   <span style="color: #93a8c6;">(</span>setf *todos*
         <span style="color: #b0b1a3;">(</span>fset:less *todos* id<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">todo-accessor ends here</span>
 
 <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>
4258e918
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defvar</span> <span style="color: #DC8CC3;">*external-host*</span>
   <span style="color: #D0BF8F;">"localhost"</span><span style="color: #8c8c8c;">)</span>
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defvar</span> <span style="color: #DC8CC3;">*external-port*</span>
   5000<span style="color: #8c8c8c;">)</span>
 
4c3588fc
 <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>
   <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>
     <span style="color: #b0b1a3;">(</span>setf <span style="color: #97b098;">(</span>todo id<span style="color: #97b098;">)</span>
           <span style="color: #97b098;">(</span>alexandria:alist-hash-table
            <span style="color: #aebed8;">(</span>rutilsx.threading:-&gt;&gt;
             value
             <span style="color: #b0b0b3;">(</span>acons <span style="color: #D0BF8F;">"completed"</span> 'yason:false<span style="color: #b0b0b3;">)</span>
5fd28f99
             <span style="color: #b0b0b3;">(</span>acons <span style="color: #D0BF8F;">"url"</span>
                    <span style="color: #90a890;">(</span>format nil <span style="color: #D0BF8F;">"http://~a:~d/todo/~d"</span>
                            *external-host*
                            *external-port*
                            id<span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span>
4c3588fc
            <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>
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">new-todo ends here</span>
 
 <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>
 <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>
5fd28f99
   <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">let*</span> <span style="color: #b0b1a3;">(</span><span style="color: #97b098;">(</span>old-todo <span style="color: #aebed8;">(</span>or <span style="color: #b0b0b3;">(</span>todo id<span style="color: #b0b0b3;">)</span>
                        <span style="color: #b0b0b3;">(</span>make-hash-table <span style="font-weight: bold;">:test</span> 'equal<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
          <span style="color: #97b098;">(</span>in-hash-table <span style="color: #aebed8;">(</span>alexandria:alist-hash-table v <span style="font-weight: bold;">:test</span> 'equal<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
          <span style="color: #97b098;">(</span>update <span style="color: #aebed8;">(</span>data-lens.lenses:over *completed-lens*
                                         'bool-to-yason
                                         in-hash-table<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
     <span style="color: #b0b1a3;">(</span>setf <span style="color: #97b098;">(</span>todo id<span style="color: #97b098;">)</span>
           <span style="color: #97b098;">(</span>serapeum:merge-tables old-todo
                                  update<span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
4c3588fc
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">update-todo ends here</span>
 
 <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>
   `<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>
      ,@body<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 </pre>
 </div>
 </div>
 </div>
 
55a11d3d
 <div id="outline-container-org8641414" class="outline-3">
 <h3 id="org8641414"><span class="section-number-3">4.2</span> routing.lisp source</h3>
4258e918
 <div class="outline-text-3" id="text-4-2">
4c3588fc
 <div class="org-src-container">
4258e918
 <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>
 <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>
4c3588fc
 
4258e918
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">package-include ends here</span>
 
7762e7b6
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">[[file:~/git_repos/lisp-sandbox/todo/README.org::defroutes][defroutes]]</span>
4c3588fc
 <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>
   <span style="color: #93a8c6;">(</span>alexandria:once-only <span style="color: #b0b1a3;">(</span>app<span style="color: #b0b1a3;">)</span>
4258e918
     `<span style="color: #b0b1a3;">(</span>setf
       ,@<span style="color: #97b098;">(</span><span style="color: #F0DFAF;">loop</span> for <span style="color: #aebed8;">(</span>target . descriptors<span style="color: #aebed8;">)</span> in routes
               append <span style="color: #aebed8;">(</span><span style="color: #F0DFAF;">loop</span> for <span style="color: #b0b0b3;">(</span>method callback<span style="color: #b0b0b3;">)</span> in descriptors
                            append `<span style="color: #b0b0b3;">(</span><span style="color: #90a890;">(</span>ningle:route ,app ,target
9360d164
                                                   <span style="font-weight: bold;">:method</span> ,method<span style="color: #90a890;">)</span>
4258e918
                                     ,callback<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>
7762e7b6
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">defroutes ends here</span>
4c3588fc
 
7762e7b6
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">[[file:~/git_repos/lisp-sandbox/todo/README.org::routing-helpers][routing-helpers]]</span>
4c3588fc
 <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>
7762e7b6
   <span style="color: #93a8c6;">(</span>list 200 '<span style="color: #b0b1a3;">(</span><span style="font-weight: bold;">:conent-type</span> <span style="color: #D0BF8F;">"application/json"</span><span style="color: #b0b1a3;">)</span> value<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
4c3588fc
 
 <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>
   `<span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">lambda</span> <span style="color: #b0b1a3;">(</span>,sym<span style="color: #b0b1a3;">)</span>
      <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>
      <span style="color: #b0b1a3;">(</span>success
       <span style="color: #97b098;">(</span>fwoar.lack.json.middleware:wrap-result
        <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>
4258e918
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">routing-helpers ends here</span>
4c3588fc
 
7762e7b6
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">[[file:~/git_repos/lisp-sandbox/todo/README.org::todo-routes][todo-routes]]</span>
4258e918
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">routing</span>
4c3588fc
 <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>
   <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>
 
 <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>
   <span style="color: #93a8c6;">(</span>defroutes app
4258e918
     <span style="color: #b0b1a3;">(</span><span style="color: #D0BF8F;">"/"</span> <span style="color: #97b098;">(</span><span style="font-weight: bold;">:GET</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">()</span> <span style="color: #b0b0b3;">(</span>todos<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
          <span style="color: #97b098;">(</span><span style="font-weight: bold;">:POST</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">(</span>v<span style="color: #b0b0b3;">)</span> <span style="color: #b0b0b3;">(</span>new-todo v<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
          <span style="color: #97b098;">(</span><span style="font-weight: bold;">:DELETE</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">()</span> <span style="color: #b0b0b3;">(</span>clear-todos<span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span><span style="color: #b0b1a3;">)</span>
     <span style="color: #b0b1a3;">(</span><span style="color: #D0BF8F;">"/todo/:id"</span> <span style="color: #97b098;">(</span><span style="font-weight: bold;">:GET</span>    <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">(</span>v<span style="color: #b0b0b3;">)</span> <span style="color: #b0b0b3;">(</span>todo <span style="color: #90a890;">(</span>get-id v<span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span><span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
                  <span style="color: #97b098;">(</span><span style="font-weight: bold;">:DELETE</span> <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">(</span>v<span style="color: #b0b0b3;">)</span>
                             <span style="color: #b0b0b3;">(</span>delete-todo <span style="color: #90a890;">(</span>get-id v<span style="color: #90a890;">)</span><span style="color: #b0b0b3;">)</span>
                             nil<span style="color: #aebed8;">)</span><span style="color: #97b098;">)</span>
                  <span style="color: #97b098;">(</span><span style="font-weight: bold;">:PATCH</span>  <span style="color: #aebed8;">(</span>handler <span style="color: #b0b0b3;">(</span>v<span style="color: #b0b0b3;">)</span>
                             <span style="color: #b0b0b3;">(</span>update-todo <span style="color: #90a890;">(</span>get-id v<span style="color: #90a890;">)</span> 
                                          <span style="color: #90a890;">(</span>remove <span style="font-weight: bold;">:id</span> v <span style="font-weight: bold;">:key</span> #'car<span style="color: #90a890;">)</span><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>
7762e7b6
 <span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">todo-routes ends here</span>
4c3588fc
 </pre>
 </div>
 </div>
 </div>
 
55a11d3d
 <div id="outline-container-org99a4e68" class="outline-3">
 <h3 id="org99a4e68"><span class="section-number-3">4.3</span> main.lisp source</h3>
4258e918
 <div class="outline-text-3" id="text-4-3">
4c3588fc
 <div class="org-src-container">
 <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>
 
 <span style="color: #A6A689; background-color: #2B2B2B;">;;; </span><span style="color: #A6A689; background-color: #2B2B2B;">entrypoint</span>
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">setup</span> <span style="color: #93a8c6;">()</span>
   <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>
     <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>
 
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defvar</span> <span style="color: #DC8CC3;">*handler*</span><span style="color: #8c8c8c;">)</span>
 
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">is-running</span> <span style="color: #93a8c6;">()</span>
   <span style="color: #93a8c6;">(</span>and <span style="color: #b0b1a3;">(</span>boundp '*handler*<span style="color: #b0b1a3;">)</span>
        *handler*<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
 
9360d164
 <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> <span style="color: #b0b1a3;">(</span>address <span style="color: #D0BF8F;">"127.0.0.1"</span><span style="color: #b0b1a3;">)</span> <span style="color: #b0b1a3;">(</span>port 5000<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span>
   <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">declare</span> <span style="color: #b0b1a3;">(</span>ignore address port<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span>
4c3588fc
   <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>
     <span style="color: #b0b1a3;">(</span>values app
             <span style="color: #97b098;">(</span>setf *handler*
                   <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>
                       <span style="color: #b0b0b3;">(</span>apply 'clack:clackup
                              <span style="color: #90a890;">(</span>lack.builder:builder
                               <span style="font-weight: bold;">:accesslog</span>
                               'fwoar.lack.cors.middleware:cors-middleware
                               'fwoar.lack.json.middleware:json-middleware
                               app<span style="color: #90a890;">)</span>
                              r<span style="color: #b0b0b3;">)</span>
                       *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>
 
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">stop</span> <span style="color: #93a8c6;">()</span>
   <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">if</span> <span style="color: #b0b1a3;">(</span>is-running<span style="color: #b0b1a3;">)</span>
       <span style="color: #b0b1a3;">(</span><span style="color: #F0DFAF;">progn</span>
         <span style="color: #97b098;">(</span>clack:stop *handler*<span style="color: #97b098;">)</span>
         <span style="color: #97b098;">(</span>makunbound '*handler*<span style="color: #97b098;">)</span>
         nil<span style="color: #b0b1a3;">)</span>
       nil<span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
9360d164
 
 <span style="color: #8c8c8c;">(</span><span style="color: #F0DFAF;">defun</span> <span style="color: #8CD0D3;">main</span> <span style="color: #93a8c6;">(</span><span style="color: #CC9393;">&amp;rest</span> _<span style="color: #93a8c6;">)</span>
   <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">declare</span> <span style="color: #b0b1a3;">(</span>ignore _<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span>
   <span style="color: #93a8c6;">(</span>ensure-started <span style="font-weight: bold;">:address</span> <span style="color: #D0BF8F;">"0.0.0.0"</span> <span style="font-weight: bold;">:port</span> 5000<span style="color: #93a8c6;">)</span>
   <span style="color: #93a8c6;">(</span><span style="color: #F0DFAF;">loop</span> <span style="color: #b0b1a3;">(</span>sleep 5<span style="color: #b0b1a3;">)</span><span style="color: #93a8c6;">)</span><span style="color: #8c8c8c;">)</span>
4c3588fc
 </pre>
 </div>
 </div>
 </div>
 </div>
 </div>
 <div id="postamble" class="status">
 <p class="author">Author: Langley</p>
55a11d3d
 <p class="date">Created: 2019-09-25 Wed 21:02</p>
4c3588fc
 <p class="validation"><a href="http://validator.w3.org/check?uri=referer">Validate</a></p>
 </div>
 </body>
 </html>