diametric 0.1.1-java → 0.1.2-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +627 -179
- data/ext/diametric/DiametricCollection.java +696 -66
- data/ext/diametric/DiametricCommon.java +165 -0
- data/ext/diametric/DiametricDatabase.java +10 -3
- data/ext/diametric/DiametricPeer.java +12 -4
- data/ext/diametric/DiametricService.java +35 -2
- data/ext/diametric/DiametricSet.java +270 -0
- data/ext/diametric/DiametricUtils.java +52 -10
- data/lib/diametric/entity.rb +7 -5
- data/lib/diametric/persistence/common.rb +3 -3
- data/lib/diametric/persistence/peer.rb +3 -12
- data/lib/diametric/persistence/rest.rb +1 -1
- data/lib/diametric/query.rb +26 -14
- data/lib/diametric/version.rb +1 -1
- data/lib/diametric_service.jar +0 -0
- data/spec/developer_query_spec.rb +3 -3
- data/spec/diametric/peer_api_spec.rb +425 -8
- data/spec/diametric/persistence/peer_many2many_spec.rb +10 -9
- data/spec/parent_child_sample.rb +21 -16
- data/spec/peer_integration_spec.rb +43 -9
- data/spec/support/entities.rb +2 -0
- metadata +24 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 003a4465b11cc5367d54854afa10ee49174009da
|
4
|
+
data.tar.gz: e18bf5c1010b430be842eb4761ae266c9c7c320c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01275e6f3572c97559ca8a1592f87dea5b9494d455b64fc1131f1e57fd649542ee2b09590121d4b64cec89791009852951559712e23d5a62e61637df2dbb385d
|
7
|
+
data.tar.gz: 6af0a5a63dc06d64ee2c7c5f44330bd261def83c770d696822000cd94b19b2fe776dcc6b9a0909033c988bdb64971e7e55a03bdea91665a8b0c4b37a70f629f3
|
data/README.md
CHANGED
@@ -2,241 +2,689 @@
|
|
2
2
|
|
3
3
|
# Diametric
|
4
4
|
|
5
|
-
Diametric is
|
6
|
-
|
5
|
+
Diametric is ActiveRecord style wrapper for [Datomic](http://www.datomic.com/),
|
6
|
+
and a library for building schemas, queries, and transactions
|
7
|
+
for Datomic from Ruby objects. It is also used to map Ruby objects
|
7
8
|
as entities into a Datomic database.
|
8
9
|
|
9
|
-
## Thanks
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
Diametric supports both CRuby and JRuby.
|
12
|
+
When you are on CRuby, you can use Datomic's REST service.
|
13
|
+
While you are creating data and making queries, Diametric is poking around Datomic's REST API.
|
14
|
+
When you are on JRuby, you can use Datomic's REST and Peer services.
|
15
|
+
If you are using Peer service, Diametric is poking Datomic's clojure API and some clojure functions.
|
16
|
+
|
17
|
+
|
18
|
+
For Rubyists who are familiar with object oriented programming, Diametric converts:
|
19
|
+
- Ruby model definition to Datomic schema
|
20
|
+
- Creating, saving and updating model data to Datomic transaction data
|
21
|
+
- Ruby query to Datomic's datalog query
|
22
|
+
|
23
|
+
|
24
|
+
The biggest difference between REST and Peer services is available database types.
|
25
|
+
REST service can use memory database only, which means you can't save data in any file.
|
26
|
+
While Peer service can use memory and free version of transactor,
|
27
|
+
which means you can save data in a file through Datomic's transactor.
|
28
|
+
Other than that, Peer service has excellent API to use Datomic's various features.
|
14
29
|
|
15
|
-
Special thanks to Mongoid for writing some solid ORM code that was liberally borrowed from to add Rails support to Diametric.
|
16
30
|
|
17
31
|
## Documents
|
18
32
|
|
19
33
|
Other than highlights below, there are documents on Wiki.
|
20
34
|
|
21
|
-
- [
|
35
|
+
- [Entity API](https://github.com/relevance/diametric/wiki/Entity-API)
|
36
|
+
- [Query API](https://github.com/relevance/diametric/wiki/Query-API)
|
37
|
+
- [Persistence API](https://github.com/relevance/diametric/wiki/Persistence-API)
|
22
38
|
- [Rails Integration](https://github.com/relevance/diametric/wiki/Rails-Integration-%28Experimental%29)
|
23
39
|
- [Seattle Example](https://github.com/relevance/diametric/wiki/Seattle-Example)
|
24
40
|
|
25
41
|
|
26
|
-
|
42
|
+
This document shows how to use Diametric with simple examples.
|
43
|
+
|
44
|
+
## Installation
|
45
|
+
|
46
|
+
```bash
|
47
|
+
gem install diametric
|
48
|
+
```
|
49
|
+
|
50
|
+
Diametric is a multi-platoform gem. On CRuby, above installs CRuby version of gem.
|
51
|
+
On JRuby, it is JRuby version of gem with Java extension.
|
52
|
+
|
53
|
+
|
54
|
+
[note]
|
55
|
+
When you install diametric on JRuby, you'll see a message, "Building native extensions. This could take a while...."
|
56
|
+
Although Diametric doesn't rely on C library, it depends on a several Java libraries.
|
57
|
+
While installing gem, diametric downloads thoes jar archives including dependencies on your local maven repo.
|
58
|
+
The message shows up because of this.
|
59
|
+
|
60
|
+
|
61
|
+
## Preparation for CRuby
|
62
|
+
|
63
|
+
On CRuby, you need to start Datomic's REST server.
|
64
|
+
|
65
|
+
Diametric has a command to start REST server.
|
66
|
+
Type `datomic-rest -p port -a db_alias -u uri`, for example
|
67
|
+
|
68
|
+
```
|
69
|
+
datomc-rest -p 9000 -a free -u datomic:mem://
|
70
|
+
```
|
71
|
+
|
72
|
+
When you run the command at the very first time, it takes long to start running.
|
73
|
+
This is because Diametric downloads datomic if it doesn't exist in Diametric's directory tree.
|
74
|
+
If it is the second time or later, the REST server starts quickly.
|
75
|
+
|
76
|
+
To learn more about the options in the above command, please go to [http://docs.datomic.com/rest.html](http://docs.datomic.com/rest.html).
|
77
|
+
|
78
|
+
Once the REST server starts running, go to [localhost:9000](localhost:9000) on your browser.
|
79
|
+
You can see Datomic's REST service is running.
|
80
|
+
|
81
|
+
Alternatively, you can download Datomic archive from [http://downloads.datomic.com/free.htm](http://downloads.datomic.com/free.html),
|
82
|
+
and start REST server using Datomic command, `script/datomic-rest -p 9000 free datomic:mem://`
|
83
|
+
|
84
|
+
|
85
|
+
## Preparation for JRuby
|
86
|
+
|
87
|
+
You have 2 choices on JRuby, Peer and REST service.
|
88
|
+
Peer service works on the same JVM as JRuby and is just a Java library for JRuby.
|
89
|
+
|
27
90
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
91
|
+
When you choose REST service, follow *Preparation for CRuby* section.
|
92
|
+
|
93
|
+
|
94
|
+
When you choose Peer service, you don't need to prepare anything.
|
95
|
+
You even don't need to start Datomic. Diametric does everything for you.
|
96
|
+
What you need to do is just coding in Ruby using Diametric API.
|
97
|
+
|
98
|
+
|
99
|
+
Although Diametric API makes coding very easy, you still have a freedom to hit Datomic's Java API directly.
|
100
|
+
|
101
|
+
|
102
|
+
## Typical coding steps
|
103
|
+
|
104
|
+
Typical Diametric coding takes steps below:
|
105
|
+
|
106
|
+
1. Connect to Datomic
|
107
|
+
2. Define entities
|
108
|
+
3. Create schema on Datomic from entity definitions
|
109
|
+
4. Create entities and save it on Datomic
|
110
|
+
5. Make a query to Datomic
|
111
|
+
|
112
|
+
|
113
|
+
## Connect to the Datomic REST Service
|
114
|
+
|
115
|
+
To establish connection to Datomic REST Service, you can do as in below:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
require 'diametric'
|
119
|
+
Diametric::Persistence.establish_base_connection({:uri => 'http://localhost:9000', :storage => 'free', :database => 'sample'})
|
120
|
+
```
|
121
|
+
|
122
|
+
Optionally, you can connect using datomic-clinet gem:
|
123
|
+
```ruby
|
124
|
+
require 'datomic/client'
|
125
|
+
|
126
|
+
@datomic_uri = 'http://localhost:9000'
|
127
|
+
@storage = 'free'
|
128
|
+
@dbname = "sample"
|
129
|
+
@client = Datomic::Client.new @datomic_uri, @storage
|
130
|
+
@client.create_database(@dbname)
|
131
|
+
```
|
132
|
+
|
133
|
+
Each parameter should be consistent to the ones used to start REST service.
|
134
|
+
|
135
|
+
|
136
|
+
## Connect to the Datomic Peer service (JRuby only)
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
require 'diametric'
|
140
|
+
Diametric::Persistence.establish_base_connection({:uri=>'datomic:mem://sample'})
|
141
|
+
```
|
142
|
+
|
143
|
+
Or in a peer service specific way,
|
144
|
+
```ruby
|
145
|
+
datomic_uri = "datomic:mem://sample-#{SecureRandom.uuid}"
|
146
|
+
@conn = Diametric::Persistence::Peer.connect(datomic_uri)
|
147
|
+
```
|
148
|
+
|
149
|
+
## Define an entity
|
150
|
+
|
151
|
+
While a relational databse inserts a record, Diametric saves `Entity`.
|
152
|
+
To save data by Diametric, you need to define `Entity` first.
|
153
|
+
Defining entities loosely corresponds to defining database tables.
|
154
|
+
|
155
|
+
Below is an example of `Entity` definition for Peer service.
|
156
|
+
Be aware, you should include `Diametric::Entity` and
|
157
|
+
either one of `Diametric::Persistence::REST` or `Diametric::Persistence::Peer`in your Entity definition.
|
35
158
|
|
36
|
-
They do not include all `ActiveModel` modules by default, only the
|
37
|
-
ones needed to establish compliance. You may want to include others
|
38
|
-
yourself, such as `Validations` or `Callbacks`.
|
39
159
|
|
40
160
|
```ruby
|
41
161
|
require 'diametric'
|
42
162
|
|
43
163
|
class Person
|
44
164
|
include Diametric::Entity
|
165
|
+
include Diametric::Persistence::Peer
|
45
166
|
|
46
|
-
attribute :name, String, :
|
47
|
-
attribute :email, String, :cardinality => :many
|
167
|
+
attribute :name, String, index: true
|
48
168
|
attribute :birthday, DateTime
|
49
|
-
attribute :
|
169
|
+
attribute :awesomeness, Boolean, doc: "Is this person awesome?"
|
50
170
|
end
|
171
|
+
```
|
172
|
+
|
173
|
+
The `attribute` definition is consists of:
|
174
|
+
- attribute name (required)
|
175
|
+
- attribute type (required)
|
176
|
+
- attribute options (optional)
|
177
|
+
|
178
|
+
This is a Diametric way of converting [Datomic schema](http://docs.datomic.com/schema.html)
|
179
|
+
to Ruby model definition.
|
180
|
+
|
181
|
+
The attribute name goes to `:db/ident` and the value will be `:<class name>/<attribute name>`.
|
182
|
+
This is interpreted as `:<namespace>/<name>` on Datomic.
|
183
|
+
|
184
|
+
Currently, Diametric supports following data types:
|
185
|
+
|
186
|
+
| Diametric | Datomic |
|
187
|
+
| ---------- |:------------------:|
|
188
|
+
| String | `:db.type/string` |
|
189
|
+
| Symbol | `:db.type/keyword` |
|
190
|
+
| Boolean | `:db.type/boolean` |
|
191
|
+
| Integer | `:db.type/long` |
|
192
|
+
| Float | `:db.type/double` |
|
193
|
+
| Double | `:db.type/double` |
|
194
|
+
| BigDecimal | `:db.type/bigdec` |
|
195
|
+
| Ref | `:db.type/ref` |
|
196
|
+
| DateTime | `:db.type/instant` |
|
197
|
+
| UUID | `:db.type/uuid` |
|
198
|
+
|
199
|
+
In addition, Diametric supports `Enum` type.
|
200
|
+
|
201
|
+
Avaialbe options are:
|
202
|
+
|
203
|
+
| Diametric | Datomic |
|
204
|
+
| -------------------------------- |:----------------------:|
|
205
|
+
| `:cardinality => :one` (default) | `:db.cardinality/one` |
|
206
|
+
| `:cardinality => :many` | `:db.cardinality/many` |
|
207
|
+
| `:doc => "some document here"` | `:db/doc` |
|
208
|
+
| `:unique => :identity` | `:db.unique/identity` |
|
209
|
+
| `:unique => :value` | `:db.unique/value` |
|
210
|
+
| `:index => true` | `:db/index` |
|
211
|
+
| `:fulltext => true` | `:db/fulltext` |
|
212
|
+
|
51
213
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# :db/index true
|
61
|
-
# :db.install/_attribute :db.part/db}
|
62
|
-
# {:db/id #db/id[:db.part/db]
|
63
|
-
# :db/ident :person/email
|
64
|
-
# :db/valueType :db.type/string
|
65
|
-
# :db/cardinality :db.cardinality/many
|
66
|
-
# :db.install/_attribute :db.part/db}
|
67
|
-
# {:db/id #db/id[:db.part/db]
|
68
|
-
# :db/ident :person/birthday
|
69
|
-
# :db/valueType :db.type/instant
|
70
|
-
# :db/cardinality :db.cardinality/one
|
71
|
-
# :db.install/_attribute :db.part/db}
|
72
|
-
# {:db/id #db/id[:db.part/db]
|
73
|
-
# :db/ident :person/iq
|
74
|
-
# :db/valueType :db.type/long
|
75
|
-
# :db/cardinality :db.cardinality/one
|
76
|
-
# :db.install/_attribute :db.part/db}]
|
77
|
-
|
78
|
-
# To check what attributes are in Person model:
|
79
|
-
Person.attributes.keys
|
80
|
-
# [:dbid, :name, :email, :birthday, :iq]
|
81
|
-
|
82
|
-
# To create an instance from a query result
|
83
|
-
person = Person.new(Hash[*(Person.attributes.zip(results_from_query).flatten)])
|
84
|
-
# or
|
85
|
-
person = Person.from_query(results_from_query)
|
86
|
-
|
87
|
-
# To update/set values of a model:
|
88
|
-
person.iq = 180
|
89
|
-
|
90
|
-
# Below will help to understand what transaction data will be produced:
|
91
|
-
person.tx_data(:iq)
|
92
|
-
|
93
|
-
# It will be mapped to the Datomic transaction data:
|
94
|
-
# [{:db/id person.dbid
|
95
|
-
# :person/iq 180}]
|
96
|
-
|
97
|
-
# To create new model instance:
|
98
|
-
person = Person.new(:name => "Peanut")
|
99
|
-
person.tx_data
|
100
|
-
# Datomic transaction data will be:
|
101
|
-
# [{:db/id #db/id[:db.part/user]
|
102
|
-
# :person/name "Peanut"}]
|
103
|
-
```
|
104
|
-
|
105
|
-
In addition to `attribute`, Diametric supports `enum` type. This is often used on Datomic.
|
106
|
-
For details, see [Seattle Example](https://github.com/relevance/diametric/wiki/Seattle-Example)
|
107
|
-
|
108
|
-
## Query API
|
109
|
-
|
110
|
-
The query API is used for generating Datomic queries, whether to send via an external client or via the persistence API. The two methods used to generate a query are `.where` and `.filter`, both of which are chainable.
|
111
|
-
|
112
|
-
To get query data and args for a query, call `.data` on a `Query`.
|
113
|
-
|
114
|
-
If you are using a persistence API, you can ask `Query` to get the results of a Datomic query. `Diametric::Query` is an `Enumerable`. To get the results of a query, use `Enumerable` methods such as `.each` or `.first`. `Query` also provides a `.all` method to run the query and get the results.
|
115
|
-
|
116
|
-
```ruby
|
117
|
-
query = Datomic::Query.new(Person).where(:name => "Clinton Dreisbach")
|
118
|
-
query.data
|
119
|
-
# Datomic query:
|
120
|
-
# [:find ?e ?name ?email ?birthday ?iq ?website
|
121
|
-
# :from $ ?name
|
122
|
-
# :where [?e :person/name ?name]
|
123
|
-
# [?e :person/email ?email]
|
124
|
-
# [?e :person/birthday ?birthday]
|
125
|
-
# [?e :person/iq ?iq]
|
126
|
-
# [?e :person/website ?website]]
|
127
|
-
# Args:
|
128
|
-
# ["Clinton Dreisbach"]
|
129
|
-
#
|
130
|
-
# Returns as an array, [query, args].
|
131
|
-
|
132
|
-
query = Datomic::Query.new(Person).where(:name => "Clinton Dreisbach").filter(:>, :iq, 150)
|
133
|
-
query.data
|
134
|
-
# Datomic query:
|
135
|
-
# [:find ?e ?name ?email ?birthday ?iq ?website
|
136
|
-
# :from $ ?name
|
137
|
-
# :where [?e :person/name ?name]
|
138
|
-
# [?e :person/email ?email]
|
139
|
-
# [?e :person/birthday ?birthday]
|
140
|
-
# [?e :person/iq ?iq]
|
141
|
-
# [?e :person/website ?website]
|
142
|
-
# [> ?iq 150]
|
143
|
-
# Args:
|
144
|
-
# ["Clinton Dreisbach"]
|
145
|
-
#
|
146
|
-
# Returns as an array, [query, args].
|
147
|
-
```
|
148
|
-
|
149
|
-
## Persistence API
|
150
|
-
|
151
|
-
The persistence API comes in two flavors: REST- and Peer-based.
|
152
|
-
Although REST-bases API supports CRUD operations like a legacy RDBMS,
|
153
|
-
it is a subset of Peer-based API.
|
154
|
-
|
155
|
-
The suitable persistent module will be selected based on URI to connect datomic.
|
156
|
-
You don't need to care which module should be included.
|
157
|
-
However, if you like to inlcude REST or Peer module explicitely, you can write it.
|
158
|
-
|
159
|
-
|
160
|
-
### Peer
|
161
|
-
|
162
|
-
With Peer connection, you can create objects that know how to store themselves to Datomic through a Datomic Peer.
|
163
|
-
|
164
|
-
Peer connection as well as "require `diametric/persistence/peer`" are only available on JRuby.
|
165
|
-
When you install the `diametric` gem with JRuby, all `.jar` files needed to run Datomic will be downloaded.
|
214
|
+
## Create a schema
|
215
|
+
|
216
|
+
Creating schema on Datomic is similar to a migration on Ruby on Rails.
|
217
|
+
However, schema creation can be done as a part of a program.
|
218
|
+
For Datomic, the difference between schema and entity is quite small.
|
219
|
+
Like saving entities, schemas are saved in Datomic.
|
220
|
+
|
221
|
+
Creating schema is done by calling `create_schema` method of the entity.
|
166
222
|
|
167
223
|
```ruby
|
168
|
-
|
169
|
-
|
224
|
+
Person.create_schema.get
|
225
|
+
```
|
226
|
+
|
227
|
+
`Person.create_schema` (not followed by get) also creates a schema,
|
228
|
+
but it is a sort of piling up on the queue and not performed immediately.
|
229
|
+
If `get` is not specified, schema creation is done by at some point implicitly.
|
230
|
+
Adding `get` requests Datomic to create schema *now*.
|
231
|
+
This is good to know whether the entity definition is correct or not.
|
232
|
+
If some attribute definition has a problem, error will be raised.
|
233
|
+
|
234
|
+
|
235
|
+
## Create, save and update an entity
|
236
|
+
|
237
|
+
Once the entity is defined, and schema is created on Datomic,
|
238
|
+
you can create an entity instance like you create a new object in Ruby.
|
239
|
+
Below is an example:
|
240
|
+
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
person = Person.new
|
244
|
+
person.name = "Sky"
|
245
|
+
person.birthday = DateTime.parse('2005-01-01')
|
246
|
+
person.awesomeness = true
|
247
|
+
|
248
|
+
puts "dbid (before save): #{person.dbid}"
|
249
|
+
|
250
|
+
person.save
|
251
|
+
|
252
|
+
puts "dbid (after save): #{person.dbid}"
|
253
|
+
```
|
254
|
+
|
255
|
+
Above prints:
|
170
256
|
|
171
|
-
|
172
|
-
|
173
|
-
|
257
|
+
```
|
258
|
+
dbid (before save):
|
259
|
+
dbid (after save): 17592186045418
|
174
260
|
```
|
175
261
|
|
176
|
-
|
262
|
+
The *dbid* is equivalent to *id* of ordinary ActiveModel objects.
|
263
|
+
Before `save`, person object doesn't have `dbid` value.
|
264
|
+
But after `save`, it will have an auto-generated value as dbid.
|
177
265
|
|
178
|
-
|
179
|
-
|
180
|
-
|
266
|
+
Alternatively, you can create an entity in one line:
|
267
|
+
```ruby
|
268
|
+
Person.new(name: "Sky", birthday: DateTime.parse('2005-01-01'), awesomeness: true).save
|
269
|
+
```
|
181
270
|
|
271
|
+
To update attribue values of the instance, use update_attributes:
|
182
272
|
```ruby
|
183
|
-
|
184
|
-
|
273
|
+
person.update_attributes(name: "Sky Jr.", awesomeness: false)
|
274
|
+
```
|
185
275
|
|
186
|
-
|
187
|
-
|
188
|
-
|
276
|
+
|
277
|
+
## Make queries
|
278
|
+
|
279
|
+
Queries are done by Diametric::Query instance. A basic usage is:
|
280
|
+
|
281
|
+
|
282
|
+
1. Create Diametric::Query instance with a required argument.
|
283
|
+
2. Call methods of Query instance. This triggers a query to Datomic.
|
284
|
+
3. Iterate the query result and do something to get data.
|
285
|
+
|
286
|
+
Diametric::Query has three arguments in constructor:
|
287
|
+
```ruby
|
288
|
+
Diametric::Query.new(entity_class, db_or_conn=nil, resolve=false)
|
289
|
+
```
|
290
|
+
The second and the third options work only on Peer service.
|
291
|
+
|
292
|
+
|
293
|
+
On REST service, the query results are array of entity objects.
|
294
|
+
On Peer service, the result data type depends on the constructor arguments.
|
295
|
+
In default setting, the query result is a Set of Arrays.
|
296
|
+
Each array has entity id (future Daimetric version will have a couple of attributes depends on the query).
|
297
|
+
This data structure is exactly the same as what Datomic returns.
|
298
|
+
Also, to avoid overhead comes from converting to Ruby's Set and Arrays,
|
299
|
+
Diametric wraps in Diametric::Persistence::Set or Diametric::Persistence::Collection.
|
300
|
+
(Diametric's wrappers are not a perfect Ruby Array nor Ruby Set in this version.
|
301
|
+
Those will be improved in future versions.)
|
302
|
+
|
303
|
+
The reason Diametric doesn't create entity instances by defualt is to save memory.
|
304
|
+
When dealing with millions or billions of data, saving memory is key to run faster,
|
305
|
+
and, more importantly, to avoid OutOfMemoryError.
|
306
|
+
In terms of saving memory, Diametric returns minimum by default.
|
307
|
+
|
308
|
+
If you don't have a lot of data and want to get instances directly from query results,
|
309
|
+
set true to the third option, `resolve`.
|
310
|
+
|
311
|
+
When resolve option is set to true, the query result will be an Array of entity instances.
|
312
|
+
You should be careful to set this option to true. When it is true, Diametric tries to
|
313
|
+
create instances recursively following associations.
|
314
|
+
If friends or friends of friends includs self, you'll likely to get StackOverflowError.
|
315
|
+
|
316
|
+
To resolve entity from a dbid, use Entity's reify method.
|
317
|
+
The reify method creates an instance from the dbid.
|
318
|
+
|
319
|
+
The second option, `db_or_conn` is to specify the database state at some time in the past.
|
320
|
+
If it is not specified, Diametric uses the default database state, which means the latest state.
|
321
|
+
|
322
|
+
### find all
|
323
|
+
Query example1:
|
324
|
+
```ruby
|
325
|
+
# create test data
|
326
|
+
Person.new(name: "Sun", birthday: DateTime.parse('2005-01-01'), awesomeness: true).save
|
327
|
+
Person.new(name: "Cloud", birthday: DateTime.parse('1980-02-12'), awesomeness: false).save
|
328
|
+
Person.new(name: "Breeze", birthday: DateTime.parse('1911-03-23'), awesomeness: true).save
|
329
|
+
Person.new(name: "Sleet", birthday: DateTime.parse('2010-04-30'), awesomeness: false).save
|
330
|
+
Person.new(name: "Rain", birthday: DateTime.parse('2005-05-05'), awesomeness: true).save
|
331
|
+
|
332
|
+
# make a query
|
333
|
+
query = Diametric::Query.new(Person)
|
334
|
+
query.each do |ary|
|
335
|
+
person = Person.reify(ary.first)
|
336
|
+
puts "name: #{person.name}, dbid: #{person.dbid}, birthday: #{person.birthday}, awesomeness: #{person.awesomeness}"
|
337
|
+
end
|
338
|
+
```
|
339
|
+
|
340
|
+
Above prints:
|
341
|
+
```ruby
|
342
|
+
name: Rain, dbid: 17592186045429, birthday: 2005-05-04 20:00:00 -0400, awesomeness: true
|
343
|
+
name: Breeze, dbid: 17592186045425, birthday: 1911-03-22 19:00:00 -0500, awesomeness: true
|
344
|
+
name: Sleet, dbid: 17592186045427, birthday: 2010-04-29 20:00:00 -0400, awesomeness: false
|
345
|
+
name: Sun, dbid: 17592186045421, birthday: 2004-12-31 19:00:00 -0500, awesomeness: true
|
346
|
+
name: Cloud, dbid: 17592186045423, birthday: 1980-02-11 19:00:00 -0500, awesomeness: false
|
347
|
+
```
|
348
|
+
|
349
|
+
Query example2 (with resolve to true):
|
350
|
+
```ruby
|
351
|
+
query = Diametric::Query.new(Person, nil, true)
|
352
|
+
query.each do |entity|
|
353
|
+
puts "name: #{entity.name}, dbid: #{entity.dbid}, birthday: #{entity.birthday}, awesomeness: #{entity.awesomeness}"
|
354
|
+
end
|
355
|
+
```
|
356
|
+
|
357
|
+
The example2 prints exactly the same as the example1.
|
358
|
+
|
359
|
+
|
360
|
+
### where
|
361
|
+
|
362
|
+
To narrow down the query results, you can use `where` and `filter` methods.
|
363
|
+
|
364
|
+
Query example3:
|
365
|
+
```ruby
|
366
|
+
query = Diametric::Query.new(Person, nil, true).where(awesomeness: true)
|
367
|
+
query.each do |entity|
|
368
|
+
puts "name: #{entity.name}, dbid: #{entity.dbid}, birthday: #{entity.birthday}, awesomeness: #{entity.awesomeness}"
|
369
|
+
end
|
370
|
+
```
|
371
|
+
|
372
|
+
Above prints:
|
373
|
+
```ruby
|
374
|
+
name: Sun, dbid: 17592186045421, birthday: 2004-12-31 19:00:00 -0500, awesomeness: true
|
375
|
+
name: Rain, dbid: 17592186045429, birthday: 2005-05-04 20:00:00 -0400, awesomeness: true
|
376
|
+
name: Breeze, dbid: 17592186045425, birthday: 1911-03-22 19:00:00 -0500, awesomeness: true
|
189
377
|
```
|
190
378
|
|
191
|
-
###
|
379
|
+
### filter
|
192
380
|
|
381
|
+
Query example4:
|
382
|
+
```ruby
|
383
|
+
query = Diametric::Query.new(Person, nil, true).filter(:>, :birthday, DateTime.parse('2004-12-31'))
|
384
|
+
query.each do |entity|
|
385
|
+
puts "name: #{entity.name}, dbid: #{entity.dbid}, birthday: #{entity.birthday}, awesomeness: #{entity.awesomeness}"
|
386
|
+
end
|
387
|
+
```
|
388
|
+
The results are:
|
193
389
|
```ruby
|
194
|
-
|
390
|
+
name: Sun, dbid: 17592186045418, birthday: 2004-12-31 19:00:00 -0500, awesomeness: true
|
391
|
+
name: Sleet, dbid: 17592186045424, birthday: 2010-04-29 20:00:00 -0400, awesomeness: false
|
392
|
+
name: Rain, dbid: 17592186045426, birthday: 2005-05-04 20:00:00 -0400, awesomeness: true
|
393
|
+
```
|
394
|
+
|
395
|
+
Currently, filter supports simple Clojure comparisons.
|
396
|
+
For example:
|
397
|
+
```clojure
|
398
|
+
(< attribute value)
|
399
|
+
(> attribute value)
|
400
|
+
(= attribute value)
|
401
|
+
...
|
402
|
+
```
|
403
|
+
The first argument of filter is mapped to the predicate of the clojure function.
|
404
|
+
The second argument of filter is always an attrbute name,
|
405
|
+
which will be replaced by attribute values of entities.
|
406
|
+
The third argument of the filter is the value to be compared.
|
407
|
+
|
408
|
+
|
409
|
+
### chaining
|
410
|
+
|
411
|
+
Ruby's method chaining can be used for `where` and `filter` methods.
|
412
|
+
|
413
|
+
Query example5:
|
414
|
+
```ruby
|
415
|
+
query = Diametric::Query.new(Person, nil, true).where(awesomeness: true).filter(:>, :birthday, DateTime.parse('2004-12-31'))
|
416
|
+
query.all.each do |p|
|
417
|
+
puts "name: #{p.name}, dbid: #{p.dbid}, birthday: #{p.birthday}, awesomeness: #{p.awesomeness}"
|
418
|
+
end
|
419
|
+
```
|
420
|
+
The query finds entities whose awesomenesses are true and whose birthdays are after December 31, 2004.
|
421
|
+
As expected, it prints:
|
422
|
+
```ruby
|
423
|
+
name: Sun, dbid: 17592186045418, birthday: 2004-12-31 19:00:00 -0500, awesomeness: true
|
424
|
+
name: Rain, dbid: 17592186045426, birthday: 2005-05-04 20:00:00 -0400, awesomeness: true
|
425
|
+
```
|
426
|
+
|
427
|
+
### short cut methods
|
428
|
+
|
429
|
+
So far, the query examples use `Diametric::Query.new(...)`.
|
430
|
+
Other than that, Diametric supports consice short cut query methods.
|
431
|
+
The examples above can be rewritten below.
|
432
|
+
In this case, resolve option is set to true by default.
|
433
|
+
|
434
|
+
```ruby
|
435
|
+
#query = Diametric::Query.new(Person)
|
436
|
+
query = Person.all
|
437
|
+
|
438
|
+
#query = Diametric::Query.new(Person, nil, true).where(awesomeness: true)
|
439
|
+
query = Person.where(awesomeness: true)
|
440
|
+
|
441
|
+
#query = Diametric::Query.new(Person, nil, true).filter(:>, :birthday, DateTime.parse('2004-12-31'))
|
442
|
+
query = Person.filter(:>, :birthday, DateTime.parse('2004-12-31'))
|
443
|
+
```
|
444
|
+
|
445
|
+
### make query to the past (Peer only)
|
446
|
+
|
447
|
+
Datomic has the idea of time.
|
448
|
+
It allows database state to role back at some point in the past.
|
449
|
+
The example below makes the query to the current and secounds (milliseconds?) before of database.
|
450
|
+
|
451
|
+
Query example6:
|
452
|
+
```ruby
|
453
|
+
# prints current awesomeness values
|
454
|
+
Diametric::Query.new(Person, nil, true).all.collect(&:awesomeness)
|
455
|
+
=> [true, true, false, true, false]
|
456
|
+
|
457
|
+
# saves the time. this will be the past
|
458
|
+
past = Time.now
|
459
|
+
=> 2013-11-09 20:17:59 -0500
|
460
|
+
|
461
|
+
# updates awesomeness false to true
|
462
|
+
Diametric::Query.new(Person, nil, true).where(awesomeness: false).each do |person|
|
463
|
+
person.update_attributes(awesomeness: true)
|
464
|
+
end
|
465
|
+
=> [[17592186045420], [17592186045424]]
|
466
|
+
|
467
|
+
# prints updated awesomeness values
|
468
|
+
Diametric::Query.new(Person, nil, true).all.collect(&:awesomeness)
|
469
|
+
=> [true, true, true, true, true]
|
470
|
+
|
471
|
+
# pulls out the past database
|
472
|
+
past_db = @conn.db.as_of(past)
|
473
|
+
=> #<Diametric::Persistence::Database:0x49fe0bcd>
|
474
|
+
|
475
|
+
# makes a query to past database
|
476
|
+
Diametric::Query.new(Person, past_db, true).all.collect(&:awesomeness)
|
477
|
+
=> [true, true, false, true, false]
|
478
|
+
|
479
|
+
# query results shows the database before "past"
|
480
|
+
```
|
481
|
+
|
482
|
+
### using datomic's query string (Peer only)
|
483
|
+
|
484
|
+
Currently, Diametric's query can do only a part of Datomic can do.
|
485
|
+
When you want a more complicated query or customized query, you can use
|
486
|
+
`Diametric::Persistence::Peer.q` method. For example:
|
487
|
+
|
488
|
+
```ruby
|
489
|
+
result = Diametric::Persistence::Peer.q("[:find ?name :where [?person :person/name ?name]]", @conn.db)
|
490
|
+
```
|
491
|
+
|
492
|
+
You'll get the results below:
|
493
|
+
```
|
494
|
+
[["Rain"], ["Sleet"], ["Sun"], ["Cloud"], ["Breeze"]]
|
495
|
+
```
|
496
|
+
This result data type is a Set of Arrays, which is the same as the default setting.
|
497
|
+
|
498
|
+
Below is another example with a value argument. This is what Diametric's filter query does.
|
499
|
+
```ruby
|
500
|
+
result = Diametric::Persistence::Peer.q("[:find ?birthday :in $ [?value] :where [?e :person/birthday ?birthday] [(< ?birthday ?value)]]", @conn.db, DateTime.parse('2002-01-01'))
|
501
|
+
```
|
502
|
+
The result is:
|
503
|
+
```ruby
|
504
|
+
[[#inst "1980-02-12T00:00:00.000-00:00"], [#inst "1911-03-23T00:00:00.000-00:00"]]
|
505
|
+
```
|
506
|
+
|
507
|
+
This is a Set of Arrays whose elements are Datomic's Date expression.
|
508
|
+
However, Diametric wrapper converts to Ruby object when it is used.
|
509
|
+
```ruby
|
510
|
+
result.each do |ary|
|
511
|
+
puts ary.first.strftime("%m/%d/%Y")
|
512
|
+
end
|
513
|
+
```
|
514
|
+
returns:
|
515
|
+
```ruby
|
516
|
+
02/11/1980
|
517
|
+
03/22/1911
|
518
|
+
```
|
519
|
+
|
520
|
+
|
521
|
+
### Delete entities
|
522
|
+
|
523
|
+
To delete an entity, use destroy method:
|
524
|
+
|
525
|
+
```ruby
|
526
|
+
query = Person.where(:name => "Sleet")
|
527
|
+
query.all.each {|p| p.destroy }
|
528
|
+
```
|
529
|
+
|
530
|
+
## Validation
|
531
|
+
|
532
|
+
ActiveModel's validation are included by default. All you need to do is start using them!
|
533
|
+
|
534
|
+
```ruby
|
535
|
+
require 'diametric'
|
536
|
+
|
537
|
+
class Person
|
195
538
|
include Diametric::Entity
|
196
539
|
include Diametric::Persistence
|
197
|
-
|
540
|
+
|
198
541
|
attribute :name, String, :index => true
|
199
|
-
|
542
|
+
|
543
|
+
validates :name, :length => {:minimum => 5}
|
200
544
|
end
|
545
|
+
Person.create_schema
|
546
|
+
```
|
201
547
|
|
202
|
-
|
203
|
-
goat.dbid # => nil
|
204
|
-
goat.name # => "Beans"
|
205
|
-
goat.persisted? # => false
|
206
|
-
goat.new? # => true
|
548
|
+
This model validates the length of the name. If the name has less than 5 letters, the input is invalid. For example:
|
207
549
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
goat.new? # => false
|
550
|
+
```ruby
|
551
|
+
jeff = Person.new(:name => "Geoffrey")
|
552
|
+
jeff.valid?
|
553
|
+
# => true
|
213
554
|
|
214
|
-
|
215
|
-
|
555
|
+
jeff.name = "Jeff"
|
556
|
+
jeff.valid?
|
557
|
+
# => false
|
558
|
+
```
|
216
559
|
|
217
|
-
|
218
|
-
#=> Goat(id: 1, age: 2, name: "Beans")
|
560
|
+
Similarly to ActiveRecord, models cannot save until they are valid:
|
219
561
|
|
220
|
-
|
221
|
-
|
562
|
+
```ruby
|
563
|
+
jeff.name = "Jeff"
|
564
|
+
jeff.save
|
565
|
+
# => false
|
222
566
|
|
223
|
-
|
224
|
-
|
567
|
+
jeff.name = "Goeffrey"
|
568
|
+
jeff.save
|
569
|
+
# => #<... transaction results object ...>
|
225
570
|
```
|
226
571
|
|
227
|
-
##
|
572
|
+
## Cardinality
|
573
|
+
|
574
|
+
When you want to define one-to-many attributes, you should add `:cardinality => :many` to an attribute definition.
|
575
|
+
|
576
|
+
```ruby
|
577
|
+
class Profile
|
578
|
+
include Diametric::Entity
|
579
|
+
include Diametric::Persistence::Peer
|
580
|
+
|
581
|
+
attribute :name, String, :index => true
|
582
|
+
attribute :likes, String, :cardinality => :many
|
583
|
+
end
|
584
|
+
Profile.create_schema.get
|
585
|
+
```
|
586
|
+
|
587
|
+
The attribute with `:cardinality => :many` accepts either Array or Set. Usage will be:
|
588
|
+
|
589
|
+
```ruby
|
590
|
+
Profile.new(name: "Breeze", likes: ["chocolate", "biking", "afternoon"]).save
|
591
|
+
Profile.new(name: "Sun", likes: ["banana", "running", "morning"]).save
|
592
|
+
Profile.new(name: "Rain", likes: (Set.new ["pumpkin pie", "video game", "night"])).save
|
593
|
+
```
|
594
|
+
|
595
|
+
Datomic saves values in a Set object when the attribute has `:cardinality => :many` definition. For example:
|
596
|
+
```ruby
|
597
|
+
Profile.where(:name => "Breeze").each {|e| puts e.likes}
|
598
|
+
```
|
599
|
+
returns `#<Set:0x007fbb66294928>`. So, if the following gets run:
|
600
|
+
|
601
|
+
```ruby
|
602
|
+
Diametric::Query.new(Profile).each do |ary|
|
603
|
+
profile = Profile.reify(ary.first)
|
604
|
+
puts "#{profile.name} likes #{profile.likes.inspect}"
|
605
|
+
end
|
606
|
+
```
|
607
|
+
Above prints:
|
608
|
+
```ruby
|
609
|
+
Rain likes #<Set: {"pumpkin pie", "video game", "night"}>
|
610
|
+
Sun likes #<Set: {"morning", "banana", "running"}>
|
611
|
+
Breeze likes #<Set: {"chocolate", "afternoon", "biking"}>
|
612
|
+
```
|
613
|
+
The order is not guaranteed.
|
614
|
+
|
615
|
+
## Association (Peer only)
|
228
616
|
|
229
|
-
|
617
|
+
On Datomic, association is fairly easy even though it is one to many or many to many.
|
618
|
+
Association is defined by `Ref` type.
|
619
|
+
Just assigning a saved entity instance to `Ref` type attribute makes association.
|
230
620
|
|
231
|
-
|
621
|
+
The entity definition looks like below:
|
622
|
+
```ruby
|
623
|
+
class Somebody
|
624
|
+
include Diametric::Entity
|
625
|
+
include Diametric::Persistence::Peer
|
232
626
|
|
233
|
-
|
627
|
+
attribute :name, String
|
628
|
+
attribute :mom, Ref, :cardinality => :one
|
629
|
+
attribute :dad, Ref, :cardinality => :one
|
630
|
+
attribute :kids, Ref, :cardinality => :many
|
631
|
+
end
|
632
|
+
```
|
234
633
|
|
235
|
-
|
634
|
+
This entity has one to one and one to many associations.
|
635
|
+
Below is an example of creating an entity instance and making queries.
|
236
636
|
|
237
|
-
|
637
|
+
```ruby
|
638
|
+
# creates mom and dad
|
639
|
+
mom = Somebody.new(name: "Snow White")
|
640
|
+
=> #<Somebody:0x3292eff7 @changed_attributes={"name"=>nil}, @name="Snow White">
|
641
|
+
mom.save
|
642
|
+
=> {:db-before datomic.db.Db@f89fe443, :db-after datomic.db.Db@efb79a88, :tx-data [#Datum{:e 13194139534333 :a 50 :v #inst "2013-11-10T03:35:06.889-00:00" :tx 13194139534333 :added true} #Datum{:e 17592186045438 :a 67 :v "Snow White" :tx 13194139534333 :added true}], :tempids {-9223350046623220305 17592186045438}}
|
643
|
+
dad = Somebody.new(name: "Prince Brave")
|
644
|
+
=> #<Somebody:0x1425e531 @changed_attributes={"name"=>nil}, @name="Prince Brave">
|
645
|
+
dad.save
|
646
|
+
=> {:db-before datomic.db.Db@efb79a88, :db-after datomic.db.Db@3bddfe3, :tx-data [#Datum{:e 13194139534335 :a 50 :v #inst "2013-11-10T03:35:09.405-00:00" :tx 13194139534335 :added true} #Datum{:e 17592186045440 :a 67 :v "Prince Brave" :tx 13194139534335 :added true}], :tempids {-9223350046623220306 17592186045440}}
|
647
|
+
|
648
|
+
# creates sombody who has mom and dad
|
649
|
+
Somebody.new(name: "Alice Wonderland", mom: mom, dad: dad).save
|
650
|
+
=> {:db-before datomic.db.Db@3bddfe3, :db-after datomic.db.Db@df977d06, :tx-data [#Datum{:e 13194139534337 :a 50 :v #inst "2013-11-10T03:35:39.562-00:00" :tx 13194139534337 :added true} #Datum{:e 17592186045442 :a 68 :v 17592186045438 :tx 13194139534337 :added true} #Datum{:e 17592186045442 :a 69 :v 17592186045440 :tx 13194139534337 :added true} #Datum{:e 17592186045442 :a 67 :v "Alice Wonderland" :tx 13194139534337 :added true}], :tempids {-9223350046623220307 17592186045442}}
|
651
|
+
|
652
|
+
# makes a query whose name is "Alice Wonderland"
|
653
|
+
me = Diametric::Query.new(Somebody, @conn, true).where(name: "Alice Wonderland").first
|
654
|
+
=> #<Somebody:0x5f369fc6 @changed_attributes={"dad"=>nil, "mom"=>nil, "name"=>nil}, @dbid=17592186045442, @name="Alice Wonderland", @dad=#<Somebody:0x75de7009 @changed_attributes={"name"=>nil}, @dbid=17592186045440, @name="Prince Brave">, @mom=#<Somebody:0x7c840fe3 @changed_attributes={"name"=>nil}, @dbid=17592186045438, @name="Snow White">>
|
655
|
+
|
656
|
+
# creates another two others who have mom as me
|
657
|
+
mario = Somebody.new(name: "Mario", mom: me)
|
658
|
+
=> #<Somebody:0x6ad3fbe4 @changed_attributes={"name"=>nil, "mom"=>nil}, @name="Mario", @mom=#<Somebody:0x5f369fc6 @changed_attributes={"dad"=>nil, "mom"=>nil, "name"=>nil}, @dbid=17592186045442, @name="Alice Wonderland", @dad=#<Somebody:0x75de7009 @changed_attributes={"name"=>nil}, @dbid=17592186045440, @name="Prince Brave">, @mom=#<Somebody:0x7c840fe3 @changed_attributes={"name"=>nil}, @dbid=17592186045438, @name="Snow White">>>
|
659
|
+
mario.save
|
660
|
+
=> {:db-before datomic.db.Db@df977d06, :db-after datomic.db.Db@79b5b85f, :tx-data [#Datum{:e 13194139534339 :a 50 :v #inst "2013-11-10T03:36:37.567-00:00" :tx 13194139534339 :added true} #Datum{:e 17592186045444 :a 68 :v 17592186045445 :tx 13194139534339 :added true} #Datum{:e 17592186045444 :a 67 :v "Mario" :tx 13194139534339 :added true} #Datum{:e 17592186045445 :a 68 :v 17592186045447 :tx 13194139534339 :added true} #Datum{:e 17592186045445 :a 69 :v 17592186045446 :tx 13194139534339 :added true} #Datum{:e 17592186045445 :a 67 :v "Alice Wonderland" :tx 13194139534339 :added true} #Datum{:e 17592186045446 :a 67 :v "Prince Brave" :tx 13194139534339 :added true} #Datum{:e 17592186045447 :a 67 :v "Snow White" :tx 13194139534339 :added true}], :tempids {-9223350046623220311 17592186045447, -9223350046623220310 17592186045446, -9223350046623220309 17592186045445, -9223350046623220308 17592186045444}}
|
661
|
+
luigi = Somebody.new(name: "Luigi", mom: me)
|
662
|
+
=> #<Somebody:0x4ebed2b3 @changed_attributes={"name"=>nil, "mom"=>nil}, @name="Luigi", @mom=#<Somebody:0x5f369fc6 @temp_ref=-1013, @changed_attributes={}, @dbid=17592186045445, @name="Alice Wonderland", @dad=#<Somebody:0x75de7009 @temp_ref=-1014, @changed_attributes={}, @dbid=17592186045446, @name="Prince Brave", @previously_changed={"name"=>[nil, "Prince Brave"]}>, @previously_changed={"dad"=>[nil, #<Somebody:0x75de7009 @temp_ref=-1014, @changed_attributes={}, @dbid=17592186045446, @name="Prince Brave", @previously_changed={"name"=>[nil, "Prince Brave"]}>], "mom"=>[nil, #<Somebody:0x7c840fe3 @temp_ref=-1015, @changed_attributes={}, @dbid=17592186045447, @name="Snow White", @previously_changed={"name"=>[nil, "Snow White"]}>], "name"=>[nil, "Alice Wonderland"]}, @mom=#<Somebody:0x7c840fe3 @temp_ref=-1015, @changed_attributes={}, @dbid=17592186045447, @name="Snow White", @previously_changed={"name"=>[nil, "Snow White"]}>>>
|
663
|
+
luigi.save
|
664
|
+
=> {:db-before datomic.db.Db@79b5b85f, :db-after datomic.db.Db@9a8e7dab, :tx-data [#Datum{:e 13194139534344 :a 50 :v #inst "2013-11-10T03:36:37.649-00:00" :tx 13194139534344 :added true} #Datum{:e 17592186045449 :a 68 :v 17592186045445 :tx 13194139534344 :added true} #Datum{:e 17592186045449 :a 67 :v "Luigi" :tx 13194139534344 :added true}], :tempids {-9223350046623220312 17592186045449}}
|
665
|
+
me.update_attributes(kids: [mario, luigi])
|
666
|
+
=> {:db-before datomic.db.Db@9a8e7dab, :db-after datomic.db.Db@7507454d, :tx-data [#Datum{:e 13194139534346 :a 50 :v #inst "2013-11-10T03:36:38.821-00:00" :tx 13194139534346 :added true} #Datum{:e 17592186045445 :a 70 :v 17592186045444 :tx 13194139534346 :added true} #Datum{:e 17592186045445 :a 70 :v 17592186045449 :tx 13194139534346 :added true}], :tempids {}}
|
667
|
+
|
668
|
+
# again, makes a query whose name is "Alice Wonderland"
|
669
|
+
me = Diametric::Query.new(Somebody, @conn, true).where(name: "Alice Wonderland").first
|
670
|
+
=> #<Somebody:0x1eb6037d @mom=#<Somebody:0x335b3d6 @dbid=17592186045427, @changed_attributes={"name"=>nil}, @name="Snow White">, @dad=#<Somebody:0x38848217 @dbid=17592186045426, @changed_attributes={"name"=>nil}, @name="Prince Brave">, @dbid=17592186045425, @changed_attributes={"kids"=>nil, "dad"=>nil, "mom"=>nil, "name"=>nil}, @kids=#<Set: {#<Somebody:0x3c743d40 @mom=#<Java::DatomicQuery::EntityMap:0x77a9ac36>, @dbid=17592186045424, @changed_attributes={"mom"=>nil, "name"=>nil}, @name="Mario">, #<Somebody:0x2444c3df @mom=#<Java::DatomicQuery::EntityMap:0x5aac6f9f>, @dbid=17592186045429, @changed_attributes={"mom"=>nil, "name"=>nil}, @name="Luigi">}>, @name="Alice Wonderland">
|
671
|
+
|
672
|
+
# looks kids data
|
673
|
+
me.kids.collect(&:name)
|
674
|
+
=> ["Mario", "Luigi"]
|
675
|
+
|
676
|
+
# retieve mom's instance from kids
|
677
|
+
Somebody.reify(me.kids.first.mom).name
|
678
|
+
=> "Alice Wonderland"
|
679
|
+
```
|
238
680
|
|
239
|
-
|
681
|
+
## Thanks
|
682
|
+
|
683
|
+
Development of Diametric was sponsored by [Relevance][]. They are the
|
684
|
+
best Clojure shop around and one of the best Ruby shops. I highly
|
685
|
+
recommend them for help with your corporate projects.
|
686
|
+
|
687
|
+
Special thanks to Mongoid for writing some solid ORM code that was liberally borrowed from to add Rails support to Diametric.
|
240
688
|
|
241
689
|
## Contributing
|
242
690
|
|