Awesome
Introduction
What if we used a rules engine to manage all the state of a frontend UI? Can a rules engine replace reframe? Let's find out, children!
There is prior art here, including precept, which tried to use Clara Rules for this purpose. But this time I'm using a rules engine built for fast updates of facts: O'Doyle Rules.
I'm using Rum as my React wrapper, because it is flexible and is great at server-side rendering. The goal is to define Rum components as rules and make them automatically update when the data required by the rules is updated.
Examples
- odoyle-rum-todo
- Dynadoc, a documentation generator (see this file)
- The following shows a button that displays how many times it's been clicked:
(require
'[rum.core :as rum]
'[odoyle.rules :as o]
'[odoyle.rum :as orum])
(def components
(orum/ruleset
{click-counter
[:what
[::global ::clicks clicks]
:then
(let [*session (orum/prop)]
[:button {:on-click (fn [_]
(swap! *session
(fn [session]
(-> session
(o/insert ::global ::clicks (inc clicks))
o/fire-rules))))}
(str "Clicked " clicks " " (if (= 1 clicks) "time" "times"))])]}))
(def initial-session
(-> (reduce o/add-rule (o/->session) components)
(o/insert ::global ::clicks 0)
o/fire-rules))
(defonce *session
(atom initial-session))
(swap! *session ;; Required for hot reloading to work correctly
(fn [session]
(->> (o/query-all session)
(reduce o/insert initial-session)
o/fire-rules)))
(rum/mount (click-counter *session) (js/document.querySelector "#app"))
The click-counter
rule generates a var holding a valid Rum component. When the values in the :what
block are updated, the rule re-fires, which causes the component to update.
Development
- Install the Clojure CLI tool
- To develop with figwheel:
clj -M:dev
- To install the release version:
clj -M:prod install