dry-types 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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