How to remove a default from `dry-types` custom class attribute?

Hello.

I’m working on a Ruby gem. Especially its specs.

We have custom Dry::Types types. And we’re getting parsed API types for specs.

Attributes of our Ruby types have internal defaults, which external API has no.

How can I remove defaults from them?

Example:

class ReplyKeyboardRemove < Base
  attribute? :selective, Types::Bool.default(false)
end

Error:

       expected: #<Dry::Types[Sum<Constrained<Nominal<TrueClass> rule=[type?(TrueClass)]> | Constrained<Nominal<FalseClass> rule=[type?(FalseClass)]>>]>
            got: #<Dry::Types[Default<Sum<Constrained<Nominal<TrueClass> rule=[type?(TrueClass)]> | Constrained<Nominal<FalseClass> rule=[type?(FalseClass)]>> value=false>]>
     
       (compared using ==)

So I haven’t had to do this myself before, but one thing that’s with noting here is that a type with a default is just a wrapper over the original type, which you can still get to via my_type.type.

So something like this becomes possible:

Undefined = Dry::Core::Constants::Undefined

t = Types::Bool.default(true)

# Show the default in action
t[Undefined]
# => true

# Get the raw (non-defaulting) type
tt = t.type

# Show it no longer has a default
tt[Undefined]
# This errors as expected!
# => Undefined violates constraints (type?(FalseClass, Undefined) failed) (Dry::Types::ConstraintError)

# And of course it still accepts a valid value
tt[false]
# => false

You’ll probably want to be careful with how you construct your types to make sure it’s actually the Default wrapper that you’re unwrapping, but hopefully this can set you off in a good direction.

1 Like

Yeah, thanks, I’ve already found this work-around yesterday. attribute.type.type seems weird, but works. I thought maybe there is a more elegant way. OK.

About safety — yeah, just if type.default? enough.