I've just been playing with Compojure recently, and I've got a small basic webapp. For my HTML templates I'm using Enlive, and I've got a namespace that holds all the simple, static pages. The defroute call for these pages looks like this:
(defroutes public-routes
(GET "/" []
(info/index-template))
(GET "/about" []
(info/about-template))
(GET "/contact" []
(info/contact-template)))
I've actually got a few more than that, but that should give the idea of what I'm doing.
Now, I thought, that's really just bunch of repetition on my part, so I thought I would try the following:
(defroutes info-routes
(map #(GET (str "/" %) [] (ns-resolve 'webapp.pages.info
(symbol (str % "-template"))))
'("about" "contact")))
Of course, this doesn't work, as the map is returning a lazy sequence and not a body (?) of functions. Does someone know what I need to do to get this idea to work?
Or should I be using a completely different approach to cut down on repeating myself?
You can always use the routes
function which is used by defroutes:
(defroutes info-routes
(apply routes
(map #(GET (str "/" %) []
(ns-resolve 'webapp.pages.info
(symbol (str % "-template"))))
'("about" "contact"))))
But that's still quite boring, let's spice it up! ;-)
(defn templates-for [& nss]
(->> nss
(map ns-publics)
(apply concat)
(filter #(->> % first str
(re-seq #"-template$")))
(map second)))
(defn template-uri [template]
(->> template meta :name name
(re-seq #"(.*)-template$")
first second (str "/")))
(defn template->route [template]
(GET (template-uri template) [] template))
(defroutes public-routes
(GET "/" [] "foo")
(apply routes (map template->route
(templates-for 'webapp.pages.info))))
With this code, the templates-for
function will look for any functions finishing with "-template" in the namespaces given and write the appropriate route with them. Look how I'm not using any macro, but plenty of composition.