Skip to content

Commit

Permalink
* Update db:migrate:up & db:migrate:down to act more like active reco…
Browse files Browse the repository at this point in the history
…rd one
  • Loading branch information
PikachuEXE committed Nov 13, 2023
1 parent a94425a commit 0e20cb8
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 5 deletions.
56 changes: 53 additions & 3 deletions lib/sequel_rails/migrations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
module SequelRails
class Migrations
class << self
def migrate(version = nil)
opts = {}
def migrate(version = nil, migrator_class = ::Sequel::Migrator, opts = {})
opts[:target] = version.to_i if version
opts[:allow_missing_migration_files] = !!SequelRails.configuration.allow_missing_migration_files

if migrations_dir.directory?
::Sequel::Migrator.run(::Sequel::Model.db, migrations_dir, opts)
migrator_class.run(::Sequel::Model.db, migrations_dir, opts)
else
relative_path_name = migrations_dir.relative_path_from(Rails.root).to_s
raise "The #{relative_path_name} directory doesn't exist, you need to create it."
Expand All @@ -18,6 +17,14 @@ def migrate(version = nil)
alias_method :migrate_up!, :migrate
alias_method :migrate_down!, :migrate

def migrate_up_one_version!(version)
migrate(version, SequelRails::OneTimestampVersionMigrator, { direction: :up })
end

def migrate_down_one_version!(version)
migrate(version, SequelRails::OneTimestampVersionMigrator, { direction: :down })
end

def pending_migrations?
return false unless available_migrations?
!::Sequel::Migrator.is_current?(::Sequel::Model.db, migrations_dir)
Expand Down Expand Up @@ -78,4 +85,47 @@ def init_migrator
end
end
end

class OneTimestampVersionMigrator < ::Sequel::TimestampMigrator
Error = ::Sequel::Migrator::Error

# The direction of the migrator, either :up or :down
attr_reader :direction

# Set up all state for the migrator instance
def initialize(db, directory, opts = OPTS)
@direction = opts[:direction]
super

raise(Error, 'target (version) is absent') if target.nil?
raise(Error, "Invalid direction: #{direction}") unless [:up, :down].include?(direction)
# `allow_missing_migration_files` ignored, as version is from user input
raise(Error, "Migration with version #{target} not found") if migration_tuples.empty?
end

private

def get_migration_tuples
up_mts = []
down_mts = []
files.each do |path|
f = File.basename(path)
fi = f.downcase
next unless migration_version_from_file(f) == target

case [direction, applied_migrations.include?(fi)]
when [:up, false]
# Normal migrate up
up_mts << [load_migration_file(path), f, :up]
when [:down, true]
# Normal migrate down
down_mts << [load_migration_file(path), f, :down]
else
# Already run, don't re-run
raise(Error, "Migration with version #{target} is already migrated/rollback")
end
end
up_mts + down_mts.reverse
end
end
end
4 changes: 2 additions & 2 deletions lib/sequel_rails/railties/database.rake
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,15 @@ namespace sequel_rails_namespace do
task :up => :load do
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
raise 'VERSION is required' unless version
SequelRails::Migrations.migrate_up!(version)
SequelRails::Migrations.migrate_up_one_version!(version)
Rake::Task["#{sequel_rails_namespace}:dump"].invoke if SequelRails.configuration.schema_dump
end

desc 'Runs the "down" for a given migration VERSION.'
task :down => :load do
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
raise 'VERSION is required' unless version
SequelRails::Migrations.migrate_down!(version)
SequelRails::Migrations.migrate_down_one_version!(version)
Rake::Task["#{sequel_rails_namespace}:dump"].invoke if SequelRails.configuration.schema_dump
end
end
Expand Down
166 changes: 166 additions & 0 deletions spec/lib/sequel_rails/railties/database_rake_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,172 @@
end
end

describe 'db:migrate:up' do
let(:version) { nil }
around do |example|
env_value_before = ENV['VERSION']
ENV['VERSION'] = version
example.run
ENV['VERSION'] = env_value_before
end
let(:rake_task_call) do
proc { Rake::Task['db:migrate:up'].execute }
end

context 'when no version supplied' do
it 'raises error' do
Dir.chdir app_root do
begin
expect do
rake_task_call.call
end.to raise_error('VERSION is required')
ensure
SequelRails::Migrations.migrate_up!
end
end
end
end

context 'when version with no matching migration supplied' do
let(:version) { '1273253848' }

it 'raises error' do
Dir.chdir app_root do
begin
expect do
rake_task_call.call
end.to raise_error("Migration with version #{version} not found")
ensure
SequelRails::Migrations.migrate_up!
end
end
end
end

context 'when version with matching migration supplied' do
let(:version) { '1273253849' }

context 'and migration already run' do
it 'raises error' do
Dir.chdir app_root do
begin
expect do
rake_task_call.call
end.to raise_error("Migration with version #{version} is already migrated/rollback")
ensure
SequelRails::Migrations.migrate_up!
end
end
end
end

context 'and migration NOT already run' do
before do
Dir.chdir app_root do
SequelRails::Migrations.migrate_down!(0)
end
end

it 'runs the matching migration ONLY' do
Dir.chdir app_root do
begin
expect do
rake_task_call.call
end.to change { SequelRails::Migrations.current_migration }
.from(nil)
.to('1273253849_add_twitter_handle_to_users.rb')
ensure
SequelRails::Migrations.migrate_up!
end
end
end
end
end
end

describe 'db:migrate:down' do
let(:version) { nil }
around do |example|
env_value_before = ENV['VERSION']
ENV['VERSION'] = version
example.run
ENV['VERSION'] = env_value_before
end
let(:rake_task_call) do
proc { Rake::Task['db:migrate:down'].execute }
end

context 'when no version supplied' do
it 'raises error' do
Dir.chdir app_root do
begin
expect do
rake_task_call.call
end.to raise_error('VERSION is required')
ensure
SequelRails::Migrations.migrate_up!
end
end
end
end

context 'when version with no matching migration supplied' do
let(:version) { '1273253848' }

it 'raises error' do
Dir.chdir app_root do
begin
expect do
rake_task_call.call
end.to raise_error("Migration with version #{version} not found")
ensure
SequelRails::Migrations.migrate_up!
end
end
end
end

context 'when version with matching migration supplied' do
let(:version) { '1365762738' }

context 'and migration already run' do
it 'reverts the matching migration ONLY' do
Dir.chdir app_root do
begin
expect do
rake_task_call.call
end.to change { SequelRails::Migrations.previous_migration }
.from('1365762738_add_display_name_to_users.rb')
.to('1273253849_add_twitter_handle_to_users.rb')
ensure
SequelRails::Migrations.migrate_up!
end
end
end
end

context 'and migration NOT already run' do
before do
Dir.chdir app_root do
SequelRails::Migrations.migrate_down!(0)
end
end

it 'raises error' do
Dir.chdir app_root do
begin
expect do
rake_task_call.call
end.to raise_error("Migration with version #{version} is already migrated/rollback")
ensure
SequelRails::Migrations.migrate_up!
end
end
end
end
end
end

describe 'db:rollback' do
let(:version) { nil }
let(:rake_task_call) do
Expand Down
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

# Combustion initialization has to happen before loading rspec/rails
Combustion.initialize! :sequel_rails
# Load rake tasks
Combustion::Application.load_tasks

require 'rspec/rails'
require 'ammeter/init'
Expand Down

0 comments on commit 0e20cb8

Please sign in to comment.