Skip to content

Instantly share code, notes, and snippets.

@devn
Created December 14, 2009 00:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devn/255696 to your computer and use it in GitHub Desktop.
Save devn/255696 to your computer and use it in GitHub Desktop.
<p><html>
<head>
<link rel="stylesheet" href="pygmentize.css" type="text/css" media="screen" charset="utf-8">
</head>
<body></p>
<h1>Foreword</h1>
<p>This is a very rough draft of the tutorial I'm going to put on
compojure.org. It's not complete, but it covers most of the basics. There's a possibility some of the terminology (such as handlers and routes) might change, but I'll let you know if it does. The technical content, however, should be accurate and up to date. Criticism is very welcome; I'd like to know if anything is unclear or could be better worded, or if I'm missing out anything. </p>
<h1>Compojure from the bottom up</h1>
<h2>Handlers</h2>
<p>In Compojure, HTTP requests and HTTP responses are represented by
Clojure maps. A <em>handler</em> is a function that takes a request map as an
argument, and returns a response map. </p>
<div class="codehilite"><pre><span class="p">{</span> <span class="n">request</span> <span class="p">}</span> <span class="o">--&gt;</span> <span class="n">handler</span> <span class="o">--&gt;</span> <span class="p">{</span> <span class="n">response</span> <span class="p">}</span>
</pre></div>
<p>A response map consists of three keys: </p>
<ul>
<li><code>:status</code> (Required, Integer) </li>
<li>The HTTP status code. </li>
<li><code>:headers</code> (Required, Map) </li>
<li>A map of HTTP header names to header values. </li>
<li><code>:body</code> (Optional, {String, ISeq, File, InputStream}) </li>
<li>An object to be encoded as the HTTP response body. </li>
</ul>
<p>A request map consists of many more keys. The most significant ones
are: </p>
<ul>
<li><code>:request-method</code> (Keyword) </li>
<li>
<p>The HTTP request method. Either <code>:get, :head, :options, :put, :post</code> or <code>:delete</code>. </p>
<p>:uri (String)
The relative URI of the HTTP request. </p>
</li>
</ul>
<p>See the request map documentation for more standard keys.
A handler processes the request map and returns a response map. The
simplest possible handler is one that always returns the same
response: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="k">defn </span><span class="nv">hello-world</span> <span class="p">[</span><span class="nv">request</span><span class="p">]</span>
<span class="p">{</span><span class="nv">:status</span> <span class="mi">200</span>
<span class="nv">:headers</span> <span class="p">{}</span>
<span class="nv">:body</span> <span class="s">&quot;Hello World&quot;</span><span class="p">})</span>
</pre></div>
</td></tr></table>
<p>Compojure provides the inline function <code>servlet</code> to convert a handler
function into a HttpServlet proxy compatible with many Java web
servers. </p>
<p>Here is an example of the a handler being turned into a servlet and
passed to an embedded web server: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">run-server</span> <span class="p">{</span><span class="nv">:port</span> <span class="mi">8080</span><span class="p">}</span>
<span class="s">&quot;/*&quot;</span> <span class="p">(</span><span class="nf">servlet</span> <span class="nv">hello-world</span><span class="p">))</span>
</pre></div>
</td></tr></table>
<p>By combining a handler with the <code>run-server</code> function, a basic web
application can be constructed: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7
8</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">ns</span> <span class="nv">example-app</span>
<span class="p">(</span><span class="nf">:use</span> <span class="nv">compojure</span><span class="o">.</span><span class="nv">server</span><span class="o">.</span><span class="nv">jetty</span><span class="p">))</span>
<span class="p">(</span><span class="k">defn </span><span class="nv">hello-world</span> <span class="p">[</span><span class="nv">request</span><span class="p">]</span>
<span class="p">{</span><span class="nv">:status</span> <span class="mi">200</span>
<span class="nv">:headers</span> <span class="p">{}</span>
<span class="nv">:body</span> <span class="s">&quot;Hello World&quot;</span><span class="p">})</span>
<span class="p">(</span><span class="nf">run-server</span> <span class="p">{</span><span class="nv">:port</span> <span class="mi">8080</span><span class="p">}</span>
<span class="s">&quot;/*&quot;</span> <span class="p">(</span><span class="nf">servlet</span> <span class="nv">hello-world</span><span class="p">))</span>
</pre></div>
</td></tr></table>
<p>If you run this code, you should be able to access a web page at:
http://localhost:8080 </p>
<h2>Middleware</h2>
<p><em>Middleware</em> are functions that take a handler as its first argument,
and returns a new handler function based on the original. </p>
<div class="codehilite"><pre><span class="n">handler</span> <span class="o">&amp;</span> <span class="n">args</span> <span class="o">--&gt;</span> <span class="n">middleware</span> <span class="o">--&gt;</span> <span class="n">handler</span>
</pre></div>
<p>An example of a simple middleware function is one that adds a header
to the output of a handler: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="k">defn </span><span class="nv">with-header</span> <span class="p">[</span><span class="nv">handler</span> <span class="nv">header</span> <span class="nv">value</span><span class="p">]</span>
<span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">request</span><span class="p">]</span>
<span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">response</span> <span class="p">(</span><span class="nf">handler</span> <span class="nv">request</span><span class="p">)]</span>
<span class="p">(</span><span class="nf">assoc-in</span> <span class="nv">response</span> <span class="p">[</span><span class="nv">:headers</span> <span class="nv">header</span><span class="p">]</span> <span class="nv">value</span><span class="p">))))</span>
</pre></div>
</td></tr></table>
<p>To apply this to the existing <code>hello-world</code> handler, you can redefine
<code>hello-world</code> with the middleware wrapper. </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="k">def </span><span class="nv">hello-world</span>
<span class="p">(</span><span class="nb">-&gt; </span><span class="nv">hello-world</span>
<span class="p">(</span><span class="nf">with-header</span> <span class="s">&quot;X-Lang&quot;</span> <span class="s">&quot;Clojure&quot;</span><span class="p">)</span>
<span class="p">(</span><span class="nf">with-header</span> <span class="s">&quot;X-Framework&quot;</span> <span class="s">&quot;Compojure&quot;</span><span class="p">)))</span>
</pre></div>
</td></tr></table>
<p>But a more idiomatic way is to use the <code>decorate</code> macro: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">decorate</span> <span class="nv">hello-world</span>
<span class="p">(</span><span class="nf">with-header</span> <span class="s">&quot;X-Lang&quot;</span> <span class="s">&quot;Clojure&quot;</span><span class="p">)</span>
<span class="p">(</span><span class="nf">with-header</span> <span class="s">&quot;X-Framework&quot;</span> <span class="s">&quot;Compojure&quot;</span><span class="p">))</span>
</pre></div>
</td></tr></table>
<p>The decorate macro produces the same effect, but retains the original
metadata of <code>hello-world</code>. </p>
<p>A number of middleware functions are included in Compojure. These
augment handlers in various ways. You can wrap a handler in many
middleware functions, or none at all. Some of the most commonly used
middleware functions are: </p>
<ol>
<li><code>with-params</code></li>
<li><code>with-cookies</code> </li>
<li><code>with-multipart</code></li>
<li><code>with-session</code> </li>
</ol>
<h2>Routes</h2>
<h3>3.1. Route syntax</h3>
<p>A <em>route</em> is a type of handler that returns nil if the request does
not match certain criteria. A route can be written: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="k">defn </span><span class="nv">index-route</span> <span class="p">[</span><span class="nv">request</span><span class="p">]</span>
<span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">and </span><span class="p">(</span><span class="nb">= </span><span class="p">(</span><span class="nf">:request-method</span> <span class="nv">request</span><span class="p">)</span> <span class="nv">:get</span><span class="p">)</span>
<span class="p">(</span><span class="nb">= </span><span class="p">(</span><span class="nf">:uri</span> <span class="nv">request</span><span class="p">)</span> <span class="s">&quot;/&quot;</span><span class="p">))</span>
<span class="p">{</span><span class="nv">:status</span> <span class="mi">200</span>
<span class="nv">:headers</span> <span class="p">{}</span>
<span class="nv">:body</span> <span class="s">&quot;The index page&quot;</span><span class="p">))</span>
</pre></div>
</td></tr></table>
<p>But as this is a very common task, Compojure provides macros that
remove the need for such verbose boilerplate. The idiomatic way of
writing the above route in Compojure is: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="k">def </span><span class="nv">index-route</span>
<span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/&quot;</span> <span class="s">&quot;The index page&quot;</span><span class="p">))</span>
</pre></div>
</td></tr></table>
<p>The Compojure route syntax is very powerful, but is based on a few
basic principles. </p>
<h3>3.1.1. The method macro</h3>
<p>The first symbol is the <em>method macro</em> that denotes the HTTP request
method. In the above example, this is the GET macro. There are also
macros for all the other common HTTP methods: </p>
<div class="codehilite"><pre><span class="n">GET</span><span class="p">,</span> <span class="n">POST</span><span class="p">,</span> <span class="n">PUT</span><span class="p">,</span> <span class="n">DELETE</span> <span class="ow">and</span> <span class="n">HEAD</span>
</pre></div>
<p>Because sometimes you don't care what method is being used, there is
also: <code>ANY</code> -- Which matches any method. </p>
<h3>3.1.2. The path template</h3>
<p>The second item in the route form is the <em>path template</em>. This matches
against the HTTP request URI. The path template can include
parameters, which are identifiers denoted by a beginning ":": </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/product/:id&quot;</span> <span class="o">...</span><span class="p">)</span>
</pre></div>
</td></tr></table>
<p>A parameter will match a string of any character apart from "/", ".",
"," ";" and "?". The matched value is stored in a map the :route-
params key in the request map: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/product/:id&quot;</span>
<span class="p">(</span><span class="nb">str </span><span class="s">&quot;You chose product: &quot;</span>
<span class="p">(</span><span class="nb">-&gt; </span><span class="nv">request</span> <span class="nv">:route-params</span> <span class="nv">:id</span><span class="p">)))</span>
</pre></div>
</td></tr></table>
<p>You can include more than one parameter, and even the same parameter
multiple times. In the latter case the value in the route-params map
will be a vector with all the matching values from the URI.
As well as parameters, you can match wildcards, denoted by a "<em>". A
wildcard will match a string of any character. The value matched by
the wildcard is stored under the :</em> key. </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/public/*&quot;</span>
<span class="p">(</span><span class="nb">str </span><span class="s">&quot;Loading file: &quot;</span>
<span class="p">(</span><span class="nb">-&gt; </span><span class="nv">request</span> <span class="nv">:route-params</span> <span class="nv">:*</span><span class="p">)))</span>
</pre></div>
</td></tr></table>
<p>As well as relative URIs, absolute URLs can also be matched: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;http://www.example.com/&quot;</span> <span class="o">...</span><span class="p">)</span>
</pre></div>
</td></tr></table>
<p>This behaviour is triggered when the beginning of the path template is
a URL scheme, such as "http://" or "https://". You can use parameters
or wildcards in the domain: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;http://:subdomain.example.com/&quot;</span> <span class="o">...</span><span class="p">)</span>
</pre></div>
</td></tr></table>
<p>But you cannot use a parameter to match the scheme. However, the
request map does contain the :scheme key for circumstances where it is
required to place the URL scheme into a variable. </p>
<p>For more precise control over URI matching, the path template can be
specified using a regular expression: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">GET</span> <span class="o">#</span><span class="s">&quot;/product/(\d+)&quot;</span> <span class="o">...</span><span class="p">)</span>
</pre></div>
</td></tr></table>
<p>In this case the :route-params key contains a vector corresponding to
the groups matched by the expression. </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">GET</span> <span class="o">#</span><span class="s">&quot;/product/(\d+)&quot;</span>
<span class="p">(</span><span class="nb">str </span><span class="s">&quot;You chose product: &quot;</span>
<span class="p">((</span><span class="nf">:route-params</span> <span class="nv">request</span><span class="p">)</span> <span class="mi">0</span><span class="p">)))</span>
</pre></div>
</td></tr></table>
<p>Unlike re-groups, the first element of the parameter vector is not the
entire match, but the first nested group. </p>
<h3>3.1.3. The return value</h3>
<p>In the Compojure route syntax, the return value represents a
modification to a blank response map: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">{</span><span class="nv">:status</span> <span class="mi">200</span><span class="o">,</span> <span class="nv">:headers</span> <span class="p">{}}</span>
</pre></div>
</td></tr></table>
<p>The class of the return value determines how it alters the response
map. The following classes are used: </p>
<ul>
<li><code>java.lang.Integer</code></li>
<li>An integer return value sets the status code of the response </li>
<li><code>java.lang.String</code></li>
<li>A string return value is added to the response body </li>
<li><code>clojure.lang.ISeq</code> </li>
<li>A return value of a Clojure sequence sets the response body </li>
<li><code>java.io.File</code> </li>
<li>A return value of a File sets the response body </li>
<li><code>java.io.InputStream</code> </li>
<li>A return value of an InputStream sets the response body </li>
<li><code>java.net.URL</code> </li>
<li>A InputStream to the URL is opened and the response body set to the stream </li>
<li><code>clojure.lang.Keyword</code> </li>
<li>If the keyword is :next, the response is nil. Otherwise the keyword is treated as a string. </li>
<li><code>java.util.Map</code> </li>
<li>The map is intelligently merged into the response map </li>
<li><code>clojure.lang.Fn</code> </li>
<li>The request and response maps are passed to the function as arguments, and the return value of the function is used to determine the response. </li>
<li><code>clojure.lang.IPersistentVector</code> </li>
<li>Each element in the vector is used to update the response </li>
</ul>
<p>Some examples of usage follow: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/&quot;</span>
<span class="s">&quot;Index page&quot;</span><span class="p">)</span>
<span class="p">(</span><span class="nf">ANY</span> <span class="s">&quot;*&quot;</span>
<span class="p">[</span><span class="mi">404</span> <span class="s">&quot;Page Not Found&quot;</span><span class="p">])</span>
<span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/image&quot;</span>
<span class="p">(</span><span class="nf">File</span><span class="o">.</span> <span class="s">&quot;./public/image.png&quot;</span><span class="p">))</span>
<span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/new-product&quot;</span>
<span class="p">(</span><span class="k">if </span><span class="nv">product-released?</span>
<span class="s">&quot;Our product is amazing&quot;</span>
<span class="nv">:next</span><span class="p">))</span>
<span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/map-example&quot;</span>
<span class="p">{</span><span class="nv">:body</span> <span class="s">&quot;Hello World&quot;</span><span class="p">})</span>
</pre></div>
</td></tr></table>
<h3>3.1.4. Local bindings</h3>
<p>The final useful piece of functionality the route syntax provides is a
small set of useful local bindings: </p>
<ol>
<li>params =&gt; <code>(:params request)</code></li>
<li>cookies =&gt; <code>(:cookies request)</code></li>
<li>session =&gt; <code>(:session request)</code></li>
<li>flash =&gt; <code>(:flash request)</code> </li>
</ol>
<p>The <code>:params</code> key and the associated params binding provides a merged
map of all parameters from the request. This includes the contents
of <code>:route-params</code> (when a map), and the parameters added by the with-
params and with-multipart middleware.
Thus, an idiomatic and concise way of refering to route params is: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/product/:id&quot;</span>
<span class="p">(</span><span class="nb">str </span><span class="s">&quot;You chose product: &quot;</span> <span class="p">(</span><span class="nf">params</span> <span class="nv">:id</span><span class="p">)))</span>
</pre></div>
</td></tr></table>
<h3>3.2. Combining routes</h3>
<p>Routes can be combined with the <code>routes*</code> function: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="k">def </span><span class="nv">main-routes</span>
<span class="p">(</span><span class="nf">routes*</span>
<span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/&quot;</span>
<span class="s">&quot;Index page&quot;</span><span class="p">)</span>
<span class="p">(</span><span class="nf">ANY</span> <span class="s">&quot;*&quot;</span>
<span class="p">[</span><span class="mi">404</span> <span class="s">&quot;Page Not Found&quot;</span><span class="p">])))</span>
</pre></div>
</td></tr></table>
<p>The <code>routes*</code> function returns a new route. When supplied with a
request map, this new route tries each sub-route in turn until it
receieves a response that is not nil. The code for this is simple: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="k">defn </span><span class="nv">routes*</span> <span class="p">[</span><span class="nv">&amp;</span> <span class="nv">sub-routes</span><span class="p">]</span>
<span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">request</span><span class="p">]</span>
<span class="p">(</span><span class="nb">some </span><span class="o">#</span><span class="p">(</span><span class="nv">%</span> <span class="nv">request</span><span class="p">)</span> <span class="nv">sub-routes</span><span class="p">)))</span>
</pre></div>
</td></tr></table>
<p>The <code>routes*</code> function is the more primitive ancestor of the more
commonly used <code>routes</code> function. The difference between the two is
that <code>routes</code> adds two pieces of common middleware: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="k">defn </span><span class="nv">routes</span> <span class="p">[</span><span class="nv">&amp;</span> <span class="nv">sub-routes</span><span class="p">]</span>
<span class="p">(</span><span class="nb">-&gt; </span><span class="p">(</span><span class="nb">apply </span><span class="nv">routes*</span> <span class="nv">sub-routes</span><span class="p">)</span>
<span class="nv">with-params</span>
<span class="nv">with-cookies</span><span class="p">))</span>
</pre></div>
</td></tr></table>
<p>It is recommended that <code>routes</code> be preferred for normal use.
For convenience, Compojure also provides a <code>defroutes</code> macro: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="nf">defroutes</span> <span class="nv">main-routes</span>
<span class="p">(</span><span class="nf">GET</span> <span class="s">&quot;/&quot;</span>
<span class="s">&quot;Index page&quot;</span><span class="p">)</span>
<span class="p">(</span><span class="nf">ANY</span> <span class="s">&quot;*&quot;</span>
<span class="p">[</span><span class="mi">404</span> <span class="s">&quot;Page not found&quot;</span><span class="p">]))</span>
</pre></div>
</td></tr></table>
<h2>4. HTML</h2>
<h3>4.1. Syntax</h3>
<p>Compojure uses a syntax made up of vectors, maps and strings to
represent HTML. The <code>html</code> function translates this syntax into a
string of HTML. </p>
<p>Here is an example of the syntax: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">[</span><span class="nv">:h1</span> <span class="p">{</span><span class="nv">:id</span> <span class="s">&quot;title&quot;</span><span class="p">}</span> <span class="s">&quot;Hello World&quot;</span><span class="p">]</span>
</pre></div>
</td></tr></table>
<p>In Compojure, this is referred to as a tag vector, so called because
it represents a HTML tag. </p>
<ol>
<li>The first element in the vector is the tag name. This can be a
keyword, a string, or a symbol. </li>
<li>The second element can optionally be a map. If it is a map, it is
considered to represent the attributes of the tag, otherwise it is
treated as the tag's content. </li>
<li>Any further elements are treated as the content of the tag. A tag's
content can be made up of any number of strings or nested tag vectors. </li>
</ol>
<p>Here are some examples: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">[</span><span class="nv">:div</span> <span class="s">&quot;Hello&quot;</span> <span class="s">&quot;World&quot;</span><span class="p">]</span>
<span class="p">[</span><span class="nv">:div</span> <span class="p">[</span><span class="nv">:div</span> <span class="p">{</span><span class="nv">:class</span> <span class="s">&quot;inner&quot;</span><span class="p">}</span> <span class="s">&quot;Nested&quot;</span><span class="p">]]</span>
<span class="p">[</span><span class="nv">:div</span> <span class="p">[</span><span class="nv">:span</span> <span class="s">&quot;Hello&quot;</span><span class="p">]</span> <span class="p">[</span><span class="nv">:span</span> <span class="s">&quot;World&quot;</span><span class="p">]]</span>
</pre></div>
</td></tr></table>
<p>A Clojure sequence is also considered valid content. Sequences are
automatically expanded out, such that this: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">[</span><span class="nv">:div</span> <span class="p">(</span><span class="nb">list </span><span class="s">&quot;Hello&quot;</span> <span class="s">&quot;World&quot;</span><span class="p">)]</span>
</pre></div>
</td></tr></table>
<p>Is considered equivalent to: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">[</span><span class="nv">:div</span> <span class="s">&quot;Hello&quot;</span> <span class="s">&quot;World&quot;</span><span class="p">]</span>
</pre></div>
</td></tr></table>
<p>This functionality is useful for functions that have a rest-param: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">(</span><span class="k">defn </span><span class="nv">html-document</span> <span class="p">[</span><span class="nv">title</span> <span class="nv">&amp;</span> <span class="nv">body</span><span class="p">]</span>
<span class="p">(</span><span class="nf">html</span>
<span class="p">[</span><span class="nv">:html</span>
<span class="p">[</span><span class="nv">:head</span>
<span class="p">[</span><span class="nv">:title</span> <span class="nv">title</span><span class="p">]]</span>
<span class="p">[</span><span class="nv">:body</span>
<span class="nv">body</span><span class="p">]]))</span>
</pre></div>
</td></tr></table>
<p>Compojure also provides a shorthand for defining elements with id or
class attributes, based on standard CSS syntax. Any alphanumeric, "-"
or "_" after a "#" in the tag name is used as the id attribute: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">[</span><span class="nv">:h1</span><span class="no">#t</span><span class="nv">itle</span> <span class="s">&quot;Compojure&quot;</span><span class="p">]</span>
</pre></div>
</td></tr></table>
<p>Similarly, any alphanumeric, "-" or "_" after a "." is used as the
class attribute: </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">[</span><span class="nv">:div</span><span class="o">.</span><span class="nv">summary</span> <span class="s">&quot;A Clojure web framework&quot;</span><span class="p">]</span>
</pre></div>
</td></tr></table>
<p>You can define many classes, but only one id using this syntax. </p>
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="codehilite"><pre><span class="p">[</span><span class="nv">:pre</span><span class="o">#</span><span class="nv">example1</span><span class="o">.</span><span class="nv">source</span><span class="o">.</span><span class="nv">clojure</span>
<span class="s">&quot;(some example code)&quot;</span><span class="p">]</span>
</pre></div>
</td></tr></table>
<p></body>
</html></p>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment