dry-types 1.0.1 → 1.1.0

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.
@@ -71,8 +71,8 @@ module Dry
71
71
  else
72
72
  Integer(input)
73
73
  end
74
- rescue ArgumentError, TypeError => error
75
- CoercionError.handle(error, &block)
74
+ rescue ArgumentError, TypeError => e
75
+ CoercionError.handle(e, &block)
76
76
  end
77
77
 
78
78
  # @param [#to_f, Object] input
@@ -84,8 +84,8 @@ module Dry
84
84
  # @api public
85
85
  def self.to_float(input, &block)
86
86
  Float(input)
87
- rescue ArgumentError, TypeError => error
88
- CoercionError.handle(error, &block)
87
+ rescue ArgumentError, TypeError => e
88
+ CoercionError.handle(e, &block)
89
89
  end
90
90
 
91
91
  # @param [#to_d, Object] input
@@ -126,6 +126,11 @@ module Dry
126
126
  [:constrained, [type.to_ast(meta: meta), rule.to_ast]]
127
127
  end
128
128
 
129
+ # @api private
130
+ def constructor_type
131
+ type.constructor_type
132
+ end
133
+
129
134
  private
130
135
 
131
136
  # @param [Object] response
@@ -95,8 +95,8 @@ module Dry
95
95
  # @api public
96
96
  def try(input, &block)
97
97
  value = fn.(input)
98
- rescue CoercionError => error
99
- failure = failure(input, error)
98
+ rescue CoercionError => e
99
+ failure = failure(input, e)
100
100
  block_given? ? yield(failure) : failure
101
101
  else
102
102
  type.try(value, &block)
@@ -184,7 +184,7 @@ module Dry
184
184
  if type.respond_to?(method)
185
185
  response = type.__send__(method, *args, &block)
186
186
 
187
- if composable?(response)
187
+ if response.is_a?(Type) && type.class == response.class
188
188
  response.constructor_type.new(response, options)
189
189
  else
190
190
  response
@@ -193,11 +193,6 @@ module Dry
193
193
  super
194
194
  end
195
195
  end
196
-
197
- # @api private
198
- def composable?(value)
199
- value.is_a?(Builder)
200
- end
201
196
  end
202
197
  end
203
198
  end
@@ -1,4 +1,3 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'concurrent/map'
@@ -16,8 +15,8 @@ module Dry
16
15
  class Safe < Function
17
16
  def call(input, &block)
18
17
  @fn.(input, &block)
19
- rescue NoMethodError, TypeError, ArgumentError => error
20
- CoercionError.handle(error, &block)
18
+ rescue NoMethodError, TypeError, ArgumentError => e
19
+ CoercionError.handle(e, &block)
21
20
  end
22
21
  end
23
22
 
@@ -91,8 +90,8 @@ module Dry
91
90
  class PrivateSafeCall < PrivateCall
92
91
  def call(input, &block)
93
92
  @target.send(@name, input)
94
- rescue NoMethodError, TypeError, ArgumentError => error
95
- CoercionError.handle(error, &block)
93
+ rescue NoMethodError, TypeError, ArgumentError => e
94
+ CoercionError.handle(e, &block)
96
95
  end
97
96
  end
98
97
 
@@ -5,7 +5,7 @@ require 'dry/types/any'
5
5
  module Dry
6
6
  module Types
7
7
  # Primitives with {Kernel} coercion methods
8
- COERCIBLE = {
8
+ KERNEL_COERCIBLE = {
9
9
  string: String,
10
10
  integer: Integer,
11
11
  float: Float,
@@ -14,10 +14,19 @@ module Dry
14
14
  hash: ::Hash
15
15
  }.freeze
16
16
 
17
- # Primitives that are non-coercible through {Kernel} methods
17
+ # Primitives with coercions through by convention `to_*` methods
18
+ METHOD_COERCIBLE = {
19
+ symbol: Symbol
20
+ }.freeze
21
+
22
+ # By convention methods to coerce {METHOD_COERCIBLE} primitives
23
+ METHOD_COERCIBLE_METHODS = {
24
+ symbol: :to_sym
25
+ }.freeze
26
+
27
+ # Primitives that are non-coercible
18
28
  NON_COERCIBLE = {
19
29
  nil: NilClass,
20
- symbol: Symbol,
21
30
  class: Class,
22
31
  true: TrueClass,
23
32
  false: FalseClass,
@@ -28,7 +37,10 @@ module Dry
28
37
  }.freeze
29
38
 
30
39
  # All built-in primitives
31
- ALL_PRIMITIVES = COERCIBLE.merge(NON_COERCIBLE).freeze
40
+ ALL_PRIMITIVES = [KERNEL_COERCIBLE, METHOD_COERCIBLE, NON_COERCIBLE].reduce(&:merge).freeze
41
+
42
+ # All coercible types
43
+ COERCIBLE = KERNEL_COERCIBLE.merge(METHOD_COERCIBLE).freeze
32
44
 
33
45
  # All built-in primitives except {NilClass}
34
46
  NON_NIL = ALL_PRIMITIVES.reject { |name, _| name == :nil }.freeze
@@ -46,11 +58,18 @@ module Dry
46
58
  register("strict.#{name}", type)
47
59
  end
48
60
 
49
- # Register {COERCIBLE} types
50
- COERCIBLE.each do |name, primitive|
61
+ # Register {KERNEL_COERCIBLE} types
62
+ KERNEL_COERCIBLE.each do |name, primitive|
51
63
  register("coercible.#{name}", self["nominal.#{name}"].constructor(Kernel.method(primitive.name)))
52
64
  end
53
65
 
66
+ # Register {METHOD_COERCIBLE} types
67
+ METHOD_COERCIBLE.each_key do |name|
68
+ register(
69
+ "coercible.#{name}", self["nominal.#{name}"].constructor(&METHOD_COERCIBLE_METHODS[name])
70
+ )
71
+ end
72
+
54
73
  # Register optional strict {NON_NIL} types
55
74
  NON_NIL.each_key do |name|
56
75
  register("optional.strict.#{name}", self["strict.#{name}"].optional)
@@ -64,8 +83,8 @@ module Dry
64
83
  # Register `:bool` since it's common and not a built-in Ruby type :(
65
84
  register('nominal.bool', self['nominal.true'] | self['nominal.false'])
66
85
  bool = self['strict.true'] | self['strict.false']
67
- register("strict.bool", bool)
68
- register("bool", bool)
86
+ register('strict.bool', bool)
87
+ register('bool', bool)
69
88
 
70
89
  register('any', Any)
71
90
  register('nominal.any', Any)
@@ -11,6 +11,7 @@ module Dry
11
11
  include Type
12
12
  include Dry::Equalizer(:type, :mapping, inspect: false)
13
13
  include Decorator
14
+ include Builder
14
15
 
15
16
  # @return [Array]
16
17
  attr_reader :values
@@ -79,7 +79,7 @@ module Dry
79
79
  # @api public
80
80
  def default(value)
81
81
  if value.nil?
82
- raise ArgumentError, "nil cannot be used as a default of a maybe type"
82
+ raise ArgumentError, 'nil cannot be used as a default of a maybe type'
83
83
  else
84
84
  super
85
85
  end
@@ -97,6 +97,14 @@ module Dry
97
97
  end
98
98
  end
99
99
 
100
+ # @api private
101
+ class Schema::Key
102
+ # @api private
103
+ def maybe
104
+ __new__(type.maybe)
105
+ end
106
+ end
107
+
100
108
  # @api private
101
109
  class Printer
102
110
  MAPPING[Maybe] = :visit_maybe
@@ -50,8 +50,8 @@ module Dry
50
50
 
51
51
  # @api private
52
52
  def weak(*)
53
- raise "Support for old hash schemas was removed, please refer to the CHANGELOG "\
54
- "on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md"
53
+ raise 'Support for old hash schemas was removed, please refer to the CHANGELOG '\
54
+ 'on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md'
55
55
  end
56
56
  alias_method :permissive, :weak
57
57
  alias_method :strict, :weak
@@ -69,9 +69,7 @@ module Dry
69
69
  def with_type_transform(proc = nil, &block)
70
70
  fn = proc || block
71
71
 
72
- if fn.nil?
73
- raise ArgumentError, "a block or callable argument is required"
74
- end
72
+ raise ArgumentError, 'a block or callable argument is required' if fn.nil?
75
73
 
76
74
  handle = Dry::Types::FnContainer.register(fn)
77
75
  with(type_transform_fn: handle)
@@ -97,11 +95,11 @@ module Dry
97
95
  #
98
96
  # @api public
99
97
  def to_ast(meta: true)
100
- if RUBY_VERSION >= "2.5"
101
- opts = options.slice(:type_transform_fn)
102
- else
103
- opts = options.select { |k, _| k == :type_transform_fn }
104
- end
98
+ opts = if RUBY_VERSION >= '2.5'
99
+ options.slice(:type_transform_fn)
100
+ else
101
+ options.select { |k, _| k == :type_transform_fn }
102
+ end
105
103
 
106
104
  [:hash, [opts, meta ? self.meta : EMPTY_HASH]]
107
105
  end
@@ -21,11 +21,11 @@ module Dry
21
21
  type.lax.constructor(fn, meta: meta)
22
22
  end
23
23
 
24
- private
25
-
26
- # @api private
27
- def composable?(value)
28
- super && !value.is_a?(Schema::Key)
24
+ # @see Dry::Types::Array#of
25
+ #
26
+ # @api public
27
+ def schema(*args)
28
+ type.schema(*args).constructor(fn, meta: meta)
29
29
  end
30
30
  end
31
31
  end
@@ -24,6 +24,10 @@ module Dry
24
24
  self['nominal.decimal'].constructor(Coercions::JSON.method(:to_decimal))
25
25
  end
26
26
 
27
+ register('json.symbol') do
28
+ self['nominal.symbol'].constructor(Coercions::JSON.method(:to_symbol))
29
+ end
30
+
27
31
  register('json.array') { self['array'] }
28
32
 
29
33
  register('json.hash') { self['hash'] }
@@ -40,8 +40,8 @@ module Dry
40
40
  # @api public
41
41
  def try(input, &block)
42
42
  type.try(input, &block)
43
- rescue CoercionError => error
44
- result = failure(input, error.message)
43
+ rescue CoercionError => e
44
+ result = failure(input, e.message)
45
45
  block ? yield(result) : result
46
46
  end
47
47
 
@@ -67,7 +67,7 @@ module Dry
67
67
  #
68
68
  # @api private
69
69
  def decorate?(response)
70
- super || response.is_a?(constructor_type)
70
+ super || response.is_a?(type.constructor_type)
71
71
  end
72
72
  end
73
73
 
@@ -74,6 +74,7 @@ module Dry
74
74
  def try(hash)
75
75
  result = coerce(hash)
76
76
  return result if result.success? || !block_given?
77
+
77
78
  yield(result)
78
79
  end
79
80
 
@@ -100,11 +101,14 @@ module Dry
100
101
 
101
102
  # @api private
102
103
  def coerce(input)
103
- return failure(
104
- input, CoercionError.new("#{input.inspect} must be an instance of #{primitive}")
105
- ) unless primitive?(input)
104
+ unless primitive?(input)
105
+ return failure(
106
+ input, CoercionError.new("#{input.inspect} must be an instance of #{primitive}")
107
+ )
108
+ end
106
109
 
107
- output, failures = {}, []
110
+ output = {}
111
+ failures = []
108
112
 
109
113
  input.each do |k, v|
110
114
  res_k = key_type.try(k)
@@ -31,7 +31,7 @@ module Dry
31
31
  base.instance_exec(const_get(:Nominal, false)) do |nominal|
32
32
  extend Dry::Core::Deprecations[:'dry-types']
33
33
  const_set(:Definition, nominal)
34
- deprecate_constant(:Definition, message: "Nominal")
34
+ deprecate_constant(:Definition, message: 'Nominal')
35
35
  end
36
36
  end
37
37
  end
@@ -93,8 +93,8 @@ module Dry
93
93
 
94
94
  (referenced.uniq - known).each do |name|
95
95
  raise ArgumentError,
96
- "#{ name.inspect } is not a known type namespace. "\
97
- "Supported options are #{ known.map(&:inspect).join(', ') }"
96
+ "#{name.inspect} is not a known type namespace. "\
97
+ "Supported options are #{known.map(&:inspect).join(', ')}"
98
98
  end
99
99
  end
100
100
 
@@ -126,9 +126,8 @@ module Dry
126
126
  #
127
127
  # @api public
128
128
  def failure(input, error)
129
- unless error.is_a?(CoercionError)
130
- raise ArgumentError, "error must be a CoercionError"
131
- end
129
+ raise ArgumentError, 'error must be a CoercionError' unless error.is_a?(CoercionError)
130
+
132
131
  Result::Failure.new(input, error)
133
132
  end
134
133
 
@@ -202,7 +201,7 @@ module Dry
202
201
 
203
202
  extend Dry::Core::Deprecations[:'dry-types']
204
203
  Definition = Nominal
205
- deprecate_constant(:Definition, message: "Nominal")
204
+ deprecate_constant(:Definition, message: 'Nominal')
206
205
  end
207
206
  end
208
207
 
@@ -51,5 +51,9 @@ module Dry
51
51
  register('params.hash') do
52
52
  self['nominal.hash'].constructor(Coercions::Params.method(:to_hash))
53
53
  end
54
+
55
+ register('params.symbol') do
56
+ self['nominal.symbol'].constructor(Coercions::Params.method(:to_symbol))
57
+ end
54
58
  end
55
59
  end
@@ -8,6 +8,7 @@ module Dry
8
8
  Nominal => :visit_nominal,
9
9
  Constructor => :visit_constructor,
10
10
  Hash::Constructor => :visit_constructor,
11
+ Array::Constructor => :visit_constructor,
11
12
  Constrained => :visit_constrained,
12
13
  Constrained::Coercible => :visit_constrained,
13
14
  Hash => :visit_hash,
@@ -26,7 +27,7 @@ module Dry
26
27
  }
27
28
 
28
29
  def call(type)
29
- output = "".dup
30
+ output = ''.dup
30
31
  visit(type) { |str| output << str }
31
32
  "#<Dry::Types[#{output}]>"
32
33
  end
@@ -43,11 +44,11 @@ module Dry
43
44
  end
44
45
 
45
46
  def visit_any(_)
46
- yield "Any"
47
+ yield 'Any'
47
48
  end
48
49
 
49
50
  def visit_array(_)
50
- yield "Array"
51
+ yield 'Array'
51
52
  end
52
53
 
53
54
  def visit_array_member(array)
@@ -74,7 +75,7 @@ module Dry
74
75
  options = constrained.options.dup
75
76
  rule = options.delete(:rule)
76
77
 
77
- visit_options(options) do |opts|
78
+ visit_options(options) do |_opts|
78
79
  yield "Constrained<#{type} rule=[#{rule}]>"
79
80
  end
80
81
  end
@@ -110,7 +111,7 @@ module Dry
110
111
  header = "Schema<#{schema_parameters}keys={"
111
112
 
112
113
  if size.zero?
113
- yield "#{ header}}>"
114
+ yield "#{header}}>"
114
115
  else
115
116
  yield header.dup << keys.map { |key|
116
117
  visit(key) { |type| type }
@@ -126,7 +127,7 @@ module Dry
126
127
  options.delete(:key_type)
127
128
  options.delete(:value_type)
128
129
 
129
- visit_options(options) do |opts|
130
+ visit_options(options) do |_opts|
130
131
  yield "Map<#{key} => #{value}>"
131
132
  end
132
133
  end
@@ -193,7 +194,7 @@ module Dry
193
194
  yield "Enum<#{type} values={#{values}}#{opts}>"
194
195
  else
195
196
  mapping_str = mapping.map { |key, value|
196
- "#{ key.inspect }=>#{value.inspect}"
197
+ "#{key.inspect}=>#{value.inspect}"
197
198
  }.join(', ')
198
199
  yield "Enum<#{type} mapping={#{mapping_str}}#{opts}>"
199
200
  end
@@ -251,14 +252,14 @@ module Dry
251
252
 
252
253
  case fn
253
254
  when Method
254
- yield "#{ fn.receiver }.#{ fn.name }"
255
+ yield "#{fn.receiver}.#{fn.name}"
255
256
  when Proc
256
257
  path, line = fn.source_location
257
258
 
258
- if line && line.zero?
259
+ if line&.zero?
259
260
  yield ".#{path}"
260
261
  elsif path
261
- yield "#{path.sub(Dir.pwd + '/', EMPTY_STRING) }:#{line}"
262
+ yield "#{path.sub(Dir.pwd + '/', EMPTY_STRING)}:#{line}"
262
263
  elsif fn.lambda?
263
264
  yield '(lambda)'
264
265
  else