-
-
Save abp/3512256 to your computer and use it in GitHub Desktop.
Using database functions in Datomic transactions and annotating transaction history
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(use '[datomic.api :only [q db] :as d]) | |
(def uri "datomic:mem://accounts") | |
;; create database | |
(d/create-database uri) | |
;; connect to database | |
(def conn (d/connect uri)) | |
;; parse schema dtm file | |
(def schema-tx (read-string (slurp "db/schema.dtm"))) | |
;; submit schema transaction | |
@(d/transact conn schema-tx) | |
;; parse seed data dtm file | |
(def data-tx (read-string (slurp "db/accounts.dtm"))) | |
;; submit seed data transaction | |
@(d/transact conn data-tx) | |
(defn decorate | |
"Simple function to pull out all the attributes of an entity into a map" | |
[id] | |
(let [ db (d/db conn) | |
e (d/entity db id)] | |
(select-keys e (keys e)))) | |
(defn decorate-results | |
"maps through a result set where each item is a single entity and decorates it" | |
[r] | |
(map #(decorate (first %)) r)) | |
(defn accounts | |
"returns all accounts" | |
[] | |
(d/q '[:find ?a :where [?a :account/balance _]] (d/db conn))) | |
(defn history | |
"Returns all transactions" | |
([] (d/q '[:find ?tx :in $ :where [?tx :ot/amount _]] (d/db conn))) | |
([acct] (let [rules '[[(party ?t ?a) | |
[?t :ot/from ?a]] | |
[(party ?t ?a) | |
[?t :ot/to ?a ]]]] | |
(d/q '[ :find ?t :in $ % ?a :where (party ?t ?a)] | |
(d/db conn) rules acct)))) | |
(defn transfer [ from to amount note] | |
(let [txid (datomic.api/tempid :db.part/tx)] | |
(d/transact conn [[:transfer from to amount] | |
{:db/id txid, :db/doc note :ot/from from :ot/to to :ot/amount amount}]))) | |
(defn credit [ to amount ] | |
(d/transact conn [[:credit to amount]])) | |
(def issuer (ffirst (d/q '[:find ?e :where [?e :account/name "issuer"]] (d/db conn)))) | |
(def bob (ffirst (d/q '[:find ?e :where [?e :account/name "bob"]] (d/db conn)))) | |
(def alice (ffirst (d/q '[:find ?e :where [?e :account/name "alice"]] (d/db conn)))) | |
(transfer issuer alice 77M "Issuance to Alice") | |
(transfer issuer bob 23M "Issuance to Bob") | |
(transfer alice bob 7M "Tomatoes") | |
(prn (decorate-results (accounts))) | |
(println "All transactions") | |
(prn (decorate-results (history))) | |
(println "Issuer's transactions") | |
(prn (decorate-results (history issuer))) | |
(prn (decorate issuer)) | |
(println) | |
(println "Bob's transactions") | |
(prn (decorate-results (history bob))) | |
(prn (decorate bob)) | |
(println) | |
(println "Alice's transactions") | |
(prn (decorate-results (history alice))) | |
(prn (decorate alice)) | |
(println) | |
;; Throws an exception | |
(transfer alice bob 71M "Tomatoes") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{:db/id #db/id[:db.part/user -1000001], :account/name "issuer", :account/balance 0M, :account/min-balance -1000M} | |
{:db/id #db/id[:db.part/user -1000002], :account/name "bob", :account/balance 0M, :account/min-balance 0M} | |
{:db/id #db/id[:db.part/user -1000003], :account/name "alice", :account/balance 0M, :account/min-balance 0M} | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
;; accounts | |
{ :db/id #db/id[:db.part/db] | |
:db/ident :account/name | |
:db/valueType :db.type/string | |
:db/cardinality :db.cardinality/one | |
:db/fulltext true | |
:db/unique :db.unique/value | |
:db/doc "An account's name" | |
:db.install/_attribute :db.part/db} | |
{ :db/id #db/id[:db.part/db] | |
:db/ident :account/balance | |
:db/cardinality :db.cardinality/one | |
:db/valueType :db.type/bigdec | |
:db/doc "The accounts balance" | |
:db.install/_attribute :db.part/db} | |
{ :db/id #db/id[:db.part/db] | |
:db/ident :account/min-balance | |
:db/cardinality :db.cardinality/one | |
:db/valueType :db.type/bigdec | |
:db/doc "The accounts maximum balance" | |
:db.install/_attribute :db.part/db} | |
{ :db/id #db/id[:db.part/db] | |
:db/ident :ot/amount | |
:db/valueType :db.type/bigdec | |
:db/cardinality :db.cardinality/one | |
:db/doc "Amount transacted" | |
:db.install/_attribute :db.part/db} | |
{ :db/id #db/id[:db.part/db] | |
:db/ident :ot/from | |
:db/valueType :db.type/ref | |
:db/cardinality :db.cardinality/one | |
:db/doc "Transferee" | |
:db.install/_attribute :db.part/db} | |
{ :db/id #db/id[:db.part/db] | |
:db/ident :ot/to | |
:db/valueType :db.type/ref | |
:db/cardinality :db.cardinality/one | |
:db/doc "Recipient" | |
:db.install/_attribute :db.part/db} | |
{ :db/id #db/id [:db.part/user] | |
:db/ident :credit | |
:db/fn #db/fn { :lang "clojure" | |
:params [db id amount] | |
:code "(let [ e (datomic.api/entity db id) | |
min-balance (:account/min-balance e 0) | |
balance (+ (:account/balance e 0) amount) ] | |
(if (>= balance min-balance) | |
[[:db/add id :account/balance balance ]] | |
(throw (Exception. \"Insufficient funds\"))))" }} | |
{ :db/id #db/id [:db.part/user] | |
:db/ident :transfer | |
:db/fn #db/fn { :lang "clojure" | |
:params [db from to amount] | |
:code "[[:credit from (- amount)] | |
[:credit to amount]]"}} | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment