Skip to content
This repository has been archived by the owner on Dec 8, 2024. It is now read-only.

Commit

Permalink
entrypoint: dependency injection strategy; add datomic-browser demo
Browse files Browse the repository at this point in the history
  • Loading branch information
dustingetz committed Dec 3, 2023
1 parent 8aea5ec commit c73bb47
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 17 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ clojure -X:build:prod:dustingetz build-client :hyperfiddle/domain dustingetz :de
# http://localhost:8080/electric-fiddle.essay!Essay/electric-y-combinator
clj -M:prod -m prod
clj -M:prod:datomic-browser -m prod
fly deploy --remote-only --config src/hello_fiddle/fly.toml
clojure -X:build build-client :verbose true
Expand Down
11 changes: 8 additions & 3 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{:paths ["src" "resources"]
:deps
{com.hyperfiddle/electric {:local/root "vendor/electric"}
com.hyperfiddle/hfql {:local/root "vendor/hfql"}
com.hyperfiddle/rcf {:local/root "vendor/rcf"}
info.sunng/ring-jetty9-adapter
{:mvn/version "0.17.7" ; (Jetty 10) is NOT Java 8 compatible
Expand Down Expand Up @@ -54,10 +53,16 @@
{datascript/datascript {:mvn/version "1.5.2"}
reagent/reagent {:mvn/version "1.1.1"}}}

:dustingetz
:datomic-browser
{:extra-deps
{com.datomic/peer {:mvn/version "1.0.6735" #_#_:exclusions [org.slf4j/slf4j-api]}}}

:dustingetz
{:extra-deps
{com.datomic/peer {:mvn/version "1.0.6735" #_#_:exclusions [org.slf4j/slf4j-api]}
com.hyperfiddle/hfql {:local/root "vendor/hfql"}}}

:hfql-demo
{:extra-deps
{com.datomic/peer {:mvn/version "1.0.6735" #_#_:exclusions [org.slf4j/slf4j-api]}}}}}
{com.datomic/peer {:mvn/version "1.0.6735" #_#_:exclusions [org.slf4j/slf4j-api]}
com.hyperfiddle/hfql {:local/root "vendor/hfql"}}}}}
62 changes: 52 additions & 10 deletions src-dev/dev.cljc
Original file line number Diff line number Diff line change
@@ -1,24 +1,63 @@
(ns ^:dev/always dev ; rebuild everything when any file changes. Will fix
(:require #?(:clj [clojure.tools.logging :as log])
(:require [contrib.assert :refer [check]]
[contrib.clojurex :refer [bindx]]
#?(:clj [contrib.datomic-contrib :as dx])
#?(:clj [clojure.tools.logging :as log])
electric-fiddle.main
#?(:clj [electric-fiddle.server :refer [start-server!]])
[hyperfiddle :as hf]
[hyperfiddle.electric :as e]
[hyperfiddle.rcf :as rcf]

#?(:clj [models.teeshirt-orders-datomic :as model])

dustingetz.fiddles ; datomic
datomic-browser.domain
datomic-browser.fiddles ; datomic
electric-tutorial.fiddles
electric-starter-app.fiddles
hfql-demo.fiddles)) ; datomic

(comment (-main)) ; repl entrypoint

(e/def fiddle-registry
(merge
dustingetz.fiddles/fiddles
electric-starter-app.fiddles/fiddles
electric-tutorial.fiddles/fiddles
hfql-demo.fiddles/fiddles))
#_(e/def domain-registry ['datomic-browser.fiddles])

; What if we hardcode this dev entrypoint for the fiddles we're working on today?
; thus can share models, for example

(e/defn MergedFiddleMain []
(e/server
(let [[conn db] (model/init-datomic)]
(bindx [datomic-browser.domain/conn (check conn)
datomic-browser.domain/db (check db)
datomic-browser.domain/schema (check (new (dx/schema> datomic-browser.domain/db)))]
(e/client
(binding [hf/pages (merge
dustingetz.fiddles/fiddles
datomic-browser.fiddles/fiddles
electric-starter-app.fiddles/fiddles
electric-tutorial.fiddles/fiddles
hfql-demo.fiddles/fiddles)]
(electric-fiddle.main/Main.)))))))

#_
(e/defn Inject-general [Main]
; parallel strategy - note domains deploy independently in prod,
; cross-domain dependency is forbidden
(e/server
(bindx [~@server-bindings]
(e/client
(bindx [~@client-bindings
hf/pages fiddle-registry]
(Main.)))))

; sequential strategy
#_(binding [hf/pages fiddle-registry]
(datomic-browser.fiddles/Inject. ; set bindings and also allocate resources
(e/fn [Main]
(datomic-browser.fiddles/Inject.
(e/fn [Main]
(Main.)))))))

#?(:clj
(do
Expand Down Expand Up @@ -48,9 +87,12 @@

#?(:cljs
(do
(def electric-entrypoint (e/boot
(binding [hf/pages fiddle-registry]
(electric-fiddle.main/Main.))))
(def electric-entrypoint
(e/boot
; in dev, we setup a merged fiddle config,
; fiddles must all opt in to the shared routing strategy
(MergedFiddleMain.)))

(defonce reactor nil)

(defn ^:dev/after-load ^:export start! []
Expand Down
7 changes: 4 additions & 3 deletions src-prod/prod.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@
(require (symbol (str (::hf/domain config) ".fiddles"))) ; load userland server
(start-server! config)))

(defmacro install-user-fiddles [] (symbol (name hf/*hyperfiddle-user-ns*) "fiddles"))
(defmacro install-user-inject [] (symbol (name hf/*hyperfiddle-user-ns*) "FiddleMain"))

#?(:cljs
(do
(def electric-entrypoint
(e/boot
(binding [hf/pages (install-user-fiddles)]
(electric-fiddle.main/Main.))))
; in prod, fiddle owns the app and there's only one of them
(let [FiddleMain (install-user-inject)]
(FiddleMain.))))

(defonce reactor nil)

Expand Down
203 changes: 203 additions & 0 deletions src/datomic_browser/datomic_browser.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
(ns datomic-browser.datomic-browser
(:require clojure.edn
contrib.ednish
[contrib.str :refer [any-matches?]]
[contrib.data :refer [unqualify treelister]]
#?(:clj [contrib.datomic-contrib :as dx])
[contrib.datomic-m #?(:clj :as :cljs :as-alias) d]
[contrib.gridsheet :as gridsheet :refer [Explorer]]
[datomic-browser.domain :as D :refer [db conn schema]]
#?(:clj datomic.api)
[hyperfiddle.electric :as e]
[hyperfiddle.electric-dom2 :as dom]
[hyperfiddle.history :as history]
[missionary.core :as m]))

(comment (ns-unmap *ns* 'model))

(e/defn RecentTx []
(e/client (dom/h1 (dom/text "Recent Txs")))
(Explorer.
(treelister (new (->> (d/datoms> db {:index :aevt, :components [:db/txInstant]})
(m/reductions conj ())
(m/relieve {})))
(fn [_]) any-matches?)
{::gridsheet/page-size 30
::gridsheet/row-height 24
::gridsheet/columns [:db/id :db/txInstant]
::gridsheet/grid-template-columns "10em auto"
::gridsheet/Format
(e/fn [[e _ v tx op :as record] a]
(case a
:db/id (e/client (history/link [::tx tx] (dom/text tx)))
:db/txInstant (e/client (dom/text (pr-str v))) #_(e/client (.toLocaleDateString v))))}))

(e/defn Attributes []
(e/client (dom/h1 (dom/text "Attributes")))
(let [cols [:db/ident :db/valueType :db/cardinality :db/unique :db/isComponent
#_#_#_#_:db/fulltext :db/tupleType :db/tupleTypes :db/tupleAttrs]]
(Explorer.
(treelister (->> (dx/attributes> db cols)
(m/reductions conj [])
(m/relieve {})
new
(sort-by :db/ident)) ; sort by db/ident which isn't available
(fn [_]) any-matches?)
{::gridsheet/page-size 15
::gridsheet/row-height 24
::gridsheet/columns cols
::gridsheet/grid-template-columns "auto 6em 4em 4em 4em"
::gridsheet/Format
(e/fn [row col]
(e/client
(let [v (col row)]
(case col
:db/ident (history/link [::attribute v] (dom/text v))
:db/valueType (some-> v :db/ident name dom/text)
:db/cardinality (some-> v :db/ident name dom/text)
:db/unique (some-> v :db/ident name dom/text)
(dom/text (str v))))))})))

(e/defn Format-entity [[k v :as row] col]
(assert (some? schema))
(case col
::k (cond
(= :db/id k) (e/client (dom/text k)) ; :db/id is our schema extension, can't nav to it
(contains? schema k) (e/client (history/link [::attribute k] (dom/text k)))
() (e/client (dom/text (str k)))) ; str is needed for Long db/id, why?
::v (if-not (coll? v) ; don't render card :many intermediate row
(let [[valueType cardinality]
((juxt (comp unqualify dx/identify :db/valueType)
(comp unqualify dx/identify :db/cardinality)) (k schema))]
(cond
(= :db/id k) (e/client (history/link [::entity v] (dom/text v)))
(= :ref valueType) (e/client (history/link [::entity v] (dom/text v)))
() (e/client (dom/text (pr-str v))))))))

(e/defn EntityDetail [e]
(assert e)
(e/client (dom/h1 (dom/text "Entity detail: " e))) ; treeview on the entity
(Explorer.
;; TODO inject sort
(treelister (new (e/task->cp (d/pull db {:eid e :selector ['*] :compare compare})))
(partial dx/entity-tree-entry-children schema)
any-matches?)
{::gridsheet/page-size 15
::gridsheet/row-height 24
::gridsheet/columns [::k ::v]
::gridsheet/grid-template-columns "15em auto"
::gridsheet/Format Format-entity}))

(e/defn EntityHistory [e]
(assert e)
(e/client (dom/h1 (dom/text "Entity history: " (pr-str e))))
(Explorer.
; accumulate what we've seen so far, for pagination. Gets a running count. Bad?
(treelister (new (->> (dx/entity-history-datoms> db e)
(m/reductions conj []) ; track a running count as well?
(m/relieve {})))
(fn [_]) any-matches?)
{::gridsheet/page-size 20
::gridsheet/row-height 24
::gridsheet/columns [::e ::a ::op ::v ::tx-instant ::tx]
::gridsheet/grid-template-columns "10em 10em 3em auto auto 9em"
::gridsheet/Format
(e/fn [[e aa v tx op :as row] a]
(when row ; when this view unmounts, somehow this fires as nil
(case a
::op (e/client (dom/text (name (case op true :db/add false :db/retract))))
::e (e/client (history/link [::entity e] (dom/text e)))
::a (if (some? aa)
(let [ident (:db/ident (new (e/task->cp (d/pull db {:eid aa :selector [:db/ident]}))))]
(e/client (dom/text (pr-str ident)))))
::v (e/client (some-> v pr-str dom/text))
::tx (e/client (history/link [::tx tx] (dom/text tx)))
::tx-instant (let [x (:db/txInstant (new (e/task->cp (d/pull db {:eid tx :selector [:db/txInstant]}))))]
(e/client (pr-str (dom/text x))))
(str v))))}))

(e/defn AttributeDetail [a]
(e/client (dom/h1 (dom/text "Attribute detail: " a)))
(Explorer.
(treelister (new (->> (d/datoms> db {:index :aevt, :components [a]})
(m/reductions conj [])
(m/relieve {})))
(fn [_]) any-matches?)
{::gridsheet/page-size 20
::gridsheet/row-height 24
::gridsheet/columns [:e :a :v :tx]
::gridsheet/grid-template-columns "15em 15em calc(100% - 15em - 15em - 9em) 9em"
::gridsheet/Format
(e/fn [[e _ v tx op :as x] k]
(e/client
(case k
:e (history/link [::entity e] (dom/text e))
:a (dom/text (pr-str a)) #_(let [aa (new (e/task->cp (dx/ident! db aa)))] aa)
:v (some-> v str dom/text) ; todo when a is ref, render link
:tx (history/link [::tx tx] (dom/text tx)))))}))

(e/defn TxDetail [e]
(e/client (dom/h1 (dom/text "Tx detail: " e)))
(Explorer.
(treelister (new (->> (d/tx-range> conn {:start e, :end (inc e)}) ; global
(m/eduction (map :data) cat)
(m/reductions conj [])
(m/relieve {})))
(fn [_]) any-matches?)
{::gridsheet/page-size 20
::gridsheet/row-height 24
::gridsheet/columns [:e :a :v :tx]
::gridsheet/grid-template-columns "15em 15em calc(100% - 15em - 15em - 9em) 9em"
::gridsheet/Format
(e/fn [[e aa v tx op :as x] a]
(case a
:e (let [e (new (e/task->cp (dx/ident! db e)))] (e/client (history/link [::entity e] (dom/text e))))
:a (let [aa (new (e/task->cp (dx/ident! db aa)))] (e/client (history/link [::attribute aa] (dom/text aa))))
:v (pr-str v) ; when a is ref, render link
(str tx)))}))

(e/defn DbStats []
(e/client (dom/h1 (dom/text "Db stats")))
(Explorer.
(treelister
(new (e/task->cp (d/db-stats db)))
(fn [[k v]] (condp = k :attrs (into (sorted-map) v) nil))
any-matches?)
{::gridsheet/page-size 20
::gridsheet/row-height 24
::gridsheet/columns [::k ::v]
::gridsheet/grid-template-columns "20em auto"
::gridsheet/Format
(e/fn [[k v :as row] col]
(e/client
(case col
::k (dom/text (pr-str k))
::v (cond
(= k :attrs) nil ; print children instead
() (dom/text (pr-str v))))))})) ; {:count 123}

(comment
{:datoms 800958,
:attrs
{:release/script {:count 11435},
:label/type {:count 870}
... ...}})

(e/defn DatomicBrowser [& [page x]]
(dom/h1 (dom/text "Datomic browser"))
(dom/link (dom/props {:rel :stylesheet, :href "gridsheet-optional.css"}))
(dom/div (dom/props {:class "user-gridsheet-demo"})
(dom/div (dom/text "Nav: ")
(history/link [::summary] (dom/text "home")) (dom/text " ")
(history/link [::db-stats] (dom/text "db-stats")) (dom/text " ")
(history/link [::recent-tx] (dom/text "recent-tx")))
(case (or page ::summary)
::summary (history/router 1 (e/server (Attributes.)))
::attribute (history/router 2 (e/server (AttributeDetail. x)))
::tx (history/router 2 (e/server (TxDetail. x)))
::entity (do (history/router 2
(history/router ::entity-detail (e/server (EntityDetail. x)))
(history/router ::entity-history (e/server (EntityHistory. x)))))
::db-stats (history/router 1 (e/server (DbStats.)))
::recent-tx (history/router 1 (e/server (RecentTx.)))
(e/client (dom/text "no matching route: " (pr-str page))))))
6 changes: 6 additions & 0 deletions src/datomic_browser/domain.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(ns datomic-browser.domain
(:require [hyperfiddle.electric :as e]))

(e/def conn)
(e/def db)
(e/def schema)
24 changes: 24 additions & 0 deletions src/datomic_browser/fiddles.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
(ns datomic-browser.fiddles
(:require [contrib.assert :refer [check]]
[contrib.clojurex :refer [bindx]]
#?(:clj [contrib.datomic-contrib :as dx])
electric-fiddle.main
[hyperfiddle :as hf]
[hyperfiddle.electric :as e]
[datomic-browser.domain :refer [conn db schema]] ; :(
[datomic-browser.datomic-browser :refer [DatomicBrowser]]
#_models.mbrainz
#?(:clj [models.teeshirt-orders-datomic :as model])))

(e/def fiddles
{`DatomicBrowser DatomicBrowser})

(e/defn FiddleMain []
(e/server
(bindx [conn (check (model/init-datomic))
db (check model/*$*)
schema (check (new (dx/schema> db)))]
; index-route (or (seq args) [::summary])
(e/client
(binding [hf/pages fiddles]
(electric-fiddle.main/Main.))))))
3 changes: 2 additions & 1 deletion src/models/teeshirt_orders_datomic.clj
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
(alter-var-root #'*$*
(constantly
(-> *conn*
d/db (d/with -schema) :db-after fixtures))))
d/db (d/with -schema) :db-after fixtures)))
[*conn* *$*])

(tests (init-datomic))

Expand Down

0 comments on commit c73bb47

Please sign in to comment.