Skip to content

Commit

Permalink
Extract patches to separate files (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
ka8725 authored Oct 17, 2023
1 parent 0ff50ef commit c204d83
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 77 deletions.
17 changes: 16 additions & 1 deletion lib/actual_db_schema.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# frozen_string_literal: true

require "active_record/migration"
require_relative "actual_db_schema/version"
require_relative "actual_db_schema/patches/migration_proxy"
require_relative "actual_db_schema/patches/migrator"
require_relative "actual_db_schema/patches/migration_context"

# The main module definition
module ActualDbSchema
Expand All @@ -9,10 +13,21 @@ module ActualDbSchema
require "railtie"

class << self
attr_accessor :config
attr_accessor :config, :failed
end

self.failed = []
self.config = {
enabled: Rails.env.development?
}

def self.migrated_folder
Rails.root.join("tmp", "migrated").tap { |folder| FileUtils.mkdir_p(folder) }
end

def self.migration_filename(fullpath)
fullpath.split("/").last
end
end

ActiveRecord::MigrationProxy.prepend(ActualDbSchema::Patches::MigrationProxy)
40 changes: 40 additions & 0 deletions lib/actual_db_schema/patches/migration_context.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

module ActualDbSchema
module Patches
# Add new command to roll back the phantom migrations
module MigrationContext
def rollback_branches
ActualDbSchema.failed = []
migrations.reverse_each do |migration|
migrator = down_migrator_for(migration)
migrator.extend(ActualDbSchema::Patches::Migrator)
migrator.migrate
rescue StandardError => e
raise unless e.message.include?("ActiveRecord::IrreversibleMigration")

ActualDbSchema.failed << migration
end
end

private

def down_migrator_for(migration)
if ActiveRecord::Migration.current_version < 6
ActiveRecord::Migrator.new(:down, [migration], migration.version)
else
ActiveRecord::Migrator.new(:down, [migration], schema_migration, migration.version)
end
end

def migration_files
paths = Array(migrations_paths)
current_branch_files = Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
other_branches_files = Dir["#{ActualDbSchema.migrated_folder}/**/[0-9]*_*.rb"]

current_branch_file_names = current_branch_files.map { |f| ActualDbSchema.migration_filename(f) }
other_branches_files.reject { |f| ActualDbSchema.migration_filename(f).in?(current_branch_file_names) }
end
end
end
end
13 changes: 13 additions & 0 deletions lib/actual_db_schema/patches/migration_proxy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module ActualDbSchema
module Patches
# Records the migration file into the tmp folder after it's been migrated
module MigrationProxy
def migrate(direction)
super(direction)
FileUtils.copy(filename, ActualDbSchema.migrated_folder.join(basename)) if direction == :up
end
end
end
end
13 changes: 13 additions & 0 deletions lib/actual_db_schema/patches/migrator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module ActualDbSchema
module Patches
# Run only one migration that's being rolled back
module Migrator
def runnable
migration = migrations.first # there is only one migration, because we pass only one here
ran?(migration) ? [migration] : []
end
end
end
end
80 changes: 5 additions & 75 deletions lib/tasks/db.rake
Original file line number Diff line number Diff line change
@@ -1,88 +1,18 @@
# frozen_string_literal: true

return unless ActualDbSchema.config.fetch(:enabled, true)

require "active_record/migration"

def migrated_folder
Rails.root.join("tmp", "migrated").tap { |folder| FileUtils.mkdir_p(folder) }
end

def migration_filename(fullpath)
fullpath.split("/").last
end

# All patches are namespaced into this module
module ActualDbSchema
class << self
attr_accessor :failed
end

self.failed = []

# Track migrated migrations inside the tmp folder
module MigrationProxyPatch
def migrate(direction)
super(direction)
FileUtils.copy(filename, migrated_folder.join(basename)) if direction == :up
end
end

# Run only one migration that's being rolled back
module MigratorPatch
def runnable
migration = migrations.first # there is only one migration, because we pass only one here
ran?(migration) ? [migration] : []
end
end

# Add new command to roll back the phantom migrations
module MigrationContextPatch
def rollback_branches
migrations.reverse_each do |migration|
migrator = down_migrator_for(migration)
migrator.extend(ActualDbSchema::MigratorPatch)
migrator.migrate
rescue StandardError => e
raise unless e.message.include?("ActiveRecord::IrreversibleMigration")

ActualDbSchema.failed << migration
end
end

private

def down_migrator_for(migration)
if ActiveRecord::Migration.current_version < 6
ActiveRecord::Migrator.new(:down, [migration], migration.version)
else
ActiveRecord::Migrator.new(:down, [migration], schema_migration, migration.version)
end
end

def migration_files
paths = Array(migrations_paths)
current_branch_files = Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
other_branches_files = Dir["#{migrated_folder}/**/[0-9]*_*.rb"]

current_branch_file_names = current_branch_files.map { |f| migration_filename(f) }
other_branches_files.reject { |f| migration_filename(f).in?(current_branch_file_names) }
end
end
end

ActiveRecord::MigrationProxy.prepend(ActualDbSchema::MigrationProxyPatch)

namespace :db do
desc "Rollback migrations that were run inside not a merged branch."
task rollback_branches: :load_config do
unless ActualDbSchema.config.fetch(:enabled, true)
raise "ActualDbSchema is disabled. Set ActualDbSchema.config[:enabled] = true to enable it."
end

if ActiveRecord::Migration.current_version >= 6
ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:rollback_branches")
end

context = ActiveRecord::Base.connection.migration_context
context.extend(ActualDbSchema::MigrationContextPatch)
ActualDbSchema.failed = []
context.extend(ActualDbSchema::Patches::MigrationContext)
context.rollback_branches
if ActualDbSchema.failed.any?
puts ""
Expand Down
2 changes: 1 addition & 1 deletion test/dummy_app/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2013_09_06_111513) do
ActiveRecord::Schema[7.0].define(version: 0) do
end

0 comments on commit c204d83

Please sign in to comment.