Source

sjl.bitbucket.org / red-tape / index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Red Tape</title>
        <link rel="stylesheet" href="./_dmedia/bootstrap.css"/>
        <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="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-address</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>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.