V2.2: Upgrade to 2.2


These notes cover an upgrade from 2.1 to 2.2.

Update the app

  • Edit Gemfile and update the versions of the Hanami gems to "~> 2.2".

  • Remove gem "guard-puma" from your Gemfile (this is now a dependency of hanami-reloader).

  • Update spec/spec_helper.rb and update any require or require_relative lines for files in spec/support/ with the following:

# frozen_string_literal: true

require "pathname"
SPEC_ROOT = Pathname(__dir__).realpath.freeze

# <leave main file contents here>

SPEC_ROOT.glob("support/**/*.rb").each { |f| require f }

Add database support

Certain steps below apply to each of your app and slices. Where the code examples use Bookshelf as a name, replace this with the relevant name for your app or slice.

  • Add the following gems to your Gemfile:
gem "hanami-db", "~> 2.2"

# If you want a SQLite database
gem "sqlite3"
# If you want a Postgres database
gem "pg"
# If you want a MySQL database
gem "mysql2"

group :test do
  gem "database_cleaner-sequel"
end
  • Add the following lines to .gitignore:
db/*.sqlite
  • If you’re going to use a SQLite database, run mkdir db

  • Add a .env with a DATABASE_URL and check it into source control:

# This is checked into source control, so put sensitive values into `.env.local`

# For a SQLite database
DATABASE_URL=sqlite://db/bookshelf.sqlite

# For a Postgres database
DATABASE_URL=postgres://localhost/bookshelf

# For a MySQL database
DATABASE_URL=mysql2://root@localhost/bookshelf
  • Create a config/db/migrate/ dir and put a .keep file within it.

  • Create a config/db/seeds.rb:

# This seeds file should create the database records required to run the app.
#
# The code should be idempotent so that it can be executed at any time.
#
# To load the seeds, run `hanami db seed`. Seeds are also loaded as part of `hanami db prepare`.

# For example, if you have appropriate repos available:
#
#   category_repo = Hanami.app["repos.category_repo"]
#   category_repo.create(title: "General")
#
# Alternatively, you can use relations directly:
#
#   categories = Hanami.app["relations.categories"]
#   categories.insert(title: "General")
  • Add a db/relation.rb (to app/ or your slice):
# frozen_string_literal: true

require "hanami/db/relation"

module Bookshelf
  module DB
    class Relation < Hanami::DB::Relation
    end
  end
end
  • Add a db/repo.rb (to app/ or your slice):
# frozen_string_literal: true

require "hanami/db/repo"

module Bookshelf
  module DB
    class Repo < Hanami::DB::Repo
    end
  end
end
  • Add a db/struct.rb (to app/ or your slice):
# frozen_string_literal: true

require "hanami/db/struct"

module Bookshelf
  module DB
    class Struct < Hanami::DB::Struct
    end
  end
end
  • Create a relations/ dir (inside app/ or your slice) and put a .keep file within it.

  • Create a repos/ dir (inside app/ or your slice) and put a .keep file within it.

  • Create a structs/ dir (inside app/ or your slice) and put a .keep file within it.

  • Create spec/support/db.rb:

# frozen_string_literal: true

# Tag feature spec examples as `:db`
#
# See support/db/cleaning.rb for how the database is cleaned around these `:db` examples.
RSpec.configure do |config|
  config.define_derived_metadata(type: :feature) do |metadata|
    metadata[:db] = true
  end
end
  • Create spec/support/db/cleaning.rb:
# frozen_string_literal: true

require "database_cleaner/sequel"

# Clean the databases between tests tagged as `:db`
RSpec.configure do |config|
  # Returns all the configured databases across the app and its slices.
  #
  # Used in the before/after hooks below to ensure each database is cleaned between examples.
  #
  # Modify this proc (or any code below) if you only need specific databases cleaned.
  all_databases = -> {
    slices = [Hanami.app] + Hanami.app.slices.with_nested

    slices.each_with_object([]) { |slice, dbs|
      next unless slice.key?("db.rom")

      dbs.concat slice["db.rom"].gateways.values.map(&:connection)
    }.uniq
  }

  config.before :suite do
    all_databases.call.each do |db|
      DatabaseCleaner[:sequel, db: db].clean_with :truncation, except: ["schema_migrations"]
    end
  end

  config.before :each, :db do |example|
    strategy = example.metadata[:js] ? :truncation : :transaction

    all_databases.call.each do |db|
      DatabaseCleaner[:sequel, db: db].strategy = strategy
      DatabaseCleaner[:sequel, db: db].start
    end
  end

  config.after :each, :db do
    all_databases.call.each do |db|
      DatabaseCleaner[:sequel, db: db].clean
    end
  end
end

Add operations

Certain steps below apply to each of your app and slices. Where the code examples use Bookshelf as a name, replace this with the relevant name for your app or slice.

  • Add the following gem to your Gemfile:
gem "dry-operation"
  • Add an operation.rb (to app/ or your slice):
# auto_register: false
# frozen_string_literal: true

require "dry/operation"

module Bookshelf
  class Operation < Dry::Operation
  end
end
  • Create spec/support/operations.rb:
# frozen_string_literal: true

require "dry/monads"

RSpec.configure do |config|
  # Provide `Success` and `Failure` for testing operation results
  config.include Dry::Monads[:result]
end