Skip to content

Instantly share code, notes, and snippets.

@abaitam
Forked from pelle/accounts.clj
Created March 31, 2016 02:21
Show Gist options
  • Save abaitam/d646053d0d4f16ba7819c7142a0b3222 to your computer and use it in GitHub Desktop.
Save abaitam/d646053d0d4f16ba7819c7142a0b3222 to your computer and use it in GitHub Desktop.

Revisions

  1. @pelle pelle revised this gist Jul 10, 2012. 3 changed files with 57 additions and 63 deletions.
    74 changes: 52 additions & 22 deletions accounts.clj
    Original file line number Diff line number Diff line change
    @@ -20,41 +20,71 @@
    ;; submit seed data transaction
    @(d/transact conn data-tx)

    (defn accounts []
    (d/q '[:find ?c ?n ?b ?count :where [?c :account/name ?n] [?c :account/balance ?b] [?c :account/transaction-count ?count]] (db conn)))
    (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
    ([] (q '[:find ?tx ?f ?t ?a ?n ?when :in $ :where [?from :account/name ?f] [?to :account/name ?t] [?tx :ot/from ?from] [?tx :ot/to ?to] [?tx :ot/amount ?a] [?tx :ot/note ?n] [?tx :db/txInstant ?when]] (db conn)))
    ([acct] (q '[:find ?tx ?f ?t ?a ?n ?when :in $ ?e :where [?from :account/name ?f] [?to :account/name ?t] [?tx :ot/from ?from] [?tx :ot/to ?to] [?tx :ot/amount ?a] [?tx :ot/note ?n] [?tx :db/txInstant ?when] [?e :account/transactions ?tx]] (db conn) acct) ))
    "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/add from :account/transactions txid]
    [:db/add to :account/transactions txid]
    [:inc from :account/transaction-count 1]
    [:inc to :account/transaction-count 1]
    {:db/id txid, :ot/note note :ot/from from :ot/to to :ot/amount amount}])))
    (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 (first (first (q '[:find ?e :where [?e :account/name "issuer"]] (db conn)))))
    (def bob (first (first (q '[:find ?e :where [?e :account/name "bob"]] (db conn)))))
    (def alice (first (first (q '[:find ?e :where [?e :account/name "alice"]] (db conn)))))
    (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))))

    (prn (accounts))
    (transfer issuer alice 77M "Issuance to Alice")
    (transfer issuer bob 23M "Issuance to Bob")
    (transfer alice bob 7M "Tomatoes")

    (println "History")
    (prn (history))
    ;; #<HashSet [[13194139534319 "issuer" "alice" 77M "Issuance to Alice" #inst "2012-05-08T14:35:25.252-00:00"],
    ;; [13194139534320 "issuer" "bob" 23M "Issuance to Bob" #inst "2012-05-08T14:35:25.256-00:00"],
    ;; [13194139534321 "alice" "bob" 7M "Tomatoes" #inst "2012-05-08T14:35:25.262-00:00"]]>
    (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)

    (println "Accounts")
    (prn (accounts))
    ;; #<HashSet [[17592186045421 "issuer" -100M 2], [17592186045423 "alice" 70M 2], [17592186045422 "bob" 30M 2]]>
    ;; Throws an exception
    (transfer alice bob 71M "Tomatoes")
    6 changes: 3 additions & 3 deletions accounts.dtm
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    [
    {:db/id #db/id[:db.part/user -1000001], :account/name "issuer", :account/balance 0M, :account/max-balance 0M, :account/min-balance -1000M}
    {:db/id #db/id[:db.part/user -1000002], :account/name "bob", :account/balance 0M, :account/max-balance 100M, :account/min-balance 0M}
    {:db/id #db/id[:db.part/user -1000003], :account/name "alice", :account/balance 0M, :account/max-balance 100M, :account/min-balance 0M}
    {: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}
    ]
    40 changes: 2 additions & 38 deletions schema.dtm
    Original file line number Diff line number Diff line change
    @@ -17,41 +17,13 @@
    :db/doc "The accounts balance"
    :db.install/_attribute :db.part/db}

    { :db/id #db/id[:db.part/db]
    :db/ident :account/transaction-count
    :db/cardinality :db.cardinality/one
    :db/valueType :db.type/long
    :db/doc "The amount of transactions made with account"
    :db.install/_attribute :db.part/db}

    { :db/id #db/id[:db.part/db]
    :db/ident :account/max-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 :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 :account/transactions
    :db/cardinality :db.cardinality/many
    :db/valueType :db.type/ref
    :db/doc "The accounts transactions"
    :db.install/_attribute :db.part/db}

    { :db/id #db/id[:db.part/db]
    :db/ident :ot/note
    :db/valueType :db.type/string
    :db/cardinality :db.cardinality/one
    :db/doc "An note about what was transfered"
    :db.install/_attribute :db.part/db}

    { :db/id #db/id[:db.part/db]
    :db/ident :ot/amount
    :db/valueType :db.type/bigdec
    @@ -73,15 +45,6 @@
    :db/doc "Recipient"
    :db.install/_attribute :db.part/db}

    { :db/id #db/id [:db.part/user]
    :db/ident :inc
    :db/fn #db/fn { :lang "clojure"
    :params [db id attr amount]
    :code "(let [ e (datomic.api/entity db id)
    orig (attr e 0) ]
    [[:db/add id attr (+ orig amount) ]])"}}


    { :db/id #db/id [:db.part/user]
    :db/ident :credit
    :db/fn #db/fn { :lang "clojure"
    @@ -98,5 +61,6 @@
    :db/fn #db/fn { :lang "clojure"
    :params [db from to amount]
    :code "[[:credit from (- amount)]
    [:credit to amount]]"}}
    [:credit to amount]]"}}

    ]
  2. @pelle pelle revised this gist May 8, 2012. 2 changed files with 23 additions and 8 deletions.
    11 changes: 5 additions & 6 deletions accounts.clj
    Original file line number Diff line number Diff line change
    @@ -21,7 +21,7 @@
    @(d/transact conn data-tx)

    (defn accounts []
    (q '[:find ?c ?n ?b :where [?c :account/name ?n] [?c :account/balance ?b]] (db conn)))
    (d/q '[:find ?c ?n ?b ?count :where [?c :account/name ?n] [?c :account/balance ?b] [?c :account/transaction-count ?count]] (db conn)))

    (defn history
    ([] (q '[:find ?tx ?f ?t ?a ?n ?when :in $ :where [?from :account/name ?f] [?to :account/name ?t] [?tx :ot/from ?from] [?tx :ot/to ?to] [?tx :ot/amount ?a] [?tx :ot/note ?n] [?tx :db/txInstant ?when]] (db conn)))
    @@ -32,6 +32,8 @@
    (d/transact conn [[:transfer from to amount]
    [:db/add from :account/transactions txid]
    [:db/add to :account/transactions txid]
    [:inc from :account/transaction-count 1]
    [:inc to :account/transaction-count 1]
    {:db/id txid, :ot/note note :ot/from from :ot/to to :ot/amount amount}])))

    (defn credit [ to amount ]
    @@ -42,10 +44,7 @@
    (def bob (first (first (q '[:find ?e :where [?e :account/name "bob"]] (db conn)))))
    (def alice (first (first (q '[:find ?e :where [?e :account/name "alice"]] (db conn)))))

    (println "Accounts pre")
    (prn (accounts))
    ;; #<HashSet [[17592186045420 "issuer" 0M], [17592186045422 "alice" 0M], [17592186045421 "bob" 0M]]>

    (transfer issuer alice 77M "Issuance to Alice")
    (transfer issuer bob 23M "Issuance to Bob")
    (transfer alice bob 7M "Tomatoes")
    @@ -56,6 +55,6 @@
    ;; [13194139534320 "issuer" "bob" 23M "Issuance to Bob" #inst "2012-05-08T14:35:25.256-00:00"],
    ;; [13194139534321 "alice" "bob" 7M "Tomatoes" #inst "2012-05-08T14:35:25.262-00:00"]]>

    (println "Accounts post")
    (println "Accounts")
    (prn (accounts))
    ;; #<HashSet [[17592186045422 "alice" 70M], [17592186045420 "issuer" -100M], [17592186045421 "bob" 30M]]>
    ;; #<HashSet [[17592186045421 "issuer" -100M 2], [17592186045423 "alice" 70M 2], [17592186045422 "bob" 30M 2]]>
    20 changes: 18 additions & 2 deletions schema.dtm
    Original file line number Diff line number Diff line change
    @@ -17,6 +17,13 @@
    :db/doc "The accounts balance"
    :db.install/_attribute :db.part/db}

    { :db/id #db/id[:db.part/db]
    :db/ident :account/transaction-count
    :db/cardinality :db.cardinality/one
    :db/valueType :db.type/long
    :db/doc "The amount of transactions made with account"
    :db.install/_attribute :db.part/db}

    { :db/id #db/id[:db.part/db]
    :db/ident :account/max-balance
    :db/cardinality :db.cardinality/one
    @@ -66,13 +73,22 @@
    :db/doc "Recipient"
    :db.install/_attribute :db.part/db}

    { :db/id #db/id [:db.part/user]
    :db/ident :inc
    :db/fn #db/fn { :lang "clojure"
    :params [db id attr amount]
    :code "(let [ e (datomic.api/entity db id)
    orig (attr e 0) ]
    [[:db/add id attr (+ orig amount) ]])"}}


    { :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)
    balance (+ (:account/balance e) amount) ]
    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\"))))" }}
  3. @pelle pelle created this gist May 8, 2012.
    61 changes: 61 additions & 0 deletions accounts.clj
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,61 @@
    (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 accounts []
    (q '[:find ?c ?n ?b :where [?c :account/name ?n] [?c :account/balance ?b]] (db conn)))

    (defn history
    ([] (q '[:find ?tx ?f ?t ?a ?n ?when :in $ :where [?from :account/name ?f] [?to :account/name ?t] [?tx :ot/from ?from] [?tx :ot/to ?to] [?tx :ot/amount ?a] [?tx :ot/note ?n] [?tx :db/txInstant ?when]] (db conn)))
    ([acct] (q '[:find ?tx ?f ?t ?a ?n ?when :in $ ?e :where [?from :account/name ?f] [?to :account/name ?t] [?tx :ot/from ?from] [?tx :ot/to ?to] [?tx :ot/amount ?a] [?tx :ot/note ?n] [?tx :db/txInstant ?when] [?e :account/transactions ?tx]] (db conn) acct) ))

    (defn transfer [ from to amount note]
    (let [txid (datomic.api/tempid :db.part/tx)]
    (d/transact conn [[:transfer from to amount]
    [:db/add from :account/transactions txid]
    [:db/add to :account/transactions txid]
    {:db/id txid, :ot/note note :ot/from from :ot/to to :ot/amount amount}])))

    (defn credit [ to amount ]
    (d/transact conn [[:credit to amount]]))


    (def issuer (first (first (q '[:find ?e :where [?e :account/name "issuer"]] (db conn)))))
    (def bob (first (first (q '[:find ?e :where [?e :account/name "bob"]] (db conn)))))
    (def alice (first (first (q '[:find ?e :where [?e :account/name "alice"]] (db conn)))))

    (println "Accounts pre")
    (prn (accounts))
    ;; #<HashSet [[17592186045420 "issuer" 0M], [17592186045422 "alice" 0M], [17592186045421 "bob" 0M]]>

    (transfer issuer alice 77M "Issuance to Alice")
    (transfer issuer bob 23M "Issuance to Bob")
    (transfer alice bob 7M "Tomatoes")

    (println "History")
    (prn (history))
    ;; #<HashSet [[13194139534319 "issuer" "alice" 77M "Issuance to Alice" #inst "2012-05-08T14:35:25.252-00:00"],
    ;; [13194139534320 "issuer" "bob" 23M "Issuance to Bob" #inst "2012-05-08T14:35:25.256-00:00"],
    ;; [13194139534321 "alice" "bob" 7M "Tomatoes" #inst "2012-05-08T14:35:25.262-00:00"]]>

    (println "Accounts post")
    (prn (accounts))
    ;; #<HashSet [[17592186045422 "alice" 70M], [17592186045420 "issuer" -100M], [17592186045421 "bob" 30M]]>
    5 changes: 5 additions & 0 deletions accounts.dtm
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    [
    {:db/id #db/id[:db.part/user -1000001], :account/name "issuer", :account/balance 0M, :account/max-balance 0M, :account/min-balance -1000M}
    {:db/id #db/id[:db.part/user -1000002], :account/name "bob", :account/balance 0M, :account/max-balance 100M, :account/min-balance 0M}
    {:db/id #db/id[:db.part/user -1000003], :account/name "alice", :account/balance 0M, :account/max-balance 100M, :account/min-balance 0M}
    ]
    86 changes: 86 additions & 0 deletions schema.dtm
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,86 @@
    [
    ;; 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/max-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 :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 :account/transactions
    :db/cardinality :db.cardinality/many
    :db/valueType :db.type/ref
    :db/doc "The accounts transactions"
    :db.install/_attribute :db.part/db}

    { :db/id #db/id[:db.part/db]
    :db/ident :ot/note
    :db/valueType :db.type/string
    :db/cardinality :db.cardinality/one
    :db/doc "An note about what was transfered"
    :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)
    balance (+ (:account/balance e) 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]]"}}
    ]