Skip to content

Commit d775de6

Browse files
committed
* Update db:migrate:up & db:migrate:down to act more like active record one
1 parent c99bef1 commit d775de6

File tree

4 files changed

+270
-5
lines changed

4 files changed

+270
-5
lines changed

lib/sequel_rails/migrations.rb

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
module SequelRails
44
class Migrations
55
class << self
6-
def migrate(version = nil)
7-
opts = {}
6+
def migrate(version = nil, migrator_class = ::Sequel::Migrator, opts = {})
87
opts[:target] = version.to_i if version
98
opts[:allow_missing_migration_files] = !!SequelRails.configuration.allow_missing_migration_files
109

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

20+
def migrate_up_one_version!(version)
21+
migrate(version, SequelRails::OneTimestampVersionMigrator, { direction: :up })
22+
end
23+
24+
def migrate_down_one_version!(version)
25+
migrate(version, SequelRails::OneTimestampVersionMigrator, { direction: :down })
26+
end
27+
2128
def pending_migrations?
2229
return false unless available_migrations?
2330
!::Sequel::Migrator.is_current?(::Sequel::Model.db, migrations_dir)
@@ -78,4 +85,47 @@ def init_migrator
7885
end
7986
end
8087
end
88+
89+
class OneTimestampVersionMigrator < ::Sequel::TimestampMigrator
90+
Error = ::Sequel::Migrator::Error
91+
92+
# The direction of the migrator, either :up or :down
93+
attr_reader :direction
94+
95+
# Set up all state for the migrator instance
96+
def initialize(db, directory, opts = OPTS)
97+
@direction = opts[:direction]
98+
super
99+
100+
raise(Error, 'target (version) is absent') if target.nil?
101+
raise(Error, "Invalid direction: #{direction}") unless [:up, :down].include?(direction)
102+
# `allow_missing_migration_files` ignored, as version is from user input
103+
raise(Error, "Migration with version #{target} not found") if migration_tuples.empty?
104+
end
105+
106+
private
107+
108+
def get_migration_tuples
109+
up_mts = []
110+
down_mts = []
111+
files.each do |path|
112+
f = File.basename(path)
113+
fi = f.downcase
114+
next unless migration_version_from_file(f) == target
115+
116+
case [direction, applied_migrations.include?(fi)]
117+
when [:up, false]
118+
# Normal migrate up
119+
up_mts << [load_migration_file(path), f, :up]
120+
when [:down, true]
121+
# Normal migrate down
122+
down_mts << [load_migration_file(path), f, :down]
123+
else
124+
# Already run, don't re-run
125+
raise(Error, "Migration with version #{target} is already migrated/rollback")
126+
end
127+
end
128+
up_mts + down_mts.reverse
129+
end
130+
end
81131
end

lib/sequel_rails/railties/database.rake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,15 @@ namespace sequel_rails_namespace do
154154
task :up => :load do
155155
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
156156
raise 'VERSION is required' unless version
157-
SequelRails::Migrations.migrate_up!(version)
157+
SequelRails::Migrations.migrate_up_one_version!(version)
158158
Rake::Task["#{sequel_rails_namespace}:dump"].invoke if SequelRails.configuration.schema_dump
159159
end
160160

161161
desc 'Runs the "down" for a given migration VERSION.'
162162
task :down => :load do
163163
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
164164
raise 'VERSION is required' unless version
165-
SequelRails::Migrations.migrate_down!(version)
165+
SequelRails::Migrations.migrate_down_one_version!(version)
166166
Rake::Task["#{sequel_rails_namespace}:dump"].invoke if SequelRails.configuration.schema_dump
167167
end
168168
end

spec/lib/sequel_rails/railties/database_rake_spec.rb

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,219 @@
8181
end
8282
end
8383

84+
describe 'db:migrate:up' do
85+
let(:version) { nil }
86+
around do |example|
87+
env_value_before = ENV['VERSION']
88+
ENV['VERSION'] = version
89+
example.run
90+
ENV['VERSION'] = env_value_before
91+
end
92+
let(:rake_task_call) do
93+
proc { Rake::Task['db:migrate:up'].execute }
94+
end
95+
96+
context 'when no version supplied' do
97+
it 'raises error' do
98+
Dir.chdir app_root do
99+
begin
100+
expect do
101+
rake_task_call.call
102+
end.to raise_error('VERSION is required')
103+
ensure
104+
SequelRails::Migrations.migrate_up!
105+
end
106+
end
107+
end
108+
end
109+
110+
context 'when version with no matching migration supplied' do
111+
let(:version) { '1273253848' }
112+
113+
it 'raises error' do
114+
Dir.chdir app_root do
115+
begin
116+
expect do
117+
rake_task_call.call
118+
end.to raise_error("Migration with version #{version} not found")
119+
ensure
120+
SequelRails::Migrations.migrate_up!
121+
end
122+
end
123+
end
124+
end
125+
126+
context 'when version with matching migration supplied' do
127+
let(:version) { '1273253849' }
128+
129+
context 'and migration already run' do
130+
it 'raises error' do
131+
Dir.chdir app_root do
132+
begin
133+
expect do
134+
rake_task_call.call
135+
end.to raise_error("Migration with version #{version} is already migrated/rollback")
136+
ensure
137+
SequelRails::Migrations.migrate_up!
138+
end
139+
end
140+
end
141+
end
142+
143+
context 'and migration NOT already run' do
144+
context 'and target migration is 1 version later than current migration' do
145+
before do
146+
Dir.chdir app_root do
147+
SequelRails::Migrations.migrate_down!(0)
148+
end
149+
end
150+
151+
it 'runs the matching migration ONLY' do
152+
Dir.chdir app_root do
153+
begin
154+
expect do
155+
rake_task_call.call
156+
end.to change { SequelRails::Migrations.current_migration }
157+
.from(nil)
158+
.to('1273253849_add_twitter_handle_to_users.rb')
159+
ensure
160+
SequelRails::Migrations.migrate_up!
161+
end
162+
end
163+
end
164+
end
165+
166+
context 'and target migration is > 1 version later than current migration' do
167+
let(:version) { '1365762739' }
168+
before do
169+
Dir.chdir app_root do
170+
SequelRails::Migrations.migrate_down!(0)
171+
end
172+
end
173+
174+
it 'runs the matching migration ONLY' do
175+
Dir.chdir app_root do
176+
begin
177+
expect do
178+
rake_task_call.call
179+
end.to change {
180+
{
181+
current_migration: SequelRails::Migrations.current_migration,
182+
previous_migration: SequelRails::Migrations.previous_migration
183+
}
184+
}
185+
.from({
186+
current_migration: nil,
187+
previous_migration: '0'
188+
})
189+
.to({
190+
current_migration: '1365762739_add_github_username_to_users.rb',
191+
previous_migration: '0'
192+
})
193+
ensure
194+
SequelRails::Migrations.migrate_up!
195+
end
196+
end
197+
end
198+
end
199+
end
200+
end
201+
end
202+
203+
describe 'db:migrate:down' do
204+
let(:version) { nil }
205+
around do |example|
206+
env_value_before = ENV['VERSION']
207+
ENV['VERSION'] = version
208+
example.run
209+
ENV['VERSION'] = env_value_before
210+
end
211+
let(:rake_task_call) do
212+
proc { Rake::Task['db:migrate:down'].execute }
213+
end
214+
215+
context 'when no version supplied' do
216+
it 'raises error' do
217+
Dir.chdir app_root do
218+
begin
219+
expect do
220+
rake_task_call.call
221+
end.to raise_error('VERSION is required')
222+
ensure
223+
SequelRails::Migrations.migrate_up!
224+
end
225+
end
226+
end
227+
end
228+
229+
context 'when version with no matching migration supplied' do
230+
let(:version) { '1273253848' }
231+
232+
it 'raises error' do
233+
Dir.chdir app_root do
234+
begin
235+
expect do
236+
rake_task_call.call
237+
end.to raise_error("Migration with version #{version} not found")
238+
ensure
239+
SequelRails::Migrations.migrate_up!
240+
end
241+
end
242+
end
243+
end
244+
245+
context 'when version with matching migration supplied' do
246+
let(:version) { '1365762738' }
247+
248+
context 'and migration already run' do
249+
it 'reverts the matching migration ONLY' do
250+
Dir.chdir app_root do
251+
begin
252+
expect do
253+
rake_task_call.call
254+
end.to change {
255+
{
256+
current_migration: SequelRails::Migrations.current_migration,
257+
previous_migration: SequelRails::Migrations.previous_migration
258+
}
259+
}
260+
.from({
261+
current_migration: '1365762739_add_github_username_to_users.rb',
262+
previous_migration: '1365762738_add_display_name_to_users.rb'
263+
})
264+
.to({
265+
current_migration: '1365762739_add_github_username_to_users.rb',
266+
previous_migration: '1273253849_add_twitter_handle_to_users.rb'
267+
})
268+
ensure
269+
SequelRails::Migrations.migrate_up!
270+
end
271+
end
272+
end
273+
end
274+
275+
context 'and migration NOT already run' do
276+
before do
277+
Dir.chdir app_root do
278+
SequelRails::Migrations.migrate_down!(0)
279+
end
280+
end
281+
282+
it 'raises error' do
283+
Dir.chdir app_root do
284+
begin
285+
expect do
286+
rake_task_call.call
287+
end.to raise_error("Migration with version #{version} is already migrated/rollback")
288+
ensure
289+
SequelRails::Migrations.migrate_up!
290+
end
291+
end
292+
end
293+
end
294+
end
295+
end
296+
84297
describe 'db:rollback' do
85298
let(:version) { nil }
86299
let(:rake_task_call) do

spec/spec_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
# Combustion initialization has to happen before loading rspec/rails
77
Combustion.initialize! :sequel_rails
8+
# Load rake tasks
9+
Combustion::Application.load_tasks
810

911
require 'rspec/rails'
1012
require 'ammeter/init'

0 commit comments

Comments
 (0)