datomic-flare 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.env.example +4 -0
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.rubocop.yml +15 -0
- data/.ruby-version +1 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +100 -0
- data/LICENSE +9 -0
- data/README.md +1042 -0
- data/components/errors.rb +22 -0
- data/components/http.rb +55 -0
- data/controllers/api.rb +48 -0
- data/controllers/client.rb +43 -0
- data/controllers/documentation/formatter.rb +37 -0
- data/controllers/documentation/generator.rb +390 -0
- data/controllers/dsl/querying.rb +39 -0
- data/controllers/dsl/schema.rb +37 -0
- data/controllers/dsl/transacting.rb +62 -0
- data/controllers/dsl.rb +48 -0
- data/datomic-flare.gemspec +39 -0
- data/docs/CURL.md +781 -0
- data/docs/README.md +360 -0
- data/docs/api.md +395 -0
- data/docs/dsl.md +257 -0
- data/docs/templates/.rubocop.yml +15 -0
- data/docs/templates/README.md +319 -0
- data/docs/templates/api.md +267 -0
- data/docs/templates/dsl.md +206 -0
- data/helpers/h.rb +17 -0
- data/logic/dangerous_override.rb +108 -0
- data/logic/querying.rb +34 -0
- data/logic/schema.rb +91 -0
- data/logic/transacting.rb +53 -0
- data/logic/types.rb +141 -0
- data/ports/cli.rb +26 -0
- data/ports/dsl/datomic-flare/errors.rb +5 -0
- data/ports/dsl/datomic-flare.rb +20 -0
- data/static/gem.rb +15 -0
- metadata +146 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
## Flare DSL
|
2
|
+
|
3
|
+
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.
|
4
|
+
|
5
|
+
This approach should be cozy to those who are familiar with Ruby.
|
6
|
+
|
7
|
+
Learn more about Ruby and The Rails Doctrine:
|
8
|
+
|
9
|
+
- [About Ruby](https://www.ruby-lang.org/en/about/)
|
10
|
+
- [The Rails Doctrine](https://rubyonrails.org/doctrine)
|
11
|
+
|
12
|
+
### Creating a Database
|
13
|
+
|
14
|
+
```ruby:runnable
|
15
|
+
client.dsl.create_database!('radioactive')
|
16
|
+
```
|
17
|
+
|
18
|
+
```ruby:placeholder
|
19
|
+
```
|
20
|
+
|
21
|
+
### Deleting a Database
|
22
|
+
|
23
|
+
```ruby:runnable
|
24
|
+
client.dsl.destroy_database!('radioactive')
|
25
|
+
```
|
26
|
+
|
27
|
+
```ruby:placeholder
|
28
|
+
```
|
29
|
+
|
30
|
+
### Listing Databases
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
client.dsl.databases
|
34
|
+
```
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
['my-datomic-database']
|
38
|
+
```
|
39
|
+
|
40
|
+
### Transacting Schema
|
41
|
+
|
42
|
+
Like `CREATE TABLE` in SQL databases or defining document or record structures in other databases.
|
43
|
+
|
44
|
+
```ruby:runnable
|
45
|
+
client.dsl.transact_schema!(
|
46
|
+
{
|
47
|
+
book: {
|
48
|
+
title: { type: :string, doc: 'The title of the book.' },
|
49
|
+
genre: { type: :string, doc: 'The genre of the book.' },
|
50
|
+
published_at_year: { type: :long, doc: 'The year the book was first published.' }
|
51
|
+
}
|
52
|
+
})
|
53
|
+
```
|
54
|
+
|
55
|
+
```ruby:placeholder
|
56
|
+
```
|
57
|
+
|
58
|
+
### Checking Schema
|
59
|
+
|
60
|
+
Like `SHOW COLUMNS FROM` in SQL databases or checking document or record structures in other databases.
|
61
|
+
|
62
|
+
```ruby:runnable
|
63
|
+
client.dsl.schema
|
64
|
+
```
|
65
|
+
|
66
|
+
```ruby:placeholder
|
67
|
+
|
68
|
+
```
|
69
|
+
|
70
|
+
### Asserting Facts
|
71
|
+
|
72
|
+
Like `INSERT INTO` in SQL databases or creating a new document or record in other databases.
|
73
|
+
|
74
|
+
```ruby:runnable
|
75
|
+
client.dsl.assert_into!(
|
76
|
+
:book,
|
77
|
+
{ title: 'Pride and Prejudice',
|
78
|
+
genre: 'Romance',
|
79
|
+
published_at_year: 1813 }
|
80
|
+
)
|
81
|
+
```
|
82
|
+
|
83
|
+
```ruby:placeholder
|
84
|
+
```
|
85
|
+
|
86
|
+
```ruby:runnable
|
87
|
+
client.dsl.assert_into!(
|
88
|
+
:book,
|
89
|
+
[{ title: 'Near to the Wild Heart',
|
90
|
+
genre: 'Novel',
|
91
|
+
published_at_year: 1943 },
|
92
|
+
{ title: 'A Study in Scarlet',
|
93
|
+
genre: 'Detective',
|
94
|
+
published_at_year: 1887 },
|
95
|
+
{ title: 'The Tell-Tale Heart',
|
96
|
+
genre: 'Horror',
|
97
|
+
published_at_year: 1843 }]
|
98
|
+
)
|
99
|
+
```
|
100
|
+
|
101
|
+
```ruby:state
|
102
|
+
state[:wild_heart_entity_id] = result[0]
|
103
|
+
state[:scarlet_entity_id] = result[1]
|
104
|
+
```
|
105
|
+
|
106
|
+
```ruby:placeholder
|
107
|
+
```
|
108
|
+
|
109
|
+
### Reading Data by Entity
|
110
|
+
|
111
|
+
Like `SELECT` in SQL databases or querying documents or records in other databases.
|
112
|
+
|
113
|
+
```ruby:runnable/render
|
114
|
+
client.dsl.find_by_entity_id({{ state.wild_heart_entity_id }})
|
115
|
+
```
|
116
|
+
|
117
|
+
```ruby:placeholder
|
118
|
+
```
|
119
|
+
|
120
|
+
### Reading Data by Querying
|
121
|
+
|
122
|
+
Like `SELECT` in SQL databases or querying documents or records in other databases.
|
123
|
+
|
124
|
+
```ruby:runnable
|
125
|
+
client.dsl.query(
|
126
|
+
datalog: <<~EDN
|
127
|
+
[:find ?e ?title ?genre ?year
|
128
|
+
:where [?e :book/title ?title]
|
129
|
+
[?e :book/genre ?genre]
|
130
|
+
[?e :book/published_at_year ?year]]
|
131
|
+
EDN
|
132
|
+
)
|
133
|
+
```
|
134
|
+
|
135
|
+
```ruby:placeholder
|
136
|
+
```
|
137
|
+
|
138
|
+
```ruby:runnable
|
139
|
+
client.dsl.query(
|
140
|
+
params: ['The Tell-Tale Heart'],
|
141
|
+
datalog: <<~EDN
|
142
|
+
[:find ?e ?title ?genre ?year
|
143
|
+
:in $ ?title
|
144
|
+
:where [?e :book/title ?title]
|
145
|
+
[?e :book/genre ?genre]
|
146
|
+
[?e :book/published_at_year ?year]]
|
147
|
+
EDN
|
148
|
+
)
|
149
|
+
```
|
150
|
+
|
151
|
+
```ruby:state
|
152
|
+
state[:tale_heart_entity_id] = result[0][0]
|
153
|
+
```
|
154
|
+
|
155
|
+
```ruby:placeholder
|
156
|
+
```
|
157
|
+
|
158
|
+
### Accumulating Facts
|
159
|
+
|
160
|
+
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.
|
161
|
+
|
162
|
+
```ruby:runnable/render
|
163
|
+
client.dsl.assert_into!(
|
164
|
+
:book, { _id: {{ state.tale_heart_entity_id }}, genre: 'Gothic' }
|
165
|
+
)
|
166
|
+
```
|
167
|
+
|
168
|
+
```ruby:placeholder
|
169
|
+
```
|
170
|
+
|
171
|
+
### Retracting Facts
|
172
|
+
|
173
|
+
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.
|
174
|
+
|
175
|
+
Retract the value of an attribute:
|
176
|
+
|
177
|
+
```ruby:runnable/render
|
178
|
+
client.dsl.retract_from!(
|
179
|
+
:book, { _id: {{ state.tale_heart_entity_id }}, genre: 'Gothic' }
|
180
|
+
)
|
181
|
+
```
|
182
|
+
|
183
|
+
```ruby:placeholder
|
184
|
+
```
|
185
|
+
|
186
|
+
Retract an attribute:
|
187
|
+
|
188
|
+
```ruby:runnable/render
|
189
|
+
client.dsl.retract_from!(
|
190
|
+
:book, { _id: {{ state.wild_heart_entity_id }}, genre: nil }
|
191
|
+
)
|
192
|
+
```
|
193
|
+
|
194
|
+
```ruby:placeholder
|
195
|
+
```
|
196
|
+
|
197
|
+
Retract an entity:
|
198
|
+
|
199
|
+
```ruby:runnable/render
|
200
|
+
client.dsl.retract_from!(
|
201
|
+
:book, { _id: {{ state.scarlet_entity_id }} }
|
202
|
+
)
|
203
|
+
```
|
204
|
+
|
205
|
+
```ruby:placeholder
|
206
|
+
```
|
data/helpers/h.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Flare
|
4
|
+
module H
|
5
|
+
def self.symbolize_keys(structure)
|
6
|
+
result = {}
|
7
|
+
|
8
|
+
structure.each do |key, value|
|
9
|
+
string_key = key.to_sym
|
10
|
+
|
11
|
+
result[string_key] = value.is_a?(Hash) ? symbolize_keys(value) : value
|
12
|
+
end
|
13
|
+
|
14
|
+
result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Flare
|
4
|
+
module DangerousOverrideLogic
|
5
|
+
CONNECTION_DATABASE_KEYS = [:name].freeze
|
6
|
+
|
7
|
+
DATABASE_KEYS = %i[name latest as_of].freeze
|
8
|
+
|
9
|
+
def self.apply_dangerous_overrides_to_payload(path, overrides, payload)
|
10
|
+
case path
|
11
|
+
when 'datomic/create-database', 'datomic/delete-database',
|
12
|
+
'datomic/get-database-names', 'datomic/list-databases',
|
13
|
+
'meta',
|
14
|
+
'datomic/_debug/as-peer/create-database',
|
15
|
+
'datomic/_debug/as-peer/delete-database'
|
16
|
+
payload
|
17
|
+
when 'datomic/transact'
|
18
|
+
inject_connection_overrides(overrides, payload)
|
19
|
+
when 'datomic/entity', 'datomic/datoms'
|
20
|
+
inject_database_overrides(overrides, payload)
|
21
|
+
when 'datomic/q'
|
22
|
+
inject_database_overrides_into_inputs(overrides, payload)
|
23
|
+
else
|
24
|
+
raise "Unexpected path: '#{path}'"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.inject_connection_overrides(overrides, payload)
|
29
|
+
if !overrides.key?(:database) || overrides[:database].slice(
|
30
|
+
*CONNECTION_DATABASE_KEYS
|
31
|
+
).empty?
|
32
|
+
return payload
|
33
|
+
end
|
34
|
+
|
35
|
+
payload[:connection] = {} unless payload.key?(:connection)
|
36
|
+
|
37
|
+
payload[:connection][:database] = {} unless payload[:connection].key?(:database)
|
38
|
+
|
39
|
+
payload[:connection][:database] = payload[:connection][:database].merge(
|
40
|
+
overrides[:database].slice(*CONNECTION_DATABASE_KEYS)
|
41
|
+
)
|
42
|
+
|
43
|
+
payload
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.inject_overrides_into_input(overrides, input)
|
47
|
+
input_has_latest = input[:database].key?(:latest)
|
48
|
+
|
49
|
+
input_has_as_of = input[:database].key?(:as_of)
|
50
|
+
|
51
|
+
overrides_has_as_of = overrides[:database].key?(:as_of)
|
52
|
+
overrides_has_latest = overrides[:database].key?(:latest)
|
53
|
+
|
54
|
+
input[:database] = input[:database].except(:latest) if input_has_latest && overrides_has_as_of
|
55
|
+
|
56
|
+
input[:database] = input[:database].except(:as_of) if input_has_as_of && overrides_has_latest
|
57
|
+
|
58
|
+
input[:database] = input[:database].merge(
|
59
|
+
overrides[:database].slice(*DATABASE_KEYS)
|
60
|
+
)
|
61
|
+
|
62
|
+
input
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.inject_database_overrides_into_inputs(overrides, payload)
|
66
|
+
if !overrides.key?(:database) || overrides[:database].slice(
|
67
|
+
*DATABASE_KEYS
|
68
|
+
).empty?
|
69
|
+
return payload
|
70
|
+
end
|
71
|
+
|
72
|
+
payload[:inputs] = {} unless payload.key?(:inputs)
|
73
|
+
|
74
|
+
index_for_database = payload[:inputs].index do |input|
|
75
|
+
input.key?(:database)
|
76
|
+
end
|
77
|
+
|
78
|
+
if index_for_database.nil?
|
79
|
+
require 'pry'
|
80
|
+
binding.pry
|
81
|
+
end
|
82
|
+
|
83
|
+
payload[:inputs][index_for_database] = inject_overrides_into_input(
|
84
|
+
overrides,
|
85
|
+
payload[:inputs][index_for_database]
|
86
|
+
)
|
87
|
+
|
88
|
+
payload
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.inject_database_overrides(overrides, payload)
|
92
|
+
if !overrides.key?(:database) || overrides[:database].slice(
|
93
|
+
*DATABASE_KEYS
|
94
|
+
).empty?
|
95
|
+
return payload
|
96
|
+
end
|
97
|
+
|
98
|
+
payload[:database] = {} unless payload.key?(:database)
|
99
|
+
|
100
|
+
payload[:database] = inject_overrides_into_input(
|
101
|
+
overrides,
|
102
|
+
{ database: payload[:database] }
|
103
|
+
)[:database]
|
104
|
+
|
105
|
+
payload
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/logic/querying.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Flare
|
4
|
+
module QueryingLogic
|
5
|
+
def self.entity_to_dsl(entity)
|
6
|
+
namespace = entity.keys.find { |key| key != ':db/id' }
|
7
|
+
|
8
|
+
return nil if namespace.nil?
|
9
|
+
|
10
|
+
namespace = namespace.split('/').first.sub(/^:/, '').to_sym
|
11
|
+
|
12
|
+
{
|
13
|
+
namespace => keys_to_dsl(entity)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.keys_to_dsl(entity)
|
18
|
+
result = {}
|
19
|
+
|
20
|
+
entity.each do |key, value|
|
21
|
+
# TODO: Is this correct? Should the 'id' exist?
|
22
|
+
dsl_key = if [':db/id', 'id'].include?(key)
|
23
|
+
:_id
|
24
|
+
else
|
25
|
+
key.split('/').last.to_sym
|
26
|
+
end
|
27
|
+
|
28
|
+
result[dsl_key] = value.is_a?(Hash) ? keys_to_dsl(value) : value
|
29
|
+
end
|
30
|
+
|
31
|
+
result
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/logic/schema.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helpers/h'
|
4
|
+
|
5
|
+
require_relative 'types'
|
6
|
+
|
7
|
+
module Flare
|
8
|
+
module SchemaLogic
|
9
|
+
QUERY = <<~EDN
|
10
|
+
[:find
|
11
|
+
?e ?ident ?value_type ?cardinality ?doc
|
12
|
+
?unique ?index ?no_history
|
13
|
+
:in $
|
14
|
+
:where
|
15
|
+
[?e :db/ident ?ident]
|
16
|
+
|
17
|
+
[?e :db/valueType ?value_type_id]
|
18
|
+
[?value_type_id :db/ident ?value_type]
|
19
|
+
|
20
|
+
[?e :db/cardinality ?cardinality_id]
|
21
|
+
[?cardinality_id :db/ident ?cardinality]
|
22
|
+
|
23
|
+
[(get-else $ ?e :db/doc "") ?doc]
|
24
|
+
|
25
|
+
[(get-else $ ?e :db/unique -1) ?unique_id]
|
26
|
+
[(get-else $ ?unique_id :db/ident false) ?unique]
|
27
|
+
|
28
|
+
[(get-else $ ?e :db/index false) ?index]
|
29
|
+
[(get-else $ ?e :db/noHistory false) ?no_history]]
|
30
|
+
EDN
|
31
|
+
|
32
|
+
NON_SCHEMA_NAMESPACES = %w[
|
33
|
+
db
|
34
|
+
db.alter db.attr db.bootstrap db.cardinality db.entity db.excise
|
35
|
+
db.fn db.install db.lang db.part db.sys db.type db.unique
|
36
|
+
fressian
|
37
|
+
].freeze
|
38
|
+
|
39
|
+
def self.specification_to_edn(specification)
|
40
|
+
edn_schema = specification.flat_map do |namespace, attributes|
|
41
|
+
attributes.map.with_index do |(attribute, options), i|
|
42
|
+
fields = [
|
43
|
+
"#{i.zero? ? '' : ' '}{:db/ident :#{namespace}/#{attribute}",
|
44
|
+
" :db/valueType #{TypesLogic.ruby_to_datomic_type(options[:type])}",
|
45
|
+
" :db/cardinality #{TypesLogic.ruby_to_datomic_cardinality(options[:cardinality] || :one)}"
|
46
|
+
]
|
47
|
+
|
48
|
+
fields << " :db/doc \"#{options[:doc]}\"" if options[:doc]
|
49
|
+
fields << " :db/unique #{TypesLogic.ruby_to_datomic_unique(options[:unique])}" if options[:unique]
|
50
|
+
fields << ' :db/index true' if options[:index]
|
51
|
+
fields << ' :db/noHistory true' if options[:history] == false
|
52
|
+
|
53
|
+
fields[fields.size - 1] = "#{fields.last}}"
|
54
|
+
|
55
|
+
fields.join("\n")
|
56
|
+
end
|
57
|
+
end.join("\n\n")
|
58
|
+
|
59
|
+
"[#{edn_schema}]"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.datoms_to_specification(datoms)
|
63
|
+
specification = {}
|
64
|
+
|
65
|
+
datoms.filter do |datom|
|
66
|
+
!NON_SCHEMA_NAMESPACES.include?(datom[1].split('/').first)
|
67
|
+
end.each do |entry|
|
68
|
+
namespace, attribute = entry[1].split('/')
|
69
|
+
type = TypesLogic.datomic_to_ruby_type(entry[2])
|
70
|
+
cardinality = TypesLogic.datomic_to_ruby_cardinality(entry[3])
|
71
|
+
doc = entry[4].empty? ? nil : entry[4]
|
72
|
+
|
73
|
+
unique = entry[5] ? TypesLogic.datomic_to_ruby_unique(entry[5]) : false
|
74
|
+
indexed = entry[6]
|
75
|
+
no_history = entry[7]
|
76
|
+
|
77
|
+
specification[namespace] ||= {}
|
78
|
+
specification[namespace][attribute] = {
|
79
|
+
type:,
|
80
|
+
cardinality:,
|
81
|
+
doc:,
|
82
|
+
unique:,
|
83
|
+
index: indexed,
|
84
|
+
history: !no_history
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
H.symbolize_keys(specification)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'types'
|
4
|
+
|
5
|
+
module Flare
|
6
|
+
module TransactingLogic
|
7
|
+
def self.retractions_to_edn(namespace, retractions)
|
8
|
+
edn = retractions.map do |retraction|
|
9
|
+
retraction_to_edn(namespace, retraction)
|
10
|
+
end
|
11
|
+
|
12
|
+
"[#{edn.join("\n ")}]"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.retraction_to_edn(namespace, retraction)
|
16
|
+
id = retraction[:_id]
|
17
|
+
|
18
|
+
attributes = retraction.except(:_id)
|
19
|
+
|
20
|
+
if attributes.empty?
|
21
|
+
# Built-In Transaction Functions
|
22
|
+
# https://docs.datomic.com/transactions/transaction-functions.html#built-in
|
23
|
+
"[:db/retractEntity #{id}]"
|
24
|
+
else
|
25
|
+
attributes.map do |attribute, value|
|
26
|
+
if value.nil?
|
27
|
+
"[:db/retract #{id} :#{namespace}/#{attribute}]"
|
28
|
+
else
|
29
|
+
"[:db/retract #{id} :#{namespace}/#{attribute} #{TypesLogic.to_datomic_value(value)}]"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.transactions_to_edn(namespace, transactions)
|
36
|
+
edn = transactions.map do |transaction|
|
37
|
+
attributes = transaction.map.with_index do |(attribute, value), i|
|
38
|
+
ident = if %i[_id _temporary_id].include?(attribute)
|
39
|
+
':db/id'
|
40
|
+
else
|
41
|
+
":#{namespace}/#{attribute}"
|
42
|
+
end
|
43
|
+
|
44
|
+
"#{i.zero? ? '' : ' '}#{ident} #{TypesLogic.to_datomic_value(value)}"
|
45
|
+
end.join("\n")
|
46
|
+
|
47
|
+
"{#{attributes}}"
|
48
|
+
end.join("\n ")
|
49
|
+
|
50
|
+
"[#{edn}]"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/logic/types.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'bigdecimal'
|
5
|
+
|
6
|
+
module Flare
|
7
|
+
module TypesLogic
|
8
|
+
def self.to_datomic_value(value)
|
9
|
+
case value
|
10
|
+
when String
|
11
|
+
to_datomic_string(value)
|
12
|
+
when Integer
|
13
|
+
value.to_s
|
14
|
+
when Float
|
15
|
+
value.to_s
|
16
|
+
when BigDecimal
|
17
|
+
# https://github.com/relevance/edn-ruby/blob/master/lib/edn/core_ext.rb#L25
|
18
|
+
"#{value.to_s('F')}M"
|
19
|
+
when Integer, Float, TrueClass, FalseClass
|
20
|
+
value.to_s
|
21
|
+
when Time, DateTime, Date
|
22
|
+
to_datomic_instant(value)
|
23
|
+
when Symbol
|
24
|
+
":#{value}"
|
25
|
+
when Array
|
26
|
+
'[' + value.map { |v| to_datomic_value(v) }.join(' ') + ']'
|
27
|
+
when NilClass
|
28
|
+
'nil'
|
29
|
+
when Hash
|
30
|
+
raise ArgumentError, "Missing :_id for reference: #{value.class}" unless value.key?(:_id)
|
31
|
+
|
32
|
+
"{:db/id #{value[:_id]}}"
|
33
|
+
else
|
34
|
+
raise ArgumentError, "Unsupported value type: #{value.class}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.to_datomic_string(value)
|
39
|
+
# https://github.com/relevance/edn-ruby/blob/master/lib/edn/core_ext.rb#L36
|
40
|
+
array = value.chars.map do |ch|
|
41
|
+
if %w[" \\].include?(ch)
|
42
|
+
"\\#{ch}"
|
43
|
+
else
|
44
|
+
ch
|
45
|
+
end
|
46
|
+
end
|
47
|
+
"\"#{array.join}\""
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.to_datomic_instant(value)
|
51
|
+
time = case value
|
52
|
+
when Time
|
53
|
+
value
|
54
|
+
when Date
|
55
|
+
value.to_time
|
56
|
+
end
|
57
|
+
|
58
|
+
"#inst \"#{time.utc.strftime('%Y-%m-%dT%H:%M:%S.%L%:z')}\""
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.ruby_to_datomic_type(ruby_type)
|
62
|
+
case ruby_type
|
63
|
+
when :string then ':db.type/string'
|
64
|
+
when :long then ':db.type/long'
|
65
|
+
when :boolean then ':db.type/boolean'
|
66
|
+
when :double then ':db.type/double'
|
67
|
+
when :instant then ':db.type/instant'
|
68
|
+
when :keyword then ':db.type/keyword'
|
69
|
+
when :uuid then ':db.type/uuid'
|
70
|
+
when :ref then ':db.type/ref'
|
71
|
+
when :bigdec then ':db.type/bigdec'
|
72
|
+
when :bigint then ':db.type/bigint'
|
73
|
+
when :uri then ':db.type/uri'
|
74
|
+
else
|
75
|
+
raise ArgumentError, "Unknown type: #{ruby_type}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.ruby_to_datomic_cardinality(cardinality)
|
80
|
+
case cardinality
|
81
|
+
when :one then ':db.cardinality/one'
|
82
|
+
when :many then ':db.cardinality/many'
|
83
|
+
else
|
84
|
+
raise ArgumentError, "Unknown cardinality: #{cardinality}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.ruby_to_datomic_unique(unique_type)
|
89
|
+
case unique_type
|
90
|
+
when :identity then ':db.unique/identity'
|
91
|
+
when :value then ':db.unique/value'
|
92
|
+
when nil then nil
|
93
|
+
else
|
94
|
+
raise ArgumentError, "Unknown uniqueness constraint: #{unique_type}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.datomic_to_ruby_type(datomic_type)
|
99
|
+
datomic_type = ":#{datomic_type}" unless datomic_type.start_with?(':')
|
100
|
+
case datomic_type
|
101
|
+
when ':db.type/bigdec' then :bigdec
|
102
|
+
when ':db.type/bigint' then :bigint
|
103
|
+
when ':db.type/boolean' then :boolean
|
104
|
+
when ':db.type/bytes' then :bytes
|
105
|
+
when ':db.type/double' then :double
|
106
|
+
when ':db.type/float' then :float
|
107
|
+
when ':db.type/instant' then :instant
|
108
|
+
when ':db.type/keyword' then :keyword
|
109
|
+
when ':db.type/long' then :long
|
110
|
+
when ':db.type/ref' then :ref
|
111
|
+
when ':db.type/string' then :string
|
112
|
+
when ':db.type/symbol' then :symbol
|
113
|
+
when ':db.type/tuple' then :tuple
|
114
|
+
when ':db.type/uuid' then :uuid
|
115
|
+
when ':db.type/uri' then :uri
|
116
|
+
else
|
117
|
+
raise ArgumentError, "Unknown Datomic type: #{datomic_type}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.datomic_to_ruby_unique(datomic_unique)
|
122
|
+
datomic_unique = ":#{datomic_unique}" unless datomic_unique.start_with?(':')
|
123
|
+
case datomic_unique
|
124
|
+
when ':db.unique/value' then :value
|
125
|
+
when ':db.unique/identity' then :identity
|
126
|
+
else
|
127
|
+
raise ArgumentError, "Unknown Datomic uniqueness: #{datomic_unique}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.datomic_to_ruby_cardinality(datomic_cardinality)
|
132
|
+
datomic_cardinality = ":#{datomic_cardinality}" unless datomic_cardinality.start_with?(':')
|
133
|
+
case datomic_cardinality
|
134
|
+
when ':db.cardinality/one' then :one
|
135
|
+
when ':db.cardinality/many' then :many
|
136
|
+
else
|
137
|
+
raise ArgumentError, "Unknown Datomic cardinality: #{datomic_cardinality}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/ports/cli.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dotenv/load'
|
4
|
+
|
5
|
+
require_relative '../controllers/documentation/generator'
|
6
|
+
|
7
|
+
module Flare
|
8
|
+
module CLI
|
9
|
+
def self.handle(command)
|
10
|
+
case command
|
11
|
+
when 'docs:generate'
|
12
|
+
Flare::Controllers::Documentation::Generator.handler
|
13
|
+
else
|
14
|
+
puts 'Invalid command.'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if __FILE__ == $PROGRAM_NAME
|
21
|
+
if ARGV.empty?
|
22
|
+
puts 'No command provided.'
|
23
|
+
else
|
24
|
+
Flare::CLI.handle(ARGV[0])
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uuidx'
|
4
|
+
|
5
|
+
require_relative '../../static/gem'
|
6
|
+
require_relative '../../controllers/client'
|
7
|
+
|
8
|
+
module Flare
|
9
|
+
def self.new(...)
|
10
|
+
Controllers::Client.new(...)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.uuid
|
14
|
+
Uuidx
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.version
|
18
|
+
Flare::GEM[:version]
|
19
|
+
end
|
20
|
+
end
|