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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfd997d65329c763b3d9a2947bc4729bf705353b670ed92e3c6aeeb4e5a57274
4
- data.tar.gz: 3997c70bf8503357745c8ade27fd29d07853ab45bf9f992171102f9b5e79baed
3
+ metadata.gz: 67356310ddf238f27066fb0980ea10ff37b9ffcb5307f89c661fe81a1dc1a6c4
4
+ data.tar.gz: 8a5c15d6dff1209cd439dabf036ee3333bd08bee49e3481d7dd3ef233b07a679
5
5
  SHA512:
6
- metadata.gz: 3d1182a9edbe7099fcb22c9d40cb0548e6a261d2e1239e840d5f5e9bf96d44f7c23d2014ad534a561fca2e4b28fc0b309b9025cc4571d19581c932f6014e5e43
7
- data.tar.gz: a6007f94ad89cfd25efe63ad78037a9aa8cfc861e547c90fea76e4453321025dc1ab0c5acd960b67a5376aac17da738bb47840e15fe6de509b442c0371dee477
6
+ metadata.gz: ad7aa63eaae977b3fa65e210e3a66a6074d8a0ceb31a6e21c35c3f4a97dcd3a0658afd1c64fc46e3bab3e0cac198a26fdad8df1c14846aa35936b2e9feac573e
7
+ data.tar.gz: 858e4903f02a8b12a0fc153c577bc137418b56be1ae4bc522c65920c5fc8774f00598e55e8086b60e769e7959cfd94ae1408cd41f12ba8823ee350587bd6b794
data/.standard.yml ADDED
@@ -0,0 +1,5 @@
1
+ ruby_version: 3.2
2
+ ignore:
3
+ - "**/*":
4
+ - Style/StringLiterals
5
+ - Style/StringLiteralsInInterpolation
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
+ [![Test](https://github.com/smartbank-inc/fast_schema_dumper/actions/workflows/test.yml/badge.svg)](https://github.com/smartbank-inc/fast_schema_dumper/actions/workflows/test.yml)
2
+ [![Gem Version](https://badge.fury.io/rb/fast_schema_dumper.svg)](https://badge.fury.io/rb/fast_schema_dumper)
3
+
1
4
  # fast_schema_dumper
2
5
 
3
- A super fast alternative to ActiveRecord::SchemaDumper. Currently only MySQL is supported.
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
- Requiring `fast_schema_dumper/ridgepole` will overwrite `Ridgepole::Dumper.dump`, which will force Ridgepole to use fast_schema_dumper.
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 ... --apply
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
- task default: %i[]
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 './fast_dumper'
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(argv)
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
- return 0
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
- else
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
- if !fk[:fk_data][:constraint_name].start_with?("fk_rails_")
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
- private
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.reject { |name, _| name == 'PRIMARY' }.sort_by do |index_name, index_data|
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!(/\\'/, "'") # don't escape single quotes for compatibility with the original dumper
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
- column['CHARACTER_MAXIMUM_LENGTH'] != 255
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
- default =~ /^'.*'$/ ? "\"#{default[1..-2]}\"" : default
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
- idx_def += "[\"#{index_data[:columns].first}\"]"
523
+ idx_def += if index_data[:columns].size == 1
524
+ "[\"#{index_data[:columns].first}\"]"
509
525
  else
510
- idx_def += "[#{index_data[:columns].map { |c| "\"#{c}\"" }.join(', ')}]"
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
- idx_def += ", order: :#{order_hash.values.first}"
543
+ ", order: :#{order_hash.values.first}"
528
544
  else
529
545
  # For compound index, use hash syntax
530
- idx_def += ", order: { #{order_hash.map { |k, v| "#{k}: :#{v}" }.join(', ')} }"
546
+ ", order: { #{order_hash.map { |k, v| "#{k}: :#{v}" }.join(', ')} }"
531
547
  end
532
548
  end
533
549
  end
@@ -1,4 +1,6 @@
1
- require_relative './fast_dumper'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fast_dumper'
2
4
 
3
5
  # Loading this file will overwrite `Ridgepole::Dumper.dump`.
4
6
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FastSchemaDumper
4
- VERSION = "0.4.2"
4
+ VERSION = "0.5.0"
5
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "fast_schema_dumper/version"
2
4
 
3
5
  module FastSchemaDumper
@@ -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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
+ require "fast_schema_dumper"
5
+
6
+ require "minitest/autorun"
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.2
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: