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
| @@ -1,12 +1,42 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Dry
         | 
| 2 4 | 
             
              module Types
         | 
| 3 5 | 
             
                class Constrained
         | 
| 6 | 
            +
                  # Common coercion-related API for constrained types
         | 
| 7 | 
            +
                  #
         | 
| 8 | 
            +
                  # @api public
         | 
| 4 9 | 
             
                  class Coercible < Constrained
         | 
| 5 | 
            -
                    # @ | 
| 6 | 
            -
                    # | 
| 7 | 
            -
                    # @ | 
| 8 | 
            -
                     | 
| 9 | 
            -
             | 
| 10 | 
            +
                    # @return [Object]
         | 
| 11 | 
            +
                    #
         | 
| 12 | 
            +
                    # @api private
         | 
| 13 | 
            +
                    def call_unsafe(input)
         | 
| 14 | 
            +
                      coerced = type.call_unsafe(input)
         | 
| 15 | 
            +
                      result = rule.(coerced)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      if result.success?
         | 
| 18 | 
            +
                        coerced
         | 
| 19 | 
            +
                      else
         | 
| 20 | 
            +
                        raise ConstraintError.new(result, input)
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    # @return [Object]
         | 
| 25 | 
            +
                    #
         | 
| 26 | 
            +
                    # @api private
         | 
| 27 | 
            +
                    def call_safe(input)
         | 
| 28 | 
            +
                      coerced = type.call_safe(input) { return yield }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                      if rule[coerced]
         | 
| 31 | 
            +
                        coerced
         | 
| 32 | 
            +
                      else
         | 
| 33 | 
            +
                        yield(coerced)
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    # @see Dry::Types::Constrained#try
         | 
| 38 | 
            +
                    #
         | 
| 39 | 
            +
                    # @api public
         | 
| 10 40 | 
             
                    def try(input, &block)
         | 
| 11 41 | 
             
                      result = type.try(input)
         | 
| 12 42 |  | 
| @@ -16,7 +46,7 @@ module Dry | |
| 16 46 | 
             
                        if validation.success?
         | 
| 17 47 | 
             
                          result
         | 
| 18 48 | 
             
                        else
         | 
| 19 | 
            -
                          failure = failure(result.input, validation)
         | 
| 49 | 
            +
                          failure = failure(result.input, ConstraintError.new(validation, input))
         | 
| 20 50 | 
             
                          block ? yield(failure) : failure
         | 
| 21 51 | 
             
                        end
         | 
| 22 52 | 
             
                      else
         | 
| @@ -1,93 +1,142 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
            require  | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "dry/core/equalizer"
         | 
| 4 | 
            +
            require "dry/types/decorator"
         | 
| 5 | 
            +
            require "dry/types/constraints"
         | 
| 6 | 
            +
            require "dry/types/constrained/coercible"
         | 
| 4 7 |  | 
| 5 8 | 
             
            module Dry
         | 
| 6 9 | 
             
              module Types
         | 
| 10 | 
            +
                # Constrained types apply rules to the input
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                # @api public
         | 
| 7 13 | 
             
                class Constrained
         | 
| 8 14 | 
             
                  include Type
         | 
| 9 | 
            -
                  include Dry::Equalizer(:type, :options, :rule, :meta)
         | 
| 10 15 | 
             
                  include Decorator
         | 
| 11 16 | 
             
                  include Builder
         | 
| 17 | 
            +
                  include Printable
         | 
| 18 | 
            +
                  include Dry::Equalizer(:type, :rule, inspect: false, immutable: true)
         | 
| 12 19 |  | 
| 13 20 | 
             
                  # @return [Dry::Logic::Rule]
         | 
| 14 21 | 
             
                  attr_reader :rule
         | 
| 15 22 |  | 
| 16 23 | 
             
                  # @param [Type] type
         | 
| 24 | 
            +
                  #
         | 
| 17 25 | 
             
                  # @param [Hash] options
         | 
| 18 | 
            -
                   | 
| 26 | 
            +
                  #
         | 
| 27 | 
            +
                  # @api public
         | 
| 28 | 
            +
                  def initialize(type, **options)
         | 
| 19 29 | 
             
                    super
         | 
| 20 30 | 
             
                    @rule = options.fetch(:rule)
         | 
| 21 31 | 
             
                  end
         | 
| 22 32 |  | 
| 23 | 
            -
                  # @param [Object] input
         | 
| 24 33 | 
             
                  # @return [Object]
         | 
| 25 | 
            -
                  # | 
| 26 | 
            -
                   | 
| 27 | 
            -
             | 
| 34 | 
            +
                  #
         | 
| 35 | 
            +
                  # @api private
         | 
| 36 | 
            +
                  def call_unsafe(input)
         | 
| 37 | 
            +
                    result = rule.(input)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    if result.success?
         | 
| 40 | 
            +
                      type.call_unsafe(input)
         | 
| 41 | 
            +
                    else
         | 
| 28 42 | 
             
                      raise ConstraintError.new(result, input)
         | 
| 29 | 
            -
                    end | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  # @return [Object]
         | 
| 47 | 
            +
                  #
         | 
| 48 | 
            +
                  # @api private
         | 
| 49 | 
            +
                  def call_safe(input, &block)
         | 
| 50 | 
            +
                    if rule[input]
         | 
| 51 | 
            +
                      type.call_safe(input, &block)
         | 
| 52 | 
            +
                    else
         | 
| 53 | 
            +
                      yield
         | 
| 54 | 
            +
                    end
         | 
| 30 55 | 
             
                  end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
                  #  | 
| 34 | 
            -
                  #  | 
| 35 | 
            -
                  # | 
| 36 | 
            -
                  # @ | 
| 37 | 
            -
                  # | 
| 38 | 
            -
                  # | 
| 56 | 
            +
             | 
| 57 | 
            +
                  # Safe coercion attempt. It is similar to #call with a
         | 
| 58 | 
            +
                  # block given but returns a Result instance with metadata
         | 
| 59 | 
            +
                  # about errors (if any).
         | 
| 60 | 
            +
                  #
         | 
| 61 | 
            +
                  # @overload try(input)
         | 
| 62 | 
            +
                  #   @param [Object] input
         | 
| 63 | 
            +
                  #   @return [Logic::Result]
         | 
| 64 | 
            +
                  #
         | 
| 65 | 
            +
                  # @overload try(input)
         | 
| 66 | 
            +
                  #   @param [Object] input
         | 
| 67 | 
            +
                  #   @yieldparam [Failure] failure
         | 
| 68 | 
            +
                  #   @yieldreturn [Object]
         | 
| 69 | 
            +
                  #   @return [Object]
         | 
| 70 | 
            +
                  #
         | 
| 71 | 
            +
                  # @api public
         | 
| 39 72 | 
             
                  def try(input, &block)
         | 
| 40 73 | 
             
                    result = rule.(input)
         | 
| 41 74 |  | 
| 42 75 | 
             
                    if result.success?
         | 
| 43 76 | 
             
                      type.try(input, &block)
         | 
| 44 77 | 
             
                    else
         | 
| 45 | 
            -
                      failure = failure(input, result)
         | 
| 46 | 
            -
                       | 
| 78 | 
            +
                      failure = failure(input, ConstraintError.new(result, input))
         | 
| 79 | 
            +
                      block_given? ? yield(failure) : failure
         | 
| 47 80 | 
             
                    end
         | 
| 48 81 | 
             
                  end
         | 
| 49 82 |  | 
| 50 | 
            -
                  # @param [Object] value
         | 
| 51 | 
            -
                  # @return [Boolean]
         | 
| 52 | 
            -
                  def valid?(value)
         | 
| 53 | 
            -
                    rule.(value).success? && type.valid?(value)
         | 
| 54 | 
            -
                  end
         | 
| 55 | 
            -
             | 
| 56 83 | 
             
                  # @param [Hash] options
         | 
| 57 84 | 
             
                  #   The options hash provided to {Types.Rule} and combined
         | 
| 58 85 | 
             
                  #   using {&} with previous {#rule}
         | 
| 86 | 
            +
                  #
         | 
| 59 87 | 
             
                  # @return [Constrained]
         | 
| 88 | 
            +
                  #
         | 
| 60 89 | 
             
                  # @see Dry::Logic::Operators#and
         | 
| 90 | 
            +
                  #
         | 
| 91 | 
            +
                  # @api public
         | 
| 61 92 | 
             
                  def constrained(options)
         | 
| 62 93 | 
             
                    with(rule: rule & Types.Rule(options))
         | 
| 63 94 | 
             
                  end
         | 
| 64 95 |  | 
| 65 96 | 
             
                  # @return [true]
         | 
| 97 | 
            +
                  #
         | 
| 98 | 
            +
                  # @api public
         | 
| 66 99 | 
             
                  def constrained?
         | 
| 67 100 | 
             
                    true
         | 
| 68 101 | 
             
                  end
         | 
| 69 102 |  | 
| 70 103 | 
             
                  # @param [Object] value
         | 
| 104 | 
            +
                  #
         | 
| 71 105 | 
             
                  # @return [Boolean]
         | 
| 106 | 
            +
                  #
         | 
| 107 | 
            +
                  # @api public
         | 
| 72 108 | 
             
                  def ===(value)
         | 
| 73 109 | 
             
                    valid?(value)
         | 
| 74 110 | 
             
                  end
         | 
| 75 111 |  | 
| 76 | 
            -
                  #  | 
| 112 | 
            +
                  # Build lax type. Constraints are not applicable to lax types hence unwrapping
         | 
| 77 113 | 
             
                  #
         | 
| 78 | 
            -
                  # @ | 
| 114 | 
            +
                  # @return [Lax]
         | 
| 115 | 
            +
                  # @api public
         | 
| 116 | 
            +
                  def lax
         | 
| 117 | 
            +
                    type.lax
         | 
| 118 | 
            +
                  end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                  # @see Nominal#to_ast
         | 
| 121 | 
            +
                  # @api public
         | 
| 79 122 | 
             
                  def to_ast(meta: true)
         | 
| 80 | 
            -
                    [:constrained, [type.to_ast(meta: meta),
         | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 123 | 
            +
                    [:constrained, [type.to_ast(meta: meta), rule.to_ast]]
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  # @api private
         | 
| 127 | 
            +
                  def constructor_type
         | 
| 128 | 
            +
                    type.constructor_type
         | 
| 83 129 | 
             
                  end
         | 
| 84 130 |  | 
| 85 131 | 
             
                  private
         | 
| 86 132 |  | 
| 87 133 | 
             
                  # @param [Object] response
         | 
| 134 | 
            +
                  #
         | 
| 88 135 | 
             
                  # @return [Boolean]
         | 
| 136 | 
            +
                  #
         | 
| 137 | 
            +
                  # @api private
         | 
| 89 138 | 
             
                  def decorate?(response)
         | 
| 90 | 
            -
                    super || response. | 
| 139 | 
            +
                    super || response.is_a?(Constructor)
         | 
| 91 140 | 
             
                  end
         | 
| 92 141 | 
             
                end
         | 
| 93 142 | 
             
              end
         | 
| @@ -1,18 +1,32 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
            require  | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "dry/logic/rule_compiler"
         | 
| 4 | 
            +
            require "dry/logic/predicates"
         | 
| 5 | 
            +
            require "dry/logic/rule/predicate"
         | 
| 4 6 |  | 
| 5 7 | 
             
            module Dry
         | 
| 8 | 
            +
              # Helper methods for constraint types
         | 
| 9 | 
            +
              #
         | 
| 10 | 
            +
              # @api public
         | 
| 6 11 | 
             
              module Types
         | 
| 7 12 | 
             
                # @param [Hash] options
         | 
| 13 | 
            +
                #
         | 
| 8 14 | 
             
                # @return [Dry::Logic::Rule]
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # @api public
         | 
| 9 17 | 
             
                def self.Rule(options)
         | 
| 10 18 | 
             
                  rule_compiler.(
         | 
| 11 | 
            -
                    options.map { |key, val| | 
| 19 | 
            +
                    options.map { |key, val|
         | 
| 20 | 
            +
                      Logic::Rule::Predicate.build(
         | 
| 21 | 
            +
                        Logic::Predicates[:"#{key}?"]
         | 
| 22 | 
            +
                      ).curry(val).to_ast
         | 
| 23 | 
            +
                    }
         | 
| 12 24 | 
             
                  ).reduce(:and)
         | 
| 13 25 | 
             
                end
         | 
| 14 26 |  | 
| 15 27 | 
             
                # @return [Dry::Logic::RuleCompiler]
         | 
| 28 | 
            +
                #
         | 
| 29 | 
            +
                # @api private
         | 
| 16 30 | 
             
                def self.rule_compiler
         | 
| 17 31 | 
             
                  @rule_compiler ||= Logic::RuleCompiler.new(Logic::Predicates)
         | 
| 18 32 | 
             
                end
         | 
| @@ -0,0 +1,216 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "dry/core/equalizer"
         | 
| 4 | 
            +
            require "concurrent/map"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Dry
         | 
| 7 | 
            +
              module Types
         | 
| 8 | 
            +
                class Constructor < Nominal
         | 
| 9 | 
            +
                  # Function is used internally by Constructor types
         | 
| 10 | 
            +
                  #
         | 
| 11 | 
            +
                  # @api private
         | 
| 12 | 
            +
                  class Function
         | 
| 13 | 
            +
                    # Wrapper for unsafe coercion functions
         | 
| 14 | 
            +
                    #
         | 
| 15 | 
            +
                    # @api private
         | 
| 16 | 
            +
                    class Safe < Function
         | 
| 17 | 
            +
                      def call(input, &block)
         | 
| 18 | 
            +
                        @fn.(input, &block)
         | 
| 19 | 
            +
                      rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
         | 
| 20 | 
            +
                        CoercionError.handle(e, &block)
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    # Coercion via a method call on a known object
         | 
| 25 | 
            +
                    #
         | 
| 26 | 
            +
                    # @api private
         | 
| 27 | 
            +
                    class MethodCall < Function
         | 
| 28 | 
            +
                      @cache = ::Concurrent::Map.new
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                      # Choose or build the base class
         | 
| 31 | 
            +
                      #
         | 
| 32 | 
            +
                      # @return [Function]
         | 
| 33 | 
            +
                      def self.call_class(method, public, safe)
         | 
| 34 | 
            +
                        @cache.fetch_or_store([method, public, safe]) do
         | 
| 35 | 
            +
                          if public
         | 
| 36 | 
            +
                            ::Class.new(PublicCall) do
         | 
| 37 | 
            +
                              include PublicCall.call_interface(method, safe)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                              define_method(:__to_s__) do
         | 
| 40 | 
            +
                                "#<PublicCall for :#{method}>"
         | 
| 41 | 
            +
                              end
         | 
| 42 | 
            +
                            end
         | 
| 43 | 
            +
                          elsif safe
         | 
| 44 | 
            +
                            PrivateCall
         | 
| 45 | 
            +
                          else
         | 
| 46 | 
            +
                            PrivateSafeCall
         | 
| 47 | 
            +
                          end
         | 
| 48 | 
            +
                        end
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      # Coercion with a publicly accessible method call
         | 
| 52 | 
            +
                      #
         | 
| 53 | 
            +
                      # @api private
         | 
| 54 | 
            +
                      class PublicCall < MethodCall
         | 
| 55 | 
            +
                        @interfaces = ::Concurrent::Map.new
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                        # Choose or build the interface
         | 
| 58 | 
            +
                        #
         | 
| 59 | 
            +
                        # @return [::Module]
         | 
| 60 | 
            +
                        def self.call_interface(method, safe)
         | 
| 61 | 
            +
                          @interfaces.fetch_or_store([method, safe]) do
         | 
| 62 | 
            +
                            ::Module.new do
         | 
| 63 | 
            +
                              if safe
         | 
| 64 | 
            +
                                module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
         | 
| 65 | 
            +
                                  def call(input, &block)
         | 
| 66 | 
            +
                                    @target.#{method}(input, &block)
         | 
| 67 | 
            +
                                  end
         | 
| 68 | 
            +
                                RUBY
         | 
| 69 | 
            +
                              else
         | 
| 70 | 
            +
                                module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
         | 
| 71 | 
            +
                                  def call(input, &block)
         | 
| 72 | 
            +
                                    @target.#{method}(input)
         | 
| 73 | 
            +
                                  rescue ::NoMethodError, ::TypeError, ::ArgumentError => error
         | 
| 74 | 
            +
                                    CoercionError.handle(error, &block)
         | 
| 75 | 
            +
                                  end
         | 
| 76 | 
            +
                                RUBY
         | 
| 77 | 
            +
                              end
         | 
| 78 | 
            +
                            end
         | 
| 79 | 
            +
                          end
         | 
| 80 | 
            +
                        end
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                      # Coercion via a private method call
         | 
| 84 | 
            +
                      #
         | 
| 85 | 
            +
                      # @api private
         | 
| 86 | 
            +
                      class PrivateCall < MethodCall
         | 
| 87 | 
            +
                        def call(input, &block)
         | 
| 88 | 
            +
                          @target.send(@name, input, &block)
         | 
| 89 | 
            +
                        end
         | 
| 90 | 
            +
                      end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                      # Coercion via an unsafe private method call
         | 
| 93 | 
            +
                      #
         | 
| 94 | 
            +
                      # @api private
         | 
| 95 | 
            +
                      class PrivateSafeCall < PrivateCall
         | 
| 96 | 
            +
                        def call(input, &block)
         | 
| 97 | 
            +
                          @target.send(@name, input)
         | 
| 98 | 
            +
                        rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
         | 
| 99 | 
            +
                          CoercionError.handle(e, &block)
         | 
| 100 | 
            +
                        end
         | 
| 101 | 
            +
                      end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                      # @api private
         | 
| 104 | 
            +
                      #
         | 
| 105 | 
            +
                      # @return [MethodCall]
         | 
| 106 | 
            +
                      def self.[](fn, safe)
         | 
| 107 | 
            +
                        public = fn.receiver.respond_to?(fn.name)
         | 
| 108 | 
            +
                        MethodCall.call_class(fn.name, public, safe).new(fn)
         | 
| 109 | 
            +
                      end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                      attr_reader :target, :name
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                      def initialize(fn)
         | 
| 114 | 
            +
                        super
         | 
| 115 | 
            +
                        @target = fn.receiver
         | 
| 116 | 
            +
                        @name = fn.name
         | 
| 117 | 
            +
                      end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                      def to_ast
         | 
| 120 | 
            +
                        [:method, target, name]
         | 
| 121 | 
            +
                      end
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                    class Wrapper < Function
         | 
| 125 | 
            +
                      # @return [Object]
         | 
| 126 | 
            +
                      def call(input, type, &block)
         | 
| 127 | 
            +
                        @fn.(input, type, &block)
         | 
| 128 | 
            +
                      rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
         | 
| 129 | 
            +
                        CoercionError.handle(e, &block)
         | 
| 130 | 
            +
                      end
         | 
| 131 | 
            +
                      alias_method :[], :call
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                      def arity
         | 
| 134 | 
            +
                        2
         | 
| 135 | 
            +
                      end
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                    # Choose or build specialized invokation code for a callable
         | 
| 139 | 
            +
                    #
         | 
| 140 | 
            +
                    # @param [#call] fn
         | 
| 141 | 
            +
                    # @return [Function]
         | 
| 142 | 
            +
                    def self.[](fn)
         | 
| 143 | 
            +
                      raise ::ArgumentError, "Missing constructor block" if fn.nil?
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                      if fn.is_a?(Function)
         | 
| 146 | 
            +
                        fn
         | 
| 147 | 
            +
                      elsif fn.respond_to?(:arity) && fn.arity.equal?(2)
         | 
| 148 | 
            +
                        Wrapper.new(fn)
         | 
| 149 | 
            +
                      elsif fn.is_a?(::Method)
         | 
| 150 | 
            +
                        MethodCall[fn, yields_block?(fn)]
         | 
| 151 | 
            +
                      elsif yields_block?(fn)
         | 
| 152 | 
            +
                        new(fn)
         | 
| 153 | 
            +
                      else
         | 
| 154 | 
            +
                        Safe.new(fn)
         | 
| 155 | 
            +
                      end
         | 
| 156 | 
            +
                    end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                    # @return [Boolean]
         | 
| 159 | 
            +
                    def self.yields_block?(fn)
         | 
| 160 | 
            +
                      *, (last_arg,) =
         | 
| 161 | 
            +
                        if fn.respond_to?(:parameters)
         | 
| 162 | 
            +
                          fn.parameters
         | 
| 163 | 
            +
                        else
         | 
| 164 | 
            +
                          fn.method(:call).parameters
         | 
| 165 | 
            +
                        end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                      last_arg.equal?(:block)
         | 
| 168 | 
            +
                    end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                    include ::Dry::Equalizer(:fn, immutable: true)
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                    attr_reader :fn
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                    def initialize(fn)
         | 
| 175 | 
            +
                      @fn = fn
         | 
| 176 | 
            +
                    end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                    # @return [Object]
         | 
| 179 | 
            +
                    def call(input, &block)
         | 
| 180 | 
            +
                      @fn.(input, &block)
         | 
| 181 | 
            +
                    end
         | 
| 182 | 
            +
                    alias_method :[], :call
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                    # @return [Integer]
         | 
| 185 | 
            +
                    def arity
         | 
| 186 | 
            +
                      1
         | 
| 187 | 
            +
                    end
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                    def wrapper?
         | 
| 190 | 
            +
                      arity.equal?(2)
         | 
| 191 | 
            +
                    end
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                    # @return [Array]
         | 
| 194 | 
            +
                    def to_ast
         | 
| 195 | 
            +
                      if fn.is_a?(::Proc)
         | 
| 196 | 
            +
                        [:id, FnContainer.register(fn)]
         | 
| 197 | 
            +
                      else
         | 
| 198 | 
            +
                        [:callable, fn]
         | 
| 199 | 
            +
                      end
         | 
| 200 | 
            +
                    end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                    # @return [Function]
         | 
| 203 | 
            +
                    def >>(other)
         | 
| 204 | 
            +
                      f = Function[other]
         | 
| 205 | 
            +
                      Function[-> x, &b { f.(self.(x, &b), &b) }]
         | 
| 206 | 
            +
                    end
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                    # @return [Function]
         | 
| 209 | 
            +
                    def <<(other)
         | 
| 210 | 
            +
                      f = Function[other]
         | 
| 211 | 
            +
                      Function[-> x, &b { self.(f.(x, &b), &b) }]
         | 
| 212 | 
            +
                    end
         | 
| 213 | 
            +
                  end
         | 
| 214 | 
            +
                end
         | 
| 215 | 
            +
              end
         | 
| 216 | 
            +
            end
         | 
| @@ -0,0 +1,94 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dry
         | 
| 4 | 
            +
              module Types
         | 
| 5 | 
            +
                # @api public
         | 
| 6 | 
            +
                class Constructor < Nominal
         | 
| 7 | 
            +
                  module Wrapper
         | 
| 8 | 
            +
                    # @return [Object]
         | 
| 9 | 
            +
                    #
         | 
| 10 | 
            +
                    # @api private
         | 
| 11 | 
            +
                    def call_safe(input, &block)
         | 
| 12 | 
            +
                      fn.(input, type, &block)
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    # @return [Object]
         | 
| 16 | 
            +
                    #
         | 
| 17 | 
            +
                    # @api private
         | 
| 18 | 
            +
                    def call_unsafe(input)
         | 
| 19 | 
            +
                      fn.(input, type)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    # @param [Object] input
         | 
| 23 | 
            +
                    # @param [#call,nil] block
         | 
| 24 | 
            +
                    #
         | 
| 25 | 
            +
                    # @return [Logic::Result, Types::Result]
         | 
| 26 | 
            +
                    # @return [Object] if block given and try fails
         | 
| 27 | 
            +
                    #
         | 
| 28 | 
            +
                    # @api public
         | 
| 29 | 
            +
                    def try(input, &block)
         | 
| 30 | 
            +
                      value = fn.(input, type)
         | 
| 31 | 
            +
                    rescue CoercionError => e
         | 
| 32 | 
            +
                      failure = failure(input, e)
         | 
| 33 | 
            +
                      block_given? ? yield(failure) : failure
         | 
| 34 | 
            +
                    else
         | 
| 35 | 
            +
                      type.try(value, &block)
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    # Define a constructor for the type
         | 
| 39 | 
            +
                    #
         | 
| 40 | 
            +
                    # @param [#call,nil] constructor
         | 
| 41 | 
            +
                    # @param [Hash] options
         | 
| 42 | 
            +
                    # @param [#call,nil] block
         | 
| 43 | 
            +
                    #
         | 
| 44 | 
            +
                    # @return [Constructor]
         | 
| 45 | 
            +
                    #
         | 
| 46 | 
            +
                    # @api public
         | 
| 47 | 
            +
                    define_method(:constructor, Builder.instance_method(:constructor))
         | 
| 48 | 
            +
                    alias_method :append, :constructor
         | 
| 49 | 
            +
                    alias_method :>>, :constructor
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    # Build a new constructor by prepending a block to the coercion function
         | 
| 52 | 
            +
                    #
         | 
| 53 | 
            +
                    # @param [#call, nil] new_fn
         | 
| 54 | 
            +
                    # @param [Hash] options
         | 
| 55 | 
            +
                    # @param [#call, nil] block
         | 
| 56 | 
            +
                    #
         | 
| 57 | 
            +
                    # @return [Constructor]
         | 
| 58 | 
            +
                    #
         | 
| 59 | 
            +
                    # @api public
         | 
| 60 | 
            +
                    def prepend(new_fn = nil, **options, &block)
         | 
| 61 | 
            +
                      prep_fn = Function[new_fn || block]
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                      decorated =
         | 
| 64 | 
            +
                        if prep_fn.wrapper?
         | 
| 65 | 
            +
                          type.constructor(prep_fn, **options)
         | 
| 66 | 
            +
                        else
         | 
| 67 | 
            +
                          type.prepend(prep_fn, **options)
         | 
| 68 | 
            +
                        end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                      __new__(decorated)
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                    alias_method :<<, :prepend
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    # @return [Constructor]
         | 
| 75 | 
            +
                    #
         | 
| 76 | 
            +
                    # @api public
         | 
| 77 | 
            +
                    def lax
         | 
| 78 | 
            +
                      # return self back because wrapping function
         | 
| 79 | 
            +
                      # can handle failed type check
         | 
| 80 | 
            +
                      self
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    private
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                    # Replace underlying type
         | 
| 86 | 
            +
                    #
         | 
| 87 | 
            +
                    # @api private
         | 
| 88 | 
            +
                    def __new__(type)
         | 
| 89 | 
            +
                      self.class.new(type, *@__args__.drop(1), **@options)
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
              end
         | 
| 94 | 
            +
            end
         |