We’re back to round out the brand new database layer we introduced in our previous beta release. This time around, we’re adding MySQL, introducing a powerful new way of working with multiple databases, and bringing you a little treat at the same time: full validation contract support in actions!

MySQL support

Now you can generate a new Hanami app with hanami new my_app --database=mysql and have a ready-to-go MySQL-backed Hanami app! All hanami db commands (like db prepare, db migrate, etc.) have been updated to manage your MySQL database over the lifecycle of your app.

Multiple gateways

With beta1, we introduced the idea of distinct databases per slice. Now with beta2, you can mix and match multiple databases within any slice! This comes courtesy of ROM’s gateways, which you can now setup as part of Hanami’s database layer.

Gateways in ROM represent a connection to a distinct database. You can use them within your relations by declaring a gateway:

module MyApp
  module Relations
    class Uploads < MyApp::DB::Relation
      # Use the `uploads` table from the `:artifacts` gateway.
      gateway :artifacts
      schema :uploads, infer: true
    end
  end
end

By using gateways via relations, you can have your database layer seamlessly work with data from multiple sources, all while maintaining a streamlined public interface via your repos.

You can configure multiple gateways without any additional config. Just set an appropriately named ENV var:

# The standard :default gateway
DATABASE_URL=sqlite://db/app.sqlite

# An :artifacts gateway
DATABASE_URL__ARTIFACTS=sqlite://db/artifacts.sqlite

You can also use ENV vars to set up gateways for slices too:

MY_SLICE__DATABASE_URL=sqlite://db/my_slice.sqlite
MY_SLICE__DATABASE_URL__ARTIFACTS=sqlite://db/my_slice-artifacts.sqlite

For more advanced cases, you can configure gateways explicitly, inside your :db provider:

Hanami.app.configure_provider :db do
  # Explicitly configure a gateway
  config.gateway :artifacts do |gw|
    # If not configured, will still be filled from `ENV["DATABASE_URL__ARTIFACTS"]`
    gw.database_url = "..."

    # Specify an adapter to use
    gw.adapter :yaml

    # Or configure an adapter explicitly
    gw.adapter :yaml do |a|
      # You can call `a.plugin` here
      # Or also `a.extension` if this is an `:sql` adapter
    end
  end

  # Multiple gateways can be configured
  config.gateway :another do |gw|
    # ...
  end
end

All hanami db commands are multi-gateway-aware. By default, they will operate on all configured databases for an app. You can also target a specific gateway’s database by passing the --app --gateway=gw_name or --slice=slice_name --gateway=gw_name arguments.

You can also pass a --gateway argument to hanami generate migration to generate a migration for a specific gateway.

For more detail on gateway configuration, see this pull request.

ROM commands and mappers

We also completed the last piece of ROM integration work: integrated commands and mappers. These are advanced components of ROM, and if you need them in a Hanami app, now you can place them in app/db/commands/ and app/db/mappers/ and they’ll be automatically registered.

Full validation contracts for actions

By popular demand, we’ve taken a little detour from our database work to implement support for full dry-validation contracts for your actions. Now you can use contract within an action class and access all the features of dry-validation, especially rules:

module MyApp
  module Actions
    module SignUp
      class Create < MyApp::Action
        contract do
          params do
            required(:birth_date).filled(:date)
          end

          rule(:birth_date) do
            if value < (Date.today << (12 * 18))
              key.failure("you must be 18 years or older")
            end
          end
        end
      end
    end
  end
end

You can also provide a Dry::Validation::Contract class directly, which is helpful if you want to share validation rules across actions as well as other domain objects:

class Create < MyApp::Action
  contract SignUp::Contract
end

Or you can even inject a contract object as a dependency, which is useful if the contract itself has dependencies from elsewhere in your app:

class Create < MyApp::Action
  include Deps[contract: "sign_up.contract"]
end

We need your help!

With this beta, we’re another step closer to 2.2.0 proper, so we need your help with testing, especially if you’re a MySQL user, or have a use case that could be served by our multi-gateway support.

We’ve already updated our getting started guides to walk you through your first Hanami 2.2 app, database layer included. Please give this a try, then let us know how you go.

What’s next? A release candidate, then 2.2.0.

Our current goal is to have Hanami 2.2.0 ready by RubyConf, where Sean Collins will be giving an introductory workshop (Day 2, Salon A1).

Between now and then, we plan to make one more release: a single release candidate, towards the end of October. Take a look at our project board to see the work we have remaining.

What’s included?

Today we’re releasing the following gems:

  • hanami v2.2.0.beta2
  • hanami-assets v2.2.0-beta.2 (npm package)
  • hanami-assets v2.2.0.beta2
  • hanami-cli v2.2.0.beta2
  • hanami-controller v2.2.0.beta2
  • hanami-db v2.2.0.beta2
  • hanami-reloader v2.2.0.beta2
  • hanami-router v2.2.0.beta2
  • hanami-rspec v2.2.0.beta2
  • hanami-utils v2.2.0.beta2
  • hanami-validations v2.2.0.beta2
  • hanami-view v2.2.0.beta2
  • hanami-webconsole v2.2.0.beta2

For specific changes in this release, please see each gem’s own CHANGELOG.

How can I try it?

> gem install hanami --pre
> hanami new my_app
> cd my_app
> bundle exec hanami dev

Contributors

Thank you to these fine people for contributing to this release!

Thank you

Thank you as always for supporting Hanami! We can’t wait to hear from you about this beta! 🌸