This DSL generates client/server UI for Datomic entities. The UI is transactional, secure, reactive, includes data sync, etc.
(ns user.hello-world
(:require
[hyperfiddle.api :as hf]))
(def submissions-master
'[{`submissions [(:dustingetz/email
; Three hypermedia controls on ::email (which is :db.unique/identity)
{:hf/a `submissions-detail} ; hyperlink
{:hf/tx `remove} ; transaction
{:hf/class :hf/new ; classes are semantic, this allocates a tempid
:hf/prompt `submissions-detail}) ; prompt for more edits as part of the transaction
{:dustingetz/gender [:db/ident
#_`f ; Hyperfiddle can "pull" through functions, as if keywords
{`shirt-sizes [:db/ident]}]}
{:dustingetz/shirt-size [:db/ident]}]}
`genders])
(defn remove [ctx [e a v] props]
[[:db/retractEntity v]])
(defn submissions [$ & args]
(q '[:find [?e ...] ; they must return FindColl for now
:where [?e :dustingetz/email]] $))
(defn genders [$ & args]
(q '[:find [(pull ?e [:db/ident]) ...]
:where [?e :dustingetz/type :dustingetz/gender]]
$))
(defn shirt-sizes [$ & [gender]]
(q '[:in $ ?gender
:find [?e ...]
:where
[?e :dustingetz/type :dustingetz/shirt-size]
[?e :dustingetz/gender ?gender]]
$ gender))
(def submissions-detail
[:dustingetz/email
{:dustingetz/gender [:db/ident
{`shirt-sizes [:db/ident]}]}
{:dustingetz/shirt-size [:db/ident]}
`genders])
(hf/init-routes
[`submissions-master
`submissions-detail])
(def config {:domain {:databases {"$" {:database/uri #uri "datomic:mem://app-db"}}}})
(def config (hf/get-config config))
(def domain (hf/get-domain config))
(def conn (hf/connect domain "$"))
(def $ (-> (hf/db conn)
(hf/with [{:db/ident :dustingetz/email :db/valueType :db.type/string :db/cardinality :db.cardinality/one :db/unique :db.unique/identity}
{:db/ident :dustingetz/gender :db/valueType :db.type/ref :db/cardinality :db.cardinality/one}
{:db/ident :dustingetz/shirt-size :db/valueType :db.type/ref :db/cardinality :db.cardinality/one}
{:db/ident :dustingetz/type :db/valueType :db.type/keyword :db/cardinality :db.cardinality/one}])
(hf/with [{:dustingetz/type :dustingetz/gender :db/ident :dustingetz/male}
{:dustingetz/type :dustingetz/gender :db/ident :dustingetz/female}])
(hf/with [{:dustingetz/type :dustingetz/shirt-size :db/ident :dustingetz/mens-small :dustingetz/gender :dustingetz/male}
{:dustingetz/type :dustingetz/shirt-size :db/ident :dustingetz/mens-medium :dustingetz/gender :dustingetz/male}
{:dustingetz/type :dustingetz/shirt-size :db/ident :dustingetz/mens-large :dustingetz/gender :dustingetz/male}
{:dustingetz/type :dustingetz/shirt-size :db/ident :dustingetz/womens-small :dustingetz/gender :dustingetz/female}
{:dustingetz/type :dustingetz/shirt-size :db/ident :dustingetz/womens-medium :dustingetz/gender :dustingetz/female}
{:dustingetz/type :dustingetz/shirt-size :db/ident :dustingetz/womens-large :dustingetz/gender :dustingetz/female}])
(hf/with [{:dustingetz/email "alice@example.com" :dustingetz/gender :dustingetz/female :dustingetz/shirt-size :dustingetz/womens-large}
{:dustingetz/email "bob@example.com" :dustingetz/gender :dustingetz/male :dustingetz/shirt-size :dustingetz/mens-large}
{:dustingetz/email "charlie@example.com" :dustingetz/gender :dustingetz/male :dustingetz/shirt-size :dustingetz/mens-medium}])))