datomic-flare 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,1042 @@
1
+ # Datomic Flare for Ruby
2
+
3
+ A Ruby gem for interacting with [Datomic](https://www.datomic.com) through [Datomic Flare](https://github.com/gbaptista/datomic-flare).
4
+
5
+ ![The image features a logo with curved lines forming a ruby, suggesting distortion and movement like space-time.](https://media.githubusercontent.com/media/gbaptista/assets/refs/heads/main/ruby-datomic-flare/ruby-datomic-flare-canvas.png)
6
+
7
+ _This is not an official Datomic project or documentation and it is not affiliated with Datomic in any way._
8
+
9
+ ## TL;DR and Quick Start
10
+
11
+ ```ruby
12
+ gem 'datomic-flare', '~> 1.0.0'
13
+ ```
14
+
15
+ ```ruby
16
+ require 'datomic-flare'
17
+
18
+ client = Flare.new(credentials: { address: 'http://localhost:3042' })
19
+ ```
20
+
21
+ ```ruby
22
+ client.dsl.transact_schema!(
23
+ {
24
+ book: {
25
+ title: { type: :string, doc: 'The title of the book.' },
26
+ genre: { type: :string, doc: 'The genre of the book.' }
27
+ }
28
+ }
29
+ )
30
+
31
+ client.dsl.assert_into!(
32
+ :book,
33
+ {
34
+ title: 'The Tell-Tale Heart',
35
+ genre: 'Horror'
36
+ }
37
+ )
38
+
39
+ client.dsl.query(
40
+ datalog: <<~EDN
41
+ [:find ?e ?title ?genre
42
+ :where [?e :book/title ?title]
43
+ [?e :book/genre ?genre]]
44
+ EDN
45
+ )
46
+ ```
47
+
48
+ ```ruby
49
+ [[4611681620380877802, 'The Tell-Tale Heart', 'Horror']]
50
+ ```
51
+
52
+ - [TL;DR and Quick Start](#tldr-and-quick-start)
53
+ - [Flare](#flare)
54
+ - [Creating a Client](#creating-a-client)
55
+ - [Meta](#meta)
56
+ - [Flare DSL](#flare-dsl)
57
+ - [Creating a Database](#creating-a-database)
58
+ - [Deleting a Database](#deleting-a-database)
59
+ - [Listing Databases](#listing-databases)
60
+ - [Transacting Schema](#transacting-schema)
61
+ - [Checking Schema](#checking-schema)
62
+ - [Asserting Facts](#asserting-facts)
63
+ - [Reading Data by Entity](#reading-data-by-entity)
64
+ - [Reading Data by Querying](#reading-data-by-querying)
65
+ - [Accumulating Facts](#accumulating-facts)
66
+ - [Retracting Facts](#retracting-facts)
67
+ - [Flare API](#flare-api)
68
+ - [Creating a Database](#creating-a-database-1)
69
+ - [Deleting a Database](#deleting-a-database-1)
70
+ - [Listing Databases](#listing-databases-1)
71
+ - [Transacting Schema](#transacting-schema-1)
72
+ - [Checking Schema](#checking-schema-1)
73
+ - [Asserting Facts](#asserting-facts-1)
74
+ - [Reading Data by Entity](#reading-data-by-entity-1)
75
+ - [Reading Data by Querying](#reading-data-by-querying-1)
76
+ - [Accumulating Facts](#accumulating-facts-1)
77
+ - [Retracting Facts](#retracting-facts-1)
78
+ - [Development](#development)
79
+ - [Publish to RubyGems](#publish-to-rubygems)
80
+ - [Setup for Tests and Documentation](#setup-for-tests-and-documentation)
81
+ - [Running Tests](#running-tests)
82
+ - [Updating the README](#updating-the-readme)
83
+
84
+ ## Flare
85
+
86
+ ### Creating a Client
87
+
88
+ ```ruby
89
+ require 'datomic-flare'
90
+
91
+ client = Flare.new(credentials: { address: 'http://localhost:3042' })
92
+ ```
93
+
94
+ ### Meta
95
+
96
+ ```ruby
97
+ client.meta
98
+ ```
99
+
100
+ ```ruby
101
+ {
102
+ 'meta' =>
103
+ {
104
+ 'at' => '2024-09-29T13:52:52.252882446Z',
105
+ 'mode' => 'peer',
106
+ 'took' => { 'milliseconds' => 0.377446 }
107
+ },
108
+ 'data' =>
109
+ {
110
+ 'mode' => 'peer',
111
+ 'datomic-flare' => '1.0.0',
112
+ 'org.clojure/clojure' => '1.12.0',
113
+ 'com.datomic/peer' => '1.0.7187',
114
+ 'com.datomic/client-pro' => '1.0.81'
115
+ }
116
+ }
117
+ ```
118
+
119
+ ## Flare DSL
120
+
121
+ It provides a Ruby-familiar approach to working with Datomic. It brings Ruby’s conventions and idioms while preserving Datomic’s data-first principles and terminology.
122
+
123
+ This approach should be cozy to those who are familiar with Ruby.
124
+
125
+ Learn more about Ruby and The Rails Doctrine:
126
+
127
+ - [About Ruby](https://www.ruby-lang.org/en/about/)
128
+ - [The Rails Doctrine](https://rubyonrails.org/doctrine)
129
+
130
+ ### Creating a Database
131
+
132
+ ```ruby
133
+ client.dsl.create_database!('radioactive')
134
+ ```
135
+
136
+ ```ruby
137
+ true
138
+ ```
139
+
140
+ ### Deleting a Database
141
+
142
+ ```ruby
143
+ client.dsl.destroy_database!('radioactive')
144
+ ```
145
+
146
+ ```ruby
147
+ true
148
+ ```
149
+
150
+ ### Listing Databases
151
+
152
+ ```ruby
153
+ client.dsl.databases
154
+ ```
155
+
156
+ ```ruby
157
+ ['my-datomic-database']
158
+ ```
159
+
160
+ ### Transacting Schema
161
+
162
+ Like `CREATE TABLE` in SQL databases or defining document or record structures in other databases.
163
+
164
+ ```ruby
165
+ client.dsl.transact_schema!(
166
+ {
167
+ book: {
168
+ title: { type: :string, doc: 'The title of the book.' },
169
+ genre: { type: :string, doc: 'The genre of the book.' },
170
+ published_at_year: { type: :long, doc: 'The year the book was first published.' }
171
+ }
172
+ }
173
+ )
174
+ ```
175
+
176
+ ```ruby
177
+ true
178
+ ```
179
+
180
+ ### Checking Schema
181
+
182
+ Like `SHOW COLUMNS FROM` in SQL databases or checking document or record structures in other databases.
183
+
184
+ ```ruby
185
+ client.dsl.schema
186
+ ```
187
+
188
+ ```ruby
189
+ {
190
+ book: {
191
+ published_at_year: {
192
+ type: :long,
193
+ cardinality: :one,
194
+ doc: 'The year the book was first published.',
195
+ unique: false,
196
+ index: false,
197
+ history: true
198
+ },
199
+ title: {
200
+ type: :string,
201
+ cardinality: :one,
202
+ doc: 'The title of the book.',
203
+ unique: false,
204
+ index: false,
205
+ history: true
206
+ },
207
+ genre: {
208
+ type: :string,
209
+ cardinality: :one,
210
+ doc: 'The genre of the book.',
211
+ unique: false,
212
+ index: false,
213
+ history: true
214
+ }
215
+ }
216
+ }
217
+ ```
218
+
219
+ ### Asserting Facts
220
+
221
+ Like `INSERT INTO` in SQL databases or creating a new document or record in other databases.
222
+
223
+ ```ruby
224
+ client.dsl.assert_into!(
225
+ :book,
226
+ {
227
+ title: 'Pride and Prejudice',
228
+ genre: 'Romance',
229
+ published_at_year: 1813
230
+ }
231
+ )
232
+ ```
233
+
234
+ ```ruby
235
+ 4611681620380877802
236
+ ```
237
+
238
+ ```ruby
239
+ client.dsl.assert_into!(
240
+ :book,
241
+ [{
242
+ title: 'Near to the Wild Heart',
243
+ genre: 'Novel',
244
+ published_at_year: 1943
245
+ },
246
+ {
247
+ title: 'A Study in Scarlet',
248
+ genre: 'Detective',
249
+ published_at_year: 1887
250
+ },
251
+ {
252
+ title: 'The Tell-Tale Heart',
253
+ genre: 'Horror',
254
+ published_at_year: 1843
255
+ }]
256
+ )
257
+ ```
258
+
259
+
260
+ ```ruby
261
+ [4611681620380877804, 4611681620380877805, 4611681620380877806]
262
+ ```
263
+
264
+ ### Reading Data by Entity
265
+
266
+ Like `SELECT` in SQL databases or querying documents or records in other databases.
267
+
268
+ ```ruby
269
+ client.dsl.find_by_entity_id(4611681620380877804)
270
+ ```
271
+
272
+ ```ruby
273
+ {
274
+ book: {
275
+ title: 'Near to the Wild Heart',
276
+ genre: 'Novel',
277
+ published_at_year: 1943,
278
+ _id: 4611681620380877804
279
+ }
280
+ }
281
+ ```
282
+
283
+ ### Reading Data by Querying
284
+
285
+ Like `SELECT` in SQL databases or querying documents or records in other databases.
286
+
287
+ ```ruby
288
+ client.dsl.query(
289
+ datalog: <<~EDN
290
+ [:find ?e ?title ?genre ?year
291
+ :where [?e :book/title ?title]
292
+ [?e :book/genre ?genre]
293
+ [?e :book/published_at_year ?year]]
294
+ EDN
295
+ )
296
+ ```
297
+
298
+ ```ruby
299
+ [[4611681620380877805, 'A Study in Scarlet', 'Detective', 1887],
300
+ [4611681620380877804, 'Near to the Wild Heart', 'Novel', 1943],
301
+ [4611681620380877806, 'The Tell-Tale Heart', 'Horror', 1843],
302
+ [4611681620380877802, 'Pride and Prejudice', 'Romance', 1813]]
303
+ ```
304
+
305
+ ```ruby
306
+ client.dsl.query(
307
+ params: ['The Tell-Tale Heart'],
308
+ datalog: <<~EDN
309
+ [:find ?e ?title ?genre ?year
310
+ :in $ ?title
311
+ :where [?e :book/title ?title]
312
+ [?e :book/genre ?genre]
313
+ [?e :book/published_at_year ?year]]
314
+ EDN
315
+ )
316
+ ```
317
+
318
+
319
+ ```ruby
320
+ [[4611681620380877806, 'The Tell-Tale Heart', 'Horror', 1843]]
321
+ ```
322
+
323
+ ### Accumulating Facts
324
+
325
+ Like `UPDATE` in SQL databases or updating documents or records in other databases. However, Datomic never updates data. It is an immutable database that only accumulates new facts or retracts past facts.
326
+
327
+ ```ruby
328
+ client.dsl.assert_into!(
329
+ :book, { _id: 4611681620380877806, genre: 'Gothic' }
330
+ )
331
+ ```
332
+
333
+ ```ruby
334
+ 4611681620380877806
335
+ ```
336
+
337
+ ### Retracting Facts
338
+
339
+ Like `DELETE` in SQL databases or deleting documents or records in other databases. However, Datomic never deletes data. It is an immutable database that only accumulates new facts or retracts past facts.
340
+
341
+ Retract the value of an attribute:
342
+
343
+ ```ruby
344
+ client.dsl.retract_from!(
345
+ :book, { _id: 4611681620380877806, genre: 'Gothic' }
346
+ )
347
+ ```
348
+
349
+ ```ruby
350
+ true
351
+ ```
352
+
353
+ Retract an attribute:
354
+
355
+ ```ruby
356
+ client.dsl.retract_from!(
357
+ :book, { _id: 4611681620380877804, genre: nil }
358
+ )
359
+ ```
360
+
361
+ ```ruby
362
+ true
363
+ ```
364
+
365
+ Retract an entity:
366
+
367
+ ```ruby
368
+ client.dsl.retract_from!(
369
+ :book, { _id: 4611681620380877805 }
370
+ )
371
+ ```
372
+
373
+ ```ruby
374
+ true
375
+ ```
376
+
377
+
378
+ ## Flare API
379
+
380
+ It provides methods that mirror [Datomic's APIs](https://docs.datomic.com/clojure/index.html). Most interactions use EDN, closely following [Datomic’s documentation](https://docs.datomic.com).
381
+
382
+ This approach should be familiar to those who know Datomic concepts and APIs.
383
+
384
+ Learn more about Clojure and Datomic:
385
+
386
+ - [Clojure Rationale](https://clojure.org/about/rationale)
387
+ - [Datomic Introduction](https://docs.datomic.com/datomic-overview.html)
388
+
389
+ ### Creating a Database
390
+
391
+ ```ruby
392
+ client.api.create_database!({ name: 'fireball' })['data']
393
+ ```
394
+
395
+ ```ruby
396
+ true
397
+ ```
398
+
399
+ ### Deleting a Database
400
+
401
+ ```ruby
402
+ client.api.delete_database!({ name: 'fireball' })['data']
403
+ ```
404
+
405
+ ```ruby
406
+ true
407
+ ```
408
+
409
+ ### Listing Databases
410
+
411
+ ```ruby
412
+ # Flare on Peer Mode
413
+ client.api.get_database_names['data']
414
+
415
+ # Flare on Client Mode
416
+ client.api.list_databases['data']
417
+ ```
418
+
419
+ ```ruby
420
+ ['my-datomic-database']
421
+ ```
422
+
423
+ ### Transacting Schema
424
+
425
+ ```ruby
426
+ client.api.transact!(
427
+ { data: <<~EDN
428
+ [{:db/ident :book/title
429
+ :db/valueType :db.type/string
430
+ :db/cardinality :db.cardinality/one
431
+ :db/doc "The title of the book."}
432
+
433
+ {:db/ident :book/genre
434
+ :db/valueType :db.type/string
435
+ :db/cardinality :db.cardinality/one
436
+ :db/doc "The genre of the book."}
437
+
438
+ {:db/ident :book/published_at_year
439
+ :db/valueType :db.type/long
440
+ :db/cardinality :db.cardinality/one
441
+ :db/doc "The year the book was first published."}]
442
+ EDN
443
+ }
444
+ )['data']
445
+ ```
446
+
447
+ ```ruby
448
+ {
449
+ 'db-before' => 'datomic.db.Db@3062d44c',
450
+ 'db-after' => 'datomic.db.Db@7802aade',
451
+ 'tx-data' =>
452
+ [[13194139534312, 50, '2024-09-29T13:52:52.435Z', 13194139534312, true],
453
+ [72, 10, ':book/title', 13194139534312, true],
454
+ [72, 40, 23, 13194139534312, true],
455
+ [72, 41, 35, 13194139534312, true],
456
+ [72, 62, 'The title of the book.', 13194139534312, true],
457
+ [73, 10, ':book/genre', 13194139534312, true],
458
+ [73, 40, 23, 13194139534312, true],
459
+ [73, 41, 35, 13194139534312, true],
460
+ [73, 62, 'The genre of the book.', 13194139534312, true],
461
+ [74, 10, ':book/published_at_year', 13194139534312, true],
462
+ [74, 40, 22, 13194139534312, true],
463
+ [74, 41, 35, 13194139534312, true],
464
+ [74, 62, 'The year the book was first published.', 13194139534312, true],
465
+ [0, 13, 72, 13194139534312, true],
466
+ [0, 13, 73, 13194139534312, true],
467
+ [0, 13, 74, 13194139534312, true]],
468
+ 'tempids' =>
469
+ {
470
+ '-9223300668110558618' => 72,
471
+ '-9223300668110558617' => 73,
472
+ '-9223300668110558616' => 74
473
+ }
474
+ }
475
+ ```
476
+
477
+ ### Checking Schema
478
+
479
+ ```ruby
480
+ client.api.q(
481
+ {
482
+ inputs: [{ database: { latest: true } }],
483
+ query: <<~EDN
484
+ [:find
485
+ ?e ?ident ?value_type ?cardinality ?doc
486
+ ?unique ?index ?no_history
487
+ :in $
488
+ :where
489
+ [?e :db/ident ?ident]
490
+
491
+ [?e :db/valueType ?value_type_id]
492
+ [?value_type_id :db/ident ?value_type]
493
+
494
+ [?e :db/cardinality ?cardinality_id]
495
+ [?cardinality_id :db/ident ?cardinality]
496
+
497
+ [(get-else $ ?e :db/doc "") ?doc]
498
+
499
+ [(get-else $ ?e :db/unique -1) ?unique_id]
500
+ [(get-else $ ?unique_id :db/ident false) ?unique]
501
+
502
+ [(get-else $ ?e :db/index false) ?index]
503
+ [(get-else $ ?e :db/noHistory false) ?no_history]]
504
+ EDN
505
+ }
506
+ )['data'].filter do |datom|
507
+ !%w[
508
+ db
509
+ db.alter db.attr db.bootstrap db.cardinality db.entity db.excise
510
+ db.fn db.install db.lang db.part db.sys db.type db.unique
511
+ fressian
512
+ ].include?(datom[1].split('/').first)
513
+ end
514
+ ```
515
+
516
+ ```ruby
517
+ [[74,
518
+ 'book/published_at_year',
519
+ 'db.type/long',
520
+ 'db.cardinality/one',
521
+ 'The year the book was first published.',
522
+ false,
523
+ false,
524
+ false],
525
+ [72,
526
+ 'book/title',
527
+ 'db.type/string',
528
+ 'db.cardinality/one',
529
+ 'The title of the book.',
530
+ false,
531
+ false,
532
+ false],
533
+ [73,
534
+ 'book/genre',
535
+ 'db.type/string',
536
+ 'db.cardinality/one',
537
+ 'The genre of the book.',
538
+ false,
539
+ false,
540
+ false]]
541
+ ```
542
+
543
+ ### Asserting Facts
544
+
545
+ ```ruby
546
+ client.api.transact!(
547
+ { data: <<~EDN
548
+ [{:db/id -1
549
+ :book/title "Pride and Prejudice"
550
+ :book/genre "Romance"
551
+ :book/published_at_year 1813}]
552
+ EDN
553
+ }
554
+ )['data']
555
+ ```
556
+
557
+ ```ruby
558
+ {
559
+ 'db-before' => 'datomic.db.Db@5342ef1a',
560
+ 'db-after' => 'datomic.db.Db@643ac781',
561
+ 'tx-data' =>
562
+ [[13194139534313, 50, '2024-09-29T13:52:52.546Z', 13194139534313, true],
563
+ [4611681620380877802, 72, 'Pride and Prejudice', 13194139534313, true],
564
+ [4611681620380877802, 73, 'Romance', 13194139534313, true],
565
+ [4611681620380877802, 74, 1813, 13194139534313, true]],
566
+ 'tempids' => { '-1' => 4611681620380877802 }
567
+ }
568
+ ```
569
+
570
+ ```ruby
571
+ client.api.transact!(
572
+ { data: <<~EDN
573
+ [{:db/id -1
574
+ :book/title "Near to the Wild Heart"
575
+ :book/genre "Novel"
576
+ :book/published_at_year 1943}
577
+ {:db/id -2
578
+ :book/title "A Study in Scarlet"
579
+ :book/genre "Detective"
580
+ :book/published_at_year 1887}
581
+ {:db/id -3
582
+ :book/title "The Tell-Tale Heart"
583
+ :book/genre "Horror"
584
+ :book/published_at_year 1843}]
585
+ EDN
586
+ }
587
+ )['data']
588
+ ```
589
+
590
+
591
+ ```ruby
592
+ {
593
+ 'db-before' => 'datomic.db.Db@6afd3576',
594
+ 'db-after' => 'datomic.db.Db@73000a74',
595
+ 'tx-data' =>
596
+ [[13194139534315, 50, '2024-09-29T13:52:52.599Z', 13194139534315, true],
597
+ [4611681620380877804, 72, 'Near to the Wild Heart', 13194139534315, true],
598
+ [4611681620380877804, 73, 'Novel', 13194139534315, true],
599
+ [4611681620380877804, 74, 1943, 13194139534315, true],
600
+ [4611681620380877805, 72, 'A Study in Scarlet', 13194139534315, true],
601
+ [4611681620380877805, 73, 'Detective', 13194139534315, true],
602
+ [4611681620380877805, 74, 1887, 13194139534315, true],
603
+ [4611681620380877806, 72, 'The Tell-Tale Heart', 13194139534315, true],
604
+ [4611681620380877806, 73, 'Horror', 13194139534315, true],
605
+ [4611681620380877806, 74, 1843, 13194139534315, true]],
606
+ 'tempids' =>
607
+ {
608
+ '-1' => 4611681620380877804,
609
+ '-2' => 4611681620380877805,
610
+ '-3' => 4611681620380877806
611
+ }
612
+ }
613
+ ```
614
+
615
+ ### Reading Data by Entity
616
+
617
+ ```ruby
618
+ client.api.entity(
619
+ {
620
+ database: { latest: true },
621
+ id: 4611681620380877804
622
+ }
623
+ )['data']
624
+ ```
625
+
626
+ ```ruby
627
+ {
628
+ ':book/title' => 'Near to the Wild Heart',
629
+ ':book/genre' => 'Novel',
630
+ ':book/published_at_year' => 1943,
631
+ ':db/id' => 4611681620380877804
632
+ }
633
+ ```
634
+
635
+ ### Reading Data by Querying
636
+
637
+ ```ruby
638
+ client.api.q(
639
+ {
640
+ inputs: [{ database: { latest: true } }],
641
+ query: <<~EDN
642
+ [:find ?e ?title ?genre ?year
643
+ :where [?e :book/title ?title]
644
+ [?e :book/genre ?genre]
645
+ [?e :book/published_at_year ?year]]
646
+ EDN
647
+ }
648
+ )['data']
649
+ ```
650
+
651
+ ```ruby
652
+ [[4611681620380877805, 'A Study in Scarlet', 'Detective', 1887],
653
+ [4611681620380877804, 'Near to the Wild Heart', 'Novel', 1943],
654
+ [4611681620380877806, 'The Tell-Tale Heart', 'Horror', 1843],
655
+ [4611681620380877802, 'Pride and Prejudice', 'Romance', 1813]]
656
+ ```
657
+
658
+ ```ruby
659
+ client.api.q(
660
+ {
661
+ inputs: [
662
+ { database: { latest: true } },
663
+ 'The Tell-Tale Heart'
664
+ ],
665
+ query: <<~EDN
666
+ [:find ?e ?title ?genre ?year
667
+ :in $ ?title
668
+ :where [?e :book/title ?title]
669
+ [?e :book/genre ?genre]
670
+ [?e :book/published_at_year ?year]]
671
+ EDN
672
+ }
673
+ )['data']
674
+ ```
675
+
676
+
677
+ ```ruby
678
+ [[4611681620380877806, 'The Tell-Tale Heart', 'Horror', 1843]]
679
+ ```
680
+
681
+ ### Accumulating Facts
682
+
683
+ ```ruby
684
+ client.api.transact!(
685
+ { data: <<~EDN
686
+ [{:db/id 4611681620380877806 :book/genre "Gothic"}]
687
+ EDN
688
+ }
689
+ )['data']
690
+ ```
691
+
692
+ ```ruby
693
+ {
694
+ 'db-before' => 'datomic.db.Db@55e660ec',
695
+ 'db-after' => 'datomic.db.Db@385f74d4',
696
+ 'tx-data' =>
697
+ [[13194139534319, 50, '2024-09-29T13:52:52.817Z', 13194139534319, true],
698
+ [4611681620380877806, 73, 'Gothic', 13194139534319, true],
699
+ [4611681620380877806, 73, 'Horror', 13194139534319, false]],
700
+ 'tempids' => {}
701
+ }
702
+ ```
703
+
704
+ ### Retracting Facts
705
+
706
+ Retract the value of an attribute:
707
+
708
+ ```ruby
709
+ client.api.transact!(
710
+ { data: <<~EDN
711
+ [[:db/retract 4611681620380877806 :book/genre "Gothic"]]
712
+ EDN
713
+ }
714
+ )['data']
715
+ ```
716
+
717
+ ```ruby
718
+ {
719
+ 'db-before' => 'datomic.db.Db@7c2176fa',
720
+ 'db-after' => 'datomic.db.Db@6ec2acf1',
721
+ 'tx-data' =>
722
+ [[13194139534320, 50, '2024-09-29T13:52:52.869Z', 13194139534320, true],
723
+ [4611681620380877806, 73, 'Gothic', 13194139534320, false]],
724
+ 'tempids' => {}
725
+ }
726
+ ```
727
+
728
+ Retract an attribute:
729
+
730
+ ```ruby
731
+ client.api.transact!(
732
+ { data: <<~EDN
733
+ [[:db/retract 4611681620380877804 :book/genre]]
734
+ EDN
735
+ }
736
+ )['data']
737
+ ```
738
+
739
+ ```ruby
740
+ {
741
+ 'db-before' => 'datomic.db.Db@f8180a0',
742
+ 'db-after' => 'datomic.db.Db@2bc03f4d',
743
+ 'tx-data' =>
744
+ [[13194139534321, 50, '2024-09-29T13:52:52.913Z', 13194139534321, true],
745
+ [4611681620380877804, 73, 'Novel', 13194139534321, false]],
746
+ 'tempids' => {}
747
+ }
748
+ ```
749
+
750
+ Retract an entity:
751
+
752
+ ```ruby
753
+ client.api.transact!(
754
+ { data: <<~EDN
755
+ [[:db/retractEntity 4611681620380877805]]
756
+ EDN
757
+ }
758
+ )['data']
759
+ ```
760
+
761
+ ```ruby
762
+ {
763
+ 'db-before' => 'datomic.db.Db@698670bc',
764
+ 'db-after' => 'datomic.db.Db@2abc508c',
765
+ 'tx-data' =>
766
+ [[13194139534322, 50, '2024-09-29T13:52:52.955Z', 13194139534322, true],
767
+ [4611681620380877805, 72, 'A Study in Scarlet', 13194139534322, false],
768
+ [4611681620380877805, 73, 'Detective', 13194139534322, false],
769
+ [4611681620380877805, 74, 1887, 13194139534322, false]],
770
+ 'tempids' => {}
771
+ }
772
+ ```
773
+
774
+
775
+ ## Development
776
+
777
+ ```bash
778
+ bundle
779
+ rubocop -A
780
+
781
+ ```
782
+
783
+ ### Publish to RubyGems
784
+
785
+ ```bash
786
+ gem build datomic-flare.gemspec
787
+
788
+ gem signin
789
+
790
+ gem push datomic-flare-1.0.0.gem
791
+
792
+ ```
793
+
794
+ ### Setup for Tests and Documentation
795
+
796
+ Tests run against real Datomic databases, and documentation (README) is generated by interacting with real Datomic databases.
797
+
798
+ To accomplish that, we need to have [Datomic](https://github.com/gbaptista/datomic-pro-docker) and [Flare](https://github.com/gbaptista/datomic-flare) running.
799
+
800
+ **TL;DR:**
801
+
802
+ ```bash
803
+ git clone https://github.com/gbaptista/datomic-pro-docker.git
804
+
805
+ cd datomic-pro-docker
806
+
807
+ cp compose/flare-dev.yml docker-compose.yml
808
+
809
+ docker compose up -d datomic-storage
810
+
811
+ docker compose run datomic-tools psql \
812
+ -f bin/sql/postgres-table.sql \
813
+ -h datomic-storage \
814
+ -U datomic-user \
815
+ -d my-datomic-storage
816
+
817
+ docker compose up -d datomic-transactor
818
+
819
+ docker compose run datomic-tools clojure -M -e "$(cat <<'CLOJURE'
820
+ (require '[datomic.api :as d])
821
+
822
+ (d/create-database "datomic:sql://my-datomic-database?jdbc:postgresql://datomic-storage:5432/my-datomic-storage?user=datomic-user&password=unsafe")
823
+
824
+ (d/create-database "datomic:sql://my-datomic-database-test?jdbc:postgresql://datomic-storage:5432/my-datomic-storage?user=datomic-user&password=unsafe")
825
+
826
+ (d/create-database "datomic:sql://my-datomic-database-test-green?jdbc:postgresql://datomic-storage:5432/my-datomic-storage?user=datomic-user&password=unsafe")
827
+
828
+ (System/exit 0)
829
+ CLOJURE
830
+ )"
831
+
832
+ docker compose up -d datomic-peer-server
833
+
834
+ docker compose up -d datomic-flare-peer datomic-flare-client
835
+
836
+ ```
837
+
838
+ ```bash
839
+ curl -s http://localhost:3042/meta \
840
+ -X GET \
841
+ -H "Content-Type: application/json" \
842
+ | jq
843
+
844
+ ```
845
+
846
+ ```json
847
+ {
848
+ "data": {
849
+ "mode": "peer"
850
+ }
851
+ }
852
+
853
+ ```
854
+
855
+ ```bash
856
+ curl -s http://localhost:3043/meta \
857
+ -X GET \
858
+ -H "Content-Type: application/json" \
859
+ | jq
860
+
861
+ ```
862
+
863
+ ```json
864
+ {
865
+ "data": {
866
+ "mode": "client"
867
+ }
868
+ }
869
+
870
+ ```
871
+
872
+ You are ready to run tests and generate documentation.
873
+
874
+ **Detailed instructions:**
875
+
876
+ Clone the [datomic-pro-docker](https://github.com/gbaptista/datomic-pro-docker) repository and copy the Docker Compose template:
877
+
878
+ ```bash
879
+ git clone https://github.com/gbaptista/datomic-pro-docker.git
880
+
881
+ cd datomic-pro-docker
882
+
883
+ cp compose/flare-dev.yml docker-compose.yml
884
+
885
+ ```
886
+
887
+ Start PostgreSQL as Datomic's storage service:
888
+
889
+ ```bash
890
+ docker compose up -d datomic-storage
891
+
892
+ docker compose logs -f datomic-storage
893
+
894
+ ```
895
+
896
+ Create the table for Datomic databases:
897
+
898
+ ```bash
899
+ docker compose run datomic-tools psql \
900
+ -f bin/sql/postgres-table.sql \
901
+ -h datomic-storage \
902
+ -U datomic-user \
903
+ -d my-datomic-storage
904
+
905
+ ```
906
+
907
+ You will be prompted for a password, which is `unsafe`.
908
+
909
+ Start the Datomic Transactor:
910
+
911
+ ```bash
912
+ docker compose up -d datomic-transactor
913
+
914
+ docker compose logs -f datomic-transactor
915
+
916
+ ```
917
+
918
+ Create the following databases:
919
+
920
+ - `my-datomic-database`
921
+ - `my-datomic-database-test`
922
+ - `my-datomic-database-test-green`
923
+
924
+ ```bash
925
+ docker compose run datomic-tools clojure -M -e "$(cat <<'CLOJURE'
926
+ (require '[datomic.api :as d])
927
+
928
+ (d/create-database "datomic:sql://my-datomic-database?jdbc:postgresql://datomic-storage:5432/my-datomic-storage?user=datomic-user&password=unsafe")
929
+
930
+ (d/create-database "datomic:sql://my-datomic-database-test?jdbc:postgresql://datomic-storage:5432/my-datomic-storage?user=datomic-user&password=unsafe")
931
+
932
+ (d/create-database "datomic:sql://my-datomic-database-test-green?jdbc:postgresql://datomic-storage:5432/my-datomic-storage?user=datomic-user&password=unsafe")
933
+
934
+ (System/exit 0)
935
+ CLOJURE
936
+ )"
937
+
938
+ ```
939
+
940
+ Start the Peer Server:
941
+
942
+ ```bash
943
+ docker compose up -d datomic-peer-server
944
+
945
+ docker compose logs -f datomic-peer-server
946
+
947
+ ```
948
+
949
+ Start 2 instances of Flare, one in Peer Mode and another in Client Mode:
950
+
951
+ ```bash
952
+ docker compose up -d datomic-flare-peer datomic-flare-client
953
+
954
+ docker compose logs -f datomic-flare-peer
955
+ docker compose logs -f datomic-flare-client
956
+
957
+ ```
958
+
959
+ You should be able to request both:
960
+
961
+ Datomic Flare in Peer Mode:
962
+ ```bash
963
+ curl -s http://localhost:3042/meta \
964
+ -X GET \
965
+ -H "Content-Type: application/json" \
966
+ | jq
967
+
968
+ ```
969
+
970
+ ```json
971
+ {
972
+ "data": {
973
+ "mode": "peer"
974
+ }
975
+ }
976
+
977
+ ```
978
+
979
+ Datomic Flare in Client Mode:
980
+ ```bash
981
+ curl -s http://localhost:3043/meta \
982
+ -X GET \
983
+ -H "Content-Type: application/json" \
984
+ | jq
985
+
986
+ ```
987
+
988
+ ```json
989
+ {
990
+ "data": {
991
+ "mode": "client"
992
+ }
993
+ }
994
+
995
+ ```
996
+
997
+ You are ready to run tests and generate documentation.
998
+
999
+ ### Running Tests
1000
+
1001
+ Tests run against real Datomic databases, so complete the [Setup for Tests and Documentation](#setup-for-tests-and-documentation) first.
1002
+
1003
+ ```bash
1004
+ cp .env.example .env
1005
+
1006
+ bundle exec rspec
1007
+
1008
+ ```
1009
+
1010
+ ### Updating the README
1011
+
1012
+ Documentation (README) is generated by interacting with real Datomic databases, so complete the [Setup for Tests and Documentation](#setup-for-tests-and-documentation) first.
1013
+
1014
+ Update the `docs/templates/*.md` files, and then:
1015
+
1016
+ ```sh
1017
+ cp .env.example .env
1018
+
1019
+ bundle exec ruby ports/cli.rb docs:generate
1020
+
1021
+ ```
1022
+
1023
+ Trick for automatically updating the `README.md` when `docs/templates/*.md` files change:
1024
+
1025
+ ```sh
1026
+ sudo pacman -S inotify-tools # Arch / Manjaro
1027
+ sudo apt-get install inotify-tools # Debian / Ubuntu / Raspberry Pi OS
1028
+ sudo dnf install inotify-tools # Fedora / CentOS / RHEL
1029
+
1030
+ while inotifywait -e modify docs/templates/*; \
1031
+ do bundle exec ruby ports/cli.rb docs:generate; \
1032
+ done
1033
+
1034
+ ```
1035
+
1036
+ Trick for Markdown Live Preview:
1037
+ ```sh
1038
+ pip install -U markdown_live_preview
1039
+
1040
+ mlp README.md -p 8042 --no-follow
1041
+
1042
+ ```