<?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:->> 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"> (#<hash-table "url": "http://localhost:5000/todo/1", "title": "get groceries", "completed": YASON:FALSE> #<hash-table "url": "http://localhost:5000/todo/2", "title": "write-better-documentation", "completed": YASON:FALSE>) </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;">&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;">&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;">&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:->> 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;">&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;">&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;">&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;">&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:<app><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;">&rest</span> r <span style="color: #CC9393;">&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;">&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>