cldwalker / datomico

Use datomic with intention revealing names. Ease of use and sticking to datomic's principles are encouraged.
MIT License
49 stars 7 forks source link

NOTE: This library has not been used in production for some time and has practices that I would no longer recommend e.g. some of the querying practices.

Description

Use datomic with intention revealing names. Ease of use and sticking to datomic's principles are encouraged. Should work with datomic >= 0.8.3789.

Build Status

Install

Add to your project.clj:

[datomico "0.2.0"]

Usage

Let's build a datomic entity type and start datomic with build-schema and start:

(ns models.user)
(require '[datomico.core :as dc])

; Define a model's database key once and don't think of it again while
; interacting with your model
(def model-namespace :user)

; Build schemas easily without needing to think of partitions and a number of
; internal schema attributes. Basically you don't have to pore over
; http://docs.datomic.com/schema.html
(def schema (dc/build-schema model-namespace
  [[:username :string]
  [:password :string]]))

; Starting in a repl is easy;  a uri, db and connection will be auto-generated
(ns user)
(require '[datomico.core :as dc])
(dc/start {:dynamic-vars true
           :schemas [models.user/schema]})

Let's create, update, delete and query entity types with create, update, delete and find-all:

(ns models.user)
; creates several helper functions that scope your database interaction to the model.
(dc/create-model-fns model-namespace)

(create {:username "dodo" :password "bird"})
; => {:username "dodo" :password "bird" :id 1024053}
(find-all {:username "dodo"})
; => ({:username "dodo" :password "bird" :id 1024053})
(find-first {:password "bird"})
; => {:username "dodo" :password "bird" :id 1024053}

(update 1024053 {:username "big"})
(dc/delete 1024053)

Transacting Batch Data

To transact data in batches, datomico provides *-tx corollaries to fns in datomico.action and datomico.model. These fns generate transaction data which can be batched and transacted as needed.

For this example, assume we're in a url model that has string attributes :name and :desc and a many ref with :tags. Let's create a new entity that associates itself with two existing entities "clojure" and "database". We'll use create's corollary create-tx to do this

(ns models.url
  (:require [datomico.db :as db]
            [datomic.model :as model]))

(def model-namespace :url)
(def find-first (partial model/find-first model-namespace))
(def create-tx (partial model/create-tx model-namespace))

(let [input {:name "http://datomic.com" :desc "DESC" :tags ["clojure" "database"]}
      new-map (create-tx (dissoc input :tags))]
  (->> (:tags input)
       (map #(find-first {:name %}))
       (map #(create-tx {:id (:db/id new-map) :tags (:id %)}))
       (cons new-map)
       db/transact!))

Note that create-tx provides :db/id with a tempid you can use to associate it to other entities in the same transaction.

Dynamic Binding

For actual production code, it is not recommended to use :dynamic-vars with datomico.core/start. Instead, you should be more explicit about handling your *db* and *connection* as :dynamic-vars incurs a performance penalty in exchange for convenience. For example, a ring app should start datomic with:

(dc/start {:uri "datomic:mem://my-app"
           :schemas [models.user/schema]})

Then, your ring app should use datomico's ring middleware to explicitly define *db* and *connection* for the duration of a web request:

(-> app datomico.db/wrap-datomic)

Look at wrap-datomic's implementation if not in a ring context.

TODO

Bugs/Issues

Please report them on github.

License

See LICENSE.TXT

Credits

Links