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


Powered by Copland OS Enterprise wired technology.