/usr/share/doc/racket/continue/index.html is in racket-doc 6.7-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"/><title>Continue: Web Applications in Racket</title><link rel="stylesheet" type="text/css" href="../scribble.css" title="default"/><link rel="stylesheet" type="text/css" href="../racket.css" title="default"/><link rel="stylesheet" type="text/css" href="../manual-style.css" title="default"/><link rel="stylesheet" type="text/css" href="../manual-racket.css" title="default"/><link rel="stylesheet" type="text/css" href="../doc-site.css" title="default"/><script type="text/javascript" src="../scribble-common.js"></script><script type="text/javascript" src="../manual-racket.js"></script><script type="text/javascript" src="../doc-site.js"></script><script type="text/javascript" src="../local-redirect/local-redirect.js"></script><script type="text/javascript" src="../local-redirect/local-user-redirect.js"></script><!--[if IE 6]><style type="text/css">.SIEHidden { overflow: hidden; }</style><![endif]--></head><body id="doc-racket-lang-org"><div class="tocset"><div class="tocview"><div class="tocviewlist tocviewlisttopspace"><div class="tocviewtitle"><table cellspacing="0" cellpadding="0"><tr><td style="width: 1em;"><a href="javascript:void(0);" title="Expand/Collapse" class="tocviewtoggle" onclick="TocviewToggle(this,"tocview_0");">►</a></td><td></td><td><a href="" class="tocviewselflink" data-pltdoc="x">Continue:<span class="mywbr"> </span> Web Applications in Racket</a></td></tr></table></div><div class="tocviewsublistonly" style="display: none;" id="tocview_0"><table cellspacing="0" cellpadding="0"><tr><td align="right">1 </td><td><a href="#%28part._.Getting_.Started%29" class="tocviewlink" data-pltdoc="x">Getting Started</a></td></tr><tr><td align="right">2 </td><td><a href="#%28part._.The_.Application%29" class="tocviewlink" data-pltdoc="x">The Application</a></td></tr><tr><td align="right">3 </td><td><a href="#%28part._.Basic_.Blog%29" class="tocviewlink" data-pltdoc="x">Basic Blog</a></td></tr><tr><td align="right">4 </td><td><a href="#%28part._.Rendering_.H.T.M.L%29" class="tocviewlink" data-pltdoc="x">Rendering HTML</a></td></tr><tr><td align="right">5 </td><td><a href="#%28part._.Inspecting_.Requests%29" class="tocviewlink" data-pltdoc="x">Inspecting Requests</a></td></tr><tr><td align="right">6 </td><td><a href="#%28part._.Advanced_.Control_.Flow%29" class="tocviewlink" data-pltdoc="x">Advanced Control Flow</a></td></tr><tr><td align="right">7 </td><td><a href="#%28part._.Share_and_.Share_.Alike%29" class="tocviewlink" data-pltdoc="x">Share and Share Alike</a></td></tr><tr><td align="right">8 </td><td><a href="#%28part._.Extending_the_.Model%29" class="tocviewlink" data-pltdoc="x">Extending the Model</a></td></tr><tr><td align="right">9 </td><td><a href="#%28part._.Breaking_.Up_the_.Display%29" class="tocviewlink" data-pltdoc="x">Breaking Up the Display</a></td></tr><tr><td align="right">10 </td><td><a href="#%28part._.Adding_a_.Back_.Button%29" class="tocviewlink" data-pltdoc="x">Adding a Back Button</a></td></tr><tr><td align="right">11 </td><td><a href="#%28part._.Decorating_.With_.Style_%29" class="tocviewlink" data-pltdoc="x">Decorating With Style!</a></td></tr><tr><td align="right">12 </td><td><a href="#%28part._.The_.Double_.Submit_.Error%29" class="tocviewlink" data-pltdoc="x">The Double Submit Error</a></td></tr><tr><td align="right">13 </td><td><a href="#%28part._.Abstracting_the_.Model%29" class="tocviewlink" data-pltdoc="x">Abstracting the Model</a></td></tr><tr><td align="right">14 </td><td><a href="#%28part._.A_.Persistent_.Model%29" class="tocviewlink" data-pltdoc="x">A Persistent Model</a></td></tr><tr><td align="right">15 </td><td><a href="#%28part._.Using_an_.S.Q.L_database%29" class="tocviewlink" data-pltdoc="x">Using an SQL database</a></td></tr><tr><td align="right">16 </td><td><a href="#%28part._.Using_.Formlets%29" class="tocviewlink" data-pltdoc="x">Using Formlets</a></td></tr><tr><td align="right">17 </td><td><a href="#%28part._.Leaving_.Dr.Racket%29" class="tocviewlink" data-pltdoc="x">Leaving Dr<span class="mywbr"> </span>Racket</a></td></tr><tr><td align="right">18 </td><td><a href="#%28part._.Using_.H.T.T.P.S%29" class="tocviewlink" data-pltdoc="x">Using HTTPS</a></td></tr><tr><td align="right">19 </td><td><a href="#%28part._.Moving_.Forward%29" class="tocviewlink" data-pltdoc="x">Moving Forward</a></td></tr></table></div></div></div><div class="tocsub"><table class="tocsublist" cellspacing="0"><tr><td><span class="tocsublinknumber"></span><a href="#%28part._top%29" class="tocsubseclink" data-pltdoc="x">Continue:<span class="mywbr"> </span> Web Applications in Racket</a></td></tr><tr><td><span class="tocsublinknumber">1<tt> </tt></span><a href="#%28part._.Getting_.Started%29" class="tocsubseclink" data-pltdoc="x">Getting Started</a></td></tr><tr><td><span class="tocsublinknumber">2<tt> </tt></span><a href="#%28part._.The_.Application%29" class="tocsubseclink" data-pltdoc="x">The Application</a></td></tr><tr><td><span class="tocsublinknumber">3<tt> </tt></span><a href="#%28part._.Basic_.Blog%29" class="tocsubseclink" data-pltdoc="x">Basic Blog</a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-1..rkt%29._post%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">post</span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-1..rkt%29._blog%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">blog</span></a></td></tr><tr><td><span class="tocsublinknumber">4<tt> </tt></span><a href="#%28part._.Rendering_.H.T.M.L%29" class="tocsubseclink" data-pltdoc="x">Rendering HTML</a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-1..rkt%29._render-post%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">render-<wbr></wbr>post</span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-1..rkt%29._render-posts%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">render-<wbr></wbr>posts</span></a></td></tr><tr><td><span class="tocsublinknumber">5<tt> </tt></span><a href="#%28part._.Inspecting_.Requests%29" class="tocsubseclink" data-pltdoc="x">Inspecting Requests</a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._request-bindings%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym"><span class="RktValLink">request-<wbr></wbr>bindings</span></span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._extract-binding%2Fsingle%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym"><span class="RktValLink">extract-<wbr></wbr>binding/<span class="mywbr"> </span>single</span></span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._exists-binding~3f%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym"><span class="RktValLink">exists-<wbr></wbr>binding?</span></span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-2..rkt%29._can-parse-post~3f%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">can-<wbr></wbr>parse-<wbr></wbr>post?</span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-2..rkt%29._parse-post%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">parse-<wbr></wbr>post</span></a></td></tr><tr><td><span class="tocsublinknumber">6<tt> </tt></span><a href="#%28part._.Advanced_.Control_.Flow%29" class="tocsubseclink" data-pltdoc="x">Advanced Control Flow</a></td></tr><tr><td><span class="tocsublinknumber">7<tt> </tt></span><a href="#%28part._.Share_and_.Share_.Alike%29" class="tocsubseclink" data-pltdoc="x">Share and Share Alike</a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-4..rkt%29._blog%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">blog</span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-4..rkt%29._set-blog-posts%21%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">set-<wbr></wbr>blog-<wbr></wbr>posts!</span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-4..rkt%29._blog-insert-post%21%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">blog-<wbr></wbr>insert-<wbr></wbr>post!</span></a></td></tr><tr><td><span class="tocsublinknumber">8<tt> </tt></span><a href="#%28part._.Extending_the_.Model%29" class="tocsubseclink" data-pltdoc="x">Extending the Model</a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-5..rkt%29._post%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">post</span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-5..rkt%29._post-insert-comment%21%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">post-<wbr></wbr>insert-<wbr></wbr>comment!</span></a></td></tr><tr><td><span class="tocsublinknumber">9<tt> </tt></span><a href="#%28part._.Breaking_.Up_the_.Display%29" class="tocsubseclink" data-pltdoc="x">Breaking Up the Display</a></td></tr><tr><td><span class="tocsublinknumber">10<tt> </tt></span><a href="#%28part._.Adding_a_.Back_.Button%29" class="tocsubseclink" data-pltdoc="x">Adding a Back Button</a></td></tr><tr><td><span class="tocsublinknumber">11<tt> </tt></span><a href="#%28part._.Decorating_.With_.Style_%29" class="tocsubseclink" data-pltdoc="x">Decorating With Style!</a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-7..rkt%29._static-files-path%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">static-<wbr></wbr>files-<wbr></wbr>path</span></a></td></tr><tr><td><span class="tocsublinknumber">12<tt> </tt></span><a href="#%28part._.The_.Double_.Submit_.Error%29" class="tocsubseclink" data-pltdoc="x">The Double Submit Error</a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._redirect%2Fget%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym"><span class="RktValLink">redirect/<span class="mywbr"> </span>get</span></span></a></td></tr><tr><td><span class="tocsublinknumber">13<tt> </tt></span><a href="#%28part._.Abstracting_the_.Model%29" class="tocsubseclink" data-pltdoc="x">Abstracting the Model</a></td></tr><tr><td><span class="tocsublinknumber">14<tt> </tt></span><a href="#%28part._.A_.Persistent_.Model%29" class="tocsubseclink" data-pltdoc="x">A Persistent Model</a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-9..rkt%29._blog%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">blog</span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-9..rkt%29._blog-insert-post%21%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">blog-<wbr></wbr>insert-<wbr></wbr>post!</span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fiteration-9..rkt%29._post-insert-comment%21%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">post-<wbr></wbr>insert-<wbr></wbr>comment!</span></a></td></tr><tr><td><span class="tocsublinknumber">15<tt> </tt></span><a href="#%28part._.Using_an_.S.Q.L_database%29" class="tocsubseclink" data-pltdoc="x">Using an SQL database</a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fdummy-10..rkt%29._blog%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">blog</span></a></td></tr><tr><td><a href="#%28def._%28%28lib._web-server%2Fscribblings%2Ftutorial%2Fexamples%2Fdummy-10..rkt%29._post%29%29" class="tocsubnonseclink" data-pltdoc="x"><span class="RktSym">post</span></a></td></tr><tr><td><span class="tocsublinknumber">16<tt> </tt></span><a href="#%28part._.Using_.Formlets%29" class="tocsubseclink" data-pltdoc="x">Using Formlets</a></td></tr><tr><td><span class="tocsublinknumber">17<tt> </tt></span><a href="#%28part._.Leaving_.Dr.Racket%29" class="tocsubseclink" data-pltdoc="x">Leaving Dr<span class="mywbr"> </span>Racket</a></td></tr><tr><td><span class="tocsublinknumber">18<tt> </tt></span><a href="#%28part._.Using_.H.T.T.P.S%29" class="tocsubseclink" data-pltdoc="x">Using HTTPS</a></td></tr><tr><td><span class="tocsublinknumber">19<tt> </tt></span><a href="#%28part._.Moving_.Forward%29" class="tocsubseclink" data-pltdoc="x">Moving Forward</a></td></tr></table></div></div><div class="maincolumn"><div class="main"><div class="versionbox"><span class="version">6.7</span></div><div class="navsettop"><span class="navleft"><form class="searchform"><input class="searchbox" style="color: #888;" type="text" value="...search manuals..." title="Enter a search string to search the manuals" onkeypress="return DoSearchKey(event, this, "6.7", "../");" onfocus="this.style.color="black"; this.style.textAlign="left"; if (this.value == "...search manuals...") this.value="";" onblur="if (this.value.match(/^ *$/)) { this.style.color="#888"; this.style.textAlign="center"; this.value="...search manuals..."; }"/></form> <a href="../index.html" title="up to the documentation top" data-pltdoc="x" onclick="return GotoPLTRoot("6.7");">top</a></span><span class="navright"> <span class="nonavigation">← prev</span> <a href="../index.html" title="up to the documentation top" data-pltdoc="x" onclick="return GotoPLTRoot("6.7");">up</a> <span class="nonavigation">next →</span></span> </div><h2 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""top""><a name="(part._top)"></a><a name="(part._.Continue__.Web_.Applications_in_.Racket)"></a>Continue: Web Applications in Racket</h2><div class="SAuthorListBox"><span class="SAuthorList"><p class="author">Danny Yoo <<a href="mailto:dyoo@hashcollision.org">dyoo@hashcollision.org</a>><br/>and Jay McCarthy <<a href="mailto:jay@racket-lang.org">jay@racket-lang.org</a>></p></span></div><p>How do we make dynamic web applications?
In this tutorial, we show how to use Racket to achieve this goal.
We explain how to start up a web server, how to generate dynamic web content,
and how to interact with the user. Our working example will be a simple
web journal—<wbr></wbr>a “blog.”</p><p>This tutorial is intended for students who have read enough of
<span style="font-style: italic"><a href="http://www.htdp.org/">How to Design Programs</a></span> to know
how to use structures, higher-order functions, and a little bit of
mutation.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Getting_Started"">1<tt> </tt><a name="(part._.Getting_.Started)"></a>Getting Started</h3><p>Everything you need in this tutorial is provided in
<a href="http://racket-lang.org/">Racket</a>; we will be using the DrRacket module
language.</p><p>Enter the following into DrRacket’s Definitions window, then press the
<span class="ssansserif">Run</span> button.</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fxexpr..rkt%2529._response%252Fxexpr%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response/xexpr</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">html</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Under construction"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>If a web browser comes up with an “Under Construction” page, then clap your
hands with delight, because you’ve built your first web application! We
haven’t yet gotten it to do much, but we’ll get there. For now,
press the <span class="ssansserif">Stop</span> button to shut the server down.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""The_Application"">2<tt> </tt><a name="(part._.The_.Application)"></a>The Application</h3><p>We want to motivate this tutorial by showing you how to develop a blog.
Users of the blog should be able to create new posts and add comments to
existing posts. We’ll approach the task iteratively, pointing out one or two
pitfalls along the way. The game plan will be approximately as follows:</p><ul><li><p>Show a static list of posts.</p></li><li><p>Allow a user to add new posts to the system.</p></li><li><p>Extend the model to let a user add comments to a post.</p></li><li><p>Allow all users to share the same set of posts.</p></li><li><p>Serialize our data structures to disk.</p></li></ul><p>By the end of the tutorial, we’ll have a simple blogging application up and
running.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Basic_Blog"">3<tt> </tt><a name="(part._.Basic_.Blog)"></a>Basic Blog</h3><p>We begin by defining the necessary data structures. A post is:</p><blockquote class="SCodeFlow"><p><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span></p></blockquote><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>struct</p></div></div><p class="RForeground"><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="stt"> </span><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-1..rkt)._post-body))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-1..rkt)._post-title))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-1..rkt)._post~3f))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-1..rkt)._struct~3apost))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-1..rkt)._post))"></a><span class="RktSym"><span class="RktSymDef RktSym">post</span></span><span class="stt"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="stt"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span></p></blockquote></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">body</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Make a few examples of posts.</p><p>Next we define a blog to be simply a list of posts:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-1..rkt)._blog))"></a><span class="RktSym"><span class="RktSymDef RktSym">blog</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="hspace"> </span><span class="RktSym">post?</span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>Here, then, is a very simple example of a blog:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post!"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Hey, this is my first post!"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>Now let’s get our web application to show it.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Rendering_HTML"">4<tt> </tt><a name="(part._.Rendering_.H.T.M.L)"></a>Rendering HTML</h3><p>When a web browser visits our blog’s URL, the browser constructs a request
structure and sends it across the network to our application. We need a
function, which we’ll call <span class="RktSym">start</span>, to consume such requests and produce
responses to them. One basic kind of response is to show an HTML page; this is
done by the function <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fxexpr..rkt%2529._response%252Fxexpr%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response/xexpr</a></span>, which takes an <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>
representing the desired HTML. An <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a> is defined as</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktSym">xexpr/c</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._flat-rec-contract%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">flat-rec-contract</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._or%252Fc%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">or/c</a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._cons%252Fc%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">cons/c</a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=symbols.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._symbol%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">symbol?</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="hspace"> </span><span class="RktSym">xexpr</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._cons%252Fc%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">cons/c</a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=symbols.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._symbol%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">symbol?</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._cons%252Fc%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">cons/c</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._list%252Fc%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list/c</a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=symbols.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._symbol%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">symbol?</a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="hspace"> </span><span class="RktSym">xexpr</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>and the following examples illustrate how natural it is to use <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>s
to represent HTML.</p><p>The first alternative in <span class="RktSym">xexpr/c</span> is <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span>. For example,
the HTML <span class="stt">hello</span> is represented as <span class="RktVal">"hello"</span>. To guarantee valid
HTML, strings are automatically escaped when output. Thus, the <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>
<span class="RktVal">"<b>Unfinished tag"</span> is rendered as the HTML <span class="stt">&lt;b&gt;Unfinished
tag</span>, and not as <span class="stt"><b>Unfinished tag</span>. Similarly, <span class="RktVal">"<i>Finished\ntag</i>"</span> is rendered as <span class="stt">&lt;i&gt;Finished tag&lt;/i&gt;</span>, and not as
<span class="stt"><i>Finished tag</i></span>.</p><p>The second alternative in <span class="RktSym">xexpr/c</span> is the recursive contract
<span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._cons%252Fc%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">cons/c</a></span><span class="stt"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=symbols.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._symbol%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">symbol?</a></span><span class="stt"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="stt"> </span><span class="RktSym">xexpr</span><span class="RktPn">)</span><span class="RktPn">)</span>. For example, the HTML <span class="stt"><p>This is
an example</p></span> is represented by the <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a></p><p><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">p</span><span class="stt"> </span><span class="RktVal">"This is an example"</span><span class="RktVal">)</span>.</p><p>And finally, the third alternative in <span class="RktSym">xexpr/c</span> allows for parameters in
HTML tags. As examples, <span class="stt"><a href="link.html">Past</a></span> is represented by</p><p><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">a</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="stt"> </span><span class="RktVal">"link.html"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">"Past"</span><span class="RktVal">)</span></p><p>and <span class="stt"><p>This is <div class="emph">another</div> example.</p></span> is represented by</p><p><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">p</span><span class="stt"> </span><span class="RktVal">"This is "</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="stt"> </span><span class="RktVal">"emph"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">"another"</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">" example."</span><span class="RktVal">)</span>.</p><p>We could also have produced these <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>s “manually,” using
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._cons%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">cons</a></span> and <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span>, but that can get notationally heavy. For
example, the following Racket expressions both evaluate to the same
<a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Some title"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">"This is a simple static page."</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td></td></tr></table></blockquote><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Some title"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">"This is a simple static page."</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr></table></blockquote><p>But the latter is much easier to read and type, because it uses a leading
forward quote mark to express the list structure concisely. This is
how to construct static html responses with aplomb! (For more on
the extended list abbreviation form, see
<a href="http://htdp.org/2003-09-26/Book/curriculum-Z-H-17.html#node_chap_13">Section
13</a> of <a href="http://htdp.org/">How to Design Programs</a>.)</p><p>It turns out, however, that this simple kind of list abbreviation cannot
produce web content that is dynamic. For if we try to inject expressions into
an <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a> constructed by simple list abbreviation, those expressions will be
treated as part of the list structure, literally! What we need instead is a
notation that gives us the convenience of quoted list abbreviations, but that
also allows us to treat portions of the list structure as normal
expressions. That is, we would like to define a <span style="font-style: italic">template</span> whose
placeholders can be expressed easily and filled in dynamically.</p><p>Racket provides this templating functionality, in the form of a notation called
quasiquote. In quasiquotation a list is abbreviated not with a leading forward
quote but with a leading back quote. If we wish any subexpression of this
backquoted list to be evaluated normally (“unquoted”), then all we
have to do is place a comma in front that subexpression. For example:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-greeting: string -> response</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a name, and produces a dynamic response.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-greeting</span><span class="hspace"> </span><span class="RktSym">a-name</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fxexpr..rkt%2529._response%252Fxexpr%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response/xexpr</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Welcome"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string-append%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string-append</a></span><span class="hspace"> </span><span class="RktVal">"Hello "</span><span class="hspace"> </span><span class="RktSym">a-name</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Write a function that consumes a <span class="RktSym">post</span> and produces
an <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a> representing that content.</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-1..rkt)._render-post))"></a><span class="RktSym"><span class="RktSymDef RktSym">render-post</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post?</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">xexpr/c</span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>As an example, we want:</p><blockquote class="SCodeFlow"><p><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First post!"</span><span class="hspace"> </span><span class="RktVal">"This is a first post."</span><span class="RktPn">)</span><span class="RktPn">)</span></p></blockquote><p>to produce:</p><blockquote class="SCodeFlow"><p><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="hspace"> </span><span class="RktVal">"First post!"</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">"This is a first post."</span><span class="RktVal">)</span><span class="RktVal">)</span></p></blockquote><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>We will sometimes want to embed a list of <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>s into
another list that acts as a template. For example, given the list of
<a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>s <span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Larry"</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Curly"</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Moe"</span><span class="RktVal">)</span><span class="RktVal">)</span>, we may want
to create the single <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a></p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktVal">"Larry"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktVal">"Curly"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktVal">"Moe"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr></table></blockquote><p>This can’t be done using plain unquoting, because placing a comma in
front of <span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Larry"</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Curly"</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Moe"</span><span class="RktVal">)</span><span class="RktVal">)</span> will unquote
the entire list, yielding the malformed expression
<span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Larry"</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Curly"</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Moe"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span>.</p><p>Instead, we must splice the list in, like so: <span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="stt"> </span><span class="RktRdr">,@</span><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Larry"</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Curly"</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">li</span><span class="stt"> </span><span class="RktVal">"Moe"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span>. The
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=quasiquote.html%23%2528form._%2528%2528quote._%7E23%7E25kernel%2529._unquote-splicing%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">unquote-splicing</a></span> form, <span class="RktRdr">,@</span><span class="RktSym">expression</span>, allows us
conveniently to splice a list of <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a> fragments into a larger
template list. To generalize the example, here are two helper
functions that convert any list of <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>s into one <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>
representing an unordered, itemized HTML list:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-itemized-list: (listof xexpr) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a list of items, and produces a rendering</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as an unordered list.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528lib._racket%252Fprivate%252Fmap..rkt%2529._map%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">map</a></span><span class="hspace"> </span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-item: xexpr -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes an xexpr, and produces a rendering</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as a list item.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">a-fragment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-fragment</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Write a function <span class="RktSym">render-posts</span> that consumes a <span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="stt"> </span><span class="RktSym">post?</span><span class="RktPn">)</span>
and produces an <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a> for that content.</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-1..rkt)._render-posts))"></a><span class="RktSym"><span class="RktSymDef RktSym">render-posts</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="hspace"> </span><span class="RktSym">post?</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">xexpr/c</span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>As examples,</p><blockquote class="SCodeFlow"><p><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528lib._racket%252Flist..rkt%2529._empty%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">empty</a></span><span class="RktPn">)</span></p></blockquote><p>should produce</p><blockquote class="SCodeFlow"><p><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></p></blockquote><p>and</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Post 1"</span><span class="hspace"> </span><span class="RktVal">"Body 1"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Post 2"</span><span class="hspace"> </span><span class="RktVal">"Body 2"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>should produce</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="hspace"> </span><span class="RktVal">"Post 1"</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">"Body 1"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="hspace"> </span><span class="RktVal">"Post 2"</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">"Body 2"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr></table></blockquote><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>Now that we have the <span class="RktSym">render-posts</span> function handy, let’s revisit our
web application and change our <span class="RktSym">start</span> function to return an interesting
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fresponse-structs..rkt%2529._response%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response</a></span>.</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (listof post)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and a post is a (post title body)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG: blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The static blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">BLOG</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span><span class="hspace"> </span><span class="RktVal">"This is another post"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span><span class="hspace"> </span><span class="RktVal">"This is my first post"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> response</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request, and produces a page that displays all of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: blog request -> response</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a request, and produces an HTML page</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of the content of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: blog -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog, produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>If we press Run, we should see the blog posts in our web browser.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Inspecting_Requests"">5<tt> </tt><a name="(part._.Inspecting_.Requests)"></a>Inspecting Requests</h3><p>Our application is still a bit too static: we build the
page dynamically, but we don’t yet provide a way for the user
to create new posts. Let’s tackle that now, by providing a form that
lets the user add a new blog entry. When the user presses the
submit button, we want the new post to appear at the top of the
page.</p><p>We haven’t yet done anything with the <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span> object that
we’ve been passing around. As you may already have guessed, it isn’t
really supposed to be ignored so much!
When a user fills out a web form and submits it, the user’s browser constructs
a new <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span> that contains the form’s
values, which we can extract on our end, using the function
<span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._request-bindings%29%29" class="RktValLink" data-pltdoc="x">request-bindings</a></span>:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/servlet..rkt)._request-bindings))"></a><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._request-bindings%29%29" class="RktValDef RktValLink" data-pltdoc="x">request-bindings</a></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request?</a></span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">bindings?</span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>To extract a single web form value from a set of bindings, Racket provides
the function <span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._extract-binding%2Fsingle%29%29" class="RktValLink" data-pltdoc="x">extract-binding/single</a></span>, which also takes the name
of the corresponding field of the web form:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/servlet..rkt)._extract-binding/single))"></a><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._extract-binding%2Fsingle%29%29" class="RktValDef RktValLink" data-pltdoc="x">extract-binding/single</a></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=symbols.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._symbol%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">symbol?</a></span><span class="hspace"> </span><span class="RktSym">bindings?</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>To verify that a set of bindings contains a particular field, use
<span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._exists-binding~3f%29%29" class="RktValLink" data-pltdoc="x">exists-binding?</a></span>:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/servlet..rkt)._exists-binding~3f))"></a><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._exists-binding~3f%29%29" class="RktValDef RktValLink" data-pltdoc="x">exists-binding?</a></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=symbols.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._symbol%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">symbol?</a></span><span class="hspace"> </span><span class="RktSym">bindings?</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=booleans.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._boolean%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">boolean?</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>With these functions, we can design functions that consume <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span>s
and respond to them usefully.</p><p><span style="font-weight: bold">Exercise.</span> Write a function <span class="RktSym">can-parse-post?</span> that consumes a set
of bindings.
It should produce <span class="RktVal">#t</span> if there exist bindings both for the symbols
<span class="RktVal">'</span><span class="RktVal">title</span> and <span class="RktVal">'</span><span class="RktVal">body</span>, and <span class="RktVal">#f</span> otherwise.</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-2..rkt)._can-parse-post~3f))"></a><span class="RktSym"><span class="RktSymDef RktSym">can-parse-post?</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">bindings?</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=booleans.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._boolean%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">boolean?</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Write a function <span class="RktSym">parse-post</span> that consumes a set of
bindings.
Assuming that the bindings structure has values for the symbols <span class="RktVal">'</span><span class="RktVal">title</span>
and <span class="RktVal">'</span><span class="RktVal">body</span>, <span class="RktSym">parse-post</span> should produce a post containing those
values.</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-2..rkt)._parse-post))"></a><span class="RktSym"><span class="RktSymDef RktSym">parse-post</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">bindings?</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">post?</span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>Now that we have these helper functions, we can extend our web
application to handle form input. We’ll add a small form at the
bottom of the web page, and we’ll adjust our program to handle the addition of new posts.
So our new <span class="RktSym">start</span> method will check that the <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span> has a
parsable post, will then try to extend the set of posts, and will finally display the new set of blog posts:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (listof post)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and a post is a (post title body)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG: blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The static blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">BLOG</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span><span class="hspace"> </span><span class="RktVal">"This is another post"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span><span class="hspace"> </span><span class="RktVal">"This is my first post"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> response</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request and produces a page that displays all of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cond</span><span class="hspace"> </span><span class="RktPn">[</span><span class="RktPn">(</span><span class="RktSym">can-parse-post?</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cons</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="RktPn">)</span><span class="RktPn">]</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">[</span><span class="RktSym">else</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="RktPn">]</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">can-parse-post?: bindings -> boolean</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Produces true if bindings contains values for </span><span class="RktCmt">'</span><span class="RktCmt">title and </span><span class="RktCmt">'</span><span class="RktCmt">body.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">can-parse-post?</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">and</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">exists-binding?</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">exists-binding?</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">parse-post: bindings -> post</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a bindings, and produces a post out of the bindings.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: blog request -> response</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a request, and produces an HTML page</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of the content of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"title"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"body"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: blog -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog, produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>This solution seems to work, but it has a flaw! Try to add
two new posts. What happens?</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Advanced_Control_Flow"">6<tt> </tt><a name="(part._.Advanced_.Control_.Flow)"></a>Advanced Control Flow</h3><p>For the moment, let’s ignore the admittedly huge problem of having a
blog that accepts only one new blog entry. Don’t worry, we’ll fix
this!</p><p>The more pressing problem right now is a higher-level one: although we
do
have a function, <span class="RktSym">start</span>, that responds to <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span>s directed
at our application’s URL, that function has begun to take on
too much responsibility. In particular, <span class="RktSym">start</span> now handles
two different kinds of <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span>s: those for showing a
blog, and those for adding new blog posts. It has become a kind of traffic cop
—<wbr></wbr> a dispatcher —<wbr></wbr> for all of our web application’s behaviors, including any
new functionality we may want to add later. Life would be easier for
<span class="RktSym">start</span>
(and for us) if different kinds of <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span>s were instead
directed automatically to different functions. Is this possible in Racket?</p><p>Yes! The web server library provides a function,
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=servlet.html%23%2528def._%2528%2528lib._web-server%252Fservlet%252Fweb..rkt%2529._send%252Fsuspend%252Fdispatch%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">send/suspend/dispatch</a></span>, that allows us to create URLs that direct
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span>s aimed at them to specific functions in our application. We
demonstrate with a dizzying example. In a new file, enter the following in
DrRacket’s Definitions window.</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> response</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">phase-1</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">phase-1: request -> response</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">phase-1</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fxexpr..rkt%2529._response%252Fxexpr%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response/xexpr</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Phase 1"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">phase-2</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"click me!"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=servlet.html%23%2528def._%2528%2528lib._web-server%252Fservlet%252Fweb..rkt%2529._send%252Fsuspend%252Fdispatch%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">send/suspend/dispatch</a></span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">phase-2: request -> response</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">phase-2</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fxexpr..rkt%2529._response%252Fxexpr%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response/xexpr</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Phase 2"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">phase-1</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"click me!"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=servlet.html%23%2528def._%2528%2528lib._web-server%252Fservlet%252Fweb..rkt%2529._send%252Fsuspend%252Fdispatch%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">send/suspend/dispatch</a></span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>This is a web application that goes round and round. When a user
first visits the application, the user starts off in <span class="RktSym">phase-1</span>. The
generated page has a hyperlink that, when clicked, continues to
<span class="RktSym">phase-2</span>. The user can click back, and falls back to <span class="RktSym">phase-1</span>, and the
cycle repeats.</p><p>Let’s look more closely at the <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=servlet.html%23%2528def._%2528%2528lib._web-server%252Fservlet%252Fweb..rkt%2529._send%252Fsuspend%252Fdispatch%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">send/suspend/dispatch</a></span> mechanism.
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=servlet.html%23%2528def._%2528%2528lib._web-server%252Fservlet%252Fweb..rkt%2529._send%252Fsuspend%252Fdispatch%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">send/suspend/dispatch</a></span> consumes a response-generating function and
gives it another function, called <span class="RktSym">embed/url</span>, that we’ll
use to build special URLs. What makes these URLs special is this:
when a web browser visits one of them, our web application restarts,
not from <span class="RktSym">start</span>, but from the handler that we associate with the
URL. In the handler <span class="RktSym">phase-1</span>, the use of <span class="RktSym">embed/url</span> associates
the link with the handler <span class="RktSym">phase-2</span>, and vice versa.</p><p>We can be even more sophisticated about the handlers associated with
<span class="RktSym">embed/url</span>. Because a handler is just a request-consuming
function, it can be defined locally and so can see all the other
variables in the scope of its definition. Here’s another loopy
example:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> response</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">show-counter</span><span class="hspace"> </span><span class="RktVal">0</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">show-counter: number request -> doesn't return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Displays a number that's hyperlinked: when the link is pressed,</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">returns a new page with the incremented number.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">show-counter</span><span class="hspace"> </span><span class="RktSym">n</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fxexpr..rkt%2529._response%252Fxexpr%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response/xexpr</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Counting example"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">next-number-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=generic-numbers.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._number-%7E3estring%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">number->string</a></span><span class="hspace"> </span><span class="RktSym">n</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">next-number-handler</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">show-counter</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=generic-numbers.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._%252B%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">+</a></span><span class="hspace"> </span><span class="RktSym">n</span><span class="hspace"> </span><span class="RktVal">1</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=servlet.html%23%2528def._%2528%2528lib._web-server%252Fservlet%252Fweb..rkt%2529._send%252Fsuspend%252Fdispatch%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">send/suspend/dispatch</a></span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>This example shows that we can accumulate the results of an
interaction. Even though the user starts off by visiting and seeing
zero, the handlers produced by <span class="RktSym">next-number-handler</span> continue the interaction, accumulating a larger and larger number.</p><p>We’re going in circles now, so let’s move forward and return to our blog
application. We’ll adjust the form’s action so that it directs a submission
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span> to a URL associated with a separate handler, called
<span class="RktSym">insert-post-handler</span>.</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (listof post)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and a post is a (post title body)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG: blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The static blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">BLOG</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span><span class="hspace"> </span><span class="RktVal">"This is another post"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span><span class="hspace"> </span><span class="RktVal">"This is my first post"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request and produces a page that displays all of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">parse-post: bindings -> post</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Extracts a post out of the bindings.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: blog request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a request, and produces an HTML page</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of the content of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"title"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"body"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cons</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: blog -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog, produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>Note that the structure of the <span class="RktSym">render-blog-page</span> function is very
similar to that of our last <span class="RktSym">show-counter</span> example. The user can
finally add and see multiple posts to the blog.</p><p>Unfortunately, our design still suffers from a problem, which can be seen by
adding a few posts to the system, and then visiting the web application’s URL
in a new browser window. What happens when you try this?</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Share_and_Share_Alike"">7<tt> </tt><a name="(part._.Share_and_.Share_.Alike)"></a>Share and Share Alike</h3><p>The problem with our application is that each browser window keeps
track of its own distinct blog. For most people, this defeats the
purpose of a blog, which is to share with others! When we insert a
new post, rather than creating a new blog value, we’d like to modify
<span style="font-style: italic">the</span> blog. In other words, we’d like to make a structural
change. (<span style="font-style: italic"><a href="http://www.htdp.org/">How to Design
Programs</a></span>, Chapter 41). So let’s switch from just the <span class="RktSym">BLOG</span>
binding to a list and instead bind it to a mutable structure. If we
were to just use a structure, we’d write the following:</p><blockquote class="SCodeFlow"><p><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="RktPn">)</span></p></blockquote><p>But, by default, structures in Racket are immutable. To gain access
to structure mutators, we’ll need to override this default, by adding
the <span class="RktPn">#:mutable</span> keyword to some of our structure
definitions. In particular, if we want to allow changes to a blog, we
must change our definition of the blog structure to the following:</p><blockquote class="SCodeFlow"><p><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></p></blockquote><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>struct</p></div></div><p class="RForeground"><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="stt"> </span><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-4..rkt)._blog-posts))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-4..rkt)._blog~3f))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-4..rkt)._struct~3ablog))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-4..rkt)._blog))"></a><span class="RktSym"><span class="RktSymDef RktSym">blog</span></span><span class="stt"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="RktPn">)</span></p></blockquote></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">posts</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="hspace"> </span><span class="RktSym">post?</span><span class="RktPn">)</span></td></tr></table></blockquote><p>A mutable structure provides functions that change its fields; in this case, we
are provided the structure mutator <span class="RktSym">set-blog-posts!</span>,
which allows us to change the posts of a blog:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-4..rkt)._set-blog-posts!))"></a><span class="RktSym"><span class="RktSymDef RktSym">set-blog-posts!</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog?</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="hspace"> </span><span class="RktSym">post?</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=void.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._void%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">void</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Write a function <span class="RktSym">blog-insert-post!</span></p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-4..rkt)._blog-insert-post!))"></a><span class="RktSym"><span class="RktSymDef RktSym">blog-insert-post!</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog?</span><span class="hspace"> </span><span class="RktSym">post?</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=void.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._void%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">void</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>whose intended side effect is to extend a blog’s posts.</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>We must now modify the web application to use our new data representation of a
blog. Since the blog is now referred to by the global variable
<span class="RktSym">BLOG</span>, it no longer needs to be passed as a parameter to handlers like
<span class="RktSym">render-blog-page</span>. Here is our updated web application, after
adjustments that incorporate <span class="RktSym">insert-blog-post!</span>, and after a bit of
variable cleanup:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (blog posts)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where posts is a (listof post)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and post is a (post title body)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where title is a string, and body is a string</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG: blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The initial BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">BLOG</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span><span class="hspace"> </span><span class="RktVal">"This is another post"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span><span class="hspace"> </span><span class="RktVal">"This is my first post"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-insert-post!: blog post -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a post, adds the post at the top of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-blog-posts!</span><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cons</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request and produces a page that displays</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">all of the web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">parse-post: bindings -> post</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Extracts a post out of the bindings.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Produces an HTML page of the content of the BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"title"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"body"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog, produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>Now visit the blog from two separate browser windows and add
posts from each of them. You’ll be glad to see that both windows share
the same blog!</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Extending_the_Model"">8<tt> </tt><a name="(part._.Extending_the_.Model)"></a>Extending the Model</h3><p>Next, let’s extend the application so that a post can include a list
of comments. The data definition becomes:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>struct</p></div></div><table cellspacing="0" cellpadding="0" class="RForeground"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span></td><td><span class="hspace"> </span></td><td><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._set-post-comments!))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._set-post-body!))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._set-post-title!))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._post-comments))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._post-body))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._post-title))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._post~3f))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._struct~3apost))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._post))"></a><span class="RktSym"><span class="RktSymDef RktSym">post</span></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="stt"> </span><span class="RktSym">body</span><span class="stt"> </span><span class="RktSym">comments</span><span class="RktPn">)</span></td></tr></table></blockquote></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">body</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">comments</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span><span class="RktPn">)</span></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Write the updated data structure definition for posts. Make
sure to make the structure mutable, since we intend to add comments to
posts.</p><p><span style="font-weight: bold">Exercise.</span> Make up a few examples of posts.</p><p><span style="font-weight: bold">Exercise.</span> Define a function <span class="RktSym">post-insert-comment!</span></p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-5..rkt)._post-insert-comment!))"></a><span class="RktSym"><span class="RktSymDef RktSym">post-insert-comment!</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post?</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=void.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._void%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">void</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>whose intended side effect is to add a new comment to the end of the post’s
list of comments.</p><p><span style="font-weight: bold">Exercise.</span> Adjust <span class="RktSym">render-post</span> so that the produced fragment will include the
comments in an itemized list.</p><p><span style="font-weight: bold">Exercise.</span> Because we’ve extended <span class="RktSym">post</span> to include comments, you
also need to adjust other, post-manipulating parts of the application,
such as uses of <span class="RktSym">post</span>.</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>Now that we’ve adjusted our functions to accommodate <span class="RktSym">post</span>’s new
structure, our web application should be runnable. The user may even see some
of the fruits of our labor: if the initial <span class="RktSym">BLOG</span> has a post with
comments, the user should now see them. On the other hand, something is
obviously missing: the user is given no interface through which to add
comments!</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Breaking_Up_the_Display"">9<tt> </tt><a name="(part._.Breaking_.Up_the_.Display)"></a>Breaking Up the Display</h3><p>How should we incorporate comments more fully into the user’s web
experience? Seeing all the posts and comments on one page may
be a bit overwhelming, so maybe we should hold off on showing the
comments on the main blog page. Instead, let’s make a secondary
“detail” view of a post and present its comments there.
Accordingly, the top-level view of a blog will show only the title and body of
a post, and the number of its comments.</p><p>So now we need a way to visit a post’s detail page. One way to do
this is to hyperlink a post’s title: if one wishes to see a post’s
detail page, one should only have to click the post’s title. In that post’s
detail page, we can even add a form to let the user add new comments.
The page flow of this new version of our web application is then depicted
simply as:</p><p><img src="flow1.png" alt="" width="450" height="146"/></p><p>Each node (bubble) in this diagram corresponds to a request-consuming handler.
As you might expect, we’ll be using <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=servlet.html%23%2528def._%2528%2528lib._web-server%252Fservlet%252Fweb..rkt%2529._send%252Fsuspend%252Fdispatch%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">send/suspend/dispatch</a></span> some more.
Every arrow in the diagram will be realized as a URL that we generate
with <span class="RktSym">embed/url</span>.</p><p>This approach has a slightly messy consequence. Previously we rendered the list
of posts without any hyperlinks. But since any function that generates a
special dispatching URL must use <span class="RktSym">embed/url</span> to do so, we’ll need to
adjust <span class="RktSym">render-posts</span> and <span class="RktSym">render-post</span> to consume and use
<span class="RktSym">embed/url</span> itself when it makes those hyperlinked titles.</p><p>We now have a pretty sophisticated web application, one that permits the
creation of posts and the addition of comments. Here is what it looks like:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (blog posts)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where posts is a (listof post)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and post is a (post title body comments)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where title is a string, body is a string,</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and comments is a (listof string)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="hspace"> </span><span class="RktSym">comments</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG: blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The initial BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">BLOG</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is another post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is my first post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktVal">"First comment!"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-insert-post!: blog post -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a post, adds the post at the top of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-blog-posts!</span><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cons</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-insert-comment!: post string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and a comment string.</span><span class="hspace"> </span><span class="RktCmt">As a side-efect, </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">adds the comment to the bottom of the post</span><span class="RktCmt">'</span><span class="RktCmt">s list of comments.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-post-comments!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">append</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request, and produces a page that displays</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">all of the web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Produces an HTML page of the content of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"title"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"body"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">parse-post: bindings -> post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Extracts a post out of the bindings.</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post-detail-page: post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and request, and produces a detail page</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of the post. The user will be able to insert new comments.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h2</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-comment-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"comment"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-comment-handler</span><span class="hspace"> </span><span class="RktSym">a-request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">a-request</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The fragment contains a link to show a detailed view of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">view-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">view-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">number->string</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">length</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">" comment(s)"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a embed/url, and produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-itemized-list: (listof xexpr) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a list of items, and produces a rendering as</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">an unorderered list.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-item: xexpr -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes an xexpr, and produces a rendering</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as a list item.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">a-fragment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-fragment</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>But it still suffers from a problem: once in a <span class="RktSym">post-detail-page</span>, the
only way for the user to return to the blog is to use the Back button!
That’s disruptive, and it might allow the user get “stuck” in a
dark corner of the web application. To solve this problem, let’s improve the
page flow.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Adding_a_Back_Button"">10<tt> </tt><a name="(part._.Adding_a_.Back_.Button)"></a>Adding a Back Button</h3><p>Perhaps we should simply add a BACK link from the
<span class="RktSym">render-post-detail-page</span>, one that returns us to the top-level
blog. Here’s the corresponding page flow diagram:</p><p><img src="flow2.png" alt="" width="450" height="201"/></p><p><span style="font-weight: bold">Exercise.</span> Adjust <span class="RktSym">render-post-detail-page</span> to include another link that goes
back to <span class="RktSym">render-blog-page</span>.</p><p>And since a user may have a change of heart about a comment,
let’s enrich the flow to give the user a chance to back out of submitting one.</p><p><img src="flow3.png" alt="" width="500" height="257"/></p><p>Note that, although this change may seem complicated, it doesn’t affect the
general shape of our handlers:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (blog posts)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where posts is a (listof post)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and post is a (post title body comments)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where title is a string, body is a string,</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and comments is a (listof string)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="hspace"> </span><span class="RktSym">comments</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG: blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The initial BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">BLOG</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is another post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is my first post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktVal">"First comment!"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-insert-post!: blog post -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a post, adds the post at the top of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-blog-posts!</span><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cons</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-insert-comment!: post string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and a comment string.</span><span class="hspace"> </span><span class="RktCmt">As a side-efect, </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">adds the comment to the bottom of the post</span><span class="RktCmt">'</span><span class="RktCmt">s list of comments.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-post-comments!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">append</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request and produces a page that displays</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">all of the web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Produces an HTML page of the content of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"title"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"body"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">parse-post: bindings -> post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Extracts a post out of the bindings.</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post-detail-page: post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and produces a detail page of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The user will be able to either insert new comments</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">or go back to render-blog-page.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h2</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-comment-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"comment"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">back-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Back to the blog"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-comment-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">back-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-confirm-add-comment-page :</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">comment post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a comment that we intend to add to a post, as well</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as the request. If the user follows through, adds a comment </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and goes back to the display page. Otherwise, goes back to </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">the detail page of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"The comment: "</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-comment</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"will be added to "</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">yes-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Yes, add the comment."</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">cancel-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"No, I changed my mind!"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">yes-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cancel-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The fragment contains a link to show a detailed view of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">view-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">view-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">number->string</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">length</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">" comment(s)"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a embed/url, produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-itemized-list: (listof xexpr) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a list of items, and produces a rendering as</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">an unorderered list.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-item: xexpr -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes an xexpr, and produces a rendering</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as a list item.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">a-fragment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-fragment</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Decorating_With_Style_"">11<tt> </tt><a name="(part._.Decorating_.With_.Style_)"></a>Decorating With Style!</h3><p>Our web application is now functionally complete. But it’s visually
lacking, so let’s try to improve its appearance. One way to add visual panache
to our web pages is to use a cascading style sheet. For example, if we’d like
to make all of our paragraphs green, we might insert the following style
declaration into a response.</p><p><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">style</span><span class="stt"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="stt"> </span><span class="RktVal">"text/css"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="stt"> </span><span class="RktVal">"p { color: green }"</span><span class="RktVal">)</span></p><p>It is tempting to embed such declarations directly into our
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fresponse-structs..rkt%2529._response%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response</a></span>s. But our source file is already quite busy, and, as a
matter of principle, we should separate logical representation
from visual presentation. So, rather than embed the
.css in the HTML response directly, let’s instead add a link reference to a
separate .css file.</p><p>Up till now, all the content produced by our web application has
come from a response-generating handler. But this dynamic generation of HTML
is not necessary for content that doesn’t change.
Examples of such static resources include images, documents, and .css files.
To serve them alongside our web applications, we inform the web server of a
directory that we have created specially for static files. The function
<span class="RktSym">static-files-path</span>,</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-7..rkt)._static-files-path))"></a><span class="RktSym"><span class="RktSymDef RktSym">static-files-path</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Manipulating_Paths.html%23%2528def._%2528%2528lib._racket%252Fprivate%252Fmisc..rkt%2529._path-string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">path-string?</a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=void.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._void%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">void</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>tells the web server to look in the given path whenever it receives a URL
that looks like a request for a static resource.</p><p><span style="font-weight: bold">Exercise.</span> Create a simple web application called <span class="stt">"test-static.rkt"</span> with the
following content:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fxexpr..rkt%2529._response%252Fxexpr%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response/xexpr</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Testing"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">link</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">rel</span><span class="hspace"> </span><span class="RktVal">"stylesheet"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktVal">"/test-static.css"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"text/css"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Testing"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h2</span><span class="hspace"> </span><span class="RktVal">"This is a header"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">"This is "</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">span</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"hot"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="hspace"> </span><span class="RktVal">"hot"</span><span class="RktVal">)</span><span class="hspace"> </span><span class="RktVal">"."</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">static-files-path</span><span class="hspace"> </span><span class="RktVal">"htdocs"</span><span class="RktPn">)</span></td></tr></table></blockquote><p>Make a subdirectory called <span class="stt">"htdocs"</span>, rooted in the same directory as
the <span class="stt">"test-static.rkt"</span> source. Just to see that we can serve
this .css page, create a very simple .css file <span class="stt">"test-static.css"</span>
in <span class="stt">"htdocs/"</span> with the following content:</p><p><table cellspacing="0" cellpadding="0"><tr><td><p><span class="stt">body {</span></p></td></tr><tr><td><p><span class="stt"></span><span class="hspace"> </span><span class="stt">margin-left: 10%;</span></p></td></tr><tr><td><p><span class="stt"></span><span class="hspace"> </span><span class="stt">margin-right: 10%;</span></p></td></tr><tr><td><p><span class="stt">}</span></p></td></tr><tr><td><p><span class="stt">p { font-family: sans-serif }</span></p></td></tr><tr><td><p><span class="stt">h1 { color: green }</span></p></td></tr><tr><td><p><span class="stt">h2 { font-size: small }</span></p></td></tr><tr><td><p><span class="stt">span.hot { color: red }</span></p></td></tr></table></p><p>Now run the application and look at the browser’s output.
A Spartan web page should appear, but it should still have some color
in its cheeks.</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p><span style="font-weight: bold">Exercise.</span>
Improve the presentation of the blog web application by writing
an external style sheet that suits your tastes. Adjust all of the HTML
response handlers to include a link to the style sheet.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""The_Double_Submit_Error"">12<tt> </tt><a name="(part._.The_.Double_.Submit_.Error)"></a>The Double Submit Error</h3><p>Our application has yet another subtle problem. To see it,
bring the blog application up again, and add a post. Then reload the
page. Reload the page again.</p><p>What you are observing is the well known “double-submit” problem.
Whenever a user presses Reload, a request is sent to our application, and the
problem is that some requests make the application mutate data structures.</p><p>A common technique that web developers use to dodge the double-submission
problem is to redirect state-mutating requests to a different URL, one that is
safe to reload. This trick is implemented in Racket by the function
<span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._redirect%2Fget%29%29" class="RktValLink" data-pltdoc="x">redirect/get</a></span>:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/servlet..rkt)._redirect/get))"></a><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._redirect%2Fget%29%29" class="RktValDef RktValLink" data-pltdoc="x">redirect/get</a></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request?</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote><p>Its immediate side effect is to force the user’s browser to follow a
redirection to a safe URL, and it gives us back that fresh new request.</p><p>For example, consider a toy application that lets the user add
names to a roster:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A roster is a (roster names)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where names is a list of string.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">roster</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">names</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">roster-add-name!: roster string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Given a roster and a name, adds the name</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">to the end of the roster.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster-add-name!</span><span class="hspace"> </span><span class="RktSym">a-roster</span><span class="hspace"> </span><span class="RktSym">a-name</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-roster-names!</span><span class="hspace"> </span><span class="RktSym">a-roster</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">append</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster-names</span><span class="hspace"> </span><span class="RktSym">a-roster</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktSym">a-name</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">ROSTER</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">"kathi"</span><span class="hspace"> </span><span class="RktVal">"shriram"</span><span class="hspace"> </span><span class="RktVal">"dan"</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">show-roster</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">show-roster: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">show-roster</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Roster"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Roster"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster-names</span><span class="hspace"> </span><span class="RktSym">ROSTER</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">add-name-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"a-name"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-name</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">a-name</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">add-name-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster-add-name!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">ROSTER</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-name</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">show-roster</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-itemized-list: (listof xexpr) -> xexpr</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-item: xexpr -> xexpr</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">a-fragment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-fragment</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>This application suffers from the same problem as our blog does: if the user
adds a name, and then presses reload, then the same name will be added
twice.</p><p>We can fix this by changing a single expression; can you find it
below?</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A roster is a (roster names)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where names is a list of string.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">roster</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">names</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">roster-add-name!: roster string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Given a roster and a name, adds the name</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">to the end of the roster.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster-add-name!</span><span class="hspace"> </span><span class="RktSym">a-roster</span><span class="hspace"> </span><span class="RktSym">a-name</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-roster-names!</span><span class="hspace"> </span><span class="RktSym">a-roster</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">append</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster-names</span><span class="hspace"> </span><span class="RktSym">a-roster</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktSym">a-name</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">ROSTER</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">"kathi"</span><span class="hspace"> </span><span class="RktVal">"shriram"</span><span class="hspace"> </span><span class="RktVal">"dan"</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">show-roster</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">show-roster: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">show-roster</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Roster"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Roster"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster-names</span><span class="hspace"> </span><span class="RktSym">ROSTER</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">add-name-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"a-name"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-name</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">a-name</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">add-name-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">roster-add-name!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">ROSTER</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-name</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">show-roster</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">redirect/get</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-itemized-list: (listof xexpr) -> xexpr</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-item: xexpr -> xexpr</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">a-fragment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-fragment</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>So the double-submit error is easy to prevent: whenever you have
handlers that mutate the state of the system, use <span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._redirect%2Fget%29%29" class="RktValLink" data-pltdoc="x">redirect/get</a></span> when
sending back your response.</p><p><span style="font-weight: bold">Exercise.</span>
Use <span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._redirect%2Fget%29%29" class="RktValLink" data-pltdoc="x">redirect/get</a></span> to fix the double-submit error in the blog
application.</p><p>With these minor fixes, our blog application now looks like this:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (blog posts)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where posts is a (listof post)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and post is a (post title body comments)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where title is a string, body is a string,</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and comments is a (listof string)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="hspace"> </span><span class="RktSym">comments</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG: blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The initial BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">BLOG</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is another post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is my first post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktVal">"First comment!"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-insert-post!: blog post -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a post, adds the post at the top of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-blog-posts!</span><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cons</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-insert-comment!: post string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and a comment string.</span><span class="hspace"> </span><span class="RktCmt">As a side-efect, </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">adds the comment to the bottom of the post</span><span class="RktCmt">'</span><span class="RktCmt">s list of comments.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-post-comments!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">append</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request and produces a page that displays</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">all of the web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Produces an HTML page of the content of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"title"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"body"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">parse-post: bindings -> post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Extracts a post out of the bindings.</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">redirect/get</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post-detail-page: post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and produces a detail page of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The user will be able to either insert new comments</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">or go back to render-blog-page.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h2</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-comment-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"comment"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">back-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Back to the blog"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-comment-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">back-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-confirm-add-comment-page :</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">comment post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a comment that we intend to add to a post, as well</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as the request. If the user follows through, adds a comment </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and goes back to the display page. Otherwise, goes back to </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">the detail page of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"The comment: "</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-comment</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"will be added to "</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">yes-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Yes, add the comment."</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">cancel-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"No, I changed my mind!"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">yes-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">redirect/get</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cancel-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The fragment contains a link to show a detailed view of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">view-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">view-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">number->string</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">length</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">" comment(s)"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a embed/url, produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-itemized-list: (listof xexpr) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a list of items, and produces a rendering as</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">an unorderered list.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-item: xexpr -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes an xexpr, and produces a rendering</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as a list item.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">a-fragment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-fragment</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Abstracting_the_Model"">13<tt> </tt><a name="(part._.Abstracting_the_.Model)"></a>Abstracting the Model</h3><p>If we “turn off the lights” by closing the program, the state of
our application disappears into the ether. How do we get our
ephemeral state to stick around? Before we tackle this problem, note that it
does not apply to all of the application’s state, for we have no long-term
interest in things like requests. What we do care about saving is our model of
the blog.</p><p>If we look closely at our web application program, we see a seam
between the model of our blog, and the web application that uses that
model. Let’s isolate the model; it’s all the stuff near the top:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="hspace"> </span><span class="RktSym">comments</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=stx-patterns.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fstxcase-scheme..rkt%2529._......%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">...</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=stx-patterns.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fstxcase-scheme..rkt%2529._......%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">...</a></span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=stx-patterns.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fstxcase-scheme..rkt%2529._......%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">...</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=stx-patterns.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fstxcase-scheme..rkt%2529._......%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">...</a></span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=stx-patterns.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fstxcase-scheme..rkt%2529._......%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">...</a></span><span class="RktPn">)</span></td></tr></table></blockquote><p>In realistic web applications, the model and the web application are
separated by a wall of abstraction. In theory, this separation
allows us to make isolated changes in future without breaking the entire
system. So let’s start separating. First we’ll rip the model out into a
separate file, and then we’ll look into making the model persistent.</p><p>Create a new file called <span class="stt">"model.rkt"</span> with the following content.</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=index.html&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">racket/base</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (blog posts)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where posts is a (listof post)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and post is a (post title body comments)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where title is a string, body is a string, </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and comments is a (listof string)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="hspace"> </span><span class="RktSym">comments</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG: blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The initial BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">BLOG</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is another post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is my first post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktVal">"First comment!"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-insert-post!: blog post -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a post, adds the post at the top of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-blog-posts!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cons</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-insert-comment!: post string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and a comment string.</span><span class="hspace"> </span><span class="RktCmt">As a side-efect, </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">adds the comment to the bottom of the post</span><span class="RktCmt">'</span><span class="RktCmt">s list of comments.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-post-comments!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">append</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">provide</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">all-defined-out</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p>This is essentially a cut-and-paste of the lines we identified as our
model. It’s written in the <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=index.html&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">racket</span></a> language because
the model shouldn’t need to worry about web-server stuff. There’s one
additional expression that looks a little odd at first:</p><blockquote class="SCodeFlow"><p><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._provide%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">provide</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._all-defined-out%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">all-defined-out</a></span><span class="RktPn">)</span><span class="RktPn">)</span></p></blockquote><p>It tells Racket to grant other files access to
everything that’s defined in the <span class="stt">"model.rkt"</span> file.</p><p>Now we go back to our web application and change it to use this model, by
replacing the deleted model code with the expression</p><blockquote class="SCodeFlow"><p><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._require%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">require</a></span><span class="hspace"> </span><span class="RktVal">"model.rkt"</span><span class="RktPn">)</span></p></blockquote><p>which hooks our web application module up to the <span class="RktVal">"model.rkt"</span>
module.</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">require</span><span class="hspace"> </span><span class="RktVal">"model.rkt"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request and produces a page that displays</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">all of the web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Produces an HTML page of the content of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">BLOG.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"title"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"body"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">parse-post: bindings -> post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Extracts a post out of the bindings.</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">redirect/get</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post-detail-page: post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and produces a detail page of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The user will be able to either insert new comments</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">or go back to render-blog-page.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h2</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-comment-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"comment"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">back-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Back to the blog"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-comment-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">back-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-confirm-add-comment-page :</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">comment post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a comment that we intend to add to a post, as well</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as the request. If the user follows through, adds a comment </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and goes back to the display page. Otherwise, goes back to </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">the detail page of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"The comment: "</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-comment</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"will be added to "</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">yes-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Yes, add the comment."</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">cancel-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"No, I changed my mind!"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">yes-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">redirect/get</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cancel-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The fragment contains a link to show a detailed view of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">view-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">view-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">number->string</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">length</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">" comment(s)"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a embed/url, produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">BLOG</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-itemized-list: (listof xexpr) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a list of items, and produces a rendering as</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">an unorderered list.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-item: xexpr -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes an xexpr, and produces a rendering </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as a list item.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">a-fragment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-fragment</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""A_Persistent_Model"">14<tt> </tt><a name="(part._.A_.Persistent_.Model)"></a>A Persistent Model</h3><p>Now that the model resides in a separate module, we can more easily modify
it and, in particular, can make it persistent.</p><p>The first step is to make the model structures serializable. Earlier, we made the
structures mutable by adding <span class="RktPn">#:mutable</span> to their
definitions. Similarly, when the keyword <span class="RktPn">#:prefab</span> is added to the
definition of a structure, Racket understands that the structure can be
“previously fabricated,” that is, created before the program started
running—<wbr></wbr>which is exactly what we want when restoring the blog data from
disk. Our blog structure definition now looks like:</p><blockquote class="SCodeFlow"><p><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="hspace"> </span><span class="RktPn">#:prefab</span><span class="RktPn">)</span></p></blockquote><p><span class="RktSym">blog</span> structures can now be read from the outside world with
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Reading.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._read%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">read</a></span> and written to it with <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Writing.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._write%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">write</a></span>. But we also need to make
sure that everything inside a <span class="RktSym">blog</span> structure is also (transitively) marked
as <span class="RktPn">#:prefab</span>.</p><p><span style="font-weight: bold">Exercise.</span> Write the new structure definition for posts.</p><p>At this point, we <span style="font-style: italic">can</span> read and write the blog to disk. So let’s do it.
First, we’ll add to the model a path pointing to where the blog resides on
disk:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>struct</p></div></div><table cellspacing="0" cellpadding="0" class="RForeground"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span></td><td><span class="hspace"> </span></td><td><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-9..rkt)._set-blog-posts!))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-9..rkt)._set-blog-home!))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-9..rkt)._blog-posts))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-9..rkt)._blog-home))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-9..rkt)._blog~3f))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-9..rkt)._struct~3ablog))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-9..rkt)._blog))"></a><span class="RktSym"><span class="RktSymDef RktSym">blog</span></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">home</span><span class="stt"> </span><span class="RktSym">posts</span><span class="RktPn">)</span></td></tr></table></blockquote></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:mutable</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:prefab</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">home</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">posts</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=data-structure-contracts.html%23%2528def._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._listof%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">listof</a></span><span class="hspace"> </span><span class="RktSym">post?</span><span class="RktPn">)</span></td></tr></table></blockquote><p>Notice that we will need to convert the path into a string. Why didn’t we just
make the blog structure contain paths? Answer: They can’t be used with
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Reading.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._read%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">read</a></span> and <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Writing.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._write%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">write</a></span>.</p><p><span style="font-weight: bold">Exercise.</span> Write the new structure definition for blogs.</p><p>Next we create a function that allows our application to initialize the blog:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">initialize-blog! : path? -> blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Reads a blog from a path, if not present, returns default</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">initialize-blog!</span><span class="hspace"> </span><span class="RktSym">home</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">log-missing-exn-handler</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=exns.html%23%2528def._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._exn%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">exn</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Manipulating_Paths.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._path-%7E3estring%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">path->string</a></span><span class="hspace"> </span><span class="RktSym">home</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is my first post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktVal">"First comment!"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is another post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktSym">the-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=exns.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fmore-scheme..rkt%2529._with-handlers%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">with-handlers</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktPn">[</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=exns.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._exn%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">exn?</a></span><span class="hspace"> </span><span class="RktSym">log-missing-exn-handler</span><span class="RktPn">]</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=file-ports.html%23%2528def._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._with-input-from-file%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">with-input-from-file</a></span><span class="hspace"> </span><span class="RktSym">home</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Reading.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._read%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">read</a></span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-blog-home!</span><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Manipulating_Paths.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._path-%7E3estring%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">path->string</a></span><span class="hspace"> </span><span class="RktSym">home</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="RktPn">)</span></td></tr></table></blockquote><p><span class="RktSym">initialize-blog!</span> takes a path and tries to <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Reading.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._read%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">read</a></span> from it. If
the path contains a <span class="RktSym">blog</span> structure, then <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Reading.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._read%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">read</a></span> will parse it,
because <span class="RktSym">blog</span>s are <span class="RktPn">#:prefab</span>. If there is no file at the path,
or if the file has some spurious data, then <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Reading.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._read%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">read</a></span> or
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=file-ports.html%23%2528def._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._with-input-from-file%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">with-input-from-file</a></span> will throw an exception. <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=exns.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fmore-scheme..rkt%2529._with-handlers%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">with-handlers</a></span>
supplies an exception handler that reacts to any error by returning the default
<span class="RktSym">blog</span> structure. After <span class="RktSym">the-blog</span> is bound to the newly read
(or default) structure, we set the home to the correct path.</p><p>Next we need a function to save the model to the disk:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">save-blog! : blog -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Saves the contents of a blog to its home</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">save-blog!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">write-to-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Writing.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._write%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">write</a></span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=file-ports.html%23%2528def._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._with-output-to-file%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">with-output-to-file</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-home</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">write-to-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:exists</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">replace</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p><span class="RktSym">save-blog!</span> <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Writing.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._write%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">write</a></span>s the model to its home; by supplying an
<span class="RktPn">#:exists</span> clause to <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=file-ports.html%23%2528def._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._with-output-to-file%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">with-output-to-file</a></span>, it ensures that the
old contents on disk will be overwritten.</p><p>This function can now be used to save the blog structure whenever it is
modified by the user. Since modifications are made only by the model, only
<span class="RktSym">blog-insert-post!</span> and <span class="RktSym">post-insert-comment!</span> will need to be
updated.</p><p><span style="font-weight: bold">Exercise.</span> Change <span class="RktSym">blog-insert-post!</span> and
<span class="RktSym">post-insert-comment!</span> to call <span class="RktSym">save-blog!</span>.</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>You may have noticed a problem when trying to update
<span class="RktSym">post-insert-comment!</span>: the function has no blog to pass to
<span class="RktSym">save-blog!</span>. We will therefore need to give it a blog argument and
change the application appropriately. While we’re at it, let’s change
<span class="RktSym">blog-insert-post!</span> to accept the contents of the post
structure, rather the structure itself. This improves the model’s interface, by
making it more abstract:</p><p><div class="SIntrapara"><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-9..rkt)._blog-insert-post!))"></a><span class="RktSym"><span class="RktSymDef RktSym">blog-insert-post!</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog?</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=void.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._void%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">void</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote></div><div class="SIntrapara"><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>value</p></div></div><p class="RForeground"><a name="(def._((lib._web-server/scribblings/tutorial/examples/iteration-9..rkt)._post-insert-comment!))"></a><span class="RktSym"><span class="RktSymDef RktSym">post-insert-comment!</span></span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog?</span><span class="hspace"> </span><span class="RktSym">post?</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string?</a></span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=void.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._void%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">void</a></span><span class="RktPn">)</span></p></blockquote></td></tr></table></blockquote></div></p><p><span style="font-weight: bold">Exercise.</span> Write the new definitions of <span class="RktSym">blog-insert-post!</span> and <span class="RktSym">post-insert-comment!</span>.
Remember to call <span class="RktSym">save-blog!</span>.</p><p>In the previous iteration of the model, we used <span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._provide%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">provide</a></span><span class="stt"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._all-defined-out%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">all-defined-out</a></span><span class="RktPn">)</span><span class="RktPn">)</span> to expose all of the model’s definitions. This
transgresses the principle of abstraction, which tells us to hide
implementation details like private functions and internal data structures.
We’ll conform to that principle now, by using a form of
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._provide%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">provide</a></span> that names the exposed definitions explicitly.</p><p><div class="SIntrapara">For example, if we wanted to limit the module’s exposure to the functions
<span class="RktSym">blog-insert-post!</span> and <span class="RktSym">post-insert-comment!</span>, we could
do this:
</div><div class="SIntrapara"><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._provide%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">provide</a></span><span class="hspace"> </span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">post-insert-comment!</span><span class="RktPn">)</span></td></tr></table></blockquote></div></p><p><div class="SIntrapara">But this is exposing too little! So let’s change the <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._provide%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">provide</a></span> line in
the model to:
</div><div class="SIntrapara"><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._provide%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">provide</a></span><span class="hspace"> </span><span class="RktSym">blog?</span><span class="hspace"> </span><span class="RktSym">blog-posts</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">post?</span><span class="hspace"> </span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">post-comments</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">initialize-blog!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">post-insert-comment!</span><span class="RktPn">)</span></td></tr></table></blockquote></div></p><p>Since these nine functions are all we need from the module, this degree of
exposure is just right.</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>The last step is to change the application. We need to call
<span class="RktSym">initialize-blog!</span> to read in the blog structure, and, since there is no
longer a a <span class="RktSym">BLOG</span> export, we need to pass the returned blog value around
the application.</p><p>First, change <span class="RktSym">start</span> to call <span class="RktSym">initialize-blog!</span> with a path in
our home directory:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">initialize-blog!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Manipulating_Paths.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._build-path%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">build-path</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Filesystem.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._current-directory%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">current-directory</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"the-blog-data.db"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Thread the <span class="RktSym">blog</span> structure through the application
appropriately to give <span class="RktSym">blog-insert-post!</span> and
<span class="RktSym">post-insert-comment!</span> the correct values. You’ll also need to change
how <span class="RktSym">render-blog-page</span> adds new posts.</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>Our model is now:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=index.html&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">racket/base</span></a></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">require</span><span class="hspace"> </span><span class="RktSym">racket/list</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (blog home posts)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where home is a string, posts is a (listof post)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">home</span><span class="hspace"> </span><span class="RktSym">posts</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="hspace"> </span><span class="RktPn">#:prefab</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and post is a (post blog title body comments)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where title is a string, body is a string,</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and comments is a (listof string)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="hspace"> </span><span class="RktSym">comments</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">#:mutable</span><span class="hspace"> </span><span class="RktPn">#:prefab</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">initialize-blog! : path? -> blog</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Reads a blog from a path, if not present, returns default</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">initialize-blog!</span><span class="hspace"> </span><span class="RktSym">home</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">log-missing-exn-handler</span><span class="hspace"> </span><span class="RktSym">exn</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">path->string</span><span class="hspace"> </span><span class="RktSym">home</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is another post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktVal">"First Post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"This is my first post"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktVal">"First comment!"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">the-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">with-handlers</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktPn">[</span><span class="RktSym">exn?</span><span class="hspace"> </span><span class="RktSym">log-missing-exn-handler</span><span class="RktPn">]</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">with-input-from-file</span><span class="hspace"> </span><span class="RktSym">home</span><span class="hspace"> </span><span class="RktSym">read</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-blog-home!</span><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">path->string</span><span class="hspace"> </span><span class="RktSym">home</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">save-blog! : blog -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Saves the contents of a blog to its home</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">save-blog!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">write-to-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">write</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">with-output-to-file</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-home</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">write-to-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:exists</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">replace</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-insert-post!: blog string string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a post, adds the post at the top of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-blog-posts!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cons</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="hspace"> </span><span class="RktSym">empty</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">save-blog!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-insert-comment!: blog post string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog, a post and a comment string.</span><span class="hspace"> </span><span class="RktCmt">As a side-efect, </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">adds the comment to the bottom of the post</span><span class="RktCmt">'</span><span class="RktCmt">s list of comments.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">set-post-comments!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">append</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">list</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">save-blog!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">provide</span><span class="hspace"> </span><span class="RktSym">blog?</span><span class="hspace"> </span><span class="RktSym">blog-posts</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">post?</span><span class="hspace"> </span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">post-comments</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">initialize-blog!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">post-insert-comment!</span><span class="RktPn">)</span></td></tr></table></blockquote><p>And our application is:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">require</span><span class="hspace"> </span><span class="RktVal">"model-2.rkt"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request and produces a page that displays</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">all of the web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">initialize-blog!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">build-path</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">current-directory</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"the-blog-data.db"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: blog request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Produces an HTML page of the content of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"title"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"body"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">redirect/get</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post-detail-page: post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and produces a detail page of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The user will be able to either insert new comments</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">or go back to render-blog-page.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h2</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-comment-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"comment"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">back-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Back to the blog"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">extract-binding/single</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">comment</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-comment-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">parse-comment</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">request-bindings</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">back-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-confirm-add-comment-page :</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog comment post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a comment that we intend to add to a post, as well</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as the request. If the user follows through, adds a comment </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and goes back to the display page. Otherwise, goes back to </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">the detail page of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-comment</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"The comment: "</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-comment</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"will be added to "</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">yes-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Yes, add the comment."</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">cancel-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"No, I changed my mind!"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">yes-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">redirect/get</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cancel-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The fragment contains a link to show a detailed view of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">view-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">view-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">number->string</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">length</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">" comment(s)"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: blog (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a embed/url, produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-itemized-list: (listof xexpr) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a list of items, and produces a rendering as</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">an unorderered list.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-item: xexpr -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes an xexpr, and produces a rendering</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as a list item.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">a-fragment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-fragment</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>This approach to persistence can work surprisingly well for simple
applications. But as our application’s needs grow, we will have to deal with
concurrency issues, the lack of a simple query language over our data model,
etc. So, in the next section, we’ll explain how to use an SQL database to
store our blog model.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Using_an_SQL_database"">15<tt> </tt><a name="(part._.Using_an_.S.Q.L_database)"></a>Using an SQL database</h3><p>To employ an SQL database, we use the following bindings from the
<a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=index.html&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">db</span></a> library: <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=connect.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._connection%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">connection?</a></span>, <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=connect.html%23%2528def._%2528%2528lib._db%252Fmain..rkt%2529._sqlite3-connect%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">sqlite3-connect</a></span>,
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._table-exists%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">table-exists?</a></span>, <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-exec%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-exec</a></span>, <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-list</a></span>, and
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-value%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-value</a></span>. Import them by adding the following to the top of the
model:</p><blockquote class="SCodeFlow"><p><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._require%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">require</a></span><span class="hspace"> </span><span class="RktSym">db</span><span class="RktPn">)</span></p></blockquote><p>Next, we define a relational structure for our model using the following
tables:</p><p><table cellspacing="0" cellpadding="0"><tr><td><p><span class="stt">CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT, body TEXT)</span></p></td></tr><tr><td><p><span class="stt">CREATE TABLE comments (pid INTEGER, content TEXT)</span></p></td></tr></table></p><p>Like the Racket structure, a post in the database has a title and a body,
but it also has an identifier. (Actually, the Racket structure had an
identifier as well—<wbr></wbr>the memory pointer—<wbr></wbr>but the database requires it to be
explicit.)</p><p>As for the comments, each has some textual content and is connected to a post
via identifier. We could have chosen to serialize comments with
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Writing.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._write%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">write</a></span> and add a new TEXT column to the posts table to store
the value. But a separate comments table conforms better to
relational style.</p><p>A <span class="RktSym">blog</span> structure is now simply a container for the database
handle:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>struct</p></div></div><p class="RForeground"><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="stt"> </span><a name="(def._((lib._web-server/scribblings/tutorial/examples/dummy-10..rkt)._blog-db))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/dummy-10..rkt)._blog~3f))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/dummy-10..rkt)._struct~3ablog))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/dummy-10..rkt)._blog))"></a><span class="RktSym"><span class="RktSymDef RktSym">blog</span></span><span class="stt"> </span><span class="RktPn">(</span><span class="RktSym">db</span><span class="RktPn">)</span><span class="RktPn">)</span></p></blockquote></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">db</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=connect.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._connection%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">connection?</a></span></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Write the <span class="RktSym">blog</span> structure definition. It
does not need to be mutable or serializable.</p><p><div class="SIntrapara">We can now write the code to initialize a <span class="RktSym">blog</span> structure:
</div><div class="SIntrapara"><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">initialize-blog! : path? -> blog?</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Sets up a blog database (if it doesn't exist)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">initialize-blog!</span><span class="hspace"> </span><span class="RktSym">home</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktSym">db</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=connect.html%23%2528def._%2528%2528lib._db%252Fmain..rkt%2529._sqlite3-connect%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">sqlite3-connect</a></span><span class="hspace"> </span><span class="RktPn">#:database</span><span class="hspace"> </span><span class="RktSym">home</span><span class="hspace"> </span><span class="RktPn">#:mode</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">create</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktSym">db</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=when_unless.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fletstx-scheme..rkt%2529._unless%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">unless</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._table-exists%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">table-exists?</a></span><span class="hspace"> </span><span class="RktSym">db</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-exec%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-exec</a></span><span class="hspace"> </span><span class="RktSym">db</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=strings.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._string-append%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">string-append</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"CREATE TABLE posts "</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"(id INTEGER PRIMARY KEY, title TEXT, body TEXT)"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktVal">"First Post"</span><span class="hspace"> </span><span class="RktVal">"This is my first post"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span><span class="hspace"> </span><span class="RktVal">"This is another post"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=when_unless.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fletstx-scheme..rkt%2529._unless%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">unless</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._table-exists%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">table-exists?</a></span><span class="hspace"> </span><span class="RktSym">db</span><span class="hspace"> </span><span class="RktVal">"comments"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-exec%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-exec</a></span><span class="hspace"> </span><span class="RktSym">db</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"CREATE TABLE comments (pid INTEGER, content TEXT)"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528lib._racket%252Flist..rkt%2529._first%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">first</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"First comment!"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="RktPn">)</span></td></tr></table></blockquote></div></p><p>Given the <span class="RktVal">'</span><span class="RktVal">create</span> flag, <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=connect.html%23%2528def._%2528%2528lib._db%252Fmain..rkt%2529._sqlite3-connect%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">sqlite3-connect</a></span> creates a
database if one does not already exist at the <span class="RktSym">home</span> path. At
this time, it would be good to change your call to
<span class="RktSym">initialize-blog!</span> to use a different path, because otherwise
you will be trying to parse the old S-expression datafile as an SQLite
database, which will fail.</p><p>We still need to initialize the database with the table
definitions and initial data. Previously we used <span class="RktSym">blog-insert-post!</span> and
<span class="RktSym">post-insert-comment!</span> for this purpose; here are their new
implementations:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-insert-post!: blog? string? string? -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a post, adds the post at the top of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-exec%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-exec</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"INSERT INTO posts (title, body) VALUES (?, ?)"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-insert-comment!: blog? post string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog, a post and a comment string. As a side-effect,</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">adds the comment to the bottom of the post's list of comments.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">p</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-exec%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-exec</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"INSERT INTO comments (pid, content) VALUES (?, ?)"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-id</span><span class="hspace"> </span><span class="RktSym">p</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>Note that the SQL queries above use the SQL placeholder, <span class="RktInBG"><span class="hspace"></span><span class="RktIn">?</span><span class="hspace"></span></span>, to
perform string substitution. If they had performed it with
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Writing.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._format%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">format</a></span> and <span class="RktInBG"><span class="hspace"></span><span class="RktIn">~a</span><span class="hspace"></span></span> instead, a malicious user could submit a post with a
title like <span class="RktVal">"null', 'null') and INSERT INTO accounts (username,\npassword) VALUES ('ur','hacked"</span> and get <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-exec%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-exec</a></span> to make two
INSERTs instead of one. This is called an SQL injection attack.</p><p>SQL placeholders prevent such attacks by ensuring that the query is submitted
as-is to SQLite, which then parses it and applies the arguments. This approach
ensures that the arguments are treated strictly as data.</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>In <span class="RktSym">post-insert-comment!</span> we use <span class="RktSym">post-id</span> but we
have not yet defined the new <span class="RktSym">post</span> structure. Since the post table
schema uses an integer as identifier, it would seem sufficient
to do the same for the <span class="RktSym">post</span> structure.
However, a structure so defined would not indicate which blog, and consequently
which database, a post belongs to. We would thus be unable to extract
the title or body values.</p><p>The solution, of course, is to associate the blog with each post:</p><blockquote class="SVInsetFlow"><table cellspacing="0" cellpadding="0" class="boxed RBoxed"><tr><td><blockquote class="SubFlow"><div class="RBackgroundLabel SIEHidden"><div class="RBackgroundLabelInner"><p>struct</p></div></div><p class="RForeground"><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define-struct.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._struct%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">struct</a></span><span class="stt"> </span><a name="(def._((lib._web-server/scribblings/tutorial/examples/dummy-10..rkt)._post-id))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/dummy-10..rkt)._post-blog))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/dummy-10..rkt)._post~3f))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/dummy-10..rkt)._struct~3apost))"></a><a name="(def._((lib._web-server/scribblings/tutorial/examples/dummy-10..rkt)._post))"></a><span class="RktSym"><span class="RktSymDef RktSym">post</span></span><span class="stt"> </span><span class="RktPn">(</span><span class="RktSym">blog</span><span class="stt"> </span><span class="RktSym">id</span><span class="RktPn">)</span><span class="RktPn">)</span></p></blockquote></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktSym">blog?</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">id</span><span class="hspace"> </span>:<span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=number-types.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._integer%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">integer?</a></span></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Write the structure definition for posts.</p><p>The only function that creates posts is <span class="RktSym">blog-posts</span>:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-posts : blog -> (listof post?)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Queries for the post ids</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">id->post</span><span class="hspace"> </span><span class="RktSym">an-id</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">an-id</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528lib._racket%252Fprivate%252Fmap..rkt%2529._map%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">map</a></span><span class="hspace"> </span><span class="RktSym">id->post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-list</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"SELECT id FROM posts"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-list</a></span> can be used for queries that return a single
column (e.g., <span class="RktVal">"SELECT id FROM posts"</span>), and it returns a list of that
column’s values.</p><p><div class="SIntrapara">At this point we can write the functions that operate on posts:
</div><div class="SIntrapara"><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-title : post -> string?</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Queries for the title</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-value%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-value</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"SELECT title FROM posts WHERE id = ?"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-id</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote></div></p><p><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=query-api.html%23%2528def._%2528%2528lib._db%252Fbase..rkt%2529._query-value%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">query-value</a></span> is used with queries that return a single
value (that is, one row and one column).</p><p><span style="font-weight: bold">Exercise.</span> Write the definition of <span class="RktSym">post-body</span>.</p><p><span style="font-weight: bold">Exercise.</span> Write the definition of <span class="RktSym">post-comments</span>.
(Hint: Use <span class="RktSym">blog-posts</span> as a template, not <span class="RktSym">post-title</span>.)</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>The only change that we need to make to the application is to require
the new model. Note that its interface remains unchanged!</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>Our model is now:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=index.html&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">racket/base</span></a></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">require</span><span class="hspace"> </span><span class="RktSym">racket/list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">db</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A blog is a (blog db)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where db is an sqlite connection</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">db</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A post is a (post blog id)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">where blog is a blog and id is an integer?</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">struct</span><span class="hspace"> </span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktSym">id</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">initialize-blog! : path? -> blog?</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Sets up a blog database (if it doesn</span><span class="RktCmt">'</span><span class="RktCmt">t exist)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">initialize-blog!</span><span class="hspace"> </span><span class="RktSym">home</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">db</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">sqlite3-connect</span><span class="hspace"> </span><span class="RktPn">#:database</span><span class="hspace"> </span><span class="RktSym">home</span><span class="hspace"> </span><span class="RktPn">#:mode</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">create</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog</span><span class="hspace"> </span><span class="RktSym">db</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">unless</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">table-exists?</span><span class="hspace"> </span><span class="RktSym">db</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">query-exec</span><span class="hspace"> </span><span class="RktSym">db</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">string-append</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"CREATE TABLE posts "</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"(id INTEGER PRIMARY KEY, title TEXT, body TEXT)"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktVal">"First Post"</span><span class="hspace"> </span><span class="RktVal">"This is my first post"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktVal">"Second Post"</span><span class="hspace"> </span><span class="RktVal">"This is another post"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">unless</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">table-exists?</span><span class="hspace"> </span><span class="RktSym">db</span><span class="hspace"> </span><span class="RktVal">"comments"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">query-exec</span><span class="hspace"> </span><span class="RktSym">db</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"CREATE TABLE comments (pid INTEGER, content TEXT)"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">first</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"First comment!"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">the-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-posts : blog -> (listof post?)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Queries for the post ids</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">id->post</span><span class="hspace"> </span><span class="RktSym">an-id</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">an-id</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">id->post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">query-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"SELECT id FROM posts"</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-title : post -> string?</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Queries for the title</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">query-value</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"SELECT title FROM posts WHERE id = ?"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-id</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-body : post -> string?</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Queries for the body</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">p</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">query-value</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-blog</span><span class="hspace"> </span><span class="RktSym">p</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"SELECT body FROM posts WHERE id = ?"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-id</span><span class="hspace"> </span><span class="RktSym">p</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-comments : post -> (listof string?)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Queries for the comments</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">p</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">query-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-blog</span><span class="hspace"> </span><span class="RktSym">p</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"SELECT content FROM comments WHERE pid = ?"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-id</span><span class="hspace"> </span><span class="RktSym">p</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog-insert-post!: blog? string? string? -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog and a post, adds the post at the top of the blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">query-exec</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"INSERT INTO posts (title, body) VALUES (?, ?)"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">post-insert-comment!: blog? post string -> void</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a blog, a post and a comment string.</span><span class="hspace"> </span><span class="RktCmt">As a side-efect, </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">adds the comment to the bottom of the post</span><span class="RktCmt">'</span><span class="RktCmt">s list of comments.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">p</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">query-exec</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-db</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"INSERT INTO comments (pid, content) VALUES (?, ?)"</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-id</span><span class="hspace"> </span><span class="RktSym">p</span><span class="RktPn">)</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">provide</span><span class="hspace"> </span><span class="RktSym">blog?</span><span class="hspace"> </span><span class="RktSym">blog-posts</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">post?</span><span class="hspace"> </span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">post-comments</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">initialize-blog!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">post-insert-comment!</span><span class="RktPn">)</span></td></tr></table></blockquote><p>And our application is:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._require%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">require</a></span><span class="hspace"> </span><span class="RktVal">"model-3.rkt"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktSym">....</span></td></tr></table></blockquote><p>See <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=db&rel=using-db.html%23%2528part._intro-servlets%2529&version=6.7" class="Sq" data-pltdoc="x">Databases and Web Servlets</a>
for more information on writing database-backed web servlets.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Using_Formlets"">16<tt> </tt><a name="(part._.Using_.Formlets)"></a>Using Formlets</h3><p>Now let’s go back to the application code. One of our poor design choices is
to have made a loose connection between the name used to identify a form
element in the rendering code, and the name used for it in the extracting code:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: blog request -> doesnt'</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Send an HTML page of the content of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fxexpr..rkt%2529._response%252Fxexpr%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response/xexpr</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">"title" is used here</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"title"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">name</span><span class="hspace"> </span><span class="RktVal">"body"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">(</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._request-bindings%29%29" class="RktValLink" data-pltdoc="x">request-bindings</a></span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">And "title" is used here.</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._extract-binding%2Fsingle%29%29" class="RktValLink" data-pltdoc="x">extract-binding/single</a></span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._extract-binding%2Fsingle%29%29" class="RktValLink" data-pltdoc="x">extract-binding/single</a></span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">body</span><span class="hspace"> </span><span class="RktSym">bindings</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._redirect%2Fget%29%29" class="RktValLink" data-pltdoc="x">redirect/get</a></span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=servlet.html%23%2528def._%2528%2528lib._web-server%252Fservlet%252Fweb..rkt%2529._send%252Fsuspend%252Fdispatch%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">send/suspend/dispatch</a></span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p><div class="SIntrapara">The Racket Web framework provides <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlets</span></a> to abstract these names away, by
adjusting them automatically in the HTML, and by presenting the following
interface for the display and processing of forms:
</div><div class="SIntrapara"><ul><li><p><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Flib..rkt%2529._formlet-display%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">formlet-display</a></span> takes a <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a> and returns its rendering as a
list of X-expressions. This will generate unique names for its form elements.</p></li><li><p><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Flib..rkt%2529._formlet-process%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">formlet-process</a></span> takes a <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a> and a request and processes the
<a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a>, i.e., extracts the bindings from the request using the
names generated by <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Flib..rkt%2529._formlet-display%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">formlet-display</a></span>.</p></li></ul></div></p><p>A <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a> is created using the <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528form._%2528%2528lib._web-server%252Fformlets%252Fsyntax..rkt%2529._formlet%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">formlet</a></span> syntax. For example, here is
a <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a> for <span class="RktSym">render-blog-page</span>:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">new-post-formlet : formlet (values string? string?)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A formlet for requesting a title and body of a post</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktSym">new-post-formlet</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528form._%2528%2528lib._web-server%252Fformlets%252Fsyntax..rkt%2529._formlet%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">formlet</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528form._%2528%2528lib._web-server%252Fformlets%252Fsyntax..rkt%2529._%7E23%7E25%7E23%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">#%#</a></span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">{</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._input-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">input-string</a></span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=if.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fletstx-scheme..rkt%2529._%7E3d%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">=></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">title</span><span class="RktPn">}</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">{</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._input-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">input-string</a></span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=if.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fletstx-scheme..rkt%2529._%7E3d%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">=></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">}</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=values.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._values%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">values</a></span><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p><div class="SIntrapara">The first argument in the <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528form._%2528%2528lib._web-server%252Fformlets%252Fsyntax..rkt%2529._formlet%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">formlet</a></span> syntax determines how
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Flib..rkt%2529._formlet-display%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">formlet-display</a></span> should display the <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a>.
It is a quoted <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>, with two important differences:
</div><div class="SIntrapara"><ul><li><p><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528form._%2528%2528lib._web-server%252Fformlets%252Fsyntax..rkt%2529._%7E23%7E25%7E23%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">#%#</a></span> introduces a list of <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=xml&rel=index.html%23%2528tech._x._expression%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">X-expression</span></a>s</p></li><li><p><span class="RktRdr">,</span><span class="RktPn">{</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=if.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fletstx-scheme..rkt%2529._%7E3d%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">=></a></span><span class="stt"> </span><span class="RktVar">formlet</span><span class="stt"> </span><span class="RktVar">id</span><span class="RktPn">}</span> embeds the <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a> <span class="RktVar">formlet</span> as a
sub<a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a> and it attaches the name <span class="RktVar">id</span> to the result of processing
this sub<a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a>.</p><p>For example, <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._input-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">input-string</a></span> is itself a library
<a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a> that yields a string, and <span class="RktRdr">,</span><span class="RktPn">{</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=if.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fletstx-scheme..rkt%2529._%7E3d%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">=></a></span><span class="stt"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._input-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">input-string</a></span><span class="stt"> </span><span class="RktSym">title</span><span class="RktPn">}</span> embeds
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._input-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">input-string</a></span> in <span class="RktSym">new-post-formlet</span> and associates the name
<span class="RktSym">title</span> to that string.</p><p><div class="SIntrapara"><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._input-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">input-string</a></span> is rendered as <span class="RktRes">`(input<span class="stt"> </span>([type<span class="stt"> </span>"text"]<span class="stt"> </span>[name<span class="stt"> </span>,fresh_name]))</span>, so <span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Flib..rkt%2529._formlet-display%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">formlet-display</a></span><span class="stt"> </span><span class="RktSym">new-post-formlet</span><span class="RktPn">)</span> is rendered as:
</div><div class="SIntrapara"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">(</span><span class="RktRes">list</span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">'</span><span class="RktRes">(</span><span class="RktRes">input</span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">(</span><span class="RktRes">[</span><span class="RktRes">type</span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">"text"</span><span class="RktRes">]</span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">[</span><span class="RktRes">name</span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">"input_0"</span><span class="RktRes">]</span><span class="RktRes">)</span><span class="RktRes">)</span></td></tr><tr><td><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">'</span><span class="RktRes">(</span><span class="RktRes">input</span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">(</span><span class="RktRes">[</span><span class="RktRes">type</span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">"text"</span><span class="RktRes">]</span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">[</span><span class="RktRes">name</span><span class="RktRes"><span class="hspace"> </span></span><span class="RktRes">"input_1"</span><span class="RktRes">]</span><span class="RktRes">)</span><span class="RktRes">)</span><span class="RktRes">)</span></td></tr></table></div></p></li></ul></div></p><p>The second argument of <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528form._%2528%2528lib._web-server%252Fformlets%252Fsyntax..rkt%2529._formlet%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">formlet</a></span> determines how <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Flib..rkt%2529._formlet-process%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">formlet-process</a></span>
should process the <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a>. That is, it specifies how to group and order the
results of processing the <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a>’s sub<a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a>s: the identifiers on the
right-hand side of <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=if.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fletstx-scheme..rkt%2529._%7E3d%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">=></a></span> are bound to the results of processing the
sub<a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlets</span></a>.</p><p>For example, <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._input-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">input-string</a></span> is processed as
<span class="RktPn">(</span><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._extract-binding%2Fsingle%29%29" class="RktValLink" data-pltdoc="x">extract-binding/single</a></span><span class="stt"> </span><span class="RktVar">fresh_name</span><span class="stt"> </span><span class="RktPn">(</span><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._request-bindings%29%29" class="RktValLink" data-pltdoc="x">request-bindings</a></span><span class="stt"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span><span class="RktPn">)</span>. Thus, if <span class="RktVar">request</span> binds <span class="RktRes">"input_0"</span> to
<span class="RktRes">"Title"</span> and <span class="RktRes">"input_1"</span> to <span class="RktRes">"Body"</span>,
then <span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Flib..rkt%2529._formlet-process%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">formlet-process</a></span><span class="stt"> </span><span class="RktSym">new-post-formlet</span><span class="stt"> </span><span class="RktVar">request</span><span class="RktPn">)</span> returns
<span class="RktRes">(values<span class="stt"> </span>"Title"<span class="stt"> </span>"Body")</span>.</p><p><div class="SIntrapara">Finally, here is how to use <span class="RktSym">new-post-formlet</span> in <span class="RktSym">render-blog-page</span>:
</div><div class="SIntrapara"><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: blog request -> doesn't return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Sends an HTML page of the content of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fxexpr..rkt%2529._response%252Fxexpr%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response/xexpr</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">]</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Flib..rkt%2529._formlet-display%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">formlet-display</a></span><span class="hspace"> </span><span class="RktSym">new-post-formlet</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">]</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528quote._%7E23%7E25kernel%2529._define-values%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define-values</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Flib..rkt%2529._formlet-process%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">formlet-process</a></span><span class="hspace"> </span><span class="RktSym">new-post-formlet</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request</a></span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="#%28def._%28%28lib._web-server%2Fservlet..rkt%29._redirect%2Fget%29%29" class="RktValLink" data-pltdoc="x">redirect/get</a></span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=servlet.html%23%2528def._%2528%2528lib._web-server%252Fservlet%252Fweb..rkt%2529._send%252Fsuspend%252Fdispatch%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">send/suspend/dispatch</a></span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote></div></p><p><span style="font-weight: bold">Alternative.</span> The formlet shown above uses the
<span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._input-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">input-string</a></span> combinator, which combines a bunch of other
formlets into one container with sensible defaults. Sometimes it is
useful to break it apart to provide different arguments to the
subpieces. For example, suppose we wanted to add a CSS class to the
form elements? Here’s how we’d do that:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=define.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._define%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">define</a></span><span class="hspace"> </span><span class="RktSym">new-post-formlet</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528form._%2528%2528lib._web-server%252Fformlets%252Fsyntax..rkt%2529._formlet%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">formlet</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528form._%2528%2528lib._web-server%252Fformlets%252Fsyntax..rkt%2529._%7E23%7E25%7E23%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">#%#</a></span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._to-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">to-string</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._required%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">required</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._text-input%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">text-input</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:attributes</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"form-text"</span><span class="RktVal">]</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=if.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fletstx-scheme..rkt%2529._%7E3d%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">=></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">title</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._to-string%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">to-string</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._required%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">required</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528def._%2528%2528lib._web-server%252Fformlets%252Finput..rkt%2529._text-input%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">text-input</a></span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:attributes</span><span class="hspace"> </span><span class="RktVal">'</span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"form-text"</span><span class="RktVal">]</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=if.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fletstx-scheme..rkt%2529._%7E3d%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">=></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=values.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._values%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">values</a></span><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote><p><span style="font-weight: bold">Exercise.</span> Write a <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=formlets.html%23%2528tech._formlet%2529&version=6.7" class="techoutside Sq" data-pltdoc="x"><span class="techinside">formlet</span></a> and use it in <span class="RktSym">render-post-detail-page</span>.</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p>Our application is now:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">require</span><span class="hspace"> </span><span class="RktSym">web-server/formlets</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"model-3.rkt"</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">start: request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a request and produces a page that displays</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">all of the web content.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">initialize-blog!</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">build-path</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">current-directory</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"the-blog-data.sqlite"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">new-post-formlet : formlet (values string? string?)</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A formlet for requesting a title and body of a post</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">new-post-formlet</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">formlet</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">#%#</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">{</span><span class="RktSym">input-string</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym">=></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">title</span><span class="RktPn">}</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">{</span><span class="RktSym">input-string</span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym">=></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">}</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">values</span><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-blog-page: blog request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Produces an HTML page of the content of the</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"My Blog"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-post-handler</span><span class="RktPn">)</span><span class="RktVal">]</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">formlet-display</span><span class="hspace"> </span><span class="RktSym">new-post-formlet</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">]</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define-values</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">formlet-process</span><span class="hspace"> </span><span class="RktSym">new-post-formlet</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-insert-post!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">title</span><span class="hspace"> </span><span class="RktSym">body</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">redirect/get</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">new-comment-formlet : formlet string</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">A formlet for requesting a comment</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktSym">new-comment-formlet</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">input-string</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post-detail-page: post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post and produces a detail page of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The user will be able to either insert new comments</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">or go back to render-blog-page.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Post Details"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h2</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">form</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">action</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">insert-comment-handler</span><span class="RktPn">)</span><span class="RktVal">]</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">formlet-display</span><span class="hspace"> </span><span class="RktSym">new-comment-formlet</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">input</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">type</span><span class="hspace"> </span><span class="RktVal">"submit"</span><span class="RktVal">]</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">back-handler</span><span class="RktPn">)</span><span class="RktVal">]</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Back to the blog"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">insert-comment-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-blog</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">formlet-process</span><span class="hspace"> </span><span class="RktSym">new-comment-formlet</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">back-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-blog-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-confirm-add-comment-page :</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">blog comment post request -> doesn</span><span class="RktCmt">'</span><span class="RktCmt">t return</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a comment that we intend to add to a post, as well</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as the request. If the user follows through, adds a comment </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">and goes back to the display page. Otherwise, goes back to </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">the detail page of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-confirm-add-comment-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-comment</span></td></tr><tr><td><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response-generator</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">response/xexpr</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">html</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">head</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">title</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">body</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">h1</span><span class="hspace"> </span><span class="RktVal">"Add a Comment"</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"The comment: "</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-comment</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"will be added to "</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">yes-handler</span><span class="RktPn">)</span><span class="RktVal">]</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"Yes, add the comment."</span><span class="RktVal">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">cancel-handler</span><span class="RktPn">)</span><span class="RktVal">]</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"No, I changed my mind!"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">yes-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-insert-comment!</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">a-comment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">redirect/get</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">cancel-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">send/suspend/dispatch</span><span class="hspace"> </span><span class="RktSym">response-generator</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-post: post (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a post, produces an xexpr fragment of the post.</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">The fragment contains a link to show a detailed view of the post.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">view-post-handler</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post-detail-page</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">request</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"post"</span><span class="RktVal">]</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">a</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">href</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">embed/url</span><span class="hspace"> </span><span class="RktSym">view-post-handler</span><span class="RktPn">)</span><span class="RktVal">]</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-title</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">p</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">post-body</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktPn">(</span><span class="RktSym">number->string</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">length</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">post-comments</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">" comment(s)"</span><span class="RktVal">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-posts: blog (handler -> string) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a embed/url, produces an xexpr fragment</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">of all its posts.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-post</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="hspace"> </span><span class="RktSym">a-post</span><span class="hspace"> </span><span class="RktSym">embed/url</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">div</span><span class="hspace"> </span><span class="RktVal">(</span><span class="RktVal">[</span><span class="RktVal">class</span><span class="hspace"> </span><span class="RktVal">"posts"</span><span class="RktVal">]</span><span class="RktVal">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-post/embed/url</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">blog-posts</span><span class="hspace"> </span><span class="RktSym">a-blog</span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-itemized-list: (listof xexpr) -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes a list of items, and produces a rendering as</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">an unorderered list.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-itemized-list</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">ul</span><span class="hspace"> </span><span class="RktRdr">,@</span><span class="RktPn">(</span><span class="RktSym">map</span><span class="hspace"> </span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">fragments</span><span class="RktPn">)</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">render-as-item: xexpr -> xexpr</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">Consumes an xexpr, and produces a rendering</span></td></tr><tr><td><span class="RktCmt">;</span><span class="RktCmt"> </span><span class="RktCmt">as a list item.</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym">define</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">render-as-item</span><span class="hspace"> </span><span class="RktSym">a-fragment</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">`</span><span class="RktVal">(</span><span class="RktVal">li</span><span class="hspace"> </span><span class="RktRdr">,</span><span class="RktSym">a-fragment</span><span class="RktVal">)</span><span class="RktPn">)</span></td></tr></table></blockquote><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Leaving_DrRacket"">17<tt> </tt><a name="(part._.Leaving_.Dr.Racket)"></a>Leaving DrRacket</h3><p>We’ve been in the habit of pressing the <span class="ssansserif">Run</span> button to run our
application in DrRacket. But if we were actually to deploy an application, we’d
need to launch it by a different method.</p><p><div class="SIntrapara">The simplest alternative is to use <a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Fservlet-env%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/servlet-env</span></a>.
First, change the first lines in your application from
</div><div class="SIntrapara"><blockquote class="SCodeFlow"><p><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528mod-path._web-server%252Finsta%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">web-server/insta</span></a></p></blockquote></div></p><p><div class="SIntrapara">to
</div><div class="SIntrapara"><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=guide&rel=Module_Syntax.html%23%2528part._hash-lang%2529&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktMod">#lang</span></a><span class="hspace"> </span><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=index.html&version=6.7" class="RktModLink Sq" data-pltdoc="x"><span class="RktSym">racket</span></a></td></tr><tr><td><span class="hspace"> </span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._require%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">require</a></span><span class="hspace"> </span><span class="RktSym">web-server/servlet</span><span class="RktPn">)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=attaching-contracts-to-values.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fprivate%252Fprovide..rkt%2529._provide%252Fcontract%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">provide/contract</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym">start</span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Frequest-structs..rkt%2529._request%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">request?</a></span><span class="hspace"> </span><span class="RktPn">. </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=function-contracts.html%23%2528form._%2528%2528lib._racket%252Fcontract%252Fbase..rkt%2529._-%7E3e%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x"><span class="nobreak">-></span></a></span><span class="RktPn"> .</span><span class="hspace"> </span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=http.html%23%2528def._%2528%2528lib._web-server%252Fhttp%252Fresponse-structs..rkt%2529._response%7E3f%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">response?</a></span><span class="RktPn">)</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr></table></blockquote></div></p><p>Second, add the following at the bottom of your application:</p><blockquote class="SCodeFlow"><table cellspacing="0" cellpadding="0" class="RktBlk"><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=require.html%23%2528form._%2528%2528lib._racket%252Fprivate%252Fbase..rkt%2529._require%2529%2529&version=6.7" class="RktStxLink Sq" data-pltdoc="x">require</a></span><span class="hspace"> </span><span class="RktSym">web-server/servlet-env</span><span class="RktPn">)</span></td></tr><tr><td><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528def._%2528%2528lib._web-server%252Fservlet-env..rkt%2529._serve%252Fservlet%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">serve/servlet</a></span><span class="hspace"> </span><span class="RktSym">start</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:launch-browser?</span><span class="hspace"> </span><span class="RktVal">#f</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:quit?</span><span class="hspace"> </span><span class="RktVal">#f</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:listen-ip</span><span class="hspace"> </span><span class="RktVal">#f</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:port</span><span class="hspace"> </span><span class="RktVal">8000</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:extra-files-paths</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=pairs.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._list%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">list</a></span><span class="hspace"> </span><span class="RktPn">(</span><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=reference&rel=Manipulating_Paths.html%23%2528def._%2528%2528quote._%7E23%7E25kernel%2529._build-path%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">build-path</a></span><span class="hspace"> </span><span class="RktVar">your-path-here</span><span class="hspace"> </span><span class="RktVal">"htdocs"</span><span class="RktPn">)</span><span class="RktPn">)</span></td></tr><tr><td><span class="hspace"> </span><span class="RktPn">#:servlet-path</span></td></tr><tr><td><span class="hspace"> </span><span class="RktVal">"/servlets/APPLICATION.rkt"</span><span class="RktPn">)</span></td></tr></table></blockquote><p><div class="SIntrapara">Regarding the parameters of <span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528def._%2528%2528lib._web-server%252Fservlet-env..rkt%2529._serve%252Fservlet%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">serve/servlet</a></span>:
</div><div class="SIntrapara"><ul><li><p>You can change the value of the <span class="RktPn">#:port</span> parameter to use a
different port.</p></li><li><p><span class="RktPn">#:listen-ip</span> is set to <span class="RktVal">#f</span> so that the server will listen
on <span style="font-style: italic">all</span> available IPs.</p></li><li><p>You should change <span class="RktVar">your-path-here</span> to be the path to the parent
of your <span class="RktSym">htdocs</span> directory.</p></li><li><p>You should change <span class="RktVal">"APPLICATION.rkt"</span> to be the name of your
application.</p></li></ul></div></p><p>Third, to run your server, you can either press <span class="ssansserif">Run</span> in DrRacket, or type</p><p><span class="hspace"> </span><span class="stt">racket -t <file.rkt></span></p><p>(using your own file name, of course). Both of these will start a Web
server for your application. Your application will be available at
<span class="RktSym">http://localhost:8000/servlets/APPLICATION.rkt</span><span class="RktMeta"></span>.</p><blockquote class="SCentered"><p>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr>—<wbr></wbr></p></blockquote><p><span class="RktSym"><a href="https://download.racket-lang.org/docs/6.7/html/local-redirect/index.html?doc=web-server&rel=run.html%23%2528def._%2528%2528lib._web-server%252Fservlet-env..rkt%2529._serve%252Fservlet%2529%2529&version=6.7" class="RktValLink Sq" data-pltdoc="x">serve/servlet</a></span> takes other parameters and there are more advanced ways
of starting the Web Server, but you’ll have to refer to the Racket Web Server
Reference Manual for details.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Using_HTTPS"">18<tt> </tt><a name="(part._.Using_.H.T.T.P.S)"></a>Using HTTPS</h3><p>Finally, here are instructions for using the server in HTTPS mode.
This requires an SSL certificate and a private key. It is also very
platform-specific, but here are the details for using OpenSSL on UNIX:</p><p><span class="hspace"> </span><span class="stt">openssl genrsa -des3 -out private-key.pem 1024</span></p><p>This will generate a new private key, but with a passphrase, which you
can remove as follows:</p><p><div class="SIntrapara"><span class="hspace"> </span><span class="stt">openssl rsa -in private-key.pem -out private-key.pem</span></div><div class="SIntrapara"><span class="hspace"> </span><span class="stt">chmod 400 private-key.pem</span></div></p><p>Now we generate a self-signed certificate:</p><p><span class="hspace"> </span><span class="stt">openssl req -new -x509 -nodes -sha1 -days 365 -key private-key.pem > server-cert.pem</span></p><p>(Each certificate authority has a different way of generating
certificate-signing requests.)</p><p>We can now start the server with:</p><p><span class="hspace"> </span><span class="stt">plt-web-server --ssl</span></p><p>The Web Server will start on port 443 (which can be overridden with the <span class="stt">-p</span> option) using the
<span class="stt">"private-key.pem"</span> and <span class="stt">"server-cert.pem"</span> we’ve created.</p><h3 x-source-module="(lib "web-server/scribblings/tutorial/continue.scrbl")" x-source-pkg="web-server-doc" x-part-tag=""Moving_Forward"">19<tt> </tt><a name="(part._.Moving_.Forward)"></a>Moving Forward</h3><p>As you move forward with your own applications, you may find many other packages
to be useful. There are interfaces to other databases, many tools for generating
output in HTML, XML, JavaScript, etc. See <a href="https://pkgs.racket-lang.org/"><span class="url">https://pkgs.racket-lang.org/</span></a> for
a list of registered packages.</p><p>There is also an active community of users on the Racket mailing list. We
welcome new users!</p><div class="navsetbottom"><span class="navleft"><form class="searchform"><input class="searchbox" style="color: #888;" type="text" value="...search manuals..." title="Enter a search string to search the manuals" onkeypress="return DoSearchKey(event, this, "6.7", "../");" onfocus="this.style.color="black"; this.style.textAlign="left"; if (this.value == "...search manuals...") this.value="";" onblur="if (this.value.match(/^ *$/)) { this.style.color="#888"; this.style.textAlign="center"; this.value="...search manuals..."; }"/></form> <a href="../index.html" title="up to the documentation top" data-pltdoc="x" onclick="return GotoPLTRoot("6.7");">top</a></span><span class="navright"> <span class="nonavigation">← prev</span> <a href="../index.html" title="up to the documentation top" data-pltdoc="x" onclick="return GotoPLTRoot("6.7");">up</a> <span class="nonavigation">next →</span></span> </div></div></div><div id="contextindicator"> </div></body></html>
|