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