Skip to content

Instantly share code, notes, and snippets.

@terjesb
Forked from pelle/accounts.clj
Created July 18, 2012 07:35
Show Gist options
  • Save terjesb/3134849 to your computer and use it in GitHub Desktop.
Save terjesb/3134849 to your computer and use it in GitHub Desktop.
Using database functions in Datomic transactions and annotating transaction history
(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")
[
{: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}
]
[
;; 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