I’m a big fan of Sequel and have been using it for 10+ years.
It comes with a powerful and robust migration toolkit for making changes to your database schema. The docs clearly pay respect to ActiveRecord migrations which have obviously influenced Sequel’s migrations.
Whilst ActiveRecord migrations (“rails db:migrate”) are often used with all the Rails trimmings (generating models will generate a migration etc), Sequel’s are often found in more varied projects.
If you’re familiar with Sequel migrations, you can skip to the end to find the Rake task that will help speed up the generation of new migration files!
Calling Sequel Migrations
Based primarily on Sequel’s main commiter’s work (Jeremy Evans), you can start using Sequel migrations by creating a db/migrations
directory and dropping some Ruby files in there with the correct Sequel DSL scaffolding.
An example migration would be named 001_create_table.rb
and could look something like this:
Sequel.migration do
change do
create_table(:my_table) do
Integer :id, primary_key: true
String :key, null: false
String :value, null: false
index :key, unique: true
end
end
This is detailed in depth in the docs.
However, you have to dig around a bit to learn how to call the migrations. The below Rake namespace/tasks are where I’ve ended up (most of it lifted from Jeremy’s projects/examples). The migrate
lambda makes it easily callable from 3 tasks and keeps it fairly DRY.
The rollback
task will find the latest sucsessful migration in the database, then wind it back one step. The reset
task will tear the whole DB down and rebuild it up. Careful?
namespace :db do
# Ensure DB is declared
migrate = lambda do |version|
Sequel.extension :migration
DB.loggers << Logger.new($stdout) if DB.loggers.empty?
Sequel::Migrator.run(DB, "db/migrations", target: version)
end
task :migrate do
migrate.call(nil)
end
task :rollback do
latest = DB[:schema_info].select_map(:version).first
migrate.call(latest - 1)
end
task :reset do
migrate.call(0)
Sequel::Migrator.run(DB, "db/migrations")
end
end
These can be used with familar command calls:
rake db:migrate
rake db:rollback
# rake db:reset # (commented, just in case!)
If there’s no loggers already on DB
, this will log the actions to $stdout and you can see what it’s doing.
Generating Sequel Migrations
I’ve started writing some boilerplate tasks to also generate Sequel migration files. It helps with the sequencing and reduces copy-pasta when you need a new migration.
I typically put this under g
namespace for familiarity, but it is a little different to the rails g ...
commands you may be used to.
namespace :g do
desc "Generates migration file; Generate with `rake g:migration[migration_name]"
task :migration, [:name] do |t, args|
prefixes = Dir["db/migrations/*.rb"].map { |f| File.basename(f).partition("_").first }.map { |s| Integer(s, 10) }
next_prefix = ((prefixes.empty? ? 0 : prefixes.max) + 1).to_s.rjust(3, "0")
file_name = "#{next_prefix}_#{args[:name].to_s.downcase.tr(" ", "_")}.rb"
file_path = File.join("db/migrations", file_name)
File.open(file_path, "w") do |f|
f.puts "Sequel.migration do
change do
end
end"
end
puts "Created #{file_path}"
# or change to `code #{file_path}` if you have a similar env to me and want to open the file!
end
end
You can then easily create a new migration file with:
rake g:migration[create table]
…granted the template is pretty basic, but it gets you going pretty quickly. The task looks at all existing migrations, finds the next increment and creates a suitable named filed for you.
Note this only works with the incrementing integer approach vs. the timestamp approach. You can easily tweak this to generate Sequel compatible timestamp-based migrations, but I’ve not personally had the need. You can read up on the pros and cons of each here.