red-tape/index.html @ 15d3b832fdc5 default tip

cl-digraph: Update site.
author Steve Losh <steve@stevelosh.com>
date Wed, 21 Jun 2023 15:21:12 -0400
parents ded5a80325ac
children (none)
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Red Tape</title>
        <link rel="stylesheet" href="./_dmedia/tango.css"/>
        <link rel="stylesheet/less" type="text/css" href="./_dmedia/style.less">
        <script src="./_dmedia/less.js" type="text/javascript">
        </script>
    </head>
    <body class="index">
        <div class="wrap">
            <header><h1><a href="">Red Tape</a></h1></header>
                <div class="markdown">
<p>Red Tape is a Clojure library for processing user-submitted data.  It's heavily
inspired by Django's forms (the good parts).</p>
<p><strong>License:</strong> MIT/X11<br />
<strong>Documentation:</strong> <a href="http://sjl.bitbucket.org/red-tape/">http://sjl.bitbucket.org/red-tape/</a><br />
<strong>Changelog:</strong> <a href="http://sjl.bitbucket.org/red-tape/changelog/">http://sjl.bitbucket.org/red-tape/changelog/</a><br />
<strong>Issues:</strong> <a href="http://github.com/sjl/red-tape/issues/">http://github.com/sjl/red-tape/issues/</a><br />
<strong>Mercurial:</strong> <a href="http://bitbucket.org/sjl/red-tape/">http://bitbucket.org/sjl/red-tape/</a><br />
<strong>Git:</strong> <a href="http://github.com/sjl/red-tape/">http://github.com/sjl/red-tape/</a></p>
<h2 id="what-does-it-look-like">What Does it Look Like?</h2>
<p>You'll need to read the docs to really understand what's going on, but here's
a quick example so you can see the shape of the code.</p>
<p>First you'll define a form:</p>
<div class="codehilite"><pre><span class="p">(</span><span class="kd">ns </span><span class="nv">my-web-app</span>
    <span class="p">(</span><span class="ss">:require</span> <span class="p">[</span><span class="nv">red-tape.core</span> <span class="ss">:refer</span> <span class="p">[</span><span class="nv">defform</span><span class="p">]]</span>
              <span class="p">[</span><span class="nv">red-tape.cleaners</span> <span class="ss">:as</span> <span class="nv">cleaners</span><span class="p">]))</span>

<span class="p">(</span><span class="nf">defform</span> <span class="nv">comment-form</span>
  <span class="p">{</span><span class="ss">:arguments</span> <span class="p">[</span><span class="nv">user</span><span class="p">]</span>
   <span class="ss">:initial</span> <span class="p">{</span><span class="ss">:email</span> <span class="p">(</span><span class="ss">:email</span> <span class="nv">user</span><span class="p">)}}</span>

  <span class="ss">:email</span> <span class="o">^</span><span class="ss">:red-tape/optional</span>
         <span class="p">[</span><span class="o">#</span><span class="p">(</span><span class="nf">cleaners/matches</span> <span class="o">#</span><span class="s">&quot;\S+@\S+&quot;</span> <span class="nv">%</span>
                             <span class="s">&quot;Enter a valid email (or leave it blank).&quot;</span><span class="p">)]</span>
  <span class="ss">:comment</span> <span class="p">[</span><span class="nv">clojure.string/trim</span>
            <span class="nv">cleaners/non-blank</span>
            <span class="o">#</span><span class="p">(</span><span class="nf">cleaners/max-length</span> <span class="mi">2000</span> <span class="nv">%</span><span class="p">)])</span>
</pre></div>


<p>A form can have some arguments, initial data, as well as some fields.  Fields
have functions for validating data and massaging it into what you need.</p>
<p>Now we can use the form to create an initial form (which we'll display to the
user so they can fill it in):</p>
<div class="codehilite"><pre><span class="p">(</span><span class="k">def </span><span class="nv">steve</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;steve@stevelosh.com&quot;</span><span class="p">})</span>
<span class="p">(</span><span class="k">def </span><span class="nv">anon</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;&quot;</span><span class="p">})</span>

<span class="p">(</span><span class="nf">comment-form</span> <span class="nv">steve</span><span class="p">)</span>
<span class="c1">; =&gt;</span>
<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">true</span>
 <span class="ss">:valid</span> <span class="nv">nil</span>
 <span class="ss">:results</span> <span class="nv">nil</span>
 <span class="ss">:errors</span> <span class="nv">nil</span>
 <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;steve@stevelosh.com&quot;</span> <span class="ss">:comment</span> <span class="s">&quot;&quot;</span><span class="p">}</span>
 <span class="ss">:arguments</span> <span class="p">{</span><span class="ss">:user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;steve@stevelosh.com&quot;</span><span class="p">}}}</span>

<span class="p">(</span><span class="nf">comment-form</span> <span class="nv">anon</span><span class="p">)</span>
<span class="c1">; =&gt;</span>
<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">true</span>
 <span class="ss">:valid</span> <span class="nv">nil</span>
 <span class="ss">:results</span> <span class="nv">nil</span>
 <span class="ss">:errors</span> <span class="nv">nil</span>
 <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;&quot;</span> <span class="ss">:comment</span> <span class="s">&quot;&quot;</span><span class="p">}</span>
 <span class="ss">:arguments</span> <span class="p">{</span><span class="ss">:user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;&quot;</span><span class="p">}}}</span>
</pre></div>


<p>And once they submit it we use the form once again to validate and transform the
data:</p>
<div class="codehilite"><pre><span class="p">(</span><span class="nf">comment-form</span> <span class="nv">steve</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;steve+nospam@stevelosh.com&quot;</span>
                     <span class="ss">:comment</span> <span class="s">&quot;    Hello!&quot;</span><span class="p">})</span>
<span class="c1">; =&gt;</span>
<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span>
 <span class="ss">:valid</span> <span class="nv">true</span>
 <span class="ss">:results</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;steve+nospam@stevelosh.com&quot;</span>
           <span class="ss">:comment</span> <span class="s">&quot;Hello!&quot;</span><span class="p">}</span>
 <span class="ss">:errors</span> <span class="nv">nil</span>
 <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;steve+nospam@stevelosh.com&quot;</span>
        <span class="ss">:comment</span> <span class="s">&quot;    Hello!&quot;</span><span class="p">}</span>
 <span class="ss">:arguments</span> <span class="p">{</span><span class="ss">:user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;steve@stevelosh.com&quot;</span><span class="p">}}}</span>
</pre></div>


<p>If there are errors, we know the data was bad and we need to show the form to
the user again so they can fix it:</p>
<div class="codehilite"><pre><span class="p">(</span><span class="nf">comment-form</span> <span class="nv">anon</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;&quot;</span>
                    <span class="ss">:comment</span> <span class="s">&quot;&quot;</span><span class="p">})</span>
<span class="c1">; =&gt;</span>
<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span>
 <span class="ss">:valid</span> <span class="nv">false</span>
 <span class="ss">:results</span> <span class="nv">nil</span>
 <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:comment</span> <span class="s">&quot;This field is required.&quot;</span><span class="p">}</span>
 <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;&quot;</span>
        <span class="ss">:comment</span> <span class="s">&quot;&quot;</span><span class="p">}</span>
 <span class="ss">:arguments</span> <span class="p">{</span><span class="ss">:user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">&quot;&quot;</span><span class="p">}}}</span>
</pre></div>


<p>But you'll really need to read the docs to understand what's happening here.</p>
<h2 id="why-red-tape">Why Red Tape?</h2>
<p>There are a lot of other Clojure "validation" libraries out there.  I wasn't
happy with any of them for a few reasons:</p>
<ul>
<li>Some try to be too general and validate <em>anything</em>.  Red Tape is designed to
  work with user-submitted web form data with very little friction.</li>
<li>Some don't do enough.  Validating data is only the first step.  When working
  with forms you want to transform data, handle initial data, and so on.  Red
  Tape knows what real-world web forms needs and gives it to you.</li>
<li>Some are too complicated.  Red Tape uses just a little bit of macro magic
  combined with vanilla Clojure functions and Slingshot to do everything.  This
  makes it very easy to extend and work with.</li>
</ul>
<p>In a nutshell, Red Tape was made by someone who used Django's forms in client
sites for years and knows all the <a href="https://github.com/dwaiter/django-goodfields">rough edges</a> that needed sanding.</p>
<h2 id="get-started">Get Started</h2>
<p>Get started by <a href="./installation/">installing</a> Red Tape, then move on to <a href="./basics/">the
basics</a>.</p><h2>Table of Contents</h2><ol class="toc"><li><a href="installation/">Installation</a></li><li><a href="basics/">Basics</a></li><li><a href="input/">Form Input</a></li><li><a href="cleaners/">Cleaners</a></li><li><a href="result-maps/">Result Maps</a></li><li><a href="initial-data/">Initial Data</a></li><li><a href="form-arguments/">Form Arguments</a></li><li><a href="rendering/">Rendering</a></li><li><a href="reference/">Reference</a></li><li><a href="changelog/">Changelog</a></li></ol>
                </div>
            <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve
Losh</a>.</p>
<p><br/><a id='rochester-made' href='http://rochestermade.com' title='Rochester Made'><img src='http://rochestermade.com/media/images/rochester-made-dark-on-light.png' alt='Rochester Made' title='Rochester Made' /></a></p></footer>
        </div>
    </body>
</html>