It's a start
EPL-1.0 License
Here lies my experience of working through the Datomic tutorial.
My current system is a 2014 iMac, running OSX Yosemite (10.10). I plan to do the tutorial using Clojure, which is still very much a foreign language to me at present. I'm about half way through reading "The Joy of Clojure", as I write this.
Was a little confused by the distinction between the "free" and "Pro Starter" editions of Datomic, but the Getting Started guide recommended "Pro Starter", so I registered at my.datomic.com — which requires specifying an organisation — verified my email address and then downloaded the zip file.
A twelve month license key for my account was received in a subsequent email.
Unzipped the datomic zip file (version datomic-pro-0.9.5173) and checked out the ./bin and ./config/samples directories.
Tried to run maven-install
to get the "peer library" installed locally, but discovered that I don't have Maven on my system.
Installed Maven via Homebrew;
brew install maven
Running ./bin maven-install
revealed problems with the Java installation on this machine:
[datomic-pro-0.9.5173]$ ./bin/maven-install
Installing datomic-pro-0.9.5173 in local maven repository...
Exception in thread "main" java.lang.UnsupportedClassVersionError: org/apache/maven/cli/MavenCli : Unsupported major.minor version 51.0
at java.lang.ClassLoader.defineClass1(Native Method)
...
Checked the JDK version and found this:
[datomic-pro-0.9.5173]$ java -version
java version "1.6.0_65"
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-466.1-11M4716)
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-466.1, mixed mode)
Looks like this is JDK 6. Datomic requires JDK 7 or 8, so downloaded and installed JDK 8 from the Oracle website. After that was done, I tried running the ./bin/maven-install
command, which worked.
The 'Getting Started' guide was a little confusing: Instructions are given for configuring both Maven and Leiningen, but it's unclear whether I need to setup both if I'm only going to be using Leiningen.
It seems that the ./bin/maven-install
part was just to install the "peer library" locally, however, in order to use the peer library as a project dependency, it's necessary to configure Maven (pom.xml
) or Leiningen (project.clj
) to use my.datomic.com login credentials.
I'm going to try doing both: The Maven install has already completed, so in theory I should be able to use the peer library locally. I'm setting up a separate Leiningen project (this repo) to use my Datomic credentials as well, so it could potentially be deployed to a standalone "production" system.
Read a bit of the Leiningen Tutorial and created a project with lein new app datomic-tutorial
. Nice and easy.
Appreciated this line from the tutorial:
If you come from the Java world, Leiningen could be thought of as "Maven meets Ant without the pain".
Hah!
Setting up Leiningen to read GPG credentials. Initially installed gpg
and gpg-agent
with homebrew. Then read the Lein GPG guide to get help on generating suitable keypairs.
GPG keys generated. Encrypted the ~/.lein/credentials.clj
settings file, which holds the username and password credentials for my.datomic.com.
Added a dependency on Datomic in the project.clj file:
:dependencies [
[org.clojure/clojure "1.6.0"]
[com.datomic/datomic-pro "0.9.5067"]
]
Had some problems with the GPG authentication when starting lein repl
. Apparently one needs to run the gpg-agent
and specify the use-agent
configuration option in ~/.gnupg/gpg.conf
.
Weird. This strange incantation seemed to allow me to decrypt the credentials file:
killall ssh-agent gpg-agent
unset GPG_AGENT_INFO SSH_AGENT_PID SSH_AUTH_SOCK
eval $(gpg-agent --daemon --enable-ssh-support)
Next problem, dowloading the the datomic dependencies via lein threw this error:
Could not transfer artifact com.datomic:datomic-pro:pom:0.9.5067 from/to my.datomic.com (https://my.datomic.com/repo): Not authorized , ReasonPhrase:Unauthorized.
Logged in to my.datomic.com to check my password, which works for logging into the website, but also found a note saying that passcode for downloading the datomic dependencies automatically is different. Updated ~/.lein/credentials.clj
and re-encrypted, like so:
gpg --default-recipient-self -e \
~/.lein/credentials.clj > ~/.lein/credentials.clj.gpg
And running lein repl
this time seemed to work:
Retrieving com/datomic/datomic-pro/0.9.5067/datomic-pro-0.9.5067.pom from my.datomic.com
Back to the tutorial...
Note: It seems to be necessary to run the gpg-agent
in a terminal session and then run lein repl
in the same shell.
This will mostly be a straight translation of the tutorial code, with comments where things aren't obvious.
;; Import the Datomic Peer library (usable as 'Peer' from here on)
(import datomic.Peer)
;; Create the in memory database, calling the static class method Peer#createDatabase
(def uri "datomic:mem://hello")
(Peer/createDatabase uri)
;; Create a "connection" to the database
(def conn (Peer/connect uri))
;; Create a datom to describe the first bits of data we're entering into the DB:
(def datom ["db/add" (Peer/tempid "db.part/user") "db/doc" "hello world"])
;; Pass the datom to the transactor via the connection (note the 'vector of vectors' for the datom):
(def resp (.transact conn [datom]))
;; currently stuck here:
datomic-tutorial.core=> (Peer/query "[:find ?entity :where [?entity :db/doc \"hello world\"]]" db)
ClassCastException datomic.db.Db cannot be cast to [Ljava.lang.Object;
Trying a different tack: Checking out the equivalent functionality of the Datomic Clojure API. Now how to use it in the repl...
;; use the Datomic native Clojure library
datomic-tutorial.core=> (require 'datomic.api)
nil
;; a basic in memory datomic database, called 'hello'
datomic-tutorial.core=> (def uri "datomic:mem://hello")
#'datomic-tutorial.core/uri
;; ... is created
datomic-tutorial.core=> (datomic.api/create-database uri)
true
;; connect to the database
datomic-tutorial.core=> (def conn (datomic.api/connect uri))
#'datomic-tutorial.core/conn
;; a datom "adds a fact, about a new entity with this temporary id, and asserts that the attribute db/doc has the value hello world"
datomic-tutorial.core=> (def datom ["db/add" (datomic.api/tempid "db.part/user") "db/doc" "hello world"])
#'datomic-tutorial.core/datom
;; commit the fact via the Datomic transactor
datomic-tutorial.core=> (def resp (datomic.api/transact conn [datom]))
#'datomic-tutorial.core/resp
datomic-tutorial.core=> resp
#<promise$settable_future$reify__5376@b443e92: {:db-before datomic.db.Db@b515b169, :db-after datomic.db.Db@f20dfecc, :tx-data [#datom[13194139534312 50 #inst "2015-06-02T12:23:58.409-00:00" 13194139534312 true] #datom[17592186045417 62 "hello world" 13194139534312 true]], :tempids {-9223350046623220288 17592186045417}}>
;; this query "finds entities where we specify entities as an entity has the attribute db/doc with value hello world"
datomic-tutorial.core=> (def query "[:find ?entity :where [?entity :db/doc \"hello world\"]]")
#'datomic-tutorial.core/query
;; run the query against the 'db' snapshot as input, which we get from the connection
datomic-tutorial.core=> (def result (datomic.api/q query (datomic.api/db conn)))
#'datomic-tutorial.core/result
;; one result; the one we added
datomic-tutorial.core=> result
#{[17592186045417]}
Onwards and upwards to the tutorial proper...