Search code examples
clojurecompojure

Creating Compojure routes from a list


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?


Solution

  • 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.