git.fiddlerwoaroof.com
Raw Blame History
<?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>
<!-- 2019-09-25 Wed 21:02 -->
<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;
}
#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; }
* {box-sizing: border-box;}
body {
font-size: 1.2rem;
width: 75rem;
margin: 0 0 0 25rem;
background: var(--zenburn-bg);
color: var(--zenburn-fg);
font-family: "Alegreya Sans", "Lato", "Roboto", "Open Sans", "Helvetica", sans-serif;
}
a {color: var(--zenburn-blue);}
h1, h2, h3, h4, h5, h6 {margin: 0; margin-top: 1.5em; margin-bottom: 0.5em;}
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>
<li><a href="#org9cdd67c">1. Setup</a></li>
<li><a href="#org4320cff">2. todo API</a>
<ul>
<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>
</ul>
</li>
<li><a href="#orgc967bea">3. Routing</a>
<ul>
<li><a href="#org43beaea">3.1. Routing utilities</a></li>
<li><a href="#org1b71e8e">3.2. todo routes</a></li>
</ul>
</li>
<li><a href="#orga9b0a0d">4. Source</a>
<ul>
<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>
</ul>
</li>
</ul>
</div>
</div>

<div id="outline-container-org9cdd67c" class="outline-2">
<h2 id="org9cdd67c"><span class="section-number-2">1</span> Setup</h2>
<div class="outline-text-2" id="text-1">
<ul class="org-ul">
<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>

<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>

<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>
</div>
</div>

<div id="outline-container-org4320cff" class="outline-2">
<h2 id="org4320cff"><span class="section-number-2">2</span> todo API</h2>
<div class="outline-text-2" id="text-2">
<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>

<div id="outline-container-org0212081" class="outline-3">
<h3 id="org0212081"><span class="section-number-3">2.1</span> List-level APIs</h3>
<div class="outline-text-3" id="text-2-1">
<p>
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.
</p>

<div class="org-src-container">
<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>
  <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>

<div id="outline-container-org4f066d5" class="outline-3">
<h3 id="org4f066d5"><span class="section-number-3">2.2</span> Getting/Replacing a todo</h3>
<div class="outline-text-3" id="text-2-2">
<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>
function for removing a todo from the list. <code>todo</code> is what backs
the GET request for a specific todo by id.
</p>

<div class="org-src-container">
<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>
  <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>

<div id="outline-container-orge9f5122" class="outline-3">
<h3 id="orge9f5122"><span class="section-number-3">2.3</span> Adding and modifying todos</h3>
<div class="outline-text-3" id="text-2-3">
<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
properly. <code>new-todo</code> backs POST requests to the root endpoint.
</p>

<div class="org-src-container">
<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>
  <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>
  <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>
            <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>
           <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
yason-compatible boolean. <code>update-todo</code> backs PATCH requests to the
todo endpoint for a specific ID.
</p>

<div class="org-src-container">
<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>
  <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>
</pre>
</div>
</div>
</div>

<div id="outline-container-org745149a" class="outline-3">
<h3 id="org745149a"><span class="section-number-3">2.4</span> Examples</h3>
<div class="outline-text-3" id="text-2-4">
<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">
(#&lt;hash-table "url": "http://localhost:5000/todo/1",
              "title": "get groceries",
              "completed": YASON:FALSE&gt;
 #&lt;hash-table "url": "http://localhost:5000/todo/2",
              "title": "write-better-documentation",
              "completed": YASON:FALSE&gt;)
</pre>
</div>
</div>
</div>

<div id="outline-container-orgc967bea" class="outline-2">
<h2 id="orgc967bea"><span class="section-number-2">3</span> Routing</h2>
<div class="outline-text-2" id="text-3">
</div>
<div id="outline-container-org43beaea" class="outline-3">
<h3 id="org43beaea"><span class="section-number-3">3.1</span> Routing utilities</h3>
<div class="outline-text-3" id="text-3-1">
<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>

<div class="org-src-container">
<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>
  <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
                                                  <span style="font-weight: bold;">:method</span> ,method<span style="color: #90a890;">)</span>
                                    ,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>

<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>


<pre class="example">
(DEFROUTES APP
 ("/" (:GET (HANDLER NIL (TODOS))) (:POST (HANDLER (V) (NEW-TODO V)))
  (:DELETE (HANDLER NIL (CLEAR-TODOS)))))
NIL
</pre>


<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>

<div class="org-src-container">
<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>
  <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>

<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>

<div id="outline-container-org1b71e8e" class="outline-3">
<h3 id="org1b71e8e"><span class="section-number-3">3.2</span> todo routes</h3>
<div class="outline-text-3" id="text-3-2">
<p>
<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
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">
<pre class="src src-lisp" id="org148c9f4"><span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">routing</span>
<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>
</div>

<div id="outline-container-orga9b0a0d" class="outline-2">
<h2 id="orga9b0a0d"><span class="section-number-2">4</span> Source</h2>
<div class="outline-text-2" id="text-4">
</div>
<div id="outline-container-org87d2e6f" class="outline-3">
<h3 id="org87d2e6f"><span class="section-number-3">4.1</span> model.lisp source code</h3>
<div class="outline-text-3" id="text-4-1">
<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>
<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>

<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>
            <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>
           <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>
  <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>
<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>

<div id="outline-container-org8641414" class="outline-3">
<h3 id="org8641414"><span class="section-number-3">4.2</span> routing.lisp source</h3>
<div class="outline-text-3" id="text-4-2">
<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::defroutes][defroutes]]</span>
<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>
    `<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
                                                  <span style="font-weight: bold;">:method</span> ,method<span style="color: #90a890;">)</span>
                                    ,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>
<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">defroutes 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::routing-helpers][routing-helpers]]</span>
<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>
  <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>

<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>
<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">routing-helpers 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-routes][todo-routes]]</span>
<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">routing</span>
<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>
<span style="color: #A6A689; background-color: #2B2B2B;">;; </span><span style="color: #A6A689; background-color: #2B2B2B;">todo-routes ends here</span>
</pre>
</div>
</div>
</div>

<div id="outline-container-org99a4e68" class="outline-3">
<h3 id="org99a4e68"><span class="section-number-3">4.3</span> main.lisp source</h3>
<div class="outline-text-3" id="text-4-3">
<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>

<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>
  <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>

<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>
</pre>
</div>
</div>
</div>
</div>
</div>
<div id="postamble" class="status">
<p class="author">Author: Langley</p>
<p class="date">Created: 2019-09-25 Wed 21:02</p>
<p class="validation"><a href="http://validator.w3.org/check?uri=referer">Validate</a></p>
</div>
</body>
</html>