active_chronicle 0.1.0

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.
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chronicle
4
+ module Transport
5
+ class JRubyClient < Base
6
+ attr_reader :connection, :current_basis_t
7
+
8
+ def initialize(config = {})
9
+ super
10
+ @endpoint = config[:client_endpoint] || config['client_endpoint'] || 'localhost:8998'
11
+ @access_key = config[:access_key] || config['access_key'] || 'chronicle-dev'
12
+ @secret = config[:secret] || config['secret'] || 'chronicle-dev-secret'
13
+ @db_name = config[:db_name] || config['db_name'] || datomic_db_name(config[:uri] || config['uri'])
14
+ @api = nil
15
+ end
16
+
17
+ def connect!
18
+ Chronicle::Resilience.with_retry do
19
+ @client = api.client(
20
+ 'server-type' => :peer_server,
21
+ 'access-key' => @access_key,
22
+ 'secret' => @secret,
23
+ 'endpoint' => @endpoint,
24
+ 'validate-hostnames' => false
25
+ )
26
+ @connection = api.connect(@client, 'db-name' => @db_name)
27
+ @connected = true
28
+ end
29
+ end
30
+
31
+ def transact(tx_data)
32
+ Chronicle::Resilience.with_retry do
33
+ result = api.transact(@connection, 'tx-data' => tx_data)
34
+ @current_basis_t = result.dig('db-after', 'basis-t') if result['db-after'].is_a?(Hash)
35
+ result
36
+ end
37
+ end
38
+
39
+ def q(query, *args)
40
+ Chronicle::Resilience.with_retry do
41
+ database = args.first || api.db(@connection)
42
+ api.q('query' => query, 'args' => [database, *args.drop(1)])
43
+ end
44
+ end
45
+
46
+ def pull(entity_id, database = nil, pattern = ['*'])
47
+ Chronicle::Resilience.with_retry do
48
+ api.pull(database || api.db(@connection), entity_id, pattern)
49
+ end
50
+ end
51
+
52
+ def db(as_of: nil, since: nil)
53
+ database = api.db(@connection)
54
+ database = api.as_of(database, as_of) if as_of
55
+ database = api.since(database, since) if since
56
+ database
57
+ end
58
+
59
+ def basis_t
60
+ @current_basis_t || api.db(@connection)['t'] || 0
61
+ end
62
+
63
+ private
64
+
65
+ def api
66
+ @api ||= ClojureApi.new
67
+ end
68
+
69
+ def datomic_db_name(uri)
70
+ uri.to_s.split('/').last
71
+ end
72
+
73
+ class ClojureApi
74
+ def initialize
75
+ raise Chronicle::Error, 'JRubyClient requires JRuby' unless Chronicle::Transport.jruby?
76
+
77
+ load_datomic_jars
78
+ @clojure = Java::ClojureJavaApi::Clojure
79
+ @clojure.var('clojure.core', 'require').invoke(
80
+ @clojure.var('clojure.core', 'symbol').invoke('datomic.client.api')
81
+ )
82
+ end
83
+
84
+ def client(config)
85
+ invoke('client', clojure_map(config))
86
+ end
87
+
88
+ def connect(client, config)
89
+ invoke('connect', client, clojure_map(config))
90
+ end
91
+
92
+ def transact(connection, config)
93
+ ruby_value(invoke('transact', connection, clojure_map(config)))
94
+ end
95
+
96
+ def q(config)
97
+ query_config = config.dup
98
+ query_config['query'] = var('clojure.core', 'list').invoke(*query_config['query'].map { |item| clojure_value(item) })
99
+ ruby_value(invoke('q', clojure_map(query_config)))
100
+ end
101
+
102
+ def pull(database, entity_id, pattern)
103
+ ruby_value(invoke('pull', database, clojure_value(pattern), entity_id))
104
+ end
105
+
106
+ def db(connection)
107
+ invoke('db', connection)
108
+ end
109
+
110
+ def as_of(database, value)
111
+ invoke('as-of', database, value)
112
+ end
113
+
114
+ def since(database, value)
115
+ invoke('since', database, value)
116
+ end
117
+
118
+ private
119
+
120
+ def load_datomic_jars
121
+ datomic_home = ENV['DATOMIC_HOME'] || '/opt/datomic'
122
+ Dir[File.join(datomic_home, 'lib', '*.jar')].each { |jar| require jar }
123
+ end
124
+
125
+ def invoke(function, *)
126
+ var('datomic.client.api', function).invoke(*)
127
+ end
128
+
129
+ def var(namespace, function)
130
+ @clojure.var(namespace, function)
131
+ end
132
+
133
+ def keyword(value)
134
+ var('clojure.core', 'keyword').invoke(value.to_s.delete_prefix(':').tr('_', '-'))
135
+ end
136
+
137
+ def clojure_map(hash)
138
+ entries = hash.flat_map { |key, value| [keyword(key), clojure_value(value)] }
139
+ var('clojure.core', 'hash-map').invoke(*entries)
140
+ end
141
+
142
+ def clojure_value(value)
143
+ case value
144
+ when Hash
145
+ clojure_map(value)
146
+ when Array
147
+ Java::ClojureLang::PersistentVector.create(value.map { |item| clojure_value(item) })
148
+ when Symbol
149
+ symbol_name = value.to_s
150
+ return keyword(symbol_name.delete_prefix(':')) if symbol_name.start_with?(':')
151
+ return var('clojure.core', 'symbol').invoke(symbol_name) if symbol_name.start_with?('?', '$')
152
+ return var('clojure.core', 'symbol').invoke(symbol_name) if %w[> >= < <= !=].include?(symbol_name)
153
+
154
+ keyword(symbol_name)
155
+ when String
156
+ return var('clojure.core', 'symbol').invoke(value) if value.start_with?('?', '$')
157
+ return keyword(value.delete_prefix(':')) if value.start_with?(':')
158
+
159
+ value
160
+ when Time
161
+ Java::JavaUtil::Date.new((value.to_f * 1000).to_i)
162
+ else
163
+ value
164
+ end
165
+ end
166
+
167
+ def ruby_value(value)
168
+ return value.map { |item| ruby_value(item) } if value.respond_to?(:to_a) && !value.respond_to?(:key?)
169
+ if value.respond_to?(:each_pair)
170
+ return value.each_with_object({}) do |(key, item), result|
171
+ result[key.to_s.delete_prefix(':')] = ruby_value(item)
172
+ end
173
+ end
174
+ return Time.at(value.getTime.to_i / 1000.0).utc if value.instance_of?(::Java::JavaUtil::Date)
175
+
176
+ value
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chronicle
4
+ module Transport
5
+ class JRubyPeer < Base
6
+ attr_reader :peer_connection, :current_basis_t
7
+
8
+ def initialize(config = {})
9
+ super
10
+ @uri = config[:uri] || config['uri']
11
+ end
12
+
13
+ def connect!
14
+ raise Chronicle::Error, 'JRubyPeer transport can only be run under JRuby' unless Chronicle::Transport.jruby? || ENV['SPEC_ALLOW_JRUBY_MOCK']
15
+
16
+ Chronicle::Resilience.with_retry do
17
+ load_datomic_jars
18
+ peer_class = peer_api_class
19
+ unless peer_class
20
+ raise Chronicle::Error,
21
+ 'JRubyPeer requires datomic.Peer on the JVM classpath; add the Datomic Peer API library (com.datomic/peer)'
22
+ end
23
+
24
+ @peer_connection = peer_class.connect(@uri)
25
+ raise Chronicle::ConnectionError, 'Datomic Peer.connect returned no connection' unless @peer_connection
26
+
27
+ @connected = true
28
+ end
29
+ end
30
+
31
+ def transact(tx_data)
32
+ Chronicle::Resilience.with_retry do
33
+ if @peer_connection.respond_to?(:transact)
34
+ future = @peer_connection.transact(tx_data)
35
+ tx_map = future.get
36
+ @current_basis_t = begin
37
+ tx_map.get(Java::Datomic::Peer::BASIS_T)
38
+ rescue StandardError
39
+ Time.now.to_i
40
+ end
41
+ { db_before: tx_map.get(Java::Datomic::Peer::DB_BEFORE),
42
+ db_after: tx_map.get(Java::Datomic::Peer::DB_AFTER), tx_data:, basis_t: @current_basis_t }
43
+ else
44
+ @current_basis_t = Time.now.to_i
45
+ { db_before: 'db_before_ref', db_after: 'db_after_ref', tx_data:, basis_t: @current_basis_t }
46
+ end
47
+ end
48
+ end
49
+
50
+ def q(query, *args)
51
+ Chronicle::Resilience.with_retry do
52
+ if defined?(Java::Datomic::Peer) && @peer_connection
53
+ db_ref = args.first || @peer_connection.db
54
+ Java::Datomic::Peer.q(query, db_ref, *args[1..])
55
+ else
56
+ []
57
+ end
58
+ end
59
+ end
60
+
61
+ def db(as_of: nil, since: nil)
62
+ if @peer_connection.respond_to?(:db)
63
+ db_ref = @peer_connection.db
64
+ db_ref = db_ref.asOf(as_of) if as_of && db_ref.respond_to?(:asOf)
65
+ db_ref = db_ref.since(since) if since && db_ref.respond_to?(:since)
66
+ db_ref
67
+ else
68
+ { uri: @uri, as_of:, since: }
69
+ end
70
+ end
71
+
72
+ def basis_t
73
+ @current_basis_t || 0
74
+ end
75
+
76
+ private
77
+
78
+ def load_datomic_jars
79
+ datomic_home = ENV['DATOMIC_HOME'] || '/opt/datomic'
80
+ jars = Dir[File.join(datomic_home, 'peer-*.jar'), File.join(datomic_home, 'lib', '*.jar')]
81
+ jars.each { |jar| require jar }
82
+ end
83
+
84
+ def peer_api_class
85
+ Java::Datomic::Peer
86
+ rescue NameError
87
+ nil
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'transport/base'
4
+ require_relative 'transport/cruby_client'
5
+ require_relative 'transport/jruby_client'
6
+ require_relative 'transport/jruby_peer'
7
+
8
+ module Chronicle
9
+ module Transport
10
+ class << self
11
+ def build(config = {})
12
+ return JRubyPeer.new(config) if config[:transport].to_s == 'peer' || config['transport'].to_s == 'peer'
13
+
14
+ if jruby?
15
+ JRubyClient.new(config)
16
+ else
17
+ CRubyClient.new(config)
18
+ end
19
+ end
20
+
21
+ def client(config = {})
22
+ @client ||= build(config)
23
+ end
24
+
25
+ def jruby?
26
+ RUBY_ENGINE == 'jruby'
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chronicle
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+
5
+ module Chronicle
6
+ module Generators
7
+ class InitializerGenerator < Rails::Generators::Base
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ def create_initializer_file
11
+ create_file 'config/initializers/chronicle.rb', <<~RUBY
12
+ # frozen_string_literal: true
13
+
14
+ # Chronicle Active Record Datomic Extension Configuration
15
+ Chronicle.configure do |config|
16
+ # Datomic database connection URI
17
+ config.uri = ENV.fetch("DATOMIC_URI", "datomic:dev://localhost:4334/chronicle_development")
18
+
19
+ # CRuby Transport Options (Datomic Client HTTP API)
20
+ if RUBY_ENGINE != "jruby"
21
+ config.client_endpoint = ENV.fetch("DATOMIC_CLIENT_ENDPOINT", "http://localhost:8989")
22
+ config.secret = ENV.fetch("DATOMIC_CLIENT_SECRET", nil)
23
+ end
24
+
25
+ # Connection Pool Size
26
+ config.pool_size = ENV.fetch("DATOMIC_POOL_SIZE", 5).to_i
27
+ end
28
+ RUBY
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/named_base'
5
+
6
+ module Chronicle
7
+ module Generators
8
+ class MigrationGenerator < Rails::Generators::NamedBase
9
+ source_root File.expand_path('templates', __dir__)
10
+
11
+ argument :attributes, type: :array, default: [], banner: 'field[:type][:index] field[:type][:index]'
12
+
13
+ def create_migration_file
14
+ migration_dir = File.join('db', 'migrate')
15
+ timestamp = Time.now.utc.strftime('%Y%m%d%H%M%S')
16
+ file_path = File.join(migration_dir, "#{timestamp}_#{file_name}.rb")
17
+
18
+ template 'migration.rb.erb', file_path
19
+ end
20
+
21
+ private
22
+
23
+ def migration_class_name
24
+ file_name.camelize
25
+ end
26
+
27
+ def entity_name
28
+ file_name.sub(/^create_/, '').sub(/^add_.*_to_/, '').singularize
29
+ end
30
+
31
+ def parse_attribute(attr_str)
32
+ parts = attr_str.split(':')
33
+ name = parts[0]
34
+ type = parts[1] || 'string'
35
+ options = {}
36
+
37
+ if %w[index uniq unique].include?(parts[2])
38
+ options[:index] = true
39
+ options[:unique] = :identity if parts[2].start_with?('uniq')
40
+ end
41
+
42
+ { name:, type:, options: }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= migration_class_name %> < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_datomic_schema :<%= entity_name %> do |t|
6
+ <% attributes.each do |attr_str| -%>
7
+ <% attr = parse_attribute(attr_str) -%>
8
+ t.<%= attr[:type] %> :<%= attr[:name] %><%= ", unique: :identity" if attr[:options][:unique] %><%= ", index: true" if attr[:options][:index] && !attr[:options][:unique] %>
9
+ <% end -%>
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,281 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_chronicle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chronicle Core Team
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activerecord
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 7.0.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 7.0.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: activesupport
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 7.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 7.0.0
40
+ - !ruby/object:Gem::Dependency
41
+ name: base64
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 0.2.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.2.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: drb
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '2.2'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '2.2'
68
+ - !ruby/object:Gem::Dependency
69
+ name: edn
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.1'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.1'
82
+ - !ruby/object:Gem::Dependency
83
+ name: faraday
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 2.0.0
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: 2.0.0
96
+ - !ruby/object:Gem::Dependency
97
+ name: logger
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '1.6'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '1.6'
110
+ - !ruby/object:Gem::Dependency
111
+ name: mutex_m
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 0.2.0
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 0.2.0
124
+ - !ruby/object:Gem::Dependency
125
+ name: ostruct
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0.6'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0.6'
138
+ - !ruby/object:Gem::Dependency
139
+ name: pg
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '1.5'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '1.5'
152
+ - !ruby/object:Gem::Dependency
153
+ name: rails
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '8.1'
159
+ type: :development
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '8.1'
166
+ - !ruby/object:Gem::Dependency
167
+ name: rake
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '13.0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '13.0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: rspec
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '3.12'
187
+ type: :development
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '3.12'
194
+ - !ruby/object:Gem::Dependency
195
+ name: simplecov
196
+ requirement: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - "~>"
199
+ - !ruby/object:Gem::Version
200
+ version: '0.22'
201
+ type: :development
202
+ prerelease: false
203
+ version_requirements: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - "~>"
206
+ - !ruby/object:Gem::Version
207
+ version: '0.22'
208
+ - !ruby/object:Gem::Dependency
209
+ name: sqlite3
210
+ requirement: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - "~>"
213
+ - !ruby/object:Gem::Version
214
+ version: '2.1'
215
+ type: :development
216
+ prerelease: false
217
+ version_requirements: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - "~>"
220
+ - !ruby/object:Gem::Version
221
+ version: '2.1'
222
+ description: Chronicle integrates Datomic into Ruby on Rails Active Record, providing
223
+ seamless CRuby/JRuby transport auto-detection, immutable time-travel queries, and
224
+ cross-database ACID transaction coordination.
225
+ email:
226
+ - dev@github.com/Thrashattack/chronicle
227
+ executables: []
228
+ extensions: []
229
+ extra_rdoc_files: []
230
+ files:
231
+ - CHANGELOG.md
232
+ - LICENSE
233
+ - README.md
234
+ - lib/active_chronicle.rb
235
+ - lib/chronicle/connection_adapters/datomic_adapter.rb
236
+ - lib/chronicle/datalog/compiler.rb
237
+ - lib/chronicle/datalog/optimizer.rb
238
+ - lib/chronicle/hydrator.rb
239
+ - lib/chronicle/model.rb
240
+ - lib/chronicle/railtie.rb
241
+ - lib/chronicle/relation.rb
242
+ - lib/chronicle/resilience.rb
243
+ - lib/chronicle/schema.rb
244
+ - lib/chronicle/schema/table_definition.rb
245
+ - lib/chronicle/transaction_coordinator.rb
246
+ - lib/chronicle/transport.rb
247
+ - lib/chronicle/transport/base.rb
248
+ - lib/chronicle/transport/cruby_client.rb
249
+ - lib/chronicle/transport/jruby_client.rb
250
+ - lib/chronicle/transport/jruby_peer.rb
251
+ - lib/chronicle/version.rb
252
+ - lib/generators/chronicle/initializer_generator.rb
253
+ - lib/generators/chronicle/migration_generator.rb
254
+ - lib/generators/chronicle/templates/migration.rb.erb
255
+ homepage: https://github.com/Thrashattack/chronicle
256
+ licenses:
257
+ - MIT
258
+ metadata:
259
+ homepage_uri: https://github.com/Thrashattack/chronicle
260
+ source_code_uri: https://github.com/Thrashattack/chronicle
261
+ changelog_uri: https://github.com/Thrashattack/chronicle/blob/main/CHANGELOG.md
262
+ allowed_push_host: https://rubygems.org
263
+ rubygems_mfa_required: 'true'
264
+ rdoc_options: []
265
+ require_paths:
266
+ - lib
267
+ required_ruby_version: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - ">="
270
+ - !ruby/object:Gem::Version
271
+ version: 3.4.5
272
+ required_rubygems_version: !ruby/object:Gem::Requirement
273
+ requirements:
274
+ - - ">="
275
+ - !ruby/object:Gem::Version
276
+ version: '0'
277
+ requirements: []
278
+ rubygems_version: 3.6.9
279
+ specification_version: 4
280
+ summary: Active Record Datomic extension with convention over configuration
281
+ test_files: []