Switch to Datomic for storage (not persistent)

- Still not taking advantage of Datomic features but using it as a
  simple dump for Clojure data structures.
This commit is contained in:
Nicholas Kariniemi 2015-07-03 23:16:53 +03:00
parent fdd9baf23b
commit c1e0dfbf0b
5 changed files with 135 additions and 45 deletions

53
database_schema.edn Normal file
View file

@ -0,0 +1,53 @@
[
;; grubs
{:db/id #db/id[:db.part/db]
:db/ident :grub/id
:db/valueType :db.type/keyword
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity
:db/doc "Grub ID"
:db.install/_attribute :db.part/db}
{:db/id #db/id[:db.part/db]
:db/ident :grub/text
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true
:db/doc "The text of a grub item e.g. '3 apples'"
:db.install/_attribute :db.part/db}
{:db/id #db/id[:db.part/db]
:db/ident :grub/completed
:db/valueType :db.type/boolean
:db/cardinality :db.cardinality/one
:db/doc "The status of a grub item (completed or not completed)"
:db.install/_attribute :db.part/db}
;; recipes
{:db/id #db/id[:db.part/db]
:db/ident :recipe/id
:db/valueType :db.type/keyword
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity
:db/doc "Recipe ID"
:db.install/_attribute :db.part/db}
{:db/id #db/id[:db.part/db]
:db/ident :recipe/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true
:db/doc "Recipe name"
:db.install/_attribute :db.part/db}
{:db/id #db/id[:db.part/db]
:db/ident :recipe/grubs
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true
:db/doc "Recipe ingredients"
:db.install/_attribute :db.part/db}
{:db/id #db/id[:db.part/db]
:db/ident :recipe/directions
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true
:db/doc "Directions for making a recipe"
:db.install/_attribute :db.part/db}
]

View file

@ -16,7 +16,10 @@
[sablono "0.3.4"]
[cljs-uuid "0.0.4"]
[com.cognitect/transit-clj "0.8.275"]
[com.cognitect/transit-cljs "0.8.220"]]
[com.cognitect/transit-cljs "0.8.220"]
[com.datomic/datomic-pro "0.9.5173" :exclusions [com.fasterxml.jackson.core/jackson-annotations]]]
:repositories {"my.datomic.com" {:url "https://my.datomic.com/repo"
:creds :gpg}}
:profiles {:uberjar {:aot :all}
:dev {:source-paths ["dev"]
:dependencies [[org.clojure/tools.namespace "0.2.10"]

View file

@ -39,8 +39,7 @@
(def prod-system
{:index prod-index-page
:db-name "grub"
:db nil
:database-uri "datomic:mem://grub"
:db-conn nil
:port 3000
:stop-server nil
@ -48,8 +47,7 @@
(def dev-system
{:index dev-index-page
:db-name "grub-dev"
:db nil
:database-uri "datomic:mem://grub"
:db-conn nil
:port 3000
:stop-server nil
@ -92,22 +90,21 @@
(handle-websocket states new-states-pub)
(wrap-bounce-favicon)))
(defn start [{:keys [port db-name states] :as system}]
(let [{:keys [db conn]} (db/connect db-name)
(defn start [{:keys [port database-uri states] :as system}]
(let [db-conn (db/connect database-uri)
new-states (chan)
new-states-pub (a/pub new-states (fn [_] :new-state))
db-state (db/get-current-state db)
db-state (db/get-current-state db-conn)
_ (reset! states (state/new-states (if db-state db-state state/empty-state)))
stop-server (httpkit/run-server (make-handler system new-states-pub) {:port port})]
(add-watch states :db (fn [_ _ old new]
(when-not (= old new)
(let [new-state (state/get-latest new)]
(a/put! new-states new-state)
(db/update-db! db new-state)))))
(db/update-db! db-conn new-state)))))
(println "Started server on localhost:" port)
(assoc system
:db db
:db-conn conn
:db-conn db-conn
:stop-server stop-server
:states states)))
@ -149,7 +146,7 @@
(println "options:" options)
(cond
(:help options) (exit 0 (usage summary))
(not= (count arguments) 1) (exit 1 (usage summary))
(not= (count arguments) 2) (exit 1 (usage summary))
errors (exit 1 (error-msg errors)))
(case (first arguments)
"development" (start (merge dev-system options))

View file

@ -1,34 +1,71 @@
(ns grub.db
(:require [datomic.api :as d :refer [q db]]
clojure.pprint
[monger.core :as m]
[monger.collection :as mc]
[clojure.core.async :as a :refer [<! >! chan go]]))
(:require [datomic.api :as d]
[clojure.core.async :as a :refer [<! >! chan go]]
[grub.util :as util]))
;(def uri "datomic:mem://seattle")
;(d/create-database uri)
(def schema-tx (read-string (slurp "database_schema.edn")))
(defn create-db [uri]
(d/create-database uri)
(let [conn (d/connect uri)]
@(d/transact conn schema-tx)))
(def collection "grub-lists")
(defn connect [uri]
(create-db uri)
(let [conn (d/connect uri)]
(println "Connected to datomic at " uri)
conn))
(defn clear-all [db]
(mc/drop db collection))
(defn map-keys [key-maps coll]
(reduce (fn [new-coll [key new-key]] (assoc new-coll new-key (get coll key))) {} key-maps))
(defn update-db! [db state]
(mc/drop db collection)
(mc/insert db collection state))
(defn get-current-state [conn]
(let [db (d/db conn)
get-entity (fn [[id]] (d/touch (d/entity db id)))
grub-ids (d/q '[:find ?g :where [?g :grub/id]] (d/db conn))
map-grub-keys #(map-keys {:grub/id :id :grub/text :text :grub/completed :completed} %)
grubs (->> grub-ids
(map (comp map-grub-keys get-entity))
vec
(util/map-by-key :id))
recipe-ids (d/q '[:find ?r :where [?r :recipe/id]] (d/db conn))
map-recipe-keys #(map-keys {:recipe/id :id :recipe/name :name :recipe/grubs :grubs :recipe/directions :directions} %)
recipes (->> recipe-ids
(map (comp map-recipe-keys get-entity))
vec
(util/map-by-key :id))]
{:grubs grubs
:recipes recipes}))
(defn get-current-state [db]
(let [state (first (mc/find-maps db collection))]
(when state
(dissoc state :_id))))
(defn grub-tx [grub]
[{:db/id (d/tempid :db.part/user)
:grub/id (:id grub)
:grub/text (:text grub)
:grub/completed (:completed grub)}])
(defn connect [db-name]
(let [conn (m/connect)
db (m/get-db conn db-name)]
(println "Connected to mongo at localhost:" db-name)
{:conn conn
:db db}))
(defn recipe-tx [recipe]
[{:db/id (d/tempid :db.part/user)
:recipe/id (:id recipe)
:recipe/name (:name recipe)
:recipe/grubs (:grubs recipe)
:recipe/directions (:directions recipe)}])
(defn update-db! [conn state]
(let [grubs-tx (->> state
:grubs
(vals)
(map grub-tx)
(flatten)
(vec))
recipes-tx (->> state
:recipes
(vals)
(map recipe-tx)
(flatten)
(vec))
tx (into grubs-tx recipes-tx)]
@(d/transact conn tx)))
(defn disconnect [conn]
(m/disconnect conn))
(d/release conn))

View file

@ -1,8 +1,8 @@
(ns grub.util
(:require #+clj [clojure.core.async :as a :refer [<! >! chan go]]
#+cljs [cljs.core.async :as a :refer [<! >! chan]])
#+cljs (:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go]]))
(:require #?(:clj [clojure.core.async :as a :refer [<! >! chan go]]
:cljs [cljs.core.async :as a :refer [<! >! chan]]))
#?(:cljs (:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go]])))
(defn map-by-key [key coll]
(->> coll
@ -11,11 +11,11 @@
(defn printer []
(let [in (chan)]
(go (loop []
(go (loop []
(when-let [msg (<! printer)]
#+clj (clojure.pprint/pprint msg)
#+clj (println "-------")
#+cljs (logs msg)
#+cljs (log "-------")
#?(:clj (do (clojure.pprint/pprint msg)
(println "-------"))
:cljs (do (logs msg)
(log "-------")) )
(recur))))))