activerecord-virgodb-adapter 0.1.0.dev.2fa4849

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f4d9953d82781f0f15cc80deb100e9a534ebcabafe894a6cff258767e61c2e0d
4
+ data.tar.gz: d046ad46492f2de29816497f566095f77d48defe0343f1bf6c44c9a455edc0bf
5
+ SHA512:
6
+ metadata.gz: afdf198e73fe36aa1cca5036b8b1713dac85a1f62885b9bd079472916d38dc0b6225cfaa9f2564dfe1f806a5489102390284986f6388a7ad765193e59ddac7ba
7
+ data.tar.gz: fd8ff15112deb4bae56553011e7f2ac6e5d73a3d8847b276fdda719e4480a1f5a8d4b691c0316ee1056aa6d292ca3f9fa316f12fb046ec2423ae7927a8946d7a
data/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Copyright (c) 2026 Sumit Kumar. All rights reserved.
2
+
3
+ This software and associated documentation files (the "Software") are proprietary. You may install and use the compiled binary for personal, non-commercial purposes only. Any commercial use, redistribution, modification, or reverse engineering of the Software is strictly prohibited without explicit written permission.
4
+
5
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,383 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/connection_adapters/sqlite3_adapter"
4
+ require "json"
5
+
6
+ module ActiveRecord
7
+ module ConnectionAdapters
8
+ # ActiveRecord adapter for virgodb migrations -- lets any Rails app
9
+ # define and evolve a virgodb table's schema the same way it already
10
+ # does for SQLite (bin/rails db:migrate) and ClickHouse (the
11
+ # clickhouse-activerecord gem): real ActiveRecord::Migration subclasses,
12
+ # raw type strings, picked up by `rails db:migrate` alongside every
13
+ # other database.
14
+ #
15
+ # virgodb's manifest is already a real SQLite file, so subclassing
16
+ # SQLite3Adapter (not building from AbstractAdapter) gets
17
+ # structure_dump/structure_load -- the :sql schema format -- for free,
18
+ # zero custom dumper code. Only create_table/add_column need
19
+ # overriding: call `super` for a real, if never-written-to, SQLite
20
+ # table (SQLite's type-name grammar accepts arbitrary bare-word type
21
+ # declarations -- verified against a real connection, see
22
+ # crates/compact/src/schema.rs's doc comment for the exact grammar and
23
+ # why it has to be `"AnyLast String updated_at"`, not
24
+ # `"AnyLast(String, updated_at)"`), and additionally record the column
25
+ # in a `schema_versions` table matching exactly what
26
+ # `compact::schema_from_columns` on the Rust side expects to parse.
27
+ #
28
+ # One manifest can back several virgodb tables sharing a single
29
+ # `database.yml` connection and migrations path -- the same way a host
30
+ # app's `clickhouse:` connection might already hold all of its ClickHouse
31
+ # tables under one connection. `schema_versions` (and the Rust-side
32
+ # manifest's `run_files`/`inline_batches`/`orphaned_files`/
33
+ # `maintenance_lease` catalog tables) are scoped by `table_name`
34
+ # specifically to make this safe: each logical table's schema history
35
+ # and physical run files stay independent even though they live in the
36
+ # same SQLite file.
37
+ #
38
+ # Additive-only, matching `Manifest::register_schema_version`'s own
39
+ # rule and virgodb's immutable-Parquet philosophy: `remove_column`,
40
+ # `rename_column`, and `change_column` are all disabled outright rather
41
+ # than silently letting the real SQLite table drift out of sync with
42
+ # what `schema_versions` describes.
43
+ class VirgodbAdapter < SQLite3Adapter
44
+ ADAPTER_NAME = "Virgodb"
45
+
46
+ # Real bug, found (not assumed) by testing: setting `auto_vacuum =
47
+ # INCREMENTAL` from `create_table` -- even from create_table's very
48
+ # FIRST call on a brand-new manifest, before any table exists -- was
49
+ # already too late. SQLite3Adapter's own `configure_connection` sets
50
+ # `journal_mode = wal` via DEFAULT_PRAGMAS (`super`, below) as part of
51
+ # establishing the connection itself, which happens before this
52
+ # adapter's `create_table` ever runs -- and switching journal_mode to
53
+ # WAL requires SQLite to actually write to the file (creating the -wal
54
+ # file, an implicit checkpoint), which "poisons" the database from
55
+ # auto_vacuum's perspective the same way any other content would.
56
+ # Confirmed directly: a manual `PRAGMA auto_vacuum = INCREMENTAL`
57
+ # issued immediately after `establish_connection`, before ANY table or
58
+ # migration ran, still silently failed. The pragma has to be set
59
+ # before `super` runs here, not merely before the first CREATE TABLE.
60
+ def configure_connection
61
+ @raw_connection.auto_vacuum = "incremental" if @raw_connection.respond_to?(:auto_vacuum=)
62
+ super
63
+ end
64
+
65
+ def create_table(table_name, **options, &block)
66
+ result = super
67
+ record_schema_version!(table_name)
68
+ result
69
+ end
70
+
71
+ def add_column(table_name, column_name, type, **options)
72
+ result = super
73
+ record_added_column!(table_name, column_name, type, options)
74
+ result
75
+ end
76
+
77
+ def remove_column(table_name, column_name, type = nil, **options)
78
+ raise NotImplementedError, "virgodb schemas are additive-only -- columns can be added, never removed (see schema_versions)"
79
+ end
80
+
81
+ def rename_column(table_name, column_name, new_column_name)
82
+ raise NotImplementedError, "virgodb schemas are additive-only -- columns can't be renamed once created"
83
+ end
84
+
85
+ def change_column(table_name, column_name, type, **options)
86
+ raise NotImplementedError, "virgodb schemas are additive-only -- an existing column's type can't change"
87
+ end
88
+
89
+ private
90
+
91
+ # Reads the table back via ActiveRecord's own column introspection --
92
+ # `sql_type` round-trips the raw virgodb type string verbatim, proven
93
+ # against a real SQLite connection immediately after a fresh CREATE
94
+ # TABLE -- and writes a new `schema_versions` row: the full current
95
+ # column list, versioned one higher than whatever was there before FOR
96
+ # THIS table_name. Mirrors Manifest::register_schema_version's shape
97
+ # exactly (same table_name scoping, same column names, same JSON shape
98
+ # for `columns`) so the Rust side can read whatever Ruby wrote with
99
+ # zero translation. Scoping `MAX(version)` by `table_name` is what makes
100
+ # it safe for several logical tables to share one manifest/connection:
101
+ # without it, the second table's first migration would start at
102
+ # version 2 (or worse, silently share version numbers with an
103
+ # unrelated table). Rails' OWN bookkeeping tables (`schema_migrations`,
104
+ # `ar_internal_metadata`) go through this SAME overridden `create_table`
105
+ # the first time either is lazily created -- there's nothing
106
+ # virgodb-specific about them, and recording a schema_versions row for
107
+ # them is pure noise (found while dumping a real structure.sql and
108
+ # seeing them show up as rows next to the real logical tables). Guarded
109
+ # here, not by checking `table_name` against `TABLES` in some caller --
110
+ # this adapter has no such registry, `create_table`'s own argument is
111
+ # the one place that reliably sees every table, virgodb or not.
112
+ #
113
+ # ONLY safe for `create_table` (a brand-new table with no prior
114
+ # history). NOT reused by `add_column` -- see `record_added_column!`'s
115
+ # own doc comment for the real bug this avoids: `columns(table_name)`
116
+ # stops reliably returning the ORIGINAL declared type text for
117
+ # pre-existing columns once the table has been through certain ALTER
118
+ # operations.
119
+ RAILS_INTERNAL_TABLES = %w[schema_migrations ar_internal_metadata].freeze
120
+
121
+ def record_schema_version!(table_name)
122
+ return if RAILS_INTERNAL_TABLES.include?(table_name.to_s)
123
+
124
+ ensure_schema_versions_table!
125
+
126
+ cols = columns(table_name).map do |c|
127
+ { "name" => c.name, "virgodb_type" => c.sql_type, "nullable" => c.null }
128
+ end
129
+
130
+ insert_schema_version!(table_name, cols)
131
+ end
132
+
133
+ # `add_column`'s schema_versions update -- deliberately does NOT
134
+ # re-introspect the whole table via `columns(table_name)` the way
135
+ # `record_schema_version!` does. Real bug, found (not assumed) by
136
+ # testing: `super`'s real `ALTER TABLE ADD COLUMN` -- specifically for
137
+ # a `null: false` column, which SQLite's native ALTER can't add without
138
+ # a default -- makes Rails' SQLite3Adapter fall back to its "rebuild
139
+ # the table" strategy (copy to a temp table, drop, rename). Confirmed
140
+ # directly: introspecting the table immediately afterward, even via a
141
+ # completely fresh connection/process (so not a Rails-process-level
142
+ # schema cache artifact), returns Rails' own GENERIC type names for
143
+ # every PRE-EXISTING column ("INTEGER"/"datetime"/"" instead of
144
+ # "Int64"/"Timestamp"/"LowCardinality String") -- the rebuild
145
+ # reconstructs the physical table's DDL from Rails' normalized column
146
+ # metadata, silently discarding the original literal type text for
147
+ # every column it didn't just add. Re-deriving the full column list
148
+ # via introspection after ANY add_column would therefore corrupt every
149
+ # already-correct entry in `schema_versions`.
150
+ #
151
+ # The fix: never re-derive what's already known to be correct. Read
152
+ # the CURRENT (highest-version) column list straight out of
153
+ # `schema_versions` itself (the one thing guaranteed not to have been
154
+ # touched by the ALTER), and append only the ONE new column -- whose
155
+ # name/type/nullability are already fully known from `add_column`'s own
156
+ # arguments, no introspection needed for it either.
157
+ def record_added_column!(table_name, column_name, type, options)
158
+ return if RAILS_INTERNAL_TABLES.include?(table_name.to_s)
159
+
160
+ ensure_schema_versions_table!
161
+
162
+ current_json = select_value("SELECT columns FROM schema_versions WHERE table_name = #{quote(table_name)} ORDER BY version DESC LIMIT 1")
163
+ current_cols = current_json ? JSON.parse(current_json) : []
164
+
165
+ new_col = { "name" => column_name.to_s, "virgodb_type" => type.to_s, "nullable" => options[:null] != false }
166
+ insert_schema_version!(table_name, current_cols + [ new_col ])
167
+ end
168
+
169
+ def insert_schema_version!(table_name, cols)
170
+ next_version = (select_value("SELECT MAX(version) FROM schema_versions WHERE table_name = #{quote(table_name)}") || 0).to_i + 1
171
+
172
+ execute(<<~SQL)
173
+ INSERT INTO schema_versions (table_name, version, columns, applied_at)
174
+ VALUES (#{quote(table_name)}, #{next_version}, #{quote(cols.to_json)}, #{Time.now.to_i})
175
+ SQL
176
+ end
177
+
178
+ # Same DDL as Manifest::open()'s bootstrap on the Rust side (same
179
+ # table/column names, including the `table_name` scoping column) --
180
+ # IF NOT EXISTS makes this safe to call whether virgodb's own Rust
181
+ # code or this Ruby adapter created the manifest file first. Plain
182
+ # INTEGER PRIMARY KEY, not AUTOINCREMENT -- see the matching comment
183
+ # in manifest/src/lib.rs for why (breaks a real structure_dump/
184
+ # structure_load round trip the moment any host app's
185
+ # ActiveRecord::SchemaDumper.ignore_tables is non-empty).
186
+ def ensure_schema_versions_table!
187
+ execute(<<~SQL)
188
+ CREATE TABLE IF NOT EXISTS schema_versions (
189
+ id INTEGER PRIMARY KEY,
190
+ table_name TEXT NOT NULL,
191
+ version INTEGER NOT NULL,
192
+ columns TEXT NOT NULL,
193
+ applied_at INTEGER NOT NULL
194
+ )
195
+ SQL
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ ActiveRecord::ConnectionAdapters.register(
202
+ "virgodb",
203
+ "ActiveRecord::ConnectionAdapters::VirgodbAdapter",
204
+ "active_record/connection_adapters/virgodb_adapter"
205
+ )
206
+
207
+ module ActiveRecord
208
+ module Tasks
209
+ # `ActiveRecord::Tasks::SQLiteDatabaseTasks#structure_dump` just shells
210
+ # out to the real `sqlite3` CLI (`.schema --nosys`, or a raw `SELECT sql
211
+ # FROM sqlite_master` when a host app has any
212
+ # `ActiveRecord::SchemaDumper.ignore_tables` configured -- e.g. excluding
213
+ # tables owned by a separate replication/backup mechanism) against the
214
+ # configured database file --
215
+ # DDL only. `ActiveRecord::Tasks::DatabaseTasks.dump_schema` then
216
+ # special-cases exactly one table on top of that: if `schema_migrations`
217
+ # exists, it appends that table's real row data (`connection
218
+ # .dump_schema_versions`) to the same dump file, because Rails' own
219
+ # migration bookkeeping needs those rows to survive a `db:schema:load`/
220
+ # `structure_load`, not just the table shape.
221
+ #
222
+ # `schema_versions` (this adapter's own bookkeeping table, plural,
223
+ # distinct from Rails' singular `schema_migrations`) needs exactly the
224
+ # same treatment and doesn't get it for free: it holds real data (the
225
+ # versioned column list `compact::schema_from_columns` on the Rust side
226
+ # depends on), not just structure, and Rails has no way to know that a
227
+ # table it didn't create needs the same row-preserving special case.
228
+ # Confirmed empirically: without this, a fresh database prepared via
229
+ # `structure_load` (which Rails does automatically whenever a `db:migrate`
230
+ # invocation for a later environment/connection finds an existing,
231
+ # version-matching dump already on disk from an earlier one -- not
232
+ # something this adapter can opt out of) ends up with the real
233
+ # host-app table but an EMPTY `schema_versions`, so
234
+ # `Manifest::current_schema_version()` on the Rust side returns `None`
235
+ # even though the SQLite table itself is correct.
236
+ class VirgodbDatabaseTasks < SQLiteDatabaseTasks
237
+ class << self
238
+ # Optional host-app hook: `->(sql_table_name) { {sort_by:, key_column:,
239
+ # partition_column:} or nil }`. Purely a documentation comment written
240
+ # above CREATE TABLE at dump time -- nothing on the Rust or Ruby side
241
+ # ever reads it back. This adapter gem stays host-app-agnostic (see
242
+ # the class doc comment above: "lets any Rails app..."), so it can't
243
+ # hardcode any one host app's sort_by/key_column config (that's
244
+ # app/services/virgodb/registry.rb's job, which already has this
245
+ # exact data for real -- passed to Virgodb.start_maintenance on every
246
+ # boot). A host app that wants it surfaced sets this once, e.g. in an
247
+ # initializer; left nil, structure_dump behaves exactly as before.
248
+ attr_accessor :table_layout_provider
249
+ end
250
+
251
+ def structure_dump(filename, extra_flags)
252
+ super
253
+ reformat_create_table_statements!(filename)
254
+ annotate_physical_layout!(filename)
255
+ dump_schema_versions!(filename)
256
+ end
257
+
258
+ private
259
+
260
+ # Only the LATEST schema_versions row per table_name is dumped.
261
+ # `Manifest::current_schema_version` (manifest/src/lib.rs) only ever
262
+ # reads `ORDER BY version DESC LIMIT 1` -- confirmed directly, no Rust
263
+ # or Ruby code anywhere reads an older version row, so keeping full
264
+ # history here isn't a correctness requirement. It also isn't a
265
+ # readability win: each version's `columns` re-lists every column from
266
+ # scratch, so N migrations means N near-duplicate JSON blobs, longer
267
+ # every time. The version count/dates are still worth one line for
268
+ # orientation -- the full column list per version isn't; the
269
+ # migration files themselves (db/migrate_virgodb_*/*.rb, timestamped,
270
+ # under git blame) are already the authoritative "what changed when"
271
+ # record. `register_schema_version`'s additive-only check is
272
+ # unaffected: it only ever compares a new migration's proposed columns
273
+ # against the CURRENT version, which is exactly what's still dumped.
274
+ def dump_schema_versions!(filename)
275
+ return unless connection.data_source_exists?("schema_versions")
276
+
277
+ rows = connection.select_all("SELECT id, table_name, version, columns, applied_at FROM schema_versions ORDER BY table_name, version")
278
+ return if rows.to_a.empty?
279
+
280
+ File.open(filename, "a") do |f|
281
+ f.puts
282
+ rows.to_a.group_by { |r| r["table_name"] }.each do |table_name, versions|
283
+ latest = versions.max_by { |r| r["version"] }
284
+ history = versions.map { |r| "v#{r['version']} (#{Time.at(r['applied_at'].to_i).utc.strftime('%Y-%m-%d')})" }.join(", ")
285
+
286
+ f.puts "-- #{table_name} schema history: #{history} -- only the current version is dumped below; see db/migrate_virgodb_*/ for the rest"
287
+ pretty_columns = JSON.pretty_generate(JSON.parse(latest["columns"]))
288
+ f.puts(
289
+ "INSERT INTO \"schema_versions\" (id, table_name, version, columns, applied_at) VALUES (\n" \
290
+ " #{latest['id']}, #{connection.quote(table_name)}, #{latest['version']},\n" \
291
+ " #{connection.quote(pretty_columns)},\n" \
292
+ " #{latest['applied_at']}\n" \
293
+ ");"
294
+ )
295
+ f.puts
296
+ end
297
+ end
298
+ end
299
+
300
+ # Writes `table_layout_provider`'s sort_by/key_column/partition_column
301
+ # as a comment directly above each table's (already reformatted,
302
+ # one-column-per-line) CREATE TABLE -- the closest a SQLite-flavored
303
+ # dump can get to ClickHouse's own `ORDER BY`/`PARTITION BY` clauses
304
+ # being right there in the DDL. No-op (and no dump format change at
305
+ # all) when no provider is registered.
306
+ def annotate_physical_layout!(filename)
307
+ provider = self.class.table_layout_provider
308
+ return unless provider
309
+
310
+ content = File.read(filename)
311
+ content.gsub!(/^CREATE TABLE (IF NOT EXISTS )?"?(\w+)"? \(/) do
312
+ statement_start = Regexp.last_match(0)
313
+ table_name = Regexp.last_match(2)
314
+ layout = provider.call(table_name)
315
+ next statement_start unless layout
316
+
317
+ comment = [
318
+ "-- Physical layout (app/services/virgodb/registry.rb):",
319
+ "-- sort_by: #{layout[:sort_by].join(', ')}",
320
+ "-- key_column: #{layout[:key_column]} (run file min/max pruning bounds)",
321
+ "-- partition_column: #{layout[:partition_column]}"
322
+ ].join("\n")
323
+
324
+ "#{comment}\n#{statement_start}"
325
+ end
326
+ File.write(filename, content)
327
+ end
328
+
329
+ # `SQLite3Adapter#structure_dump`'s raw `SELECT sql FROM sqlite_master`
330
+ # path (used whenever `ActiveRecord::SchemaDumper.ignore_tables` is
331
+ # non-empty, e.g. excluding tables owned by a separate replication/
332
+ # backup mechanism) reproduces each
333
+ # CREATE TABLE exactly as ActiveRecord originally generated it --
334
+ # one long line, every column crammed together. Fine for SQLite to
335
+ # load back (structure_load doesn't care about whitespace), useless to
336
+ # a human reading the dump next to `db/clickhouse_structure.sql`
337
+ # (ClickHouse's own `SHOW CREATE TABLE` is naturally one-column-per-line,
338
+ # for comparison). Purely cosmetic, safe to do unconditionally: only
339
+ # rewrites the exact same statement with different whitespace, no DDL
340
+ # semantics change. Skips statements that already span multiple lines
341
+ # (e.g. this adapter's own hand-written `schema_versions` DDL, created
342
+ # via a real newline-containing heredoc) -- nothing to reformat there.
343
+ def reformat_create_table_statements!(filename)
344
+ content = File.read(filename)
345
+ content.gsub!(/^CREATE TABLE (IF NOT EXISTS )?("[\w]+"|\w+) \((.*)\);$/) do
346
+ if_not_exists = Regexp.last_match(1)
347
+ table_name = Regexp.last_match(2)
348
+ columns = split_top_level_commas(Regexp.last_match(3))
349
+ next Regexp.last_match(0) if columns.size <= 1
350
+
351
+ "CREATE TABLE #{if_not_exists}#{table_name} (\n" + columns.map { |c| " #{c.strip}" }.join(",\n") + "\n);"
352
+ end
353
+ File.write(filename, content)
354
+ end
355
+
356
+ # Splits a CREATE TABLE column list on commas that are NOT nested
357
+ # inside a type's own parens (e.g. `Decimal(18,2)`) -- a plain
358
+ # `String#split(",")` would wrongly cut `Decimal(18` and `2)` apart.
359
+ def split_top_level_commas(str)
360
+ parts = []
361
+ depth = 0
362
+ current = +""
363
+ str.each_char do |ch|
364
+ case ch
365
+ when "(" then depth += 1; current << ch
366
+ when ")" then depth -= 1; current << ch
367
+ when ","
368
+ depth.zero? ? (parts << current; current = +"") : current << ch
369
+ else
370
+ current << ch
371
+ end
372
+ end
373
+ parts << current unless current.strip.empty?
374
+ parts
375
+ end
376
+ end
377
+ end
378
+ end
379
+
380
+ # Registering our own subclass (not plain SQLiteDatabaseTasks) so
381
+ # schema_versions' row data survives the dump/load round trip -- see the
382
+ # class doc comment above for why a real Rails app can't avoid hitting this.
383
+ ActiveRecord::Tasks::DatabaseTasks.register_task(/virgodb/, "ActiveRecord::Tasks::VirgodbDatabaseTasks")
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-virgodb-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.dev.2fa4849
5
+ platform: ruby
6
+ authors:
7
+ - virgodb
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'
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'
26
+ - !ruby/object:Gem::Dependency
27
+ name: sqlite3
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '1.4'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.4'
40
+ description: |
41
+ Lets any Rails app write real ActiveRecord::Migration subclasses against a virgodb manifest,
42
+ picked up by `rails db:migrate` the same way SQLite and ClickHouse migrations already are.
43
+ Subclasses ActiveRecord::ConnectionAdapters::SQLite3Adapter (virgodb's manifest is already a
44
+ real SQLite file), so structure_dump/the :sql schema format work for free.
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - LICENSE
50
+ - lib/active_record/connection_adapters/virgodb_adapter.rb
51
+ licenses:
52
+ - Nonstandard
53
+ metadata: {}
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubygems_version: 4.0.16
69
+ specification_version: 4
70
+ summary: ActiveRecord adapter for defining and evolving virgodb table schemas via
71
+ real migrations
72
+ test_files: []