diametric 0.1.1-java → 0.1.2-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|