We are all very jazzed at Relevance about [Datomic][datomic]. But so far, the people working on JVM projects have had all the fun, as Datomic's [peer capabilities][peer] are exposed as a JVM library.
That changed a few weeks ago when the [REST API for Datomic][rest_api] was announced. Suddenly, we have client access to Datomic from any language we choose. We quickly came up with a [Ruby client for Datomic][ruby_client] and an [edn data reader and writer for Ruby][edn-ruby]. ([edn][edn] is the data format Datomic uses for input and output, and by implementing edn in Ruby, we get to use native Ruby data to talk with Datomic.
Over the last two days, I've created a [sample application using Ruby and Datomic][sample-wiki]. It is a wiki which saves all its data in Datomic and can read the history of pages from there, using only a few simple queries. The really interesting part is how easily we were able to use Ruby data structures in the place of edn to make easy-to-manipulate and easy-to-read schemas and queries. Here's the DB schema in Ruby:
[{:"db/id" => id(:"db.part/db"),
:"db/ident" => :"page/name",
:"db/valueType" =>
:"db.type/string",
:"db/cardinality" =>
:"db.cardinality/one",
:"db/fulltext" => true,
:"db/doc"
=> "The name of a page.",
:"db/unique" =>
:"db.unique/value",
:"db/index" => true,
:"db.install/_attribute" => ":db.part/db"
},
{:"db/id" =>
id(:"db.part/db"),
:"db/ident" => :"page/text",
:"db/valueType" => :"db.type/string",
:"db/cardinality" =>
:"db.cardinality/one",
:"db/fulltext" => true,
:"db/doc"
=> "The text of a page.",
:"db.install/_attribute" =>
":db.part/db"
}]
There are only a few differences between edn and this: mainly, edn keywords allow more characters than Ruby symbol literals, so we have to quote the symbols in Ruby.
Queries are even more fun:
datomic.query(dbname, [:find, ~"?id", ~"?name",
~"?text",
:in, ~"$db", ~"?name",
:where,
[~"$db", ~"?id",
:"page/name", ~"?name"],
[~"$db", ~"?id", :"page/text",
~"?text"]],
:args => [{:"db/alias" => "example/wiki"},
name])
Given a variable name
, this will pull back the
DB id, name, and text for the page named that in Datomic. In this
data structure, you'll notice one thing that probably isn't
familiar to most Rubyists. The tilde (~
) is used as a unary
operator on strings to create edn symbols, which don't have an
analog in Ruby. This capability is added by edn-ruby
, our edn
reader and writer.
To see the entire history of a page, we can write this query:
datomic.query(dbname, [:find,
~"?v", ~"?time", ~"?tx",
:in, ~"$cur", ~"$hist", ~"?uniq-attr",
~"?uniq-val", ~"?hist-attr",
:where,
[~"$cur", ~"?e",
~"?uniq-attr", ~"?uniq-val"],
[~"$cur", ~"?tx",
~":db/txInstant", ~"?time"],
[~"$hist", ~"?e", ~"?hist-attr",
~"?v", ~"?tx", true]],
:args => [
{:"db/alias" =>
dbalias},
{:"db/alias" => dbalias, :history => true},
:"page/name",
name,
:"page/text"])
Is this
particularly Ruby-ish right now? No, not yet. Is it very cool? Yes,
definitely. Datomic queries are built out of data structures:
arrays, hashes, and other primitive data types, which allows us to
easily use map
, inject
, and other Enumerable
methods with
them. A significant source of power and concision in queries comes
from the use of not one, but two distinct data types for
identifiers: symbols and keywords. Most languages cannot come close
to this expressiveness, but Ruby handles it with ease. Ruby symbols
can act as both identifier types, with the sly introduction of a
unary operator "~" to distinguish them.
We have just begun to see what kind of amazing applications we can build with Datomic. Follow the [Ruby Datomic client][ruby_client] and [the sample Ruby/Datomic wiki][sample-wiki] on Github to see what we come up with next.
[peer]: http://docs.datomic.com/clojure/index.html [datomic]: http://www.datomic.com/ [rest_api]: http://docs.datomic.com/rest.html [ruby_client]: https://github.com/cldwalker/datomic-client [edn-ruby]: https://github.com/relevance/edn-ruby [edn]: https://github.com/edn-format/edn [sample-wiki]: https://github.com/crnixon/datomic-sinatra-wiki