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.