Types.Constructor failing on Ruby 3.1

I saw Ruby 3.1 issue threads for some other dry gems, but not dry-types. Apologies if I missed this somewhere and am duplicating things.

I was trying to follow along with this example and hit an error.

Given this class:

class User
  def initialize(name:)
    @name = name
  end
end

…the docs say I should be able to do this:

user_type = Types.Constructor(User)

# It is equivalent to User.new(name: 'John')
user_type[name: 'John']

On Ruby 2.7.7, this works as expected and I get an instance of User with the given name.

On Ruby 3.1.2, I get:

Dry::Types::CoercionError: wrong number of arguments (given 1, expected 0; required keyword: name)
Caused by ArgumentError: wrong number of arguments (given 1, expected 0; required keyword: name)
from (pry):2:in `initialize'

I’m assuming this is due to Ruby 3.1’s changes to how keyword args are handled.

Confirmed, I see this behavior also. I’m not certain that this actually falls within Dry::Types’s use-case; if you wanted attribute-based types you could use Dry::Struct for that.

I would suggest opening a support ticket on GitHub. In the meantime, you can work around it like this:

user_type = Types.Constructor(User) { |input| User.new(**input) }

It’s not a compatible User declaration. dry-types doesn’t pass value as keywords. It should be

class User
  def initialize(attributes)
    attributes.each { instance_variable_set(:"@{_1}", _2) }
  end
end

Otherwise, you can use a custom block as @alassek suggested