Skip to content

Instantly share code, notes, and snippets.

@cgrand
Created May 11, 2009 12:29
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save cgrand/109955 to your computer and use it in GitHub Desktop.
Save cgrand/109955 to your computer and use it in GitHub Desktop.
;; this file is a walkthrough of Moustache features, a web framework for Clojure
;; http://github.com/cgrand/moustache/tree/master
;; Moustache allows to declare routes, apply middlewares and dispatch on http methods.
;; Moustache is compatible with all frameworks built on Ring, including Compojure
(ns demo
(:use net.cgrand.moustache)
(:use [ring.adapter.jetty :only [run-jetty]])) ;; hmmm Ring without servlets
;; set up a server that serves my-app
(declare my-app)
(def server (doto (Thread. #(run-jetty #'my-app {:port 8080})) .start))
;; #'my-app is a trick to allow to redefine my-app without restarting the server thread.
;; now define my-app
(def my-app (app ["hi"] "hello world!"))
;; go to http://localhost:8080/hi
;; our app replies to all HTTP methods, let's be more speficic:
(def my-app (app ["hi"] {:get "hello world only for GET!"}))
;; go to http://localhost:8080/hi
;; thanks to the #'my-app in the server declaration you see a magnificient "hello world only for GET!"
;; What about customizing the greeting?
(def my-app (app ["hi" name] {:get ["hello " name]}))
;; refresh http://localhost:8080/hi and you get a 404 Not found
;; go to http://localhost:8080/hi/you to admire a perfect "hello you"
;; Can I have both hi and hi/you?
(def my-app (app
["hi"] {:get "hello world only for GET!"}
["hi" name] {:get ["hello " name]}))
;; You can even greet several people at once:
(def my-app (app
["hi"] {:get "hello world only for GET!"}
["hi" & names] {:get ["hello " (apply str (interpose ", " names)) "!"]}))
;; and go to http://localhost:8080/hi/you/me/everybody
;; You can nest your apps:
(def my-app (app
["hi"] {:get "hello world only for GET!"}
["hi" &]
(app [& names] {:get ["hello " (apply str (interpose ", " names)) "!"]})))
;; thus you can write
(declare multiple-hi)
(def my-app (app
["hi"] {:get "hello world only for GET!"}
["hi" &] multiple-hi))
(def multiple-hi (app [& names] {:get ["hello " (apply str (interpose ", " names)) "!"]}))
;; now you can redefine only one small part of your application:
(def multiple-hi (app [& names] {:get ["bonjour " (apply str (interpose ", " names)) "!"]}))
;; when you simply want to nest app, there's some macro sugar: you can use a vector form instead of a (app ...) form.
(def my-app (app
["hi"] {:get "hello world only for GET!"}
["hi" &]
[[& names] {:get ["hello " (apply str (interpose ", " names)) "!"]}]))
;; the whole truth is that {} (as in {:get ...}) is also sugar:
(def my-app (app ["hi"] [:get ["hello world!"]])) ; works as well
(def my-app (app ["hi"] (app :get ["hello world!"]))) ; sugar-free
;; back on track: everybody loves regexes and falling through routes!
(def my-app (app
["hi" #"fred|lucy|ethel"] {:get ["hello!"]}
["hi" _] {:get "I don't talk to strangers"}))
;; go and try to visit http://localhost:8080/hi/you and http://localhost:8080/hi/lucy
;; you can get the result of the match
(def my-app (app
["hi" [name #"fred|lucy|ethel"]] {:get ["hello " name "!"]}
["hi" _] {:get "I don't talk to strangers"}))
;; in truth you can also write your own validators:
(defn integer [s]
(try (Integer/parseInt s) (catch Exception e)))
(def my-app (app ["countdown" [n integer]] {:get ["counting down: " (apply str (interpose " " (range n 0 -1)))]}))
;; try http://localhost:8080/countdown/42 and please note that http://localhost:8080/countdown/foo returns a 404
;; Back to our regexes
(def my-app (app
["hi" [name #"fred|lucy|ethel"]] {:get ["hello " name "!"]}
["hi" _] {:get "I don't talk to strangers"}))
;; since {:get } is a sugarized (app ...) form, we can rewrite the above def as:
(def my-app (app :get
[["hi" [name #"fred|lucy|ethel"]] ["hello " name "!"]
["hi" _] "I don't talk to strangers"]))
;; and now some examples on how to add middlewares:
(def my-app (app
(middleware1 arg1)
middleware-without-arg
:get [["hi" [name #"fred|lucy|ethel"]] ["hello " name "!"]
["hi" _] "I don't talk to strangers"]))
;; it's the same as writing:
(def my-app (->
(app :get
[["hi" [name #"fred|lucy|ethel"]] ["hello " name "!"]
["hi" _] "I don't talk to strangers"])
middleware-without-arg
(middleware1 arg1)))
;; Note that you can also declare middlewares in the vector form:
(def my-app (app
:get [(middleware1 arg1)
middleware-without-arg
["hi" [name #"fred|lucy|ethel"]] ["hello " name "!"]
["hi" _] "I don't talk to strangers"]))
;; and least but not last: every (app ... form) is a Ring handler and
;; every right-hand form can be a Ring handler.
(def my-app (app :get (fn [req] (returns a response))))
(def my-app (app ["hi"] (fn [req] (returns a response))))
(def my-app (app ["hi"] {:get (fn [req] (returns a response))}))
;; (btw if your Ring handler returns nil, the request is passed to the next route)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment