I've recently published a simple Open Source micro-framework which allows server-side Clojure code to update web page content or invoke any JavaScript operations dynamically through XHR requests and thin JavaScript layer.
Ganelon is fully Ring compatible and based on Compojure and lib-noir. Being a AJAX focused, it is not a direct replacement for Noir. It can rather ease the pain of handling dynamic page requests in any Ring-based web application.
The execution model is heavily influenced by Weblocks or Vaadin, but without session-statefulness and out-of-the-box rich user interface:
The source codes are on GitHub: http://github.com/tlipski/ ganelon and demo site and documentation is available at http://ganelon.tomeklipski.com .
Sample code (a shoutbox) is listed below:
;A Widget, returning HTML code:
(defn box-widget []
(widgets/with-div
[:p "Call count (since restart): " [:b @COUNTER] ". Last 4 entries:"]
(for [entry @ENTRIES]
[:div.hibox [:b (:time entry)] ": "
(hiccup.util/escape-html (:msg entry))])
(widgets/action-form "say-hi" {} {:class "form-inline"}
[:input {:name "msg" :placeholder "Say hi!" :type "text"
:maxlength "20" :size "20"}] " "
[:button {:class "btn btn-primary"} "Send!"])))
;An Action, performing side effects and returning part of the page to be updated
(actions/defwidgetaction "say-hi" [msg]
(swap! COUNTER inc)
(swap! ENTRIES #(util/smart-subvec (flatten [(mkmsg msg) %]) 0 4))
(box-widget))
JavaScript code uses jQuery and optionally Bootstrap, but the implementation is really trivial and therefore easy to replace using your favorite JS library.
Ganelon is fully Ring compatible and based on Compojure and lib-noir. Being a AJAX focused, it is not a direct replacement for Noir. It can rather ease the pain of handling dynamic page requests in any Ring-based web application.
The execution model is heavily influenced by Weblocks or Vaadin, but without session-statefulness and out-of-the-box rich user interface:
- Certain parts of the page can be scoped as Widgets (for example using id attribute in HTML or ganelon.web.widgets.with-div utility macro) and referenced by id in Actions, and updated by Operations. Widgets can reference Actions to be invoked.
- Actions are invoked as XHR requests and return a set of Operations to be performed client-side. Actions are in fact simple Ring handlers.
- Operations provide abstraction layer over client-side JavaScript execution - e.g. using Bootstrap Modal or just updating part of DOM tree (a Widget or any other) or open certain URL in a browser window - or just anything else that has a simple integration layer provided.
The source codes are on GitHub: http://github.com/tlipski/
Sample code (a shoutbox) is listed below:
;A Widget, returning HTML code:
(defn box-widget []
(widgets/with-div
[:p "Call count (since restart): " [:b @COUNTER] ". Last 4 entries:"]
(for [entry @ENTRIES]
[:div.hibox [:b (:time entry)] ": "
(hiccup.util/escape-html (:msg entry))])
(widgets/action-form "say-hi" {} {:class "form-inline"}
[:input {:name "msg" :placeholder "Say hi!" :type "text"
:maxlength "20" :size "20"}] " "
[:button {:class "btn btn-primary"} "Send!"])))
;An Action, performing side effects and returning part of the page to be updated
(actions/defwidgetaction "say-hi" [msg]
(swap! COUNTER inc)
(swap! ENTRIES #(util/smart-subvec (flatten [(mkmsg msg) %]) 0 4))
(box-widget))
JavaScript code uses jQuery and optionally Bootstrap, but the implementation is really trivial and therefore easy to replace using your favorite JS library.