Follow-up: Extract datomish.transact.explode.
This required pushing the ID literal out of transact. I elected to put them in DB, since literal allocation will end up in IDB eventually.
This commit is contained in:
parent
d8c976c3ad
commit
c948902c52
4 changed files with 111 additions and 93 deletions
|
@ -34,6 +34,29 @@
|
|||
(uncaughtException [_ thread ex]
|
||||
(println ex "Uncaught exception on" (.getName thread))))))
|
||||
|
||||
;; ----------------------------------------------------------------------------
|
||||
;; define data-readers to be made available to EDN readers. in CLJS
|
||||
;; they're magically available. in CLJ, data_readers.clj may or may
|
||||
;; not work, but you can always simply do
|
||||
;;
|
||||
;; (clojure.edn/read-string {:readers datomish/data-readers} "...")
|
||||
;;
|
||||
|
||||
(defonce -id-literal-idx (atom -1000000))
|
||||
|
||||
(defrecord TempId [part idx])
|
||||
|
||||
(defn id-literal
|
||||
([part]
|
||||
(if (sequential? part)
|
||||
(apply id-literal part)
|
||||
(->TempId part (swap! -id-literal-idx dec))))
|
||||
([part idx]
|
||||
(->TempId part idx)))
|
||||
|
||||
(defn id-literal? [x]
|
||||
(and (instance? TempId x)))
|
||||
|
||||
(defprotocol IClock
|
||||
(now
|
||||
[clock]
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
[datomish.query.projection :as projection]
|
||||
[datomish.query.source :as source]
|
||||
[datomish.query :as query]
|
||||
[datomish.db :as db]
|
||||
[datomish.db :as db :refer [id-literal id-literal?]]
|
||||
[datomish.datom :as dd :refer [datom datom? #?@(:cljs [Datom])]]
|
||||
[datomish.util :as util #?(:cljs :refer-macros :clj :refer) [raise raise-str cond-let]]
|
||||
[datomish.schema :as ds]
|
||||
|
@ -20,6 +20,7 @@
|
|||
[datomish.sqlite :as s]
|
||||
[datomish.sqlite-schema :as sqlite-schema]
|
||||
[datomish.transact.bootstrap :as bootstrap]
|
||||
[datomish.transact.explode :as explode]
|
||||
#?@(:clj [[datomish.pair-chan :refer [go-pair <?]]
|
||||
[clojure.core.async :as a :refer [chan go <! >!]]])
|
||||
#?@(:cljs [[datomish.pair-chan]
|
||||
|
@ -53,29 +54,6 @@
|
|||
(defn conn? [x]
|
||||
(and (satisfies? IConnection x)))
|
||||
|
||||
;; ----------------------------------------------------------------------------
|
||||
;; define data-readers to be made available to EDN readers. in CLJS
|
||||
;; they're magically available. in CLJ, data_readers.clj may or may
|
||||
;; not work, but you can always simply do
|
||||
;;
|
||||
;; (clojure.edn/read-string {:readers datomish/data-readers} "...")
|
||||
;;
|
||||
|
||||
(defonce -id-literal-idx (atom -1000000))
|
||||
|
||||
(defrecord TempId [part idx])
|
||||
|
||||
(defn id-literal
|
||||
([part]
|
||||
(if (sequential? part)
|
||||
(apply id-literal part)
|
||||
(->TempId part (swap! -id-literal-idx dec))))
|
||||
([part idx]
|
||||
(->TempId part idx)))
|
||||
|
||||
(defn id-literal? [x]
|
||||
(and (instance? TempId x)))
|
||||
|
||||
(defrecord TxReport [db-before ;; The DB before the transaction.
|
||||
db-after ;; The DB after the transaction.
|
||||
current-tx ;; The tx ID represented by the transaction in this report.
|
||||
|
@ -127,73 +105,6 @@
|
|||
true
|
||||
entity))
|
||||
|
||||
(defn- #?@(:clj [^Boolean reverse-ref?]
|
||||
:cljs [^boolean reverse-ref?]) [attr]
|
||||
(if (keyword? attr)
|
||||
(= \_ (nth (name attr) 0))
|
||||
(raise "Bad attribute type: " attr ", expected keyword"
|
||||
{:error :transact/syntax, :attribute attr})))
|
||||
|
||||
(defn- reverse-ref [attr]
|
||||
(if (keyword? attr)
|
||||
(if (reverse-ref? attr)
|
||||
(keyword (namespace attr) (subs (name attr) 1))
|
||||
(keyword (namespace attr) (str "_" (name attr))))
|
||||
(raise "Bad attribute type: " attr ", expected keyword"
|
||||
{:error :transact/syntax, :attribute attr})))
|
||||
|
||||
(declare explode-entity)
|
||||
|
||||
(defn- explode-entity-a-v [db entity eid a v]
|
||||
;; a should be symbolic at this point. Map it. TODO: use ident/entid to ensure we have a symbolic attr.
|
||||
(let [reverse? (reverse-ref? a)
|
||||
straight-a (if reverse? (reverse-ref a) a)
|
||||
straight-a* (get-in db [:idents straight-a] straight-a)
|
||||
_ (when (and reverse? (not (ds/ref? (db/schema db) straight-a*)))
|
||||
(raise "Bad attribute " a ": reverse attribute name requires {:db/valueType :db.type/ref} in schema"
|
||||
{:error :transact/syntax, :attribute a, :op entity}))
|
||||
a* (get-in db [:idents a] a)]
|
||||
(cond
|
||||
reverse?
|
||||
(explode-entity-a-v db entity v straight-a eid)
|
||||
|
||||
(and (map? v)
|
||||
(not (id-literal? v)))
|
||||
;; Another entity is given as a nested map.
|
||||
(if (ds/ref? (db/schema db) straight-a*)
|
||||
(let [other (assoc v (reverse-ref a) eid
|
||||
;; TODO: make the new ID have the same part as the original eid.
|
||||
;; TODO: make the new ID not show up in the tempids map. (Does Datomic exposed the new ID this way?)
|
||||
:db/id (id-literal :db.part/user))]
|
||||
(explode-entity db other))
|
||||
(raise "Bad attribute " a ": nested map " v " given but attribute name requires {:db/valueType :db.type/ref} in schema"
|
||||
{:error :transact/entity-map-type-ref
|
||||
:op entity }))
|
||||
|
||||
(sequential? v)
|
||||
(if (ds/multival? (db/schema db) a*) ;; dm/schema
|
||||
(mapcat (partial explode-entity-a-v db entity eid a) v) ;; Allow sequences of nested maps, etc. This does mean [[1]] will work.
|
||||
(raise "Sequential values " v " but attribute " a " is :db.cardinality/one"
|
||||
{:error :transact/entity-sequential-cardinality-one
|
||||
:op entity }))
|
||||
|
||||
true
|
||||
[[:db/add eid a* v]])))
|
||||
|
||||
(defn- explode-entity [db entity]
|
||||
(if (map? entity)
|
||||
(if-let [eid (:db/id entity)]
|
||||
(mapcat (partial apply explode-entity-a-v db entity eid) (dissoc entity :db/id))
|
||||
(raise "Map entity missing :db/id, got " entity
|
||||
{:error :transact/entity-missing-db-id
|
||||
:op entity }))
|
||||
[entity]))
|
||||
|
||||
(defn explode-entities [db entities]
|
||||
"Explode map shorthand, such as {:db/id e :attr value :_reverse ref}, to a list of vectors,
|
||||
like [[:db/add e :attr value] [:db/add ref :reverse e]]."
|
||||
(mapcat (partial explode-entity db) entities))
|
||||
|
||||
(defn maybe-ident->entid [db [op e a v tx :as orig]]
|
||||
(let [e (get (db/idents db) e e) ;; TODO: use ident, entid here.
|
||||
a (get (db/idents db) a a)
|
||||
|
@ -276,7 +187,7 @@
|
|||
;; Normalize Datoms into :db/add or :db/retract vectors.
|
||||
(update :entities (partial map maybe-datom->entity))
|
||||
|
||||
(update :entities (partial explode-entities db))
|
||||
(update :entities (partial explode/explode-entities db))
|
||||
|
||||
(update :entities (partial map ensure-entity-form))
|
||||
|
||||
|
|
84
src/datomish/transact/explode.cljc
Normal file
84
src/datomish/transact/explode.cljc
Normal file
|
@ -0,0 +1,84 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
(ns datomish.transact.explode
|
||||
#?(:cljs
|
||||
(:require-macros
|
||||
[datomish.pair-chan :refer [go-pair <?]]
|
||||
[cljs.core.async.macros :refer [go]]))
|
||||
(:require
|
||||
[datomish.db :as db]
|
||||
[datomish.util :as util #?(:cljs :refer-macros :clj :refer) [raise raise-str cond-let]]
|
||||
[datomish.schema :as ds]
|
||||
#?@(:clj [[datomish.pair-chan :refer [go-pair <?]]
|
||||
[clojure.core.async :as a :refer [chan go <! >!]]])
|
||||
#?@(:cljs [[datomish.pair-chan]
|
||||
[cljs.core.async :as a :refer [chan <! >!]]])))
|
||||
|
||||
(defn- #?@(:clj [^Boolean reverse-ref?]
|
||||
:cljs [^boolean reverse-ref?]) [attr]
|
||||
(if (keyword? attr)
|
||||
(= \_ (nth (name attr) 0))
|
||||
(raise "Bad attribute type: " attr ", expected keyword"
|
||||
{:error :transact/syntax, :attribute attr})))
|
||||
|
||||
(defn- reverse-ref [attr]
|
||||
(if (keyword? attr)
|
||||
(if (reverse-ref? attr)
|
||||
(keyword (namespace attr) (subs (name attr) 1))
|
||||
(keyword (namespace attr) (str "_" (name attr))))
|
||||
(raise "Bad attribute type: " attr ", expected keyword"
|
||||
{:error :transact/syntax, :attribute attr})))
|
||||
|
||||
(declare explode-entity)
|
||||
|
||||
(defn- explode-entity-a-v [db entity eid a v]
|
||||
;; a should be symbolic at this point. Map it. TODO: use ident/entid to ensure we have a symbolic attr.
|
||||
(let [reverse? (reverse-ref? a)
|
||||
straight-a (if reverse? (reverse-ref a) a)
|
||||
straight-a* (get-in db [:idents straight-a] straight-a)
|
||||
_ (when (and reverse? (not (ds/ref? (db/schema db) straight-a*)))
|
||||
(raise "Bad attribute " a ": reverse attribute name requires {:db/valueType :db.type/ref} in schema"
|
||||
{:error :transact/syntax, :attribute a, :op entity}))
|
||||
a* (get-in db [:idents a] a)]
|
||||
(cond
|
||||
reverse?
|
||||
(explode-entity-a-v db entity v straight-a eid)
|
||||
|
||||
(and (map? v)
|
||||
(not (db/id-literal? v)))
|
||||
;; Another entity is given as a nested map.
|
||||
(if (ds/ref? (db/schema db) straight-a*)
|
||||
(let [other (assoc v (reverse-ref a) eid
|
||||
;; TODO: make the new ID have the same part as the original eid.
|
||||
;; TODO: make the new ID not show up in the tempids map. (Does Datomic exposed the new ID this way?)
|
||||
:db/id (db/id-literal :db.part/user))]
|
||||
(explode-entity db other))
|
||||
(raise "Bad attribute " a ": nested map " v " given but attribute name requires {:db/valueType :db.type/ref} in schema"
|
||||
{:error :transact/entity-map-type-ref
|
||||
:op entity }))
|
||||
|
||||
(sequential? v)
|
||||
(if (ds/multival? (db/schema db) a*) ;; dm/schema
|
||||
(mapcat (partial explode-entity-a-v db entity eid a) v) ;; Allow sequences of nested maps, etc. This does mean [[1]] will work.
|
||||
(raise "Sequential values " v " but attribute " a " is :db.cardinality/one"
|
||||
{:error :transact/entity-sequential-cardinality-one
|
||||
:op entity }))
|
||||
|
||||
true
|
||||
[[:db/add eid a* v]])))
|
||||
|
||||
(defn- explode-entity [db entity]
|
||||
(if (map? entity)
|
||||
(if-let [eid (:db/id entity)]
|
||||
(mapcat (partial apply explode-entity-a-v db entity eid) (dissoc entity :db/id))
|
||||
(raise "Map entity missing :db/id, got " entity
|
||||
{:error :transact/entity-missing-db-id
|
||||
:op entity }))
|
||||
[entity]))
|
||||
|
||||
(defn explode-entities [db entities]
|
||||
"Explode map shorthand, such as {:db/id e :attr value :_reverse ref}, to a list of vectors,
|
||||
like [[:db/add e :attr value] [:db/add ref :reverse e]]."
|
||||
(mapcat (partial explode-entity db) entities))
|
|
@ -35,6 +35,6 @@
|
|||
;; https://github.com/ztellman/potemkin/issues/31) to improve this re-exporting process.
|
||||
(def <close transact/close)
|
||||
|
||||
(def id-literal transact/id-literal)
|
||||
(def id-literal db/id-literal)
|
||||
|
||||
(def db transact/db)
|
||||
|
|
Loading…
Reference in a new issue