Routing

Ganelon framework comes with several convienience functions for defining and managing Ring routes, while still retaining the power and flexibility of Compojure/Ring.

It is not required to use them - if you retain classical Ring approach, AJAX support and other features provided by Ganelon will still be available.

Simple route

To define a simple route, just use Noir style ganelon.web.dyna-routes/defpage macro:

(ganelon.web.dyna-routes/defpage "/routing/sample" []
  "Hello world!")

The defined route will respond to any HTTP request type (GET, POST, PUT, DELETE, ...).

Run sample (opens in a new browser tab/window)

Compojure routes

It is also possible to use setroute! function and define a route as a direct Compojure (or any other Ring-compliant) route:

(ganelon.web.dyna-routes/setroute! :example2
  (compojure.core/GET "/routing/sample2" []
    "Hello world !!!"))

Run compojure sample (opens in a new browser tab/window)

You can also use this to define AJAX operations or any other Compojure/Ring compliant handlers/middleware (for example - Cemerick's friend framework):

(ganelon.web.dyna-routes/setroute! :example2
  (compojure.core/GET "/routing/sample2" []
    "Hello world !!!"))

Run sample action (opens in a new browser tab/window)

Ring support

The mechanisms provided in ganelon.web.dyna-routes are 100% Ring-compatible. You can mix them with Ring middleware or other Ring-complaint functions.

The example below wraps all requests to /routing/sample4/* with a middleware that checks for an admin parameter and if it is not present - blocks the call with 401 Forbidden HTTP error code.

(defn wrap-fake-admin [handler]
  (fn [req] (if (:admin (:params req))
              (handler req) {:status 401 :body "401 Forbidden"})))

(ganelon.web.dyna-routes/setroute! :example4
  (compojure.core/GET "/routing/sample4/*" []
    (->
      (compojure.core/routes
        (compojure.core/GET "/routing/sample4/test1" []
          (hiccup.core/html "A very sensitive information,
                             requiring admin privileges"))
        (compojure.core/GET "/routing/sample4/test2" []
          (hiccup.core/html "A moderately sensitive information,
                              requiring admin privileges")))
      wrap-fake-admin)))

Sample#1 without admin (opens in a new browser tab/window)

Sample#2 without admin (opens in a new browser tab/window)

Sample#1 with admin param (opens in a new browser tab/window)

Sample#2 with admin param (opens in a new browser tab/window)

Route groups

Routes can be grouped into specific subsets by supplying namespace to ganelon.web.dyna-routes/setroute!, and referenced later on using ganelon.web.dyna-routes/route-ns-fn.

When namespace is not supplied, the :default namespace is used.

The example below approaches this using :sample-admin group and applying wrap-fake-admin (see above) middleware to it.

(ganelon.web.dyna-routes/setroute! :sample-admin :example51
  (compojure.core/GET "/routing/sample5/test1" []
      "Even more sensitive information,
       requiring admin privileges."))

(ganelon.web.dyna-routes/setroute! :sample-admin :example52
  (compojure.core/GET "/routing/sample5/test2" []
      "Quite moderately sensitive information,
       requiring admin privileges."))

(ganelon.web.dyna-routes/setroute! :example5
  (compojure.core/GET "/routing/sample5/*" []
    (-> (ganelon.web.dyna-routes/route-ns-fn :sample-admin)
        wrap-fake-admin)))

Sample#1 without admin (opens in a new browser tab/window)

Sample#2 without admin (opens in a new browser tab/window)

Sample#1 with admin param (opens in a new browser tab/window)

Sample#2 with admin param (opens in a new browser tab/window)

Route groups can also be referenced when starting ring adapter (e.g. jetty).

Please take note of the ganelon.web.app/app-handler function, which sets up a default routing and utility middleware, providing access to keywordized params and making resources from /public path available. This is a convienience function and you don't have to use it.

In production, sensitive resources can be protected by cemerick/friend or any other Ring-compliant solution.