Intro to Clojure on the Web

Web Development

Now that you have seen the basics of Clojure, let's consider what is involved in Web development. You need to have an HTTP server that will accept requests, as well as a program (typically known as a "router") that decides which functions will be executed for each requested URL. Then, you want to return data to the user, typically in the form of HTML files.

Now, various Web application frameworks approach this problem differently. In the most primitive ones (for example, CGI programs), the "application" really is invoked only on a single program. Large frameworks, such as Ruby on Rails, handle all of these parts and even allow you to swap parts out—and each of those parts is done using instances of different classes that handle the appropriate information.

Now, although Clojure is built on top of the JVM and uses some of the primitive Java classes as its fundamental data types, it is not an object-oriented language. Clojure is a functional language, which means that all of the aforementioned steps will be handled using functions—either those that you define or those that have been defined for you by a Web framework.

For the purposes of this article, I'm going to use Compojure, a simple Web framework for Clojure. To create a basic Compojure project, you can use Leiningen:


lein new compojure cjtest

This potentially will download a number of libraries from clojars (the Clojure library repository), then the new Clojure project ("cjtest"). Once that is done, you can run it by going into the cjtest directory and executing:


lein ring server

This will download and install whatever dependencies exist, and it then will start a simple HTTP server, by default on port 3000. You then can visit this simple application at http://localhost:3000, where you'll be greeted by "Hello, world" from Compojure.

Now, how does this work? How does Compojure know what to do?

The answer is in the Clojure project file (project.clj). The default, generic Compojure project is defined using (of course!) Lisp code. Indeed, it's pretty standard in the Lisp world to use Lisp code as data. The default project created for me by Leiningen looks like this:


(defproject cjtest "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [compojure "1.1.5"]]
  :plugins [[lein-ring "0.8.5"]]
  :ring {:handler cjtest.handler/app}
  :profiles
  {:dev {:dependencies [[ring-mock "0.1.5"]]}})

This uses Clojure's defproject macro, giving it a name "cjtest" and a version number. Then there are several keywords, a Clojure data type used as (among other things) the keys in maps. Thus, the above example shows Clojure that the program has the default description, and it depends on both Clojure and Compojure (and specifies the versions of each).

A second configuration file, also written in Lisp code, sits within the src/cjtest directory and is called handler.clj. The default looks like this:


(ns cjtest.handler
  (:use compojure.core)
  (:require [compojure.handler :as handler]
            [compojure.route :as route]))

(defroutes app-routes
  (GET "/" [] "Hello World")
  (route/resources "/")
  (route/not-found "Not Found"))

(def app
  (handler/site app-routes))

In other words, if someone requests "/", it will return "Hello World". You then add a route for any static resources you might want to server, which should be in the resources directory of your Clojure project. You then add a not-found route for handling 404 errors.

You can run this amazing new Web application with:


lein ring server

If you're wondering what "ring" is doing there, suffice it to say that Ring is the Clojure library that handles just about all Web-related activity. It's a low-level library, however, and it doesn't function as an actual Web application framework. It uses the standard Jetty system for Java Web applications, although you don't need to worry about that at this stage, given the automation that Leiningen provides; any missing libraries or dependencies will be downloaded when you run Ring.

Sure enough, after running the above, if you go to /, you get a "Hello world" message back from the server, because you had mapped GET / to a string. If you want, you instead can map it to a function by having square brackets following the route name, and then adding a function body:


(defroutes app-routes
  (GET "/" [] "Hello World")
  (GET "/fancy/:name" [name]
       (str "Hello, " name))
  (route/resources "/")
  (route/not-found "Not Found"))

You even can add HTML tags for some formatting:


(defroutes app-routes
  (GET "/" [] "Hello World")
  (GET "/fancy/:name" [name]
       (str "<p><b>Hello</b>, " name "<p>"))
  (route/resources "/")
  (route/not-found "Not Found"))

Notice how in Lisp, because of the prefix syntax, you don't need to use + or any other operator to create a string from three (or any number) parts. You just add additional parameters, separated by spaces, and they are added to the new string that str returns.

If you don't want to have your function inline, you always can define it elsewhere and then pass it to defroutes:


(defn say-hello
  [req]
  (str "<p><b>Hello</b>, " 
  ↪(get (get req :route-params) :name) "<p>"))

(defroutes app-routes
  (GET "/" [] "Hello World")
  (GET "/fancy/:name" [name] say-hello)
  (route/resources "/")
  (route/not-found "Not Found"))

Notice how this function, say-hello, takes a single argument (called req), a map of maps that contains all the data having to do with the HTTP request you received. If you want to grab the name parameter that was defined in the route, you have to get it from the route-params map inside the req map that your function is passed. You probably will want to explore the req object to understand just what Compojure is passing and how to work with such data.

Getting Fancier

Now, Compojure advertises itself as a routing mechanism for Web applications. This raises the question of what sorts of templates Compojure brings to the table. And, the answer is none. That's right, Compojure itself expects you to return strings containing HTML, but how you make those strings is up to you.

Thus, you can create strings, as shown above, in order to return a value. Or, you can use a templating engine, which in the Clojure world, simply means including another dependency and using the functions defined by that dependency:


(defproject cjtest "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [compojure "1.1.5"]
                 [hiccup "1.0.3"]]
  :plugins [[lein-ring "0.8.5"]]
  :ring {:handler cjtest.handler/app}
  :profiles
  {:dev {:dependencies [[ring-mock "0.1.5"]]}})

Now, within the handler.clj file, you need to make another change, so that you can have access to the "hiccup" HTML-generation system:


(ns cjtest.handler
  (:use compojure.core hiccup.core)
  (:require [compojure.handler :as handler]
            [compojure.route :as route]))

With these in place, you now can create HTML by using a function and Clojure's vector syntax:


(defn say-hello
  [req]
  (html [:p [:b "Hello, "  
  ↪(get (get req :route-params) :name) ]]))

Now, if you look at the parentheses and say, "Yikes, that's what I dislike about Lisp", I'll understand. But if you look at this as Lisp code and realize that this means your templates automatically are valid HTML if they are valid Lisp, you're thinking in the terms that Clojure wants to encourage—that is, using Lisp data structures as much as possible and feeding them to functions that know how to work with them.

Conclusion

As you can see, Clojure provides the simplicity of Lisp's structure and syntax while staying within the world of Java and the JVM. My next article will look a bit deeper at Compojure, investigating forms, database connectivity and other issues that modern Web applications want to use.

Resources

The home page for the Clojure language is http://clojure.org and includes a great deal of documentation.

You can read more about Leiningen at its home page: http://leiningen.org. Similarly, documentation for Compojure is at its home page, http://compojure.org, and Hiccup is at https://github.com/weavejester/hiccup.

Two good books about Clojure are Programming Clojure by Stuart Halloway and Aaron Bedra (published by the Pragmatic Programmers) and Clojure Programming by Chas Emerick, Brian Carper and Christophe Grand (published by O'Reilly). I've read both during the past year or two, and I enjoyed both of them for different reasons, without a clear preference.

______________________

Reuven M. Lerner, Linux Journal Senior Columnist, a longtime Web developer, consultant and trainer, is completing his PhD in learning sciences at Northwestern University.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

LISP

Callen's picture

very interesting topic for Lisp programming language. It is very useful part to anyone.thanks for sharing this valuable information. http://nopalea.jigsy.com/nopalea

Lisp programming language

Jony's picture

I think Lisp programming language growing popular day by day. Also Clojure for Web development sector Lisp programming language is important. Thanks to introduce me on Clojure Basics. http://www.healthforus.info

The maintenance cost

sollen's picture

The maintenance cost estimates are based on the cost to maintain 50W 24V LED Fire Engine Work lamps a vehicle and perform needed repairs for five years and 75,000 miles, including labor expenses, replacement part prices and the purchase of an extended warranty.

LISP very rarely used....

mishainfotech's picture

I agree with you. Now a days, we only use the LISP in college days not in real life as it is rarely used and new technologies and advancements are used in real practice.

++++

cnbestmall's picture

++++ http://www.cnbestmall.com ++++

Accept Paypal and Credit Card, FREE SHIPPING

Nike AIr max, Shox, Rift, dunk, blazer, air force 1 shoes: 48 USD

Nike free running shoes: 42 USD

D&G, LV, Gucci, parda DC, polo, puma, supra shoes: 42 USD.

Timberland boot: 50 USD

T-shirts (polo, ed hardy, lacoste,gucci, lv, etc) $28

Jeans (AF, armani, bape, BBC, CA, coogi, D&G, Diesel, Evisu, Levis, gucci, true religion, versace) 45 USD

Down Coat jacket parka vests (moncler, canada goose, barbour, parajumpers, woolrich) 168 USD-268 USD

++++ http://www.cnbestmall.com ++++

Well, OK

Anonymous's picture

If you like LISP then I guess clojure is fine for you.

Frankly, if I were programming functionally I'd be using either erlang or haskell. LISP is over 50 years old and looks its age.

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState