Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update db:migrate:up & db:migrate:down to act more like active record one #200

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 56 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,50 @@ 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

# This is overriding parent method, no way to choose another method name
# rubocop:disable Naming/AccessorMethodName
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
# rubocop:enable Naming/AccessorMethodName
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
213 changes: 213 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,219 @@
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
context 'and target migration is 1 version later than current migration' 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

context 'and target migration is > 1 version later than current migration' do
let(:version) { '1365762739' }
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 {
{
current_migration: SequelRails::Migrations.current_migration,
previous_migration: SequelRails::Migrations.previous_migration
}
}
.from({
current_migration: nil,
previous_migration: '0'
})
.to({
current_migration: '1365762739_add_github_username_to_users.rb',
previous_migration: '0'
})
ensure
SequelRails::Migrations.migrate_up!
end
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 {
{
current_migration: SequelRails::Migrations.current_migration,
previous_migration: SequelRails::Migrations.previous_migration
}
}
.from({
current_migration: '1365762739_add_github_username_to_users.rb',
previous_migration: '1365762738_add_display_name_to_users.rb'
})
.to({
current_migration: '1365762739_add_github_username_to_users.rb',
previous_migration: '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