Future Gadget #8 Code;Blog

(name subject to change)

First Clojure Web Application (Wrap-up)

I'm going to finish my first clojure web application by making a more serious interface and incorporating a silly little algorithm to generate actual vampire names based on the user's input.

Front End Improvement

So far I've made basic dynamically generated pages for my application. However, there's nothing to serve up static content like vampire themed CSS, javascript, fashionable images, or downloads. As a courtesy to my users, I'd also like to display a friendly error message should a mistake be made while navigating to my website. With compojure, this is as easy as including a couple of the included utility routes.

(ns ... 
  (:require [compojure.route :as route])) ;; I added this namespace reference

(defroutes approutes
  (GET "/"   [] ...)                     ; the routes from before
  (POST "/bestow-name" [human-name] ...) ; don't need to be changed

  (route/resources "/")
  (route/not-found "Page not found"))

With this code in place, content in the [project directory]/resources/public folder will be accessible from the web root directory. Any path that doesn't match a route or static file will fall through to my 404 handler.

I can now begin to enrich the user experience with shiny HTML and CSS chrome. I've pulled out the view logic (and data, because programs are data) into a separate file. Here are my final view templates (CSS not included):

(ns vampire-web.views
  (:use hiccup.page))

(defn layout
  [title & content]
  (html5 [:head
           [:title (str title " - Vampire Web")]
           (include-css "main.css")]
         [:body
           [:div#wrap
             [:h1 title]
             [:div#content content]]]))

(def index
  (layout "Vampire Name Generator"
          [:form {:method "POST" :action "/bestow-name"}
            [:input.name {:type "text" :placeholder "enter your human name" :required true :name "human-name"}]
            [:input {:type "submit" :value "Tell Me My Vampire Name"}]]))

(defn show-name
  [vampire-name]
  (layout "Your Vampire Name"
          [:p "HUMAN, I bestow upon thee the vampire name"]
          [:p.vampire-name vampire-name]))

I'm not exactly thrilled with the way my hiccup templates look compared to HTML. However, the elegance and simplicity that hiccup brings to the table blew my mind. None of this would be possible without Clojure's brilliant data structures and it's really my favorite example of a functional paradigm in action. Hiccup is extremely quick to pick up and start coding; there is no new templating syntax or magic templating engine. I build up some Clojure data structures and Hiccup just turns the data into HTML.

Fun with Clojure

Some of my favorite algorithms to play with are hash functions, esoteric cryptography, and random stuff generators. I also like playing with new programming toys. Luckily this is a written post and you can't hear my jubilant squeals of laughter on account of how exciting this topic is. Without further ado let me introduce to you the full patent pending vampire name generator:

(ns vampire-web.namegen)

(defn- psnr
  "Linear congruential generator with parameters taken from glibc"
  [prev]
  (mod (+ 12345 (* prev 1103515245)) 2147483648))

(defn- generate-seed
  "Transforms the input string into a seed for the psnr"
  [human-name]
  (->> (vec (str "My name is " human-name " and I love vampires!"))
       (map int)
       (map * (range))
       (reduce +)))

(def ^:private names
  ["Domenico" "Bryce" "Cain" "Eli" "Giovanni" "Kristopher" "Nicholai"
   "Sirius" "Xavier" "Angelus" "Christian" "Julius" "Theron" "Virgil"
   "Isaac" "Julian" "Solomon" "Reyes" "Raul" "Rodolfo" "Alessandro"
   "Edwin" "Warren" "Duradel" "Wolfram" "Morgan" "Hunter" "Edward"
   "Drake" "Horatio" "Sterling" "Sebastian" "Alucard"])

(def ^:private surname1
  ["Cull" "Blood" "Mor" "Morg" "Van" "Goss" "Nos" "Black" "Beau"
   "San" "Moon" "Night" "Locke" "God" "Grim" "Grief" "Long" "Le"])

(def ^:private surname2
  ["mont" "hart" "ric" "fang" "ren" "fel" "mer" "imer" "gloom" "blanc"
   "bane" "isses" "rik" "erit" "anger" "zel" "cia" "man" "sen"])

(defn- mod-nth
  [col n]
  (nth col (mod n (count col))))

(defn vampire-name
  "Given a human name calculates a random vampire name!"
  [human-name]
  (let [seed (generate-seed human-name)
        r1 (psnr seed)
        r2 (psnr r1)
        r3 (psnr r2)]
    (str (mod-nth names r1)
         " "
         (mod-nth surname1 r2) (mod-nth surname2 r3))))

Let me break down the functions here one by one.

psnr generates the next number in a sequence given the previous number. I used this because I wanted my generated names to be unpredictable and at the same time a function of only the input. I also wanted to keep things functional.

generate-seed is just a silly function to distill user input into a single number for seeding psnr. It doesn't really have any special properties.

mod-nth returns the nth element of a sequence modulus its length. I feel like there's probably an implementation of this in the Clojure library somewhere.

Finally, vampire-name uses the above functions to select a random vampire first name and then constructs a surname using two random parts.

Apparently people on the internet take modern day vampire lore very seriously. There were hundreds of lists of vampire names to use for inspiration. My favorite name is Alucard because it sounds pretty wicked and is actually just Dracula spelled backwards.

Putting it all together

Here's my final core.clj file showing how compojure hooks together with my hiccup views and my vampire name generator.

(ns vampire-web.core
  (:use compojure.core)
  (:use ring.middleware.params)
  (:require [compojure.route :as route])
  (:require [vampire-web.views :as views])
  (:require [vampire-web.namegen :as namegen]))

(defroutes approutes
  (GET "/"   [] views/index)

  (POST "/bestow-name" [human-name]
    (views/show-name (namegen/vampire-name human-name)))

  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (-> approutes
      wrap-params))

And here is a demonstration:

Compojure and Hiccup provide a simple and powerful set of tools for web application development. I was a little worried that trying a purely functional approach to web development would introduce difficulties akin to jamming objects into relational databases. I found that the exact opposite appears to be the case. I'm still wrapping my head around functional web development but it seems to me that given the right tools the task is well suited to a functional solution.

Download the source code from github: https://github.com/bregger/vampire-namegen


Recommended Reading: Clojure for the Brave and True

Friends, countrymen, and strangers lend me your ears. I have uncovered a conspiracy of terrible proportions, at the focal point of which I have no choice but to place myself. In the year 2038 Earth will be overrun by legions of extraterrestrial cyborg werewolves because of something about unix date format or whatever. The invaders' only weakness: parentheses.

That's when my future (and much better at writing) self is discovered to be the Chosen One and in humanity's last act of defiance is sent back in time under a ridiculous pseudonym. His task: to write a book about Clojure so good and so funny that I would one day read said book and be prepared to defend humanity from the imminent cyborg threat.

In other words: this book gets me. I am given no choice but to dream up wild time travel scenarios in an effort to explain how this must have happened.

OK, Seriously Now

To clear things up, I got into Clojure by starting with the book Clojure for the Brave and True by Daniel Higginbotham. The entire book is available for free online and a DRM free ebook is available for purchase. In case you have not yet noticed, let me say that I really enjoyed reading it.

Clojure for the Brave and True was the perfect format for learning Clojure: 75% example code and 100% comedy. The book is a joy to read and the examples are equally as fun. It's been some time since I've wanted to read anything substantial to learn a new programming language, but when I found Clojure I quickly realized that it wasn't anything like the other programming languages I have come to know.

Perhaps my only criticism is that I can't say I would have solved every coding problem posed by the author in the same way. At times I found the examples to feel forced or unnecessarily complex. However, every example program is as solid a tutorial device in Clojure as it is ridiculous, entertaining, and inspirational. If you don't want to go out and write Clojure after reading this book, then nothing will ever convince you!

Ok, perhaps this book isn't for everyone, but I am far too in love with it to make an objective verdict either way. The only way you'll know for yourself is if you go here and read it.


First Clojure Web Application

Recently I've been playing with Clojure in my free time. This will be part blog post and part instructional as I walk through developing a simple web application.

Clojure Tools

I've installed the following tools for clojure development:

  • Leinengin - a ridiculously easy to use Clojure REPL, build tool, dependency manager, and project automation tool. It will even cut your sandwiches into triangles if that's what you want it to do.
  • Light Table by Chris Granger. At the time of writing this post, Light Table is under development. It's an open source IDE written in ClojureScript that runs on top of node-webkit. Light Table had a sweet kickstarter and the creator is on a mission to transform the way we interact with source code. It's not at all a bad way to start using clojure.

Both of these tools are available on Windows, Linux, and Mac. (But I'm using them on Windows)

Frameworks and Libraries

Web frameworks materialize and are subsequently abandoned to float into the aether at an alarming rate. Maybe it's because we haven't solved the problem that web frameworks are supposed to solve. Maybe nobody can agree on what that problem is or how to solve it. I look forward to writing my own general purpose web framework and abandoning it just to see what it feels like.

Quite refreshingly, however, Clojure web frameworks look like they're on the lightweight side across the board. I'll be developing my example application using the following libraries:

These are all maintained on github primarily by James Reeves. You should definitely read the source code for Hiccup and Compojure.

Hello, Compojure!

I have big plans for this website: I've been perfecting my state of the art algorithm for generating vampire names for years. With this site I will solve the identity crises of angsty teenagers all over the world. Never again will youth agonize over having names that just aren't edgy enough.

First, I type the command lein new vampire-web to create a new lein project using the default template. This creates a nearly empty project. There's also a useful template for compojure applications, but writing the skeleton code out once helps dispel any illusion of wizardry that might come from using a template.

Here's my project definition:

(defproject vampire-web "0.0.1"
  :description "Extreme Vampire Name Generator"

  :dependencies [[org.clojure/clojure "1.5.1"]
                 [ring "1.3.0"]
                 [hiccup "1.0.5"]
                 [compojure "1.1.8"]]

  :plugins [[lein-ring "0.8.11"]]

  :ring {:handler vampire-web.core/app})

The :ring entry configures which handler to have lein-ring start with. (I'm sure there's a way to wire up lein-ring to Light Table. I'll be leaving that adventure for another day.)

And now for my Hello World of compojure and hiccup applications. In core.clj:

(ns vampire-web.core
  (:use compojure.core)
  (:require [hiccup.page :as page])
  (:require ring.middleware.params)
  (:require [compojure.route :as route]))

(defroutes approutes
  (GET "/"   [] 
    (page/html5 [:head [:title "Vampire Name Generator"]]
           [:body
             [:h1 "The Vampire Name Generator"]
             [:form {:method "POST" :action "/bestow-name"}
               [:label "Enter your human name:"] [:br]
               [:input {:type "text" :name "human-name"}]
               [:input {:type "submit" :value "Get Name"}]]]))

  (POST "/bestow-name" [human-name]
    (page/html5 [:head [:title "Your Vampire Name"]]
           [:body
             [:h1 "Your Vampire Name"]
             [:p "Sir " human-name " Van Alucard"]])))

(def app
  (-> approutes
      ring.middleware.params/wrap-params))

My web application has two routes: GET / and POST /bestow-name. Compojure hooks up querystring and post parameters to the variable names in square brackets. However, to make querystring and post parameters available I composed my routes with ring.middleware.params/wrap-params.

I use the lein-ring plugin to start and stop my development server. Type the command lein ring server from your project directory to fire it up and open a browser window. Here's what I see:

My first Vampire name generator Brilliant Vampire name

Alright, now we're talking. I've got compojure and hiccup working to serve a very basic form along with some sweet form post action. (Note: If you aren't seeing parameters in your post routes make sure that you didn't forget wrap-params. It took me a while to figure this out myself because outdated blog posts do not include this detail. Compojure used to automatically apply this middleware for you.)

Conclusion

I'm very happy with the way that Compojure and Hiccup cut down the boilerplate required to build a web application without introducing heavy wizardry. This was my first experience with Clojure but reading through source code and documentation for Hiccup and Compojure wasn't too difficult. To anyone new to Clojure web development, I recommend expanding Compojure macros like route and defroutes and reading up on ring. Also, check out this talk James Reeves gave on compojure. It really let me start solidifying my understanding of Clojure web development.

Where Next

I'll build onto this site in my next post. The codebase is going to get bigger, so I'll need to group functions out into multiple files before I can upgrade the page's aesthetic and name generation algorithm.



Powered by Copland OS Enterprise wired technology.