Skip to content
Permalink
Browse files

Calling transaction fns through idents directly (closes #185)

  • Loading branch information
tonsky committed Dec 7, 2018
1 parent 0273a73 commit 34c122e0b5192bb58a797137fbb1f6bd1b236e6d
Showing with 45 additions and 33 deletions.
  1. +1 −0 CHANGELOG.md
  2. +1 −1 deps.edn
  3. +1 −1 project.clj
  4. +25 −9 src/datascript/db.cljc
  5. +17 −22 test/datascript/test/transact.cljc
@@ -6,6 +6,7 @@
- Support `:as` in Pull API an attr-with-opts syntax (#270, PR #271, thx @Jumblemuddle)
- Support idents expansion (PR #245, thx bamarco)
- JS API correctly handles nested maps with `{":db/id"}` in transactions (#228, thx @serebrianyi)
- Calling transaction fns through idents directly (PR #185, thx @refset)

# 0.16.9

@@ -7,7 +7,7 @@

:1.10 {
:override-deps {
org.clojure/clojure {:mvn/version "1.10.0-RC3"}
org.clojure/clojure {:mvn/version "1.10.0-RC4"}
}
}

@@ -90,7 +90,7 @@
]}

:profiles {
:1.10 { :dependencies [[org.clojure/clojure "1.10.0-RC2" :scope "provided"]
:1.10 { :dependencies [[org.clojure/clojure "1.10.0-RC4" :scope "provided"]
[org.clojure/clojurescript "1.10.439" :scope "provided"]] }
:dev { :source-paths ["bench/src" "test" "dev"]
:dependencies [[org.clojure/tools.nrepl "0.2.13"]
@@ -1049,6 +1049,16 @@
(transact-tx-data (assoc-in report [:tempids tempid] upserted-eid)
es)))

(def builtin-fn?
#{:db.fn/call
:db.fn/cas
:db/cas
:db/add
:db/retract
:db.fn/retractAttribute
:db.fn/retractEntity
:db/retractEntity})

(defn transact-tx-data [initial-report initial-es]
(when-not (or (nil? initial-es)
(sequential? initial-es))
@@ -1115,6 +1125,18 @@
(= op :db.fn/call)
(let [[_ f & args] entity]
(recur report (concat (apply f db args) entities)))

(and (keyword? op)
(not (builtin-fn? op)))
(if-some [ident (entid db op)]
(let [fun (-> (-search db [ident :db/fn]) first :v)
args (next entity)]
(if (fn? fun)
(recur report (concat (apply fun db args) entities))
(raise "Entity " op " expected to have :db/fn attribute with fn? value"
{:error :transact/syntax, :operation :db.fn/call, :tx-data entity})))
(raise "Can’t find entity for transaction fn " op
{:error :transact/syntax, :operation :db.fn/call, :tx-data entity}))

(or (= op :db.fn/cas)
(= op :db/cas))
@@ -1142,8 +1164,8 @@
(and (ref? db a) (tx-id? v))
(recur (allocate-eid report v (current-tx report)) (cons [op e a (current-tx report)] entities))

(and (tempid? e) (some #(= op %) [:db/add :db.fn/call :db.fn/cas :db/cas :db/retract :db.fn/retractAttribute :db.fn/retractEntity]))
(if (some #(= op %) [:db.fn/call :db.fn/cas :db/cas :db/retract :db.fn/retractAttribute :db.fn/retractEntity])
(tempid? e)
(if (not= op :db/add)
(raise "Tempids are resolved for :db/add only"
{ :error :transact/syntax
:op entity })
@@ -1191,13 +1213,7 @@
(recur report entities))

:else
(let [[ident & args] entity]
(let [{e :e v :v} (first (-datoms db :avet [:db/ident ident]))
f (:v (first (-search db [e :db/fn])))]
(if (and (= v ident) (fn? f))
(recur report (concat (apply f db args) entities))
(raise "Unknown operation at " entity ", expected :db/add, :db/retract, :db.fn/call, :db.fn/retractAttribute, :db.fn/retractEntity or an ident corresponding to an installed transaction function (e.g. {:db/ident <keyword> :db/fn <Ifn>}, usage of :db/ident requires {:db/unique :db.unique/identity} in schema)"
{:error :transact/syntax, :operation op, :tx-data entity}))))))
(raise "Unknown operation at " entity ", expected :db/add, :db/retract, :db.fn/call, :db.fn/retractAttribute, :db.fn/retractEntity or an ident corresponding to an installed transaction function (e.g. {:db/ident <keyword> :db/fn <Ifn>}, usage of :db/ident requires {:db/unique :db.unique/identity} in schema)" {:error :transact/syntax, :operation op, :tx-data entity})))

(datom? entity)
(let [[e a v tx added] entity]
@@ -207,32 +207,27 @@
(is (:had-birthday e)))))

(deftest test-db-ident-fn
(let [conn (d/create-conn {:aka { :db/cardinality :db.cardinality/many }
:db/ident { :db/unique :db.unique/identity}})
(let [conn (d/create-conn {:name {:db/unique :db.unique/identity}})
inc-age (fn [db name]
(if-let [[eid age] (first (d/q '{:find [?e ?age]
:in [$ ?name]
:where [[?e :name ?name]
[?e :age ?age]]}
db name))]
(do (println eid age) [{:db/id eid :age (inc age)} [:db/add eid :had-birthday true]])
(if-some [ent (d/entity db [:name name])]
[{:db/id (:db/id ent)
:age (inc (:age ent))}
[:db/add (:db/id ent) :had-birthday true]]
(throw (new Throwable (str "No entity with name: " name)))))]
(d/transact! conn [{:db/id 1 :name "Ivan" :age 31}])
(d/transact! conn [[:db/add 1 :name "Petr"]])
(d/transact! conn [[:db/add 1 :aka "Devil"]])
(d/transact! conn [[:db/add 1 :aka "Tupen"]])
(d/transact! conn [{:db/ident :inc-age :db/fn inc-age}])
(is (= (d/q '[:find ?v ?a
:where [?e :name ?v]
[?e :age ?a]] @conn)
#{["Petr" 31]}))
(is (= (d/q '[:find ?v
:where [?e :aka ?v]] @conn)
#{["Devil"] ["Tupen"]}))
(d/transact! conn [{:db/id 1
:name "Petr"
:age 31
:db/ident :Petr}
{:db/ident :inc-age
:db/fn inc-age}])
(is (thrown-with-msg? Throwable #"Can’t find entity for transaction fn :unknown-fn"
(d/transact! conn [[:unknown-fn]])))
(is (thrown-with-msg? Throwable #"Entity :Petr expected to have :db/fn attribute with fn\? value"
(d/transact! conn [[:Petr]])))
(is (thrown-with-msg? Throwable #"No entity with name: Bob"
(d/transact! conn [[:inc-age "Bob"]])))
(let [{:keys [db-after]} (d/transact! conn [[:inc-age "Petr"]])
e (d/entity db-after 1)]
(d/transact! conn [[:inc-age "Petr"]])
(let [e (d/entity @conn 1)]
(is (= (:age e) 32))
(is (:had-birthday e)))))

0 comments on commit 34c122e

Please sign in to comment.
You can’t perform that action at this time.