Using contracts with schemas

Hi,

I was wondering how would I implement a contract that validates HTTP params using other contracts as “schemas” for its params argument.

For example, if I have this contract:

class UserSchema < Dry::Validation::Contract
  params do
    required(:first_name).value(:string, min_size?: 2)
    required(:last_name).value(:string, min_size?: 2)
    optional(:password).filled(:string)
    optional(:confirm_password).filled(:string)
  end

  rule(:password, :confirm_password) do
    if values[:password] == values[:confirm_password]
      key.failure('password does not match password confirmation.')
    end
  end
end

And then I have another contract:

class AccountSchema < Dry::Validation::Contract
  optional(:user1).hash(UserSchema)
  required(:user2).hash(UserSchema)
end

I’m not sure how to “express” the logic of the second contract - an argument to hash can be a dry validation schema params, but not a contract (and I need rules on the child contract to validate input).

Can something like this be implemented in the current version of dry-validation?
Thanks!

Can something like this be implemented in the current version of dry-validation?

No, it’s not possible to mix schemas and contracts. I would just define a macro and reuse it:

class AppContract < Dry::Validation::Contract
  register_macro(:password_confirmation) do
    unless values[:password].eql?(values[:confirm_password])
      key.failure('password does not match password confirmation.')
    end
  end
end

UserSchema = Dry::Schema.Params do
  required(:first_name).value(:string, min_size?: 2)
  required(:last_name).value(:string, min_size?: 2)
  optional(:password).filled(:string)
  optional(:confirm_password).filled(:string)
end

class AccountContract < AppContract
  optional(:user1).hash(UserSchema)
  required(:user2).hash(UserSchema)

  rule(:user1).validate(:password_confirmation)
  rule(:user2).validate(:password_confirmation)
end

This could be automated, so we may consider adding some features to make it nicer in the future.

Thanks, @solnic!