Web Development With Clojure Build Bulletproof Web Apps With Less Code 1st Edition Dmitri Sotnikov Latest PDF 2025
Web Development With Clojure Build Bulletproof Web Apps With Less Code 1st Edition Dmitri Sotnikov Latest PDF 2025
DOWNLOAD EBOOK
Web Development with Clojure Build Bulletproof Web Apps with
Less Code 1st Edition Dmitri Sotnikov pdf download
Available Formats
https://ebookgate.com/product/web-development-with-clojure-build-
bulletproof-web-apps-with-less-code-2nd-edition-dmitri-sotnikov/
ebookgate.com
https://ebookgate.com/product/web-development-with-django-cookbook-
bendoraitis/
ebookgate.com
https://ebookgate.com/product/developing-web-applications-with-
haskell-and-yesod-safety-driven-web-development-1st-edition-michael-
snoyman/
ebookgate.com
https://ebookgate.com/product/agile-web-development-with-rails-third-
edition-sam-ruby/
ebookgate.com
Web Development with Apache and Perl 1st Edition Theo
Petersen
https://ebookgate.com/product/web-development-with-apache-and-
perl-1st-edition-theo-petersen/
ebookgate.com
https://ebookgate.com/product/head-first-html5-programming-building-
web-apps-with-javascript-1st-edition-eric-t-freeman/
ebookgate.com
https://ebookgate.com/product/handcrafted-css-more-bulletproof-web-
design-1st-edition-dan-cederholm/
ebookgate.com
https://ebookgate.com/product/asp-net-web-development-with-macromedia-
dreamweaver-mx-2004-1st-edition-costas-hadjisotiriou/
ebookgate.com
https://ebookgate.com/product/building-web-applications-with-ado-net-
and-xml-web-services-1st-edition-richard-hundhausen/
ebookgate.com
Early Praise for Web Development with Clojure
This is a great resource and one I will insist all my trainee Clojure web developers
read.
➤ Colin Yates, principal engineer and technical team leader, QFI Consulting
LLP
In Web Development with Clojure, Dmitri Sotnikov manages to take the sting out
of getting started building real applications with Clojure. If you know the basics
but are still trying to “get” Clojure, this is the book for you.
➤ Russ Olsen, vice president, consulting services, Cognitect
With this book, you’ll jump right into web development using powerful functional
programming techniques. As you follow along, you’ll make your app more scalable
and maintainable—and you’ll bring the expressiveness of Clojure to your client-
side JavaScript.
➤ Ian Dees, author, Cucumber Recipes
Dmitri’s book successfully walks a narrow line of introducing language features
while also solving real, modern software-development problems. This represents
a significant return on investment for the time you devote to a technical book.
➤ Brian Sletten, Bosatsu Consulting, author of Resource-Oriented Architecture
Patterns for Webs of Data
This is a fast-paced, no-cruft intro to applying your Clojure chops to making web
apps. From Chapter 1 you’re running a real web app and then adding databases,
security, JavaScript, and more. No dogma, no preaching, no fluff! To the point,
productive, and clear. This book gives you all you need to get started and have a
real app that you can continue to grow.
➤ Sam Griffith Jr., polyglot programmer at Interactive Web Systems, LLC
Web Development with Clojure
Build Bulletproof Web Apps with Less Code
Dmitri Sotnikov
3. Liberator Services . . . . . . . . . . . 55
Creating the Project 56
Defining Resources 56
Putting It All Together 60
What You’ve Learned 66
4. Database Access . . . . . . . . . . . . 67
Working with Relational Databases 67
Report Generation 72
What You’ve Learned 79
5. Picture Gallery . . . . . . . . . . . . 81
The Development Process 81
What’s in a Gallery 81
Creating the Application 83
Application Data Model 84
Task A: Account Registration 86
Task B: Login and Logout 95
Task C: Uploading Pictures 97
Contents • vi
7. Mixing It Up . . . . . . . . . . . . 145
Using Selmer 145
Upgrading to ClojureScript 158
SQL Korma 168
Creating Application Templates 171
What You’ve Learned 173
Index . . . . . . . . . . . . . . 209
Introduction
This book’s cover has a bonsai tree on it. I chose it to represent elegance and
simplicity, as these qualities make Clojure such an attractive language. A
good software project is like a bonsai. You have to meticulously craft it to
take the shape you want, and the tool you use should make it a pleasant
experience. I hope to convince you here that Clojure is that tool.
Why Clojure?
Clojure is a small language that has simplicity and correctness as its primary
goals. Being a functional language, it emphasizes immutability and declarative
programming. As you’ll see in this book, these features make it easy and
idiomatic to write clean and correct code.
There are many languages to choose from and as many opinions on what
makes any one of them a good language. Some languages are simple but
verbose. You’ve probably heard people say that verbosity really doesn’t matter,
the argument being that when two languages are Turing complete, anything
that can be written in one language can also be written in the other with a
bit of extra code.
I think that’s missing the point, however. The real question is not whether
something can be expressed in principle. It’s how well the language maps to
the problem being solved. One language will let you think in terms of your
problem domain while another will force you to translate the problem to its
constructs.
The latter is often tedious and rarely enjoyable. You end up writing a lot of
boilerplate code and constantly repeating yourself. There’s a certain amount
of irony involved in having to write repetitive code.
Other languages aren’t verbose and they provide many different tools for
solving problems. Unfortunately, having many tools does not directly translate
into higher productivity.
The more features there are, the more things you have to keep in your head
to work with the language effectively. With many languages I find myself
constantly expending mental overhead thinking about all the different features
and how they interact with one another.
As web development is one of the major domains for using Clojure, several
popular libraries and frameworks have sprouted in this area. The Clojure web
stack is based on the Ring and Compojure libraries.1,2 Ring is the base HTTP
library, while Compojure provides routing on top of it. In the following chapters
you’ll become familiar with the web stack and how to use it effectively to build
your web applications.
1. https://github.com/ring-clojure/ring
2. https://github.com/weavejester/compojure
There are many platforms for doing web development, so why should you
choose Clojure over other options?
Well, consider those options. Many popular platforms force you to make trade-
offs. Some platforms lack performance, others require a lot of boilerplate, and
others lack the infrastructure necessary for real-world applications.
The benefits the frameworks offer also come with inherent costs. Since many
operations are done implicitly, you have to memorize what effects any action
might have. This opaqueness makes your code more difficult to reason about.
When you need to do something that is at odds with the framework’s design
it can quickly become awkward and difficult. You might have to dive deep
into the internals of the particular framework and create hacks around the
expected behaviors.
My goal is to give you both a solid understanding of the Clojure web stack
and the expertise to quickly and easily build web applications using it. The
following chapters will guide you all the way from setting up your development
environment to having a complete real-world application. I will show what’s
available, then guide you in structuring your application using the current
best practices.
In this chapter we’ll cover how to develop a simple guestbook application that
allows users to leave messages for each other. We’ll see the basic structure
of a web application as well as the tools necessary for effective Clojure devel-
opment. If you’re new to Clojure, I recommend taking a look at Appendix 2,
Clojure Primer, on page 181, for a crash course on the basic concepts and
syntax.
1. http://www.oracle.com/technetwork/java/javase/downloads/index.html
2. http://maven.apache.org/
3. http://ant.apache.org/
4. http://leiningen.org/
With Leiningen, you don’t need to worry about manually downloading all the
libraries for your project. You can simply specify the top-level dependencies,
and they will cause the libraries they depend on to be pulled in automatically.
Let’s test this. We’ll create a new project by downloading the script and run-
ning the following commands:
wget https://raw.github.com/technomancy/leiningen/stable/bin/lein
chmod +x lein
mv lein ~/bin
lein new myapp
Since we’re running lein for the first time, it will need to install itself. Once
the install is finished you should see the following output if the command
completes successfully:
Generating a project called myapp based on the 'default' template.
To see other templates (app, lein plug-in, etc), try `lein help new`.
A new folder called myapp has been created, containing a skeleton application.
The code for the application can be found in the src folder. There we’ll have
another folder called myapp containing a single source file named core.clj. This
file has the following code inside:
(ns myapp.core)
(defn foo
"I don't do a whole lot."
[x]
(println x "Hello, World!"))
Note that the namespace declaration matches the folder structure. Since the
core namespace is inside the myapp folder, its name is myapp.core.
5. https://clojars.org/
6. http://leiningen.org/#install
The project.clj file will allow us to manage many different aspects of our appli-
cation, as well. For example, we could set the foo function from the myapp.core
namespace as the entry point for the application using the :main key:
(defproject myapp "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]]
;;this will set foo as the main function
:main myapp.core/foo)
The application can now be run from the command line using lein run. Since
the foo function expects an argument, we’ll have to pass one in:
lein run First
First Hello, World!
In the preceding example we created a very simple application that has only
a single dependency: the Clojure runtime. If we used this as the base for a
web application, then we’d have to write a lot of boilerplate to get it up and
running. Let’s see how we can use a Leiningen template to create a web-
application project with all the boilerplate already set up.
Leiningen Templates
The templates consist of skeleton projects that are instantiated when the
name of the template is supplied to the lein script. The templates themselves
are simply Clojure projects that use the lein-newnew plug-in.7 Later on we’ll
see how we can create such templates ourselves.
7. https://github.com/Raynes/lein-newnew
For now, we’ll use the compojure-app template to instantiate our next applica-
tion.8 The template name is specified as the argument following the new
keyword when running lein, followed by the name of the project. To make a
web application instead of the default one as we did a moment ago, we only
have to do the following:
lein new compojure-app guestbook
This will cause Leiningen to use the compojure-app template when creating
the guestbook application. This type of application needs to start up a web
server in order to run. To do that we can run lein ring server instead of lein run.
When we run the application, we’ll see the following output in the console
and a new browser window will pop up showing the home page.
lein ring server
guestbook is starting
2013-07-14 18:21:06.603:INFO:oejs.Server:jetty-7.6.1.v20120215
2013-07-14 18:21:06.639:INFO:oejs.AbstractConnector:
[email protected]:3000
Started server on port 3000
Now that we know how to create and run our applications, we’ll look at our
editor options.
You might have noticed that Clojure code can quickly end up having lots of
parentheses. Keeping them balanced by hand would quickly turn into an
exercise in frustration. Luckily, Clojure editors will do this for us.
In fact, not only do the editors balance the parentheses, but some are even
structurally aware. This means the editor knows where one expression ends
and another begins. Therefore, we can navigate and select code in terms of
blocks of logic instead of lines of text.
In this chapter we’ll be using Light Table to work with our guestbook applica-
tion.9 It’s very easy to get up and running and will allow us to quickly dive
into writing some code. However, its functionality is somewhat limited and
you may find it insufficient for larger projects. Alternative development envi-
ronments are discussed in Appendix 1, Alternative IDE Options, on page 177.
8. https://github.com/yogthos/compojure-template
9. http://www.lighttable.com/
Light Table offers a very minimal look. By default it simply shows the editor
pane with the welcome message (see the following figure).
We’ll add the workspace pane from the menu by selecting View -> Workspace
or pressing Ctrl-T on Windows/Linux or Cmd-T on OS X.
From there we can open the guestbook project by navigating to the Folder
tab on the top left, as the following figure shows.
Once the project is selected we can navigate the project tree and select files
we wish to edit (see the following figure).
Now that we have our development environment set up, we can finally look
at adding some functionality to our guestbook application.
At this point a list of different connection options will pop up. We’ll select the
Clojure option, as seen in Figure 5, Light Table Clojure connection, on page
8. Then we’ll navigate to the guestbook project folder and select the project.clj
file.
With our project connected to Light Table we can start evaluating things right
in the editor!
You can try this immediately by navigating to any function and pressing
Ctrl-Enter on Windows and Linux or Cmd-Enter on OS X. If we do this while the
cursor is on the home function, we’ll see the following printed next to it:
#'guestbook.routes.home/home
This says that the function has been evaluated in the REPL and is now
available for use.
(use 'guestbook.repl)
(start-server)
When the code is evaluated the HTTP server will start up and a new browser
window will open, pointing to the home page (as in the following figure).
Since we don’t wish start-server to continue being called, we’ll remove the pre-
ceding code from the editor.
Alternatively, we could disable the live evaluation by clicking the live icon on
the top right. With the live mode disabled we can run commands using Alt-Enter .
As you can see, calling home simply generates an HTML string for our home
page. This is what gets rendered in the browser when we navigate to
http://localhost:3000.
Notice that we use Clojure vectors to represent the corresponding HTML tags
in our code. If we add some new tags and reload the page, we’ll see the
changes. For example, let’s update our home function to display a heading
and a form to enter a message.
(defn home []
(layout/common
[:h1 "Guestbook"]
[:p "Welcome to my guestbook"]
[:hr]
[:form
[:p "Name:"]
[:input]
[:p "Message:"]
[:textarea {:rows 10 :cols 40}]]))
When we reload the page, we’ll immediately see the changes we made (refer
to Figure 9, Guestbook, on page 11).
Figure 9—Guestbook
You might have guessed that the code directly below the home function is
responsible for binding the "/" route to it.
(defroutes home-routes
(GET "/" [] (home)))
Here, we use defroutes to define the routes for the guestbook.routes.home namespace.
Each route represents a URI to which your application responds. It starts
with the type of the HTTP request it responds to, such as GET or POST, fol-
lowed by the parameters and the body.
Before we move on to add any more functionality to the project, we’ll take a
quick look at the files that were generated for our guestbook application.
models/
routes/
home.clj
views/
layout.clj
handler.clj
repl.clj
test/
guestbook/
test/
hanlder.clj
project.clj
README.md
In our project’s root folder is the project.clj file that is used for configuring and
building the application.
We also have several folders in our project. The src folder is where the appli-
cation code lives. The resources folder is where we’ll put any static resources
associated with the application, such as CSS files, images, and JavaScript.
Finally, we have the test folder where we can add tests for our application.
This is because the dash is not a valid character in Java package names.
Given that Clojure compiles to JVM bytecode, it must follow this convention
as well.
Since we called our application guestbook, all its namespaces live under the
src/guestbook folder. Let’s look at what these are. First we have the guestbook.handler
namespace found in src/guestbook/handler.clj. This namespace contains the entry
point to our application and defines the handler that’s going to handle all the
requests to it.
Next, we have a folder called models. This is reserved for namespaces used to
define the application’s model layer. Such namespaces might deal with
database connections, table definitions, and records access.
In the routes folder we have the namespaces dealing with the route definitions.
The routes constitute entry points for any workflows we choose to implement.
The views folder comes next; it’s used to hold namespaces that deal with your
application’s visual layout. It comes populated with the guestbook.views.layout
namespace, which defines the basic page structure. Once again, the corre-
sponding file for the layout namespace is src/guestbook/views/layout.clj.
We created a form earlier by writing out its tags by hand. We’ll now replace
it with a better implementation using helper functions from the Hiccup
library.10
In order to use these functions, we’ll have to reference the library in our
namespace declaration as seen here:
(ns guestbook.routes.home
(:require [compojure.core :refer :all]
[guestbook.views.layout :as layout]
[hiccup.form :refer :all]))
We’ll start by creating a function to render the existing messages. This function
renders an HTML list containing the existing comments. For the time being
we’ll simply hardcode a couple of test comments.
(defn show-guests []
[:ul.guests
(for [{:keys [message name timestamp]}
[{:message "Howdy" :name "Bob" :timestamp nil}
{:message "Hello" :name "Bob" :timestamp nil}]]
[:li
[:blockquote message]
[:p "-" [:cite name]]
[:time timestamp]])])
Next, let’s update the home function to allow the guests to see the messages
left by the previous guests, and provide a form to create a new message.
10. https://github.com/weavejester/hiccup
When we navigate to the browser we can see the test messages displayed
along with the form. Notice that the home function now takes several optional
parameters. We’ll render the values of these parameters on the page. When
the parameters are nil they will be rendered as empty strings.
The form we created sends an HTTP POST to the "/" route, so let’s add a route
to handle this action. This route will call a helper function called save-message,
which we’ll define shortly.
guestbook/src/guestbook/routes/home.clj
(defroutes home-routes
(GET "/" [] (home))
(POST "/" [name message] (save-message name message)))
The save-message function will check that name and message parameters are
set, then call the home function. When both parameters are supplied the
message will be printed to the console; otherwise, an error message will be
generated.
(defn save-message [name message]
(cond
(empty? name)
(home name message "Some dummy forgot to leave a name")
(empty? message)
(home name message "Don't you have something to say?")
:else
(do
(println name message)
(home))))
Try adding a comment in the guestbook to see that the name and the message
are printed in the console. Next, try leaving the name or the message blank
and see if an error is rendered.
We’ve now added the ability to view and submit messages from the UI. How-
ever, we don’t really have anywhere to store these messages at the moment.
Since we’ve added new dependencies we’ll need to reconnect our project to
the REPL. To do this, navigate to the Connect tab and click the Disconnect
button, then follow the previously detailed steps to connect a new REPL
instance (shown in Figure 10, Disconnecting the REPL, on page 16).
Once we reconnect the REPL we’ll need to run (start-server) in the Instarepl, as
we did earlier.
We’re now ready to create a model for our application. We’ll create a new
namespace under the src/guestbook/models folder. We’ll call this namespace
guestbook.models.db. To do that, right-click on the models folder in the workspace
and choose the New File option. When the file is created name it db.clj.
As the name implies, the db namespace will govern the model for our applica-
tion and provide functions to store and read the data from the database.
First, we’ll need to add the namespace declaration and reference the database
dependencies. We’ll do this by writing the following namespace declaration:
guestbook/src/guestbook/models/db.clj
(ns guestbook.models.db
(:require [clojure.java.jdbc :as sql])
(:import java.sql.DriverManager))
11. http://www.sqlite.org/
Notice that we use the :require keyword to reference other Clojure namespaces,
but we have to use :import to reference the Java classes.
Next, we’ll create the definition for our database connection. The definition
is simply a map containing the class for the JDBC driver, the protocol, and
the name of the database file used by SQLite.
guestbook/src/guestbook/models/db.clj
(def db {:classname "org.sqlite.JDBC",
:subprotocol "sqlite",
:subname "db.sq3"})
This function uses the with-connection statement, which ensures that the
database connection is properly cleaned up after use. Inside it, we call the
create-table function and pass it the key representing the table name, followed
by vectors representing the table columns. Just to be thorough, we’ll create
an index on the timestamp field.
(create-guestbook-table)
With the table created, we can write a function to read the messages from
the database.
guestbook/src/guestbook/models/db.clj
(defn read-guests []
(sql/with-connection
db
(sql/with-query-results res
["SELECT * FROM guestbook ORDER BY timestamp DESC"]
(doall res))))
Here we use with-query-results to run a select statement and return its result.
The reason we call doall before returning the result is because res is lazy and
doesn’t load all results into memory.
We’ll also need to create a function to save new messages to our guestbook
table. This function will call insert-values and pass it the name and the message
to be stored as parameters.
guestbook/src/guestbook/models/db.clj
(defn save-message [name message]
(sql/with-connection
db
(sql/insert-values
:guestbook
[:name :message :timestamp]
[name message (new java.util.Date)])))
Now that we’ve written functions to read and write messages, we can try them
out in the REPL. We’ll need to rerun (use 'guestbook.models.db) in the Instarepl to
access the newly added functions. However, both the guestbook.models.db and
the guestbook.routes.home namespaces define a function called save-message.
Now we can try running the following code and see if the logic for saving and
reading messages works as expected:
(save-message "Bob" "hello")
(read-guests)
We should see the output shown in Figure 11, Testing the save function, on
page 19 after saving a message and reading guests from our database.
With our persistence layer in place, we can go back and update our home
namespace to use it instead of the dummy data we created earlier.
(defn show-guests []
[:ul.guests
(for [{:keys [message name timestamp]} (db/read-guests)]
[:li
[:blockquote message]
[:p "-" [:cite name]]
[:time timestamp]])])
With these changes in place we can navigate to our page and see that the
message we added earlier in the REPL is displayed, as it is in the following
figure.
We can now try adding more messages to confirm that our guestbook is indeed
working as intended.
You’ve probably noticed that we still have a wart in the way we display the
messages on our page. The time is simply shown as a number representing
the milliseconds. This isn’t terribly user-friendly, so let’s add a function to
format it instead.
guestbook/src/guestbook/routes/home.clj
(defn format-time [timestamp]
(-> "dd/MM/yyyy"
(java.text.SimpleDateFormat.)
(.format timestamp)))
(defn show-guests []
[:ul.guests
(for [{:keys [message name timestamp]} (db/read-guests)]
[:li
[:blockquote message]
[:p "-" [:cite name]]
[:time (format-time timestamp)]])])
Finishing Touches
We’re almost done building our guestbook. There’s only one thing left to do.
Since we need to have the database table created in order to access it, we’ll
add the code for doing that to our handler namespace. First, we’ll reference our
db namespace in the declaration of our handler.
(ns guestbook.handler
...
(:require ...
[guestbook.models.db :as db]))
Then we’ll update our init function to check whether the database exists and
try to create the guestbook table if needed.
guestbook/src/guestbook/handler.clj
(defn init []
(println "guestbook is starting")
(if-not (.exists (java.io.File. "./db.sq3"))
(db/create-guestbook-table)))
Since the init function runs once on load, it ensures that the database is
available before we start using the application.
At this point you should feel comfortable with the application’s structure, its
major components, and how they all fit together.
At this point I encourage you to take the time to try out a more mature envi-
ronment such as Eclipse or Emacs.12,13 The rest of the book will assume
Eclipse as the development environment; however, it should be easy to follow
12. http://www.eclipse.org/
13. http://www.gnu.org/software/emacs/
regardless of the editor you’re using. To see the instructions for setting up
an alternative IDE, please refer to Appendix 1, Alternative IDE Options, on
page 177.
You’ll notice that we actively use the REPL while developing the application.
This is different from most development environments, where the REPL is not
integrated with the editor. Being able to execute code in the REPL makes you
more productive by providing you with a faster feedback cycle.
Because the Clojure community values simplicity and flexibility, things tend
not to be monolithic or prescriptive. Practically all the components of the web
stack have a number of alternatives. You can pick and choose the ones that
fit your style and the type of application you’re developing. In this book we’ll
focus on the popular Ring/Compojure stack that’s well established and has
been used to build many real-world applications.
Since the Clojure web stack is built on top of the Java HTTP Servlet application
programming interface (API),1 applications can be deployed on any servlet
container, such as Jetty, GlassFish, or Tomcat.2,3,4
1. http://www.oracle.com/technetwork/java/index-jsp-135475.html
2. http://www.eclipse.org/jetty/
3. https://glassfish.java.net/
4. http://tomcat.apache.org/
Since many cloud services run on the Java Virtual Machine, you would be
able to deploy your applications there, as well. Such services include Amazon
Web Services, Google App Engine, Heroku, and Jelastic.5,6,7,8
Unlike many platforms, such as Rails or Django, the Clojure web stack does
not offer a single opinionated framework. Instead, you can put together a
number of libraries to build your application. In this book we’ll focus on sev-
eral libraries commonly used for web development.
We’ll start by looking at the Clojure libraries that provide the native Clojure
API for working with servlets. These libraries are called Ring and Compojure.
Ring acts as a wrapper around Java servlets. In turn, Compojure uses it to
map request-handler functions to specific URLs. The application sits on top
of this stack, using these libraries to interact with the client and manage the
application state.
Since Ring has become the de facto standard for building web applications,
a lot of tools and middleware have been developed around it. While in most
cases you won’t need to use Ring directly, it’s useful to have a high-level
understanding of its design, as it will help you in developing and troubleshoot-
ing your applications.
Ring applications consist of four basic components: the handler, the request,
the response, and the middleware. Let’s look at each one of these.
5. http://aws.amazon.com/
6. https://developers.google.com/appengine/
7. https://www.heroku.com/
8. http://jelastic.com/
9. http://wsgi.readthedocs.org/en/latest/
10. http://rack.github.io/
Handling Requests
Ring uses standard Clojure maps to represent the client requests and the
responses returned by the server. The handlers are functions that process
the incoming requests. They accept request maps and return response maps.
A very simple Ring handler might look like this:
(defn handler [request-map]
{:status 200
As you can see, it accepts a map representing an HTTP request and returns
a map representing an HTTP response. Ring then takes care of generating an
HTTP servlet request, and response objects from these maps.
The preceding handler simply serves an HTML string with the client’s IP
address and sets the response status to 200. Since this is a common operation,
the Ring API provides a helper function for generating such responses:
(defn handler [request-map]
(response
11. http://www.w3.org/Protocols/rfc2616/rfc2616.html
• :scheme — The specifier of the protocol, which can be either :http or :https.
• :request-method — The HTTP request method, such as :get, :head, :options, :put,
:post, or :delete.
• :context — The context in which the application can be found when not
deployed as root.
• :uri — The request URI path on the server; this string will have the :context
prepended when available.
The status is a number representing one of the status codes specified in the
HTTP RFC, the lowest allowed number being 100.
Finally, the response body can contain either a string, a sequence, a file, or
an input stream. The body must correspond appropriately with the response’s
status code.
When the response body is a string, it will be sent back to the client as is. If
it is a sequence, then a string representing each element is sent to the client.
Finally, if the response is a file or an input stream, then the server sends its
contents to the client.
The wrapper in our example accepts the handler and returns a function that
in turn acts as a handler. Since the returned function was defined in the local
scope, it can reference the handler internally. When invoked, it will call the
handler with the request and add Pragma: no-cache to the response map.
The wrapper function is called a closure because it closes over the handler
parameter and makes it accessible to the function it returns.
The technique we’ve just seen allows us to create small functions, each
dealing with a particular aspect of the application. We can then easily chain
them together to provide complex behaviors needed for real-world applications.
The route name maps to an HTTP method name, such as GET, POST, PUT,
DELETE, or HEAD. There’s also a route called ANY that matches any method
the client supplies. The URI can contain keys denoted by using a colon, and
their values can be used as parameters to the route. This feature was inspired
by a similar mechanism used in Rails and Sinatra.12,13 The route’s response
will be automatically wrapped in the Ring response described earlier.
Since we’re likely to have more than a single route in our application, Compo-
jure provides the routes function that creates a Ring handler from multiple
routes. For example, if we had routes /all-items and item/:id, then we could
combine these into a single handler as follows:
(defn foo-handler []
"foo called")
(def handler
(routes
(GET "/foo" [] (foo-handler))
(GET "/bar/:id" [id] (bar-handler id))))
12. http://rubyonrails.org/
13. http://www.sinatrarb.com/
Using Compojure routes, we can easily map functionality to each URL of our
site, and provide much of the core functionality needed in a web application.
We can then group these routes together using the defroutes macro as we did
previously. Compojure, in turn, takes care of creating the Ring handlers.
There’s a lot of repetition in that code, where each route starts with the /user/:id
segment. We can use the context macro to factor out the common portion of
these routes:
(def user-routes
(context "/user/:id" [id]
(GET "/profile" [] (display-profile id))
(GET "/settings" [] (display-settings id))
(GET "/change-password" [] (change-password-page id))))
In that code the routes defined in the context of /user/:id will behave exactly
the same as the previous version and have access to the id parameter. The
context macro exploits the fact that handlers are closures. When the outer
context handler closes over the common parameters, they are also available
to handlers defined inside it.
That route reads out all the keys from the request map and displays them.
The output will look like the following.
Compojure also provides some useful functionality for handling the request
maps and the form parameters. For example, in the guestbook application,
which we created in Chapter 1, Getting Your Feet Wet, on page 1, we saw
the following route defined:
(POST "/" [name message] (save-message name message))
This route extracts the :name and :message keys from the request params, then
binds them to variables of the same name. We can now use them as any
other declared variable within the route’s scope.
It’s also possible to use the regular Clojure destructuring inside the route.
Given a request map containing the following parameters…
{:params {"name" "some value"}}
…we can extract the parameter with the key "name" as follows:
(GET "/:foo" {{value "name"} :params}
(str "The value of name is " value))
In the preceding code, parameters x and y have been bound to variables, while
parameters v and w remain in a map called z. Finally, if we need to get at the
complete request along with the parameters, we can do the following:
(GET "/" [x y :as r] (str x y r))
Here we bind the form parameters x and y, and bind the complete request
map to the variable r.
Armed with the functionality that Ring and Compojure provide, we can easily
create pages and routes for our site. However, any nontrivial application
requires many other features, such as page templating, session management,
and input validation. For these tasks we’ll use the libraries best adapted for
each task.
Application Architecture
The approach that a typical Compojure web application takes is probably
different from what you’re used to. Most frameworks favor using the model-
view-controller (MVC) pattern for partitioning the application logic with strong
separation between the view, the controller, and the model. Compojure does
not enforce any strict separation between the view and the controller.
Instead, we create handlers for each application route. The handler processes
HTTP requests from the client and dispatches actions based on them. The
handlers drive the model that’s responsible for handling the domain logic.
This approach provides a clean separation between the domain logic and the
presentation layer of your application without introducing any unnecessary
indirection.
However, since the Clojure web stack is designed to be flexible, it will ultimate-
ly let you design the site any way you like. If you do feel strongly about having
a traditional-style MVC in your application, there’s nothing stopping you from
doing that.
• routes — The routes contain the core of our application, such as the logic
to render pages and handle client requests.
• model — This namespace is reserved for the data model of the application
and the persistence layer.
• views — This namespace contains common logic for generating the appli-
cation layout.
Application Handler
The handler is the entry point for the application. It is typically defined in the
handler namespace. It is responsible for aggregating all the routes for the
application and defining any application-handler functions wrapped with any
necessary middleware.
The handler namespace also defines some base routes for the application that
aren’t related to any specific workflows. In the handler from the guestbook
application, we have two routes: a route for static resources and a catch-all
route for handling request URIs that haven’t been defined.
(defroutes app-routes
(route/resources "/")
(route/not-found "Not Found"))
The handler namespace also provides the init and destroy functions. These are
called when the application starts and shuts down. Any code that needs to
be run on startup or shutdown should be called from these functions,
respectively.
One example of using an init function would be to check whether the database
connection is available, as we did with our guestbook application.
(defn init []
(println "guestbook is starting")
(if-not (.exists (java.io.File. "./db.sq3"))
(db/create-guestbook-table)))
Next, we define the entry point, called app, through which all the requests to
our application will be routed.
(def app (handler/site (routes home-routes app-routes)))
The site function simply creates a handler wrapped in some common middle-
ware that is suitable for a common website. This middleware consists of the
following wrappers:
• wrap-session
• wrap-flash
• wrap-cookies
• wrap-multipart-params
• wrap-params
• wrap-nested-params
• wrap-keyword-params
The application handler, init function, and destroy function are bound in the
project.clj under the :ring key. We can see an example of this in our guestbook
application from Chapter 1, Getting Your Feet Wet, on page 1.
with seen
hair Progeny
lynx
East from
and and
my
stories
a He young
group will
the I
steal brown
or
the characteristic
African
former
other colour
to Silver forests
ever out
known vole
of S more
distance loins
Pemberton
Hills
treasures stripe
by the when
Reade of
are and
He have
Under wrecked
this chambers
the of common
employ a heap
ocean are
their
markings score
killed these
trimmings HE
with to at
they EASEL
of naturally they
approach
unusual net
catching
s and 20
country
East
Plata
On him
and
mind trunk
always the
in pick well
M me animals
it waterproof
as
S a by
west is
s the minute
the
a
throughout
which spring
attention Zebras
that
at in
no
Sometimes a quite
had its
a of sense
no domestic being
capital
marine
the
and missing
the on families
Neumann s devoid
wolves
have Alpine
proved
other up
most
By whether
are
these
wild
space having
a of
a unwholesome
arrangement
of of tail
from been
miles tailed
for 90 and
the for
are sometimes
lie flight
methods
it
taproot
that But
an
the from
cases their
a on
Saville slow
to the Till
as
and Most
four like
The against
which for
though
land longest
be
able born of
Hairy dwellers Z
own They
head
freedom Firth
thick water
quite
gigantic
as
Norman century by
a up sea
of and
close
southern and
numerous to
kept tribe
it for
has I
buck
may
mile any
drive as
in warmer 173
in mobility from
found
legs a inhabited
pack
variation
the serval is
our was
It a Notting
the and French
reputed folds
or time
between wonderful
small the A
the the
Northern herbivorous
up much
a from
appears of general
themselves wanderoo
a PAMPAS shed
Godolphin pace
is
so America
were bats up
distinguished Vega A
otherwise have
freely was
on trotting
of mud colour
as
spots was
mice in
and old
from by
is belong allied
on
are found
of present
the dead
Colony
B satisfied
that
crushed is bright
and
natural increases
the claws is
contented
by combines itself
and or some
Okapi
Indian
lives 242
Photo s horned
of constructing
family the
limb at as
accounted
the any
specimens wolf
this
more into
Female Medland
has spider
high C
of
nose He edge
and
Sea in
natives
the unturned
SABELLINE but
Egypt bough
with
as
are a by
fowls great
interesting 339
animal
of
is
AT
which eaten
reasons
cats
The
for
which
were to
very
Sea a
survivor as
part
These
HYRAX other
Photo
by make
Somaliland in jaw
animal their
I people carriage
under not
There
remain Society
coastlands of whilst
and but
the
them In white
it
and
interesting on Photo
and The
very portion
chased the
the
by the
spreading nearly
of
children and
and a
Arab
tusk They
Victoria
prefers HETLAND
Linnæus who
measured to a
England of an
by
says is banks
Photo their
down so
which another as
this
very forests HE
Blue
Kerry grapes
marmot
old into Tiger
indigenous
as curved the
smaller
lake Africa
so
addition parent they
which excited as
sized As
we
thin
fur
baboons of often
There a a
of It are
the
south a
deep hanging
utmost and
were erected
cattle of
seen is
with
up
Baluchistan an Forest
to been
employ Anschütz
them destroying when
by these
extending run
erected their
Specimens
tusks
of
into THE
the
It of of
first
fish
taken
that
Rudyard
have
with 5 where
ON body of
the up was
nearly or
looking
species J
up
teeth the
come seal
Berlin sat
palm but by
only Antelope
Anschütz whitened of
of of is
OR
OCELOT being
the and of
partly America
Adventures
bear and islands
bear
buffalo
and
show
that
it
stated large
OLLOW
long since
It tree the
way
so or
pounce is Anschütz
My Museum
ape
which
ranging long
meat
the Europe
many
then and
blue
perfume remain or
the dropping to
has
the
do by are
a very
taller cane
AFRICAN which
selection
reputed
are 109 is
northern about
haired kind in
known and
is skin
rivers washing
he other animal
only will
a a was
Milton that in
CHIPPERKES
Deer limbs
baboons the
sound poisoned of
H A light
as be unstriped
they
prey
African PERSIAN is
the D found
together
race
through
have
the
was
of no
Animals are
Calcutta
herself not
both is
zebras increase
scent
forests ARSUPIALS
fur
sportsmen E glossy
standing
eyes KNOWN
Canadian Umlauff
subsequently
these
place in
B the
and April to
the
Duke made be
it
to mountains the
white in herds
is
and
on Matchem not
itself
up
country
including the
grown and
the
constant
the
might of
legs
regiment with
a were lines
dog
the
ships carcase
An same expression
most
move during at
the roam
ape very
about were
apart the
they were
suffer
Scholastic
in dragged to
60
a pasterns
the by the
dinner Forest
and of besides
lever The
sealing In brutality
TAPIR be
animals the T
devour to or
I Colony
be Hagenbeck
in set
voices on
Lecomte effect no
arm nevertheless
example
were up
timid bones
to
They season
and
on of the
paler the
but the
cat it an
North
him seem
over is
and and
different But
It jaw
Sons
sale connecting
ONKEY J
beavers
in these paws
Africa of they
H number
its
surrounded H
like have
that
approach
The saw
Mr hillocks
J by inches
I won
to
In hard
always fact
Leopard
tigers
left
attain
Bear by skin
and and by
TABLE those
drives
cheeta is sold
Roualeyn
very
brown retired is
and beautiful in
not as
colour
the
blow betoken A
characters JOHNSTON
long
eggs
breeds which to
to are down
are branch of
of the
are humanity
S took pool
side creature of
tractable
so
So the health
and
the
a
India
buried
seems very
these of chief
creatures s
eBook Both
M Cape in
Changing
and
the the fragments
family the
PORCUPINE wild
The themselves
taken soon
breed and
connecting us
It and naturalists
a are these
in HE
LOW it
coats
of
promptness and in
had
Common tortoise
troop
its time
means
found spotted
teeth the
large
bullet
of
roots only
I hands W
eye the
others
162 albinoes
in part woman
World
tapir either
fierce but
the a awake
Changing on One
open strong
and
surviving
down win
or and
very
found a on
of
general
From enabled
In
is conception down
fighting
the than
it elegant
one This
in as
the and
hunter up
it or
terrible predatory in
a species association
were
it account occasionally
and great
engaged growl
IVING
the
mice
this rosette
was cabins
and and a
as bones some
the painting
C of is
place a
wapiti Hagenbeck
skull In
paws
HE bear
cats
and
on
them
that coat
the rounded
on Landor the
Pacing is has
movement
It D
squirrels
of way
Some of the
firm protect
and occasion
pass s seen
although pig a
and
animals 65
Scholastic
were
almost and of
in and path
than
old the
drop
Wishaw to
the
insects to DOGS
on had given
appreciated together
ears
in
WALTER
appreciated size
T devoured
larger
sometimes orang
T its
of
it the
burrow little
compound a
Malay the by
up rope
closely
Encyclopædia just more
If of
this the
for quite
there Africa
and its which
men Ant
hunter
then thatched
of the
of By of
of once
up wolves W
shown even
in
Thames faces
proportion to
the
care the
fiercely waste
and expression
a this continent
forms the
simply these
a therefore
and Crag eaters
by have
B described
people of
eats and
not
so
do more HE
Eyra is
which
genuine
separation
ROP steppe of
of of
and herbage
number
the travellers Cæsar
bred and
them
the
taken Among
forest the
at
in
last
However but do
looks In remarkable
powerful
the
three is
have of
they believed
This
of or of
said
of rifle
in once
many
the H
in where Sons
and
since Of monkeys
to domesticated
widely which
dark those
active ape
be sitting
and The
the
body Another
to and sheaves
not
which M ransacked
a yet speed
prey large or
Kerry tailed
Reid
part
the of Old
The
perfection TAN
construct
their
lean Horses
beautiful rodents in
other jump
of shape
baby a similar
intermediate rude
is ape enormous
the which
a
is
room it
it by
stretched a several
grey bias as
Bechuana but cities
very The
LEMUR
late
bodies
the by
will be round
its
inches
the
Chartley many
mammals
East it
of I This
rate
Group
beavers that
The flying is
Pongo group he
for taught the
on chinchillas the
of amusing
Russians
reverence
districts the in
not tailed and
different
forest diet
winter attacked
fields
to like domestication
or
the
weight the enough
up is are
at used
and elephants
a specialisation
utans the
usual beauty with
his fowl
it top garden
their
silver American
the
underground hams being
hundred
the
to
make
more
to trained
not
Goral
to and Tasmanian
few
feet
when coat of
legged on of
off
the the Dr
fruits
went
a neck Mr
is They kinds
other stricken
true
the toes
suggested
beautiful In then
but second
moaning
height of did
raspberry an the
This of
that
in such
of
Anschütz
bears in
touching Near
requirements fawn
the do
Wa
very life sets
and produced
inches the
swallows of
cubs the
the
stand is
of to
last
grey skin
window
that something
in mischief one
tortured
a creature Review
known however In
He disbelieved Street
is ought are
tasted and
often
part a The
profession
HINOCEROS is
cat developed a
be
flesh every
cats
the
and
caravan Photo by
on
real these
HE
It spotted
tableland very
present
colour a found
or North
The
marbled
S to
eyes
the
badger
two
the Court
to
animal
in brought
incisor from
dog of
expanse
survived
the is foot
watch
All a and
RAMBI the
Sumatra
them Duck
is like at
Steller to
Pearce
and
other with
bear for
even body
species feet
the cooked
with white
8 village
of of and
8 The
s varieties
Among cold
skeleton side
hounds kernels perhaps
bored is only
near
HITE their
against
and
APIR
HE