(ns emptyhead.idea.crud "Functions for Creating, Updating and Deleting ideas. The 'R' in 'CRUD' is implemented by [[emptyhead.idea.protocol/value]]." (:require [emptyhead.idea.state :refer [state]] [emptyhead.idea.property :as prop] [emptyhead.idea.protocol :as prtc])) (defn- register-idea! "Helper function to scaffold an 'empty' idea." [ref] (swap! state assoc-in [ref :_meta :_properties] #{}) (swap! state assoc-in [ref :_meta :_reference] ref) ref) (defn swap-idea! "Swap data inside `idea` with given `data`. Returns a reference to `idea`." [idea data] (swap! state assoc (prtc/reference idea) (merge data {:_meta (prtc/val-fn :_meta idea)})) (prtc/reference idea)) (defn extend-idea! "Merge `data` into state of `idea`. Returns a reference to `idea`." [idea data] (swap! state assoc (prtc/reference idea) (prtc/val-fn merge idea data)) (prtc/reference idea)) (defn mutate-idea! "Evaluate `fun` on `idea` with optional extra `args`, then replace `idea` by the result. Returns a reference to `idea`." [fun idea & args] (swap-idea! idea (prtc/val-fn fun idea args)) (prtc/reference idea)) (defn forget-idea! "Delete `idea` from the state. Returns a copy of the `idea`." [idea] (let [val (prtc/value idea)] (apply prop/remove-property! idea (prop/properties idea)) (prtc/ref-fn #(swap! state dissoc %) idea) (prtc/copy val))) (defn have-idea! "Instantiate up to `count` new ideas, optionally prefixing reference symbol with `prefix`. Additionally allows you to immediately attach `properties` and `data`. Returns a single idea or a list of ideas depending on whether `count` was given." [& {:keys [prefix count properties data] :or {count 1 prefix "idea_" properties []}}] (let [fun #(register-idea! (gensym prefix)) ideas (take count (repeatedly fun))] (run! #(apply prop/register-property! % properties) ideas) (when data (run! #(extend-idea! % data) ideas)) (if (= count 1) (first ideas) ideas)))