dry-rb / dry-types

Flexible type system for Ruby with coercions and constraints
https://dry-rb.org/gems/dry-types
MIT License
860 stars 134 forks source link

Future-proof local override for Types::Instance/Constructor classes that potentially collide with predicates #438

Open doriantaylor opened 2 years ago

doriantaylor commented 2 years ago

Describe the bug

This is a design issue I'm not sure how to tackle. It seems as though there will invariably be situations where there are Types::Instance or Types::Constructor types for which the (lower-cased, last segment of the) class name matches a predicate (as is the case with URI).

I understand that inferring predicates based on class name has been deprecated, though it isn't clear to me why I would have ever wanted this or what these were even used for. All I want (for now) is a type based on the URI class, and the predicate-inferring behaviour is interfering with that. This was first an issue in dry-rb/dry-schema#335 and I appreciate the workaround in #414 but it seems like I am going to have to monitor my code for breaking upstream changes when the workaround itself is deprecated.

Moreover, I know that one is supposed to invoke Dry::Types::PredicateInferrer::Compiler.infer_predicate_by_class_name false to eliminate this deprecation error, but it seems to me like this kind of thing would make more sense to have localized by the individual type as opposed to blanket behaviour for all types.

To Reproduce

module My
  # make a types module

  module Types
    include Dry::Types()

    # make a URI type
    URI = Types.Constructor(::URI) do |x|
      begin
        out = ::URI.parse(x)
      rescue ::URI::InvalidURIError => e
        raise Dry::Types::CoercionError.new e
      end

      out
    end
  end

  # make a schema
  Config = Dry::Schema.Params do
    # include uri type; this will predictably complain
    required(:base).value Types::URI
  end
end

Expected behavior

I'm not sure what exact mechanism would make the most sense but attaching the predicate-inferring state to the individual type objects seems like it could work:

My::Types::URI.ignore_predicate_test = true
# …or something

…then pick that up in Dry::Schema or whatever.

My environment

Ruby 2.7 or 3.1 (depending on the machine) and all newest releases of dry-rb modules.