Using boolean-like predicates as constraints in dry-types

When specifying a schema using dry-validations, I can do something like this to check for a particular type of UUID:

required(:uuid).value(:string, :uuid_v4?)

This is invoking the uuid_v4? predicate provided by dry-logic. I would like to simplify this by creating a custom type to use instead. Something like:

required(:uuid).filled(My::Types::ContactUuid)

However, I am having trouble creating this custom type. The dry-types constraints docs say that “Under the hood it uses dry-logic and all of its predicates are supported.” However, the examples given only show predicates that take an argument:

string = Types::String.constrained(min_size: 3)

If the predicate does not take an argument, like uuid_v4 (and many other boolean-like predicates), then how can it be used in the constrained method?

I tried everything I can think of and the only thing that works is to pass a dummy argument. I use nil here, but literally anything should work.

module My
  module Types
    include Dry.Types()

    ContactUuid = Types::String.constrained(uuid_v4: nil)
  end
end

Is this expected behavior? It appears to be so from the implementation of self.Rule:

# frozen_string_literal: true

module Dry
  # Helper methods for constraint types
  #
  # @api public
  module Types
    # @param [Hash] options
    #
    # @return [Dry::Logic::Rule]
    #
    # @api public
    def self.Rule(options)
      rule_compiler.(
        options.map { |key, val|
          ::Dry::Logic::Rule::Predicate.build(
            ::Dry::Logic::Predicates[:"#{key}?"]
          ).curry(val).to_ast
        }
      ).reduce(:and)
    end

    # @return [Dry::Logic::RuleCompiler]
    #
    # @api private
    def self.rule_compiler
      @rule_compiler ||= ::Dry::Logic::RuleCompiler.new(::Dry::Logic::Predicates)
    end
  end
end

Am I missing something? Is there a better way?

Thank you!

It seems you can also use an array of symbols Types::String.constrained([:uuid_v4]). I think we should allow accepting a single symbol, .constrained(:uuid_v4) for cases when there’s just one predicate. If you agree, can you submit a request on github?

Thank you! That’s a good trick that I didn’t think of. :thinking:

This would only work if the array only contained boolean-like predicates. (By “boolean-like,” I mean predicates that don’t take any arguments. I just learned from Wikipedia that you can call these “nullary predicates”). I don’t think it would work if you had to mix the boolean-like predicates with other predicates that require an argument (gt: 3 or similar, which may also be called “unary predicates”–thanks Wikipedia!).

Thank you for your help! I will open an issue on Github.