fast_schema_dumper 0.4.2 → 0.5.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.
- checksums.yaml +4 -4
- data/.standard.yml +5 -0
- data/CHANGELOG.md +12 -0
- data/README.md +14 -4
- data/Rakefile +10 -1
- data/lib/fast_schema_dumper/cli.rb +5 -5
- data/lib/fast_schema_dumper/fast_dumper.rb +37 -21
- data/lib/fast_schema_dumper/ridgepole.rb +3 -1
- data/lib/fast_schema_dumper/version.rb +1 -1
- data/lib/fast_schema_dumper.rb +2 -0
- data/test/fast_schema_dumper_test.rb +73 -0
- data/test/test_helper.rb +6 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 67356310ddf238f27066fb0980ea10ff37b9ffcb5307f89c661fe81a1dc1a6c4
|
|
4
|
+
data.tar.gz: 8a5c15d6dff1209cd439dabf036ee3333bd08bee49e3481d7dd3ef233b07a679
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad7aa63eaae977b3fa65e210e3a66a6074d8a0ceb31a6e21c35c3f4a97dcd3a0658afd1c64fc46e3bab3e0cac198a26fdad8df1c14846aa35936b2e9feac573e
|
|
7
|
+
data.tar.gz: 858e4903f02a8b12a0fc153c577bc137418b56be1ae4bc522c65920c5fc8774f00598e55e8086b60e769e7959cfd94ae1408cd41f12ba8823ee350587bd6b794
|
data/.standard.yml
ADDED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2026-04-09
|
|
4
|
+
|
|
5
|
+
- Handle generated columns in fast dumper. [#23](https://github.com/smartbank-inc/fast_schema_dumper/pull/23)
|
|
6
|
+
- Add frozen string literal pragmas and update README. [#25](https://github.com/smartbank-inc/fast_schema_dumper/pull/25)
|
|
7
|
+
- Add RubyGems version badge to README. [#17](https://github.com/smartbank-inc/fast_schema_dumper/pull/17)
|
|
8
|
+
|
|
9
|
+
## [0.4.3] - 2026-03-11
|
|
10
|
+
|
|
11
|
+
- Introduce StandardRB for code linting. [#9](https://github.com/smartbank-inc/fast_schema_dumper/pull/9)
|
|
12
|
+
- Test setup. [#10](https://github.com/smartbank-inc/fast_schema_dumper/pull/10)
|
|
13
|
+
- Add `.github/dependabot.yml`. [#12](https://github.com/smartbank-inc/fast_schema_dumper/pull/12)
|
|
14
|
+
|
|
3
15
|
## [0.4.0] - 2025-11-06
|
|
4
16
|
|
|
5
17
|
## [0.3.0] - 2025-10-31
|
data/README.md
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
|
+
[](https://github.com/smartbank-inc/fast_schema_dumper/actions/workflows/test.yml)
|
|
2
|
+
[](https://badge.fury.io/rb/fast_schema_dumper)
|
|
3
|
+
|
|
1
4
|
# fast_schema_dumper
|
|
2
5
|
|
|
3
|
-
A super fast alternative to ActiveRecord::SchemaDumper
|
|
6
|
+
A super fast alternative to `ActiveRecord::SchemaDumper`. Currently only MySQL is supported.
|
|
7
|
+
|
|
8
|
+
## What It Does
|
|
9
|
+
|
|
10
|
+
- Reads schema metadata directly from `INFORMATION_SCHEMA`
|
|
11
|
+
- Emits Rails-style `schema.rb` output
|
|
12
|
+
- Works as a standalone executable
|
|
13
|
+
- Can override `Ridgepole::Dumper.dump`
|
|
4
14
|
|
|
5
15
|
## Usage
|
|
6
16
|
|
|
7
17
|
### Ridgepole integration
|
|
8
18
|
|
|
9
|
-
|
|
19
|
+
Require `fast_schema_dumper/ridgepole` after loading `ridgepole` to replace `Ridgepole::Dumper.dump` with `fast_schema_dumper`.
|
|
10
20
|
|
|
11
|
-
```
|
|
12
|
-
RUBYOPT='-rridgepole -rfast_schema_dumper' ridgepole
|
|
21
|
+
```bash
|
|
22
|
+
RUBYOPT='-rridgepole -rfast_schema_dumper/ridgepole' ridgepole --apply
|
|
13
23
|
```
|
|
14
24
|
|
|
15
25
|
#### Environment variables for Ridgepole
|
data/Rakefile
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
|
-
|
|
4
|
+
require "rake/testtask"
|
|
5
|
+
require "standard/rake"
|
|
6
|
+
|
|
7
|
+
Rake::TestTask.new(:test) do |t|
|
|
8
|
+
t.libs << "test"
|
|
9
|
+
t.libs << "lib"
|
|
10
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
task default: :test
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'erb'
|
|
2
4
|
require 'active_record'
|
|
3
5
|
require 'active_record/database_configurations'
|
|
4
6
|
|
|
5
|
-
require_relative '
|
|
7
|
+
require_relative 'fast_dumper'
|
|
6
8
|
|
|
7
9
|
module FastSchemaDumper
|
|
8
10
|
class CLI
|
|
@@ -10,9 +12,7 @@ module FastSchemaDumper
|
|
|
10
12
|
new.run(...)
|
|
11
13
|
end
|
|
12
14
|
|
|
13
|
-
def run(
|
|
14
|
-
argv = argv.dup
|
|
15
|
-
|
|
15
|
+
def run(_argv)
|
|
16
16
|
env = ENV['RAILS_ENV'] || 'development'
|
|
17
17
|
|
|
18
18
|
database_yml_path = File.join(Dir.pwd, 'config', 'database.yml')
|
|
@@ -27,7 +27,7 @@ module FastSchemaDumper
|
|
|
27
27
|
|
|
28
28
|
SchemaDumper.dump
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
0
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
end
|
|
@@ -38,6 +38,7 @@ module FastSchemaDumper
|
|
|
38
38
|
, NUMERIC_SCALE
|
|
39
39
|
, COLUMN_TYPE
|
|
40
40
|
, EXTRA
|
|
41
|
+
, GENERATION_EXPRESSION
|
|
41
42
|
, COLUMN_COMMENT
|
|
42
43
|
, DATETIME_PRECISION
|
|
43
44
|
, COLLATION_NAME
|
|
@@ -77,7 +78,7 @@ module FastSchemaDumper
|
|
|
77
78
|
# include
|
|
78
79
|
# nulls_not_distinct
|
|
79
80
|
# type
|
|
80
|
-
comment: idx['INDEX_COMMENT']
|
|
81
|
+
comment: idx['INDEX_COMMENT']
|
|
81
82
|
# enabled
|
|
82
83
|
}
|
|
83
84
|
hash[idx['TABLE_NAME']][idx['INDEX_NAME']][:columns] << idx['COLUMN_NAME']
|
|
@@ -99,7 +100,7 @@ module FastSchemaDumper
|
|
|
99
100
|
").each_with_object({}) do |row, hash|
|
|
100
101
|
hash[row['TABLE_NAME']] = {
|
|
101
102
|
collation: row['TABLE_COLLATION'],
|
|
102
|
-
comment: row['TABLE_COMMENT']
|
|
103
|
+
comment: row['TABLE_COMMENT']
|
|
103
104
|
}
|
|
104
105
|
end
|
|
105
106
|
|
|
@@ -187,7 +188,7 @@ module FastSchemaDumper
|
|
|
187
188
|
column: fk['COLUMN_NAME'],
|
|
188
189
|
referenced_table: fk['REFERENCED_TABLE_NAME'],
|
|
189
190
|
referenced_column: fk['REFERENCED_COLUMN_NAME'],
|
|
190
|
-
constraint_name: fk['CONSTRAINT_NAME']
|
|
191
|
+
constraint_name: fk['CONSTRAINT_NAME']
|
|
191
192
|
}
|
|
192
193
|
end
|
|
193
194
|
|
|
@@ -197,7 +198,7 @@ module FastSchemaDumper
|
|
|
197
198
|
all_foreign_keys << {
|
|
198
199
|
table_name: table_name,
|
|
199
200
|
constraint_name: constraint_name,
|
|
200
|
-
fk_data: fk_data
|
|
201
|
+
fk_data: fk_data
|
|
201
202
|
}
|
|
202
203
|
end
|
|
203
204
|
end
|
|
@@ -217,12 +218,10 @@ module FastSchemaDumper
|
|
|
217
218
|
if fk[:fk_data][:column] != inferred_column
|
|
218
219
|
# Column name is custom, need to specify it
|
|
219
220
|
fk_line += ", column: \"#{fk[:fk_data][:column]}\""
|
|
220
|
-
|
|
221
|
+
elsif !fk[:fk_data][:constraint_name].start_with?("fk_rails_")
|
|
221
222
|
# Column matches default, check if constraint name is custom
|
|
222
223
|
# Rails generates constraint names starting with "fk_rails_"
|
|
223
|
-
|
|
224
|
-
fk_line += ", name: \"#{fk[:fk_data][:constraint_name]}\""
|
|
225
|
-
end
|
|
224
|
+
fk_line += ", name: \"#{fk[:fk_data][:constraint_name]}\""
|
|
226
225
|
end
|
|
227
226
|
|
|
228
227
|
@output << fk_line
|
|
@@ -231,7 +230,7 @@ module FastSchemaDumper
|
|
|
231
230
|
stream.print @output.join("\n")
|
|
232
231
|
end
|
|
233
232
|
|
|
234
|
-
|
|
233
|
+
private
|
|
235
234
|
|
|
236
235
|
def escape_string(str)
|
|
237
236
|
str.gsub("\\", "\\\\\\\\").gsub('"', '\"').gsub("\n", "\\n").gsub("\r", "\\r").gsub("\t", "\\t")
|
|
@@ -317,7 +316,7 @@ module FastSchemaDumper
|
|
|
317
316
|
# Indexes
|
|
318
317
|
# Rails orders indexes lexicographically by their column arrays
|
|
319
318
|
# Example: ["a", "b"] < ["a"] < ["b", "c"] < ["b"] < ["d"]
|
|
320
|
-
sorted_indexes = indexes.
|
|
319
|
+
sorted_indexes = indexes.except('PRIMARY').sort_by do |index_name, index_data|
|
|
321
320
|
# Create an array padded with high values for comparison
|
|
322
321
|
# This ensures that missing columns sort after existing ones
|
|
323
322
|
max_cols = indexes.values.map { |data| data[:columns].size }.max || 1
|
|
@@ -349,7 +348,7 @@ module FastSchemaDumper
|
|
|
349
348
|
end
|
|
350
349
|
end
|
|
351
350
|
|
|
352
|
-
check_clause.gsub!(
|
|
351
|
+
check_clause.gsub!("\\'", "'") # don't escape single quotes for compatibility with the original dumper
|
|
353
352
|
|
|
354
353
|
ck_line = " t.check_constraint \"#{check_clause}\""
|
|
355
354
|
|
|
@@ -366,11 +365,13 @@ module FastSchemaDumper
|
|
|
366
365
|
end
|
|
367
366
|
|
|
368
367
|
def format_column(column)
|
|
368
|
+
return format_generated_column(column) if generated_column?(column)
|
|
369
|
+
|
|
369
370
|
col_def = "t.#{map_column_type(column)} \"#{column['COLUMN_NAME']}\""
|
|
370
371
|
|
|
371
372
|
# limit (varchar, char)
|
|
372
373
|
if ['varchar', 'char'].include?(column['DATA_TYPE']) && column['CHARACTER_MAXIMUM_LENGTH'] &&
|
|
373
|
-
|
|
374
|
+
column['CHARACTER_MAXIMUM_LENGTH'] != 255
|
|
374
375
|
col_def += ", limit: #{column['CHARACTER_MAXIMUM_LENGTH']}"
|
|
375
376
|
end
|
|
376
377
|
|
|
@@ -435,6 +436,21 @@ module FastSchemaDumper
|
|
|
435
436
|
col_def
|
|
436
437
|
end
|
|
437
438
|
|
|
439
|
+
def format_generated_column(column)
|
|
440
|
+
col_def = "t.virtual \"#{column['COLUMN_NAME']}\", type: :#{map_column_type(column)}, as: \"#{escape_string(column['GENERATION_EXPRESSION'])}\""
|
|
441
|
+
col_def += ", stored: true" if stored_generated_column?(column)
|
|
442
|
+
col_def += ", comment: \"#{escape_string(column['COLUMN_COMMENT'])}\"" if column['COLUMN_COMMENT'] && !column['COLUMN_COMMENT'].empty?
|
|
443
|
+
col_def
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def generated_column?(column)
|
|
447
|
+
column['EXTRA'].include?(' GENERATED')
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def stored_generated_column?(column)
|
|
451
|
+
column['EXTRA'].include?('STORED GENERATED')
|
|
452
|
+
end
|
|
453
|
+
|
|
438
454
|
def map_column_type(column)
|
|
439
455
|
# Check for boolean (tinyint(1))
|
|
440
456
|
if column['COLUMN_TYPE'] == 'tinyint(1)'
|
|
@@ -476,7 +492,7 @@ module FastSchemaDumper
|
|
|
476
492
|
|
|
477
493
|
# Special handling for boolean (tinyint(1))
|
|
478
494
|
if column_type == 'tinyint(1)'
|
|
479
|
-
return default == '1' ? 'true' : 'false'
|
|
495
|
+
return (default == '1') ? 'true' : 'false'
|
|
480
496
|
end
|
|
481
497
|
|
|
482
498
|
case data_type
|
|
@@ -495,19 +511,19 @@ module FastSchemaDumper
|
|
|
495
511
|
# MySQL double is mapped to Type::Float in Rails
|
|
496
512
|
default.to_f.inspect
|
|
497
513
|
when 'json'
|
|
498
|
-
default == "'[]'" ? '[]' : '{}'
|
|
514
|
+
(default == "'[]'") ? '[]' : '{}'
|
|
499
515
|
else
|
|
500
|
-
|
|
516
|
+
/^'.*'$/.match?(default) ? "\"#{default[1..-2]}\"" : default
|
|
501
517
|
end
|
|
502
518
|
end
|
|
503
519
|
|
|
504
520
|
def format_index(index_name, index_data)
|
|
505
521
|
idx_def = "t.index "
|
|
506
522
|
|
|
507
|
-
if index_data[:columns].size == 1
|
|
508
|
-
|
|
523
|
+
idx_def += if index_data[:columns].size == 1
|
|
524
|
+
"[\"#{index_data[:columns].first}\"]"
|
|
509
525
|
else
|
|
510
|
-
|
|
526
|
+
"[#{index_data[:columns].map { |c| "\"#{c}\"" }.join(', ')}]"
|
|
511
527
|
end
|
|
512
528
|
|
|
513
529
|
idx_def += ", name: \"#{index_name}\""
|
|
@@ -522,12 +538,12 @@ module FastSchemaDumper
|
|
|
522
538
|
end
|
|
523
539
|
|
|
524
540
|
unless order_hash.empty?
|
|
525
|
-
if index_data[:columns].size == 1
|
|
541
|
+
idx_def += if index_data[:columns].size == 1
|
|
526
542
|
# For single column index, use simplified syntax
|
|
527
|
-
|
|
543
|
+
", order: :#{order_hash.values.first}"
|
|
528
544
|
else
|
|
529
545
|
# For compound index, use hash syntax
|
|
530
|
-
|
|
546
|
+
", order: { #{order_hash.map { |k, v| "#{k}: :#{v}" }.join(', ')} }"
|
|
531
547
|
end
|
|
532
548
|
end
|
|
533
549
|
end
|
data/lib/fast_schema_dumper.rb
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
require "fast_schema_dumper/fast_dumper"
|
|
5
|
+
|
|
6
|
+
class FastSchemaDumperTest < Minitest::Test
|
|
7
|
+
def test_that_it_has_a_version_number
|
|
8
|
+
refute_nil ::FastSchemaDumper::VERSION
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_format_generated_column
|
|
12
|
+
dumper = FastSchemaDumper::SchemaDumper.new
|
|
13
|
+
column = {
|
|
14
|
+
'COLUMN_NAME' => 'active_unique_key',
|
|
15
|
+
'DATA_TYPE' => 'int',
|
|
16
|
+
'COLUMN_TYPE' => 'int',
|
|
17
|
+
'EXTRA' => 'STORED GENERATED',
|
|
18
|
+
'GENERATION_EXPRESSION' => 'if((`deleted_at` is null),1,NULL)',
|
|
19
|
+
'COLUMN_COMMENT' => '',
|
|
20
|
+
'IS_NULLABLE' => 'YES'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
actual = dumper.send(:format_column, column)
|
|
24
|
+
|
|
25
|
+
assert_equal(
|
|
26
|
+
't.virtual "active_unique_key", type: :integer, as: "if((`deleted_at` is null),1,NULL)", stored: true',
|
|
27
|
+
actual
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_format_generated_column_with_comment
|
|
32
|
+
dumper = FastSchemaDumper::SchemaDumper.new
|
|
33
|
+
column = {
|
|
34
|
+
'COLUMN_NAME' => 'full_name',
|
|
35
|
+
'DATA_TYPE' => 'varchar',
|
|
36
|
+
'COLUMN_TYPE' => 'varchar(255)',
|
|
37
|
+
'EXTRA' => 'VIRTUAL GENERATED',
|
|
38
|
+
'GENERATION_EXPRESSION' => 'concat(`first_name`,`last_name`)',
|
|
39
|
+
'COLUMN_COMMENT' => 'generated name',
|
|
40
|
+
'IS_NULLABLE' => 'YES'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
actual = dumper.send(:format_column, column)
|
|
44
|
+
|
|
45
|
+
assert_equal(
|
|
46
|
+
't.virtual "full_name", type: :string, as: "concat(`first_name`,`last_name`)", comment: "generated name"',
|
|
47
|
+
actual
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def test_generated_column_detection_does_not_match_default_generated
|
|
52
|
+
dumper = FastSchemaDumper::SchemaDumper.new
|
|
53
|
+
column = {
|
|
54
|
+
'COLUMN_NAME' => 'created_at',
|
|
55
|
+
'DATA_TYPE' => 'datetime',
|
|
56
|
+
'COLUMN_TYPE' => 'datetime',
|
|
57
|
+
'EXTRA' => 'DEFAULT_GENERATED',
|
|
58
|
+
'COLUMN_DEFAULT' => 'CURRENT_TIMESTAMP',
|
|
59
|
+
'COLUMN_COMMENT' => '',
|
|
60
|
+
'IS_NULLABLE' => 'NO',
|
|
61
|
+
'COLUMN_KEY' => '',
|
|
62
|
+
'CHARACTER_MAXIMUM_LENGTH' => nil,
|
|
63
|
+
'NUMERIC_PRECISION' => nil,
|
|
64
|
+
'NUMERIC_SCALE' => nil,
|
|
65
|
+
'DATETIME_PRECISION' => nil,
|
|
66
|
+
'COLLATION_NAME' => nil
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
actual = dumper.send(:format_column, column)
|
|
70
|
+
|
|
71
|
+
assert_equal('t.datetime "created_at", default: -> { "CURRENT_TIMESTAMP" }, null: false', actual)
|
|
72
|
+
end
|
|
73
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fast_schema_dumper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daisuke Aritomo
|
|
@@ -46,6 +46,7 @@ executables:
|
|
|
46
46
|
extensions: []
|
|
47
47
|
extra_rdoc_files: []
|
|
48
48
|
files:
|
|
49
|
+
- ".standard.yml"
|
|
49
50
|
- CHANGELOG.md
|
|
50
51
|
- README.md
|
|
51
52
|
- Rakefile
|
|
@@ -55,6 +56,8 @@ files:
|
|
|
55
56
|
- lib/fast_schema_dumper/fast_dumper.rb
|
|
56
57
|
- lib/fast_schema_dumper/ridgepole.rb
|
|
57
58
|
- lib/fast_schema_dumper/version.rb
|
|
59
|
+
- test/fast_schema_dumper_test.rb
|
|
60
|
+
- test/test_helper.rb
|
|
58
61
|
homepage: https://github.com/osyoyu/fast_schema_dumper
|
|
59
62
|
licenses: []
|
|
60
63
|
metadata:
|