dry-types 0.14.1 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +631 -134
 - data/LICENSE +17 -17
 - data/README.md +15 -13
 - data/dry-types.gemspec +27 -30
 - data/lib/dry/types/any.rb +32 -12
 - data/lib/dry/types/array/constructor.rb +32 -0
 - data/lib/dry/types/array/member.rb +75 -16
 - data/lib/dry/types/array.rb +19 -6
 - data/lib/dry/types/builder.rb +131 -15
 - data/lib/dry/types/builder_methods.rb +49 -20
 - data/lib/dry/types/coercions/json.rb +43 -7
 - data/lib/dry/types/coercions/params.rb +118 -31
 - data/lib/dry/types/coercions.rb +76 -22
 - data/lib/dry/types/compat.rb +0 -2
 - data/lib/dry/types/compiler.rb +56 -41
 - data/lib/dry/types/constrained/coercible.rb +36 -6
 - data/lib/dry/types/constrained.rb +81 -32
 - data/lib/dry/types/constraints.rb +18 -4
 - data/lib/dry/types/constructor/function.rb +216 -0
 - data/lib/dry/types/constructor/wrapper.rb +94 -0
 - data/lib/dry/types/constructor.rb +126 -56
 - data/lib/dry/types/container.rb +7 -0
 - data/lib/dry/types/core.rb +54 -21
 - data/lib/dry/types/decorator.rb +38 -17
 - data/lib/dry/types/default.rb +61 -16
 - data/lib/dry/types/enum.rb +43 -20
 - data/lib/dry/types/errors.rb +75 -9
 - data/lib/dry/types/extensions/maybe.rb +74 -16
 - data/lib/dry/types/extensions/monads.rb +29 -0
 - data/lib/dry/types/extensions.rb +7 -1
 - data/lib/dry/types/fn_container.rb +6 -1
 - data/lib/dry/types/hash/constructor.rb +33 -0
 - data/lib/dry/types/hash.rb +86 -67
 - data/lib/dry/types/inflector.rb +3 -1
 - data/lib/dry/types/json.rb +18 -16
 - data/lib/dry/types/lax.rb +75 -0
 - data/lib/dry/types/map.rb +76 -33
 - data/lib/dry/types/meta.rb +51 -0
 - data/lib/dry/types/module.rb +120 -0
 - data/lib/dry/types/nominal.rb +210 -0
 - data/lib/dry/types/options.rb +13 -26
 - data/lib/dry/types/params.rb +39 -25
 - data/lib/dry/types/predicate_inferrer.rb +238 -0
 - data/lib/dry/types/predicate_registry.rb +34 -0
 - data/lib/dry/types/primitive_inferrer.rb +97 -0
 - data/lib/dry/types/printable.rb +16 -0
 - data/lib/dry/types/printer.rb +315 -0
 - data/lib/dry/types/result.rb +29 -3
 - data/lib/dry/types/schema/key.rb +156 -0
 - data/lib/dry/types/schema.rb +408 -0
 - data/lib/dry/types/spec/types.rb +103 -33
 - data/lib/dry/types/sum.rb +84 -35
 - data/lib/dry/types/type.rb +49 -0
 - data/lib/dry/types/version.rb +3 -1
 - data/lib/dry/types.rb +156 -76
 - data/lib/dry-types.rb +3 -1
 - metadata +65 -78
 - data/.gitignore +0 -10
 - data/.rspec +0 -2
 - data/.travis.yml +0 -27
 - data/.yardopts +0 -5
 - data/CONTRIBUTING.md +0 -29
 - data/Gemfile +0 -24
 - data/Rakefile +0 -20
 - data/benchmarks/hash_schemas.rb +0 -51
 - data/lib/dry/types/compat/form_types.rb +0 -27
 - data/lib/dry/types/compat/int.rb +0 -14
 - data/lib/dry/types/definition.rb +0 -113
 - data/lib/dry/types/hash/schema.rb +0 -199
 - data/lib/dry/types/hash/schema_builder.rb +0 -75
 - data/lib/dry/types/safe.rb +0 -59
 - data/log/.gitkeep +0 -0
 
    
        data/lib/dry/types/options.rb
    CHANGED
    
    | 
         @@ -1,42 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Dry
         
     | 
| 
       2 
4 
     | 
    
         
             
              module Types
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Common API for types with options
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # @api private
         
     | 
| 
       3 
8 
     | 
    
         
             
                module Options
         
     | 
| 
       4 
9 
     | 
    
         
             
                  # @return [Hash]
         
     | 
| 
       5 
10 
     | 
    
         
             
                  attr_reader :options
         
     | 
| 
       6 
11 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
                  # @see  
     | 
| 
       8 
     | 
    
         
            -
                   
     | 
| 
      
 12 
     | 
    
         
            +
                  # @see Nominal#initialize
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 15 
     | 
    
         
            +
                  def initialize(*args, **options)
         
     | 
| 
       9 
16 
     | 
    
         
             
                    @__args__ = args.freeze
         
     | 
| 
       10 
17 
     | 
    
         
             
                    @options = options.freeze
         
     | 
| 
       11 
     | 
    
         
            -
                    @meta = meta.freeze
         
     | 
| 
       12 
18 
     | 
    
         
             
                  end
         
     | 
| 
       13 
19 
     | 
    
         | 
| 
       14 
20 
     | 
    
         
             
                  # @param [Hash] new_options
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #
         
     | 
| 
       15 
22 
     | 
    
         
             
                  # @return [Type]
         
     | 
| 
       16 
     | 
    
         
            -
                  def with(new_options)
         
     | 
| 
       17 
     | 
    
         
            -
                    self.class.new(*@__args__, **options, meta: @meta, **new_options)
         
     | 
| 
       18 
     | 
    
         
            -
                  end
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
                  # @overload meta
         
     | 
| 
       21 
     | 
    
         
            -
                  #   @return [Hash] metadata associated with type
         
     | 
| 
       22 
23 
     | 
    
         
             
                  #
         
     | 
| 
       23 
     | 
    
         
            -
                  # @ 
     | 
| 
       24 
     | 
    
         
            -
                   
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                  def meta(data = nil)
         
     | 
| 
       27 
     | 
    
         
            -
                    if !data
         
     | 
| 
       28 
     | 
    
         
            -
                      @meta
         
     | 
| 
       29 
     | 
    
         
            -
                    elsif data.empty?
         
     | 
| 
       30 
     | 
    
         
            -
                      self
         
     | 
| 
       31 
     | 
    
         
            -
                    else
         
     | 
| 
       32 
     | 
    
         
            -
                      with(meta: @meta.merge(data))
         
     | 
| 
       33 
     | 
    
         
            -
                    end
         
     | 
| 
       34 
     | 
    
         
            -
                  end
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
                  # Resets meta
         
     | 
| 
       37 
     | 
    
         
            -
                  # @return [Dry::Types::Type]
         
     | 
| 
       38 
     | 
    
         
            -
                  def pristine
         
     | 
| 
       39 
     | 
    
         
            -
                    with(meta: EMPTY_HASH)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def with(**new_options)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    self.class.new(*@__args__, **options, **new_options)
         
     | 
| 
       40 
27 
     | 
    
         
             
                  end
         
     | 
| 
       41 
28 
     | 
    
         
             
                end
         
     | 
| 
       42 
29 
     | 
    
         
             
              end
         
     | 
    
        data/lib/dry/types/params.rb
    CHANGED
    
    | 
         @@ -1,53 +1,67 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "dry/types/coercions/params"
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
            module Dry
         
     | 
| 
       4 
6 
     | 
    
         
             
              module Types
         
     | 
| 
       5 
     | 
    
         
            -
                register( 
     | 
| 
       6 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 7 
     | 
    
         
            +
                register("params.nil") do
         
     | 
| 
      
 8 
     | 
    
         
            +
                  self["nominal.nil"].constructor(Coercions::Params.method(:to_nil))
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                register("params.date") do
         
     | 
| 
      
 12 
     | 
    
         
            +
                  self["nominal.date"].constructor(Coercions::Params.method(:to_date))
         
     | 
| 
       7 
13 
     | 
    
         
             
                end
         
     | 
| 
       8 
14 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                register( 
     | 
| 
       10 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 15 
     | 
    
         
            +
                register("params.date_time") do
         
     | 
| 
      
 16 
     | 
    
         
            +
                  self["nominal.date_time"].constructor(Coercions::Params.method(:to_date_time))
         
     | 
| 
       11 
17 
     | 
    
         
             
                end
         
     | 
| 
       12 
18 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                register( 
     | 
| 
       14 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 19 
     | 
    
         
            +
                register("params.time") do
         
     | 
| 
      
 20 
     | 
    
         
            +
                  self["nominal.time"].constructor(Coercions::Params.method(:to_time))
         
     | 
| 
       15 
21 
     | 
    
         
             
                end
         
     | 
| 
       16 
22 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
                register( 
     | 
| 
       18 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 23 
     | 
    
         
            +
                register("params.true") do
         
     | 
| 
      
 24 
     | 
    
         
            +
                  self["nominal.true"].constructor(Coercions::Params.method(:to_true))
         
     | 
| 
       19 
25 
     | 
    
         
             
                end
         
     | 
| 
       20 
26 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                register( 
     | 
| 
       22 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 27 
     | 
    
         
            +
                register("params.false") do
         
     | 
| 
      
 28 
     | 
    
         
            +
                  self["nominal.false"].constructor(Coercions::Params.method(:to_false))
         
     | 
| 
       23 
29 
     | 
    
         
             
                end
         
     | 
| 
       24 
30 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                register( 
     | 
| 
       26 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 31 
     | 
    
         
            +
                register("params.bool") do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  self["params.true"] | self["params.false"]
         
     | 
| 
       27 
33 
     | 
    
         
             
                end
         
     | 
| 
       28 
34 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                register( 
     | 
| 
       30 
     | 
    
         
            -
                   
     | 
| 
      
 35 
     | 
    
         
            +
                register("params.integer") do
         
     | 
| 
      
 36 
     | 
    
         
            +
                  self["nominal.integer"].constructor(Coercions::Params.method(:to_int))
         
     | 
| 
       31 
37 
     | 
    
         
             
                end
         
     | 
| 
       32 
38 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
                register( 
     | 
| 
       34 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 39 
     | 
    
         
            +
                register("params.float") do
         
     | 
| 
      
 40 
     | 
    
         
            +
                  self["nominal.float"].constructor(Coercions::Params.method(:to_float))
         
     | 
| 
       35 
41 
     | 
    
         
             
                end
         
     | 
| 
       36 
42 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
                register( 
     | 
| 
       38 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 43 
     | 
    
         
            +
                register("params.decimal") do
         
     | 
| 
      
 44 
     | 
    
         
            +
                  self["nominal.decimal"].constructor(Coercions::Params.method(:to_decimal))
         
     | 
| 
       39 
45 
     | 
    
         
             
                end
         
     | 
| 
       40 
46 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
                register( 
     | 
| 
       42 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 47 
     | 
    
         
            +
                register("params.array") do
         
     | 
| 
      
 48 
     | 
    
         
            +
                  self["nominal.array"].constructor(Coercions::Params.method(:to_ary))
         
     | 
| 
       43 
49 
     | 
    
         
             
                end
         
     | 
| 
       44 
50 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
                register( 
     | 
| 
       46 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 51 
     | 
    
         
            +
                register("params.hash") do
         
     | 
| 
      
 52 
     | 
    
         
            +
                  self["nominal.hash"].constructor(Coercions::Params.method(:to_hash))
         
     | 
| 
       47 
53 
     | 
    
         
             
                end
         
     | 
| 
       48 
54 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                register( 
     | 
| 
       50 
     | 
    
         
            -
                  self[ 
     | 
| 
      
 55 
     | 
    
         
            +
                register("params.symbol") do
         
     | 
| 
      
 56 
     | 
    
         
            +
                  self["nominal.symbol"].constructor(Coercions::Params.method(:to_symbol))
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                register("params.string", self["string"])
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                COERCIBLE.each_key do |name|
         
     | 
| 
      
 62 
     | 
    
         
            +
                  next if name.equal?(:string)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  register("optional.params.#{name}", self["params.nil"] | self["params.#{name}"])
         
     | 
| 
       51 
65 
     | 
    
         
             
                end
         
     | 
| 
       52 
66 
     | 
    
         
             
              end
         
     | 
| 
       53 
67 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,238 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "dry/core/cache"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "dry/core/class_attributes"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "dry/types/predicate_registry"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module Dry
         
     | 
| 
      
 8 
     | 
    
         
            +
              module Types
         
     | 
| 
      
 9 
     | 
    
         
            +
                # PredicateInferrer returns the list of predicates used by a type.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 12 
     | 
    
         
            +
                class PredicateInferrer
         
     | 
| 
      
 13 
     | 
    
         
            +
                  extend Core::Cache
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  TYPE_TO_PREDICATE = {
         
     | 
| 
      
 16 
     | 
    
         
            +
                    ::DateTime => :date_time?,
         
     | 
| 
      
 17 
     | 
    
         
            +
                    ::Date => :date?,
         
     | 
| 
      
 18 
     | 
    
         
            +
                    ::Time => :time?,
         
     | 
| 
      
 19 
     | 
    
         
            +
                    ::FalseClass => :false?,
         
     | 
| 
      
 20 
     | 
    
         
            +
                    ::Integer => :int?,
         
     | 
| 
      
 21 
     | 
    
         
            +
                    ::Float => :float?,
         
     | 
| 
      
 22 
     | 
    
         
            +
                    ::NilClass => :nil?,
         
     | 
| 
      
 23 
     | 
    
         
            +
                    ::String => :str?,
         
     | 
| 
      
 24 
     | 
    
         
            +
                    ::TrueClass => :true?,
         
     | 
| 
      
 25 
     | 
    
         
            +
                    ::BigDecimal => :decimal?,
         
     | 
| 
      
 26 
     | 
    
         
            +
                    ::Array => :array?
         
     | 
| 
      
 27 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  REDUCED_TYPES = {
         
     | 
| 
      
 30 
     | 
    
         
            +
                    [[[:true?], [:false?]]] => %i[bool?]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  HASH = %i[hash?].freeze
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  ARRAY = %i[array?].freeze
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  NIL = %i[nil?].freeze
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  # Compiler reduces type AST into a list of predicates
         
     | 
| 
      
 40 
     | 
    
         
            +
                  #
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 42 
     | 
    
         
            +
                  class Compiler
         
     | 
| 
      
 43 
     | 
    
         
            +
                    extend Core::ClassAttributes
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    defines :infer_predicate_by_class_name
         
     | 
| 
      
 46 
     | 
    
         
            +
                    infer_predicate_by_class_name nil
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    # @return [PredicateRegistry]
         
     | 
| 
      
 49 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 50 
     | 
    
         
            +
                    attr_reader :registry
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 53 
     | 
    
         
            +
                    def initialize(registry)
         
     | 
| 
      
 54 
     | 
    
         
            +
                      @registry = registry
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 58 
     | 
    
         
            +
                    def infer_predicate(type)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      pred = TYPE_TO_PREDICATE.fetch(type) do
         
     | 
| 
      
 60 
     | 
    
         
            +
                        if type.name.nil? || self.class.infer_predicate_by_class_name.equal?(false)
         
     | 
| 
      
 61 
     | 
    
         
            +
                          nil
         
     | 
| 
      
 62 
     | 
    
         
            +
                        else
         
     | 
| 
      
 63 
     | 
    
         
            +
                          candidate = :"#{type.name.split("::").last.downcase}?"
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                          if registry.key?(candidate)
         
     | 
| 
      
 66 
     | 
    
         
            +
                            if self.class.infer_predicate_by_class_name
         
     | 
| 
      
 67 
     | 
    
         
            +
                              candidate
         
     | 
| 
      
 68 
     | 
    
         
            +
                            else
         
     | 
| 
      
 69 
     | 
    
         
            +
                              raise ::KeyError, <<~MESSAGE
         
     | 
| 
      
 70 
     | 
    
         
            +
                                Automatic predicate inferring from class names is deprecated
         
     | 
| 
      
 71 
     | 
    
         
            +
                                and will be removed in dry-types 2.0.
         
     | 
| 
      
 72 
     | 
    
         
            +
                                Use `Dry::Types::PredicateInferrer::Compiler.infer_predicate_by_class_name true`
         
     | 
| 
      
 73 
     | 
    
         
            +
                                to restore the previous behavior
         
     | 
| 
      
 74 
     | 
    
         
            +
                                or `Dry::Types::PredicateInferrer::Compiler.infer_predicate_by_class_name false`
         
     | 
| 
      
 75 
     | 
    
         
            +
                                to explicitly opt-out (i.e. no exception + no inferring).
         
     | 
| 
      
 76 
     | 
    
         
            +
                                Note: for dry-schema and dry-validation use Dry::Schema::PredicateInferrer::Compiler.
         
     | 
| 
      
 77 
     | 
    
         
            +
                              MESSAGE
         
     | 
| 
      
 78 
     | 
    
         
            +
                            end
         
     | 
| 
      
 79 
     | 
    
         
            +
                          else
         
     | 
| 
      
 80 
     | 
    
         
            +
                            nil
         
     | 
| 
      
 81 
     | 
    
         
            +
                          end
         
     | 
| 
      
 82 
     | 
    
         
            +
                        end
         
     | 
| 
      
 83 
     | 
    
         
            +
                      end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                      if pred.nil?
         
     | 
| 
      
 86 
     | 
    
         
            +
                        EMPTY_ARRAY
         
     | 
| 
      
 87 
     | 
    
         
            +
                      else
         
     | 
| 
      
 88 
     | 
    
         
            +
                        [pred]
         
     | 
| 
      
 89 
     | 
    
         
            +
                      end
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 93 
     | 
    
         
            +
                    def visit(node)
         
     | 
| 
      
 94 
     | 
    
         
            +
                      meth, rest = node
         
     | 
| 
      
 95 
     | 
    
         
            +
                      public_send(:"visit_#{meth}", rest)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 99 
     | 
    
         
            +
                    def visit_nominal(node)
         
     | 
| 
      
 100 
     | 
    
         
            +
                      type = node[0]
         
     | 
| 
      
 101 
     | 
    
         
            +
                      predicate = infer_predicate(type)
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                      if !predicate.empty? && registry.key?(predicate[0])
         
     | 
| 
      
 104 
     | 
    
         
            +
                        predicate
         
     | 
| 
      
 105 
     | 
    
         
            +
                      else
         
     | 
| 
      
 106 
     | 
    
         
            +
                        [type?: type]
         
     | 
| 
      
 107 
     | 
    
         
            +
                      end
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 111 
     | 
    
         
            +
                    def visit_hash(_)
         
     | 
| 
      
 112 
     | 
    
         
            +
                      HASH
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
                    alias_method :visit_schema, :visit_hash
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 117 
     | 
    
         
            +
                    def visit_array(_)
         
     | 
| 
      
 118 
     | 
    
         
            +
                      ARRAY
         
     | 
| 
      
 119 
     | 
    
         
            +
                    end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 122 
     | 
    
         
            +
                    def visit_lax(node)
         
     | 
| 
      
 123 
     | 
    
         
            +
                      visit(node)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 127 
     | 
    
         
            +
                    def visit_constructor(node)
         
     | 
| 
      
 128 
     | 
    
         
            +
                      other, * = node
         
     | 
| 
      
 129 
     | 
    
         
            +
                      visit(other)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 133 
     | 
    
         
            +
                    def visit_enum(node)
         
     | 
| 
      
 134 
     | 
    
         
            +
                      other, * = node
         
     | 
| 
      
 135 
     | 
    
         
            +
                      visit(other)
         
     | 
| 
      
 136 
     | 
    
         
            +
                    end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 139 
     | 
    
         
            +
                    def visit_sum(node)
         
     | 
| 
      
 140 
     | 
    
         
            +
                      left_node, right_node, = node
         
     | 
| 
      
 141 
     | 
    
         
            +
                      left = visit(left_node)
         
     | 
| 
      
 142 
     | 
    
         
            +
                      right = visit(right_node)
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                      if left.eql?(NIL)
         
     | 
| 
      
 145 
     | 
    
         
            +
                        right
         
     | 
| 
      
 146 
     | 
    
         
            +
                      else
         
     | 
| 
      
 147 
     | 
    
         
            +
                        [[left, right]]
         
     | 
| 
      
 148 
     | 
    
         
            +
                      end
         
     | 
| 
      
 149 
     | 
    
         
            +
                    end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 152 
     | 
    
         
            +
                    def visit_constrained(node)
         
     | 
| 
      
 153 
     | 
    
         
            +
                      other, rules = node
         
     | 
| 
      
 154 
     | 
    
         
            +
                      predicates = visit(rules)
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                      if predicates.empty?
         
     | 
| 
      
 157 
     | 
    
         
            +
                        visit(other)
         
     | 
| 
      
 158 
     | 
    
         
            +
                      else
         
     | 
| 
      
 159 
     | 
    
         
            +
                        [*visit(other), *merge_predicates(predicates)]
         
     | 
| 
      
 160 
     | 
    
         
            +
                      end
         
     | 
| 
      
 161 
     | 
    
         
            +
                    end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 164 
     | 
    
         
            +
                    def visit_any(_)
         
     | 
| 
      
 165 
     | 
    
         
            +
                      EMPTY_ARRAY
         
     | 
| 
      
 166 
     | 
    
         
            +
                    end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 169 
     | 
    
         
            +
                    def visit_and(node)
         
     | 
| 
      
 170 
     | 
    
         
            +
                      left, right = node
         
     | 
| 
      
 171 
     | 
    
         
            +
                      visit(left) + visit(right)
         
     | 
| 
      
 172 
     | 
    
         
            +
                    end
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 175 
     | 
    
         
            +
                    def visit_predicate(node)
         
     | 
| 
      
 176 
     | 
    
         
            +
                      pred, args = node
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                      if pred.equal?(:type?)
         
     | 
| 
      
 179 
     | 
    
         
            +
                        EMPTY_ARRAY
         
     | 
| 
      
 180 
     | 
    
         
            +
                      elsif registry.key?(pred)
         
     | 
| 
      
 181 
     | 
    
         
            +
                        *curried, _ = args
         
     | 
| 
      
 182 
     | 
    
         
            +
                        values = curried.map { |_, v| v }
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                        if values.empty?
         
     | 
| 
      
 185 
     | 
    
         
            +
                          [pred]
         
     | 
| 
      
 186 
     | 
    
         
            +
                        else
         
     | 
| 
      
 187 
     | 
    
         
            +
                          [pred => values[0]]
         
     | 
| 
      
 188 
     | 
    
         
            +
                        end
         
     | 
| 
      
 189 
     | 
    
         
            +
                      else
         
     | 
| 
      
 190 
     | 
    
         
            +
                        EMPTY_ARRAY
         
     | 
| 
      
 191 
     | 
    
         
            +
                      end
         
     | 
| 
      
 192 
     | 
    
         
            +
                    end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                    private
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 197 
     | 
    
         
            +
                    def merge_predicates(nodes)
         
     | 
| 
      
 198 
     | 
    
         
            +
                      preds, merged = nodes.each_with_object([[], {}]) do |predicate, (ps, h)|
         
     | 
| 
      
 199 
     | 
    
         
            +
                        if predicate.is_a?(::Hash)
         
     | 
| 
      
 200 
     | 
    
         
            +
                          h.update(predicate)
         
     | 
| 
      
 201 
     | 
    
         
            +
                        else
         
     | 
| 
      
 202 
     | 
    
         
            +
                          ps << predicate
         
     | 
| 
      
 203 
     | 
    
         
            +
                        end
         
     | 
| 
      
 204 
     | 
    
         
            +
                      end
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                      merged.empty? ? preds : [*preds, merged]
         
     | 
| 
      
 207 
     | 
    
         
            +
                    end
         
     | 
| 
      
 208 
     | 
    
         
            +
                  end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                  # @return [Compiler]
         
     | 
| 
      
 211 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 212 
     | 
    
         
            +
                  attr_reader :compiler
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 215 
     | 
    
         
            +
                  def initialize(registry = PredicateRegistry.new)
         
     | 
| 
      
 216 
     | 
    
         
            +
                    @compiler = Compiler.new(registry)
         
     | 
| 
      
 217 
     | 
    
         
            +
                  end
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
      
 219 
     | 
    
         
            +
                  # Infer predicate identifier from the provided type
         
     | 
| 
      
 220 
     | 
    
         
            +
                  #
         
     | 
| 
      
 221 
     | 
    
         
            +
                  # @param [Type] type
         
     | 
| 
      
 222 
     | 
    
         
            +
                  # @return [Symbol]
         
     | 
| 
      
 223 
     | 
    
         
            +
                  #
         
     | 
| 
      
 224 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 225 
     | 
    
         
            +
                  def [](type)
         
     | 
| 
      
 226 
     | 
    
         
            +
                    self.class.fetch_or_store(type) do
         
     | 
| 
      
 227 
     | 
    
         
            +
                      predicates = compiler.visit(type.to_ast)
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                      if predicates.is_a?(::Hash)
         
     | 
| 
      
 230 
     | 
    
         
            +
                        predicates
         
     | 
| 
      
 231 
     | 
    
         
            +
                      else
         
     | 
| 
      
 232 
     | 
    
         
            +
                        REDUCED_TYPES[predicates] || predicates
         
     | 
| 
      
 233 
     | 
    
         
            +
                      end
         
     | 
| 
      
 234 
     | 
    
         
            +
                    end
         
     | 
| 
      
 235 
     | 
    
         
            +
                  end
         
     | 
| 
      
 236 
     | 
    
         
            +
                end
         
     | 
| 
      
 237 
     | 
    
         
            +
              end
         
     | 
| 
      
 238 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "dry/logic/predicates"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Dry
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Types
         
     | 
| 
      
 7 
     | 
    
         
            +
                # A registry with predicate objects from `Dry::Logic::Predicates`
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 10 
     | 
    
         
            +
                class PredicateRegistry
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 12 
     | 
    
         
            +
                  attr_reader :predicates
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 15 
     | 
    
         
            +
                  attr_reader :has_predicate
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def initialize(predicates = Logic::Predicates)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @predicates = predicates
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @has_predicate = ::Kernel.instance_method(:respond_to?).bind(@predicates)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def [](name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    predicates[name]
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 29 
     | 
    
         
            +
                  def key?(name)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    has_predicate.(name)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,97 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "dry/core/cache"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Dry
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Types
         
     | 
| 
      
 7 
     | 
    
         
            +
                # PrimitiveInferrer returns the list of classes matching a type.
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 10 
     | 
    
         
            +
                class PrimitiveInferrer
         
     | 
| 
      
 11 
     | 
    
         
            +
                  extend Core::Cache
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  # Compiler reduces type AST into a list of primitives
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 16 
     | 
    
         
            +
                  class Compiler
         
     | 
| 
      
 17 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 18 
     | 
    
         
            +
                    def visit(node)
         
     | 
| 
      
 19 
     | 
    
         
            +
                      meth, rest = node
         
     | 
| 
      
 20 
     | 
    
         
            +
                      public_send(:"visit_#{meth}", rest)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 24 
     | 
    
         
            +
                    def visit_nominal(node)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      type, _ = node
         
     | 
| 
      
 26 
     | 
    
         
            +
                      type
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 30 
     | 
    
         
            +
                    def visit_hash(_)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      ::Hash
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    alias_method :visit_schema, :visit_hash
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 36 
     | 
    
         
            +
                    def visit_array(_)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      ::Array
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 41 
     | 
    
         
            +
                    def visit_lax(node)
         
     | 
| 
      
 42 
     | 
    
         
            +
                      visit(node)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 46 
     | 
    
         
            +
                    def visit_constructor(node)
         
     | 
| 
      
 47 
     | 
    
         
            +
                      other, * = node
         
     | 
| 
      
 48 
     | 
    
         
            +
                      visit(other)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 52 
     | 
    
         
            +
                    def visit_enum(node)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      other, * = node
         
     | 
| 
      
 54 
     | 
    
         
            +
                      visit(other)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 58 
     | 
    
         
            +
                    def visit_sum(node)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      left, right = node
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                      [visit(left), visit(right)].flatten(1)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 65 
     | 
    
         
            +
                    def visit_constrained(node)
         
     | 
| 
      
 66 
     | 
    
         
            +
                      other, * = node
         
     | 
| 
      
 67 
     | 
    
         
            +
                      visit(other)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 71 
     | 
    
         
            +
                    def visit_any(_)
         
     | 
| 
      
 72 
     | 
    
         
            +
                      ::Object
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  # @return [Compiler]
         
     | 
| 
      
 77 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 78 
     | 
    
         
            +
                  attr_reader :compiler
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 81 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 82 
     | 
    
         
            +
                    @compiler = Compiler.new
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  # Infer primitives from the provided type
         
     | 
| 
      
 86 
     | 
    
         
            +
                  #
         
     | 
| 
      
 87 
     | 
    
         
            +
                  # @return [Array[Class]]
         
     | 
| 
      
 88 
     | 
    
         
            +
                  #
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 90 
     | 
    
         
            +
                  def [](type)
         
     | 
| 
      
 91 
     | 
    
         
            +
                    self.class.fetch_or_store(type) do
         
     | 
| 
      
 92 
     | 
    
         
            +
                      Array(compiler.visit(type.to_ast)).freeze
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
              end
         
     | 
| 
      
 97 
     | 
    
         
            +
            end
         
     |