I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 635 posts at DZone. You can read more from them at their website. View Full User Profile

Web application in Clojure: the starting point

01.05.2012
| 6961 views |
  • submit to reddit

Ring is a basic tool for executing your Clojure code into a web server environment, by satisfying HTTP requests and producing responses. In scope, Ring is similar to the Servlet API, but it's not as standard and diffused; by the way, servlets are always used internally by Clojure implementations of the HTTP stack.

There are other open source tools for Clojure web development, that use Ring as a component, such as Compojure. There are even full-stack frameworks like Noir, but in this article we'll stick to the simple API of Ring and leave more elaborated approaches for later. Links to the code shown in this article are included in the Resources section.

Hello World

Clojure projects use Leiningen *link* to pull in dependencies and to run their code. Leiningen is a wrapper for Ant and Maven that will fetch JARs for you and execute the test suite or the application itself.

In a Leiningen-enabled project, the project.clj file defines dependencies:

(defproject clojure-web-hello-world-ring "1.0.0-SNAPSHOT"
  :description "Web application Hello World with Clojure"
  :dependencies [[org.clojure/clojure "1.2.1"]
                 [ring/ring "1.0.1"]]
  :main clojure-web-hello-world-ring.core)

The first two lines specify a name, a version and a description; these are non-functional properties in this example and you can put there anything.

The :dependencies key specifies what to pull in: in this case, the Facade project for ring which will depend in turn both on production code facilities like Jetty and on developers tools.

Finally, project.clj specifies the name of the namespace where the -main function resides: this function will be called with lein run.

The src/clojure-web-hello-world-ring/core.clj defines the actual code to run:

(ns clojure-web-hello-world-ring.core
    (:use ring.adapter.jetty))
(defn app [req]
    {:status 200
     :headers {"Content-Type" "text/html"}
     :body    "Hello World!"})
(defn -main [] (run-jetty #'app {:port 8080}))

The dependency we import is ring.adapter.jetty, a wrapper over the Jetty embedded servlet container which will run our code.

We also define a function, app, which takes as an argument a request map and returns a response map. These two are plain old Clojure data structures.

The -main function starts Jetty on port 8080 and tells it to handle all request with this simple handler, which just outputs "Hello World!".

Slightly more complex example

Now that we have bridged a Servlet container with Clojure code you have a Turing-complete web application: you can build anything with this pattern (although other libraries may save you time.)

As an example, let's serve more than one kind of request:

(ns clojure-web-hello-world-ring.core
    (:use ring.adapter.jetty)
    (:use clojure.string))
(defn extract-name [uri]
    (replace-first uri "/" ""))
(defn app [req]
    {:status 200
     :headers {"Content-Type" "text/html"}
     :body    (if (= (:uri req) "/")
                  "Hello, World!"
                  (str "Hello, " (extract-name (:uri req)) "!"))})
(defn -main [] (run-jetty #'app {:port 8080}))

Now http://localhost:8080 will show "Hello, World!". http://localhost:8080/giorgio (or any other name) will show "Hello, giorgio!" instead.

Further resources

The sample code for this article is on Github; if you go back one revision in the history you'll find also the first Hello World example. To run the code, remember to run both lein deps and lein run after the cloning.

Ring contains a small specification describing all the keys available in the request and response maps.

This a bit outdated tutorial (for Clojure 1.1 and Ring 0.2) shows how to setup some developer tools for continuously reloading code during development and displaying stack traces in the browser.

The Ring API documentation (updated to 1.0.0) describes all the available namespaces if you pull in ring/ring with Leiningen.

Conclusions

We have only scratched the surface of Ring, but it is a very low-level library anyway, which works close to the HTTP request.

We will need more tools to get at least a Servlet API equivalent, with routing of requests and parsing of parameters from the GET query string. In the next articles we will see how Compojure can be used over Ring to quickly build web applications in the same way as Sinatra and Spark work.

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)