migsupo 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,31 @@
1
+ module Migsupo
2
+ module Schema
3
+ class TableDefinition
4
+ attr_reader :name, :columns, :indexes, :options
5
+
6
+ def initialize(name:, columns: [], indexes: [], options: {})
7
+ @name = name.to_s
8
+ @columns = columns.freeze
9
+ @indexes = indexes.freeze
10
+ @options = options.transform_keys(&:to_sym).freeze
11
+
12
+ @columns_by_name = columns.each_with_object({}) { |c, h| h[c.name] = c }.freeze
13
+ @indexes_by_name = indexes.each_with_object({}) { |i, h| h[i.name] = i }.freeze
14
+
15
+ freeze
16
+ end
17
+
18
+ def column(col_name)
19
+ @columns_by_name[col_name.to_s]
20
+ end
21
+
22
+ def index(idx_name)
23
+ @indexes_by_name[idx_name.to_s]
24
+ end
25
+
26
+ def to_h
27
+ { name: name, columns: columns.map(&:to_h), indexes: indexes.map(&:to_h), options: options }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,56 @@
1
+ namespace :db do
2
+ namespace :generate_migration do
3
+ desc "Show diff between Schemafile and current DB schema without generating files"
4
+ task diff: :environment do
5
+ diff = Migsupo.calculate_diff(
6
+ schemafile_path: ENV.fetch("SCHEMAFILE", Migsupo.configuration.schemafile_path),
7
+ loader: ENV.fetch("LOADER", Migsupo.configuration.loader.to_s).to_sym
8
+ )
9
+ puts diff.to_s
10
+ end
11
+
12
+ desc "Exit with code 1 if Schemafile and current DB schema are not in sync"
13
+ task check: :environment do
14
+ diff = Migsupo.calculate_diff(
15
+ schemafile_path: ENV.fetch("SCHEMAFILE", Migsupo.configuration.schemafile_path),
16
+ loader: ENV.fetch("LOADER", Migsupo.configuration.loader.to_s).to_sym
17
+ )
18
+ if diff.empty?
19
+ puts "Schema is in sync."
20
+ else
21
+ puts "Schema is out of sync:"
22
+ puts diff.to_s
23
+ exit 1
24
+ end
25
+ end
26
+ end
27
+
28
+ desc "Generate Rails migration files from diff between Schemafile and current DB schema"
29
+ task generate_migration: :environment do
30
+ schemafile_path = ENV.fetch("SCHEMAFILE", Migsupo.configuration.schemafile_path)
31
+ output_dir = ENV.fetch("OUTPUT_DIR", Migsupo.configuration.migrations_dir)
32
+ loader = ENV.fetch("LOADER", Migsupo.configuration.loader.to_s).to_sym
33
+ dry_run = ENV["DRY_RUN"] == "true"
34
+ verbose = ENV["VERBOSE"] == "true"
35
+
36
+ diff = Migsupo.calculate_diff(schemafile_path: schemafile_path, loader: loader)
37
+
38
+ if diff.empty?
39
+ puts "No changes detected. No migration files generated."
40
+ next
41
+ end
42
+
43
+ if verbose
44
+ puts "Detected changes:"
45
+ puts diff.to_s
46
+ puts
47
+ end
48
+
49
+ files = Migsupo.generate_migrations(diff, output_dir: output_dir, dry_run: dry_run)
50
+
51
+ unless dry_run
52
+ puts "Generated #{files.size} migration file(s):"
53
+ files.each { |f| puts " #{f}" }
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module Migsupo
2
+ VERSION = "0.1.0"
3
+ end
data/lib/migsupo.rb ADDED
@@ -0,0 +1,75 @@
1
+ require_relative "migsupo/version"
2
+ require_relative "migsupo/configuration"
3
+ require_relative "migsupo/schema/column_definition"
4
+ require_relative "migsupo/schema/index_definition"
5
+ require_relative "migsupo/schema/table_definition"
6
+ require_relative "migsupo/schema/schema_definition"
7
+ require_relative "migsupo/parser/schemafile_parser"
8
+ require_relative "migsupo/loader/active_record_loader"
9
+ require_relative "migsupo/loader/schema_rb_loader"
10
+ require_relative "migsupo/differ/diff_calculator"
11
+ require_relative "migsupo/generator/migration_generator"
12
+
13
+ module Migsupo
14
+ class << self
15
+ def configure
16
+ yield configuration
17
+ end
18
+
19
+ def configuration
20
+ @configuration ||= Configuration.new
21
+ end
22
+
23
+ def calculate_diff(schemafile_path: nil, loader: nil)
24
+ schemafile_path ||= configuration.schemafile_path
25
+ loader ||= configuration.loader
26
+
27
+ desired = Parser::SchemafileParser.parse(schemafile_path)
28
+ current = build_loader(loader).load_schema
29
+
30
+ Differ::DiffCalculator.new(rename_hints: configuration.rename_hints)
31
+ .calculate(desired: desired, current: current)
32
+ end
33
+
34
+ def generate_migrations(diff, output_dir: nil, dry_run: false)
35
+ output_dir ||= configuration.migrations_dir
36
+
37
+ Generator::MigrationGenerator.new(rails_version: detect_rails_version)
38
+ .generate(diff, output_dir: output_dir, dry_run: dry_run)
39
+ end
40
+
41
+ private
42
+
43
+ def build_loader(loader)
44
+ case loader.to_sym
45
+ when :active_record
46
+ Loader::ActiveRecordLoader.new(ignored_tables: configuration.ignored_tables)
47
+ when :schema_rb
48
+ Loader::SchemaRbLoader.new(
49
+ schema_rb_path: resolve_schema_rb_path,
50
+ ignored_tables: configuration.ignored_tables
51
+ )
52
+ else
53
+ raise ArgumentError, "Unknown loader: #{loader}. Use :active_record or :schema_rb"
54
+ end
55
+ end
56
+
57
+ def resolve_schema_rb_path
58
+ if defined?(Rails)
59
+ Rails.root.join("db", "schema.rb").to_s
60
+ else
61
+ "db/schema.rb"
62
+ end
63
+ end
64
+
65
+ def detect_rails_version
66
+ return configuration.migration_version if configuration.migration_version
67
+ return nil unless defined?(Rails)
68
+
69
+ major_minor = Rails::VERSION::STRING.split(".").first(2).join(".")
70
+ major_minor
71
+ end
72
+ end
73
+ end
74
+
75
+ require_relative "migsupo/railtie" if defined?(Rails::Railtie)
data/migsupo.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ require_relative "lib/migsupo/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "migsupo"
5
+ spec.version = Migsupo::VERSION
6
+ spec.authors = ["masak1yu"]
7
+ spec.summary = "Generate Rails migrations from a Schemafile diff"
8
+ spec.description = "Like ridgepole but outputs Rails migration files instead of applying schema changes directly to the DB"
9
+ spec.license = "MIT"
10
+
11
+ spec.files = Dir["lib/**/*", "*.gemspec", "README.md"]
12
+ spec.require_paths = ["lib"]
13
+
14
+ spec.required_ruby_version = ">= 3.0"
15
+
16
+ spec.add_dependency "activerecord", ">= 6.1"
17
+ spec.add_dependency "activesupport", ">= 6.1"
18
+
19
+ spec.add_development_dependency "railties", ">= 6.1"
20
+ spec.add_development_dependency "rspec", "~> 3.12"
21
+ spec.add_development_dependency "rubocop", "~> 1.60"
22
+ spec.add_development_dependency "sqlite3", ">= 1.4"
23
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: migsupo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - masak1yu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-03-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '6.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '6.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: railties
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '6.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '6.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.60'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.60'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '1.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '1.4'
97
+ description: Like ridgepole but outputs Rails migration files instead of applying
98
+ schema changes directly to the DB
99
+ email:
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - README.md
105
+ - lib/migsupo.rb
106
+ - lib/migsupo/configuration.rb
107
+ - lib/migsupo/differ/diff.rb
108
+ - lib/migsupo/differ/diff_calculator.rb
109
+ - lib/migsupo/differ/operations/add_column.rb
110
+ - lib/migsupo/differ/operations/add_index.rb
111
+ - lib/migsupo/differ/operations/change_column.rb
112
+ - lib/migsupo/differ/operations/create_table.rb
113
+ - lib/migsupo/differ/operations/drop_table.rb
114
+ - lib/migsupo/differ/operations/remove_column.rb
115
+ - lib/migsupo/differ/operations/remove_index.rb
116
+ - lib/migsupo/differ/operations/rename_column.rb
117
+ - lib/migsupo/generator/migration_builder.rb
118
+ - lib/migsupo/generator/migration_generator.rb
119
+ - lib/migsupo/generator/naming.rb
120
+ - lib/migsupo/loader/active_record_loader.rb
121
+ - lib/migsupo/loader/schema_rb_loader.rb
122
+ - lib/migsupo/parser/dsl_context.rb
123
+ - lib/migsupo/parser/schemafile_parser.rb
124
+ - lib/migsupo/parser/table_dsl_context.rb
125
+ - lib/migsupo/railtie.rb
126
+ - lib/migsupo/schema/column_definition.rb
127
+ - lib/migsupo/schema/index_definition.rb
128
+ - lib/migsupo/schema/schema_definition.rb
129
+ - lib/migsupo/schema/table_definition.rb
130
+ - lib/migsupo/tasks/migsupo.rake
131
+ - lib/migsupo/version.rb
132
+ - migsupo.gemspec
133
+ homepage:
134
+ licenses:
135
+ - MIT
136
+ metadata: {}
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '3.0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.2.32
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Generate Rails migrations from a Schemafile diff
156
+ test_files: []