oakes / odoyle-rum

The Unlicense
71 stars 4 forks source link

Clojars Project

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

(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