Ganelon framework provides additional features on top of Ring/Compojure and can be used just as any other Ring library. What makes it different, are the provided JavaScript libraries, allowing server-side to fully control browser behaviour.
The framework introduces three basic constructs, which allow the programmer to built dynamic web apps effectively, while still retaining the benefits of generating HTML server-side:
An application built with these constructs is easy to understand and maintain due to a standarized approach, and contains as little boilerplate code as possible.
In addition to that, Ganelon provides:
ganelon.web.dyna-routes
- helper utilities to manage page routes dynamically.ganelon.web.middleware
- helper middleware, such as CDN-ification of urls or access to remote IP when running behind a reverse proxy (e.g. lighttpd or nginx).ganelon.util
- general purpose utility helper functions.ganelon.util.logging
- general purpose logging functions on top of clojure.tools.logging
To apply Ganelon in your web app project, simply add the following leiningen dependency:
[ganelon "0.9.0"]
Client side JavaScript is provided through the following files (assuming that /public resources are published as /):
(hiccup/include-js "/ganelon/js/jquery-1.8.1.min.js") ;jQuery - required (hiccup/include-js "/ganelon/js/bootstrap.js") ;Bootstrap - optional (hiccup/include-js "/ganelon/js/ganelon.js") ;basic actions support (hiccup/include-js "/ganelon/js/ext/ganelon.ops.bootstrap.js") ;additional Bootstrap related actions (hiccup/include-js "/ganelon/js/ext/ganelon.ops.gritter.js") ;growl-style notifications through gritter.js (hiccup/include-js "/ganelon/actions.js") ;dynamic actions interface
The Ganelon JavaScript files are available in public.ganelon.js package- in case you need to merge them with your other client-side modules.
The Bootstrap and gritter support is optional - you can easily overwrite them with your own plugins.
You might also want to include CSS file for Growl-style notifications or Bootstrap (2.3.0) - or merge them with your CSS files:
(hiccup/include-css "/ganelon/css/bootstrap.css") ;Bootstrap - optional (hiccup/include-css "/ganelon/css/jquery.gritter.css") ;growl-style notifications - optional
The Ganelon CSS files are available in public.ganelon.css package- in case you need to merge them with your other client-side stylesheets.
To run Ganelon-powered application just use standard Ring mechanisms. For example, code used to run this entire site
with ring.adapter.jetty
is listed below:
(ns ganelon.test.demo (:require [ganelon.test.demo-pages] [ring.middleware.stacktrace] [ring.middleware.reload] [ring.adapter.jetty :as jetty] [ganelon.web.middleware :as middleware] [ganelon.web.app :as webapp] [noir.session :as sess])) (defonce SERVER (atom nil)) (defn start-demo [port] (jetty/run-jetty (-> (ganelon.web.app/app-handler (ganelon.web.app/javascript-actions-route)) middleware/wrap-x-forwarded-for (ring.middleware.stacktrace/wrap-stacktrace) (ring.middleware.reload/wrap-reload {:dirs ["test/ganelon/test/pages"]})) {:port port :join? false})) (let [mode :dev port (Integer. (get (System/getenv) "PORT" "8097"))] (swap! SERVER (fn [s] (when s (.stop s)) (start-demo port))))
Of course, it is also possible to use lein-war and put your whole web application in a neat .war file. Or use highly performant http-kit - anything that is compatible with Ring.
There is nothing easier in Ganelon framework than executing AJAX request and applying its results to the page contents.
The simple and easy to use mechanism translates JSON response into JavaScript calls. Ganelon provides out-of-the-box support for:
The list above can be easily extended using only JavaScript.
Code below updates part of the page (widget) and displays Growl-style notifications:
(defn demo1-widget [msg] (widgets/with-div [:p "The message is: " [:b (hiccup.util/escape-html msg)]] [:ul [:li (widgets/action-link "demo-action" {:msg "test1"} {} "Set message to <b>test1</b>.")] [:li (widgets/action-link "demo-action" {:msg "test2"} {} "Set message to <b>test2</b>.")]]))
First, we have defined a widget rendering function. It is a function like any other, and we are just using ganelon.web.widgets/with-div
macro to set widget id. Next, ganelon.web.widgets/widget-update-link
generates ajax call link with msg
parameter.
(actions/defjsonaction "demo-action" [msg widget-id] (actions/put-operation! (ui/notification "Success" (h/html "Message set to: " [:b (hiccup.util/escape-html msg)]))) (actions/put-operation! (ui/notification "Another message" (h/html "Widget-id is: " [:b (hiccup.util/escape-html widget-id)]))) (ui/fade widget-id (demo1-widget msg)))
The action handler returns two operations for client-side JavaScript to perform:
ganelon.web.ui-operations/notification
results are put into operation queue with ganelon.web.actions/put-operation!
.demo1-widget
. The div id is provided from widget-id parameter, autogenerated by ganelon.web.widgets/with-div
in a demo1-widget
function in the step before.Please note, that you can use Ganelon's AJAX action as any other Ring-compliant handlers. The defjsonaction
macro is here merely for you convience!
Try it here:
Ganelon is 100% Ring-compliant, so it is possible to define handler hierarchy as in any other
Ring/Compojure app and still benefit from all of the features of the framework.
In addition to that, a ganelon.web.dyna-routes
package is available.
This package provides a simple in-memory registry for handlers, and the simplest use is as follows:
(ganelon.web.dyna-routes/defpage "/routing/sample" [] "Hello world!")
Try it (open a new window/tab)
It is also possible to use setroute!
function and define a route as a direct Compojure (or other) route:
(ganelon.web.dyna-routes/setroute! :example2 (compojure.core/GET "/routing/sample2" [] "Hello world !!!"))