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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -134
  3. data/LICENSE +17 -17
  4. data/README.md +15 -13
  5. data/dry-types.gemspec +27 -30
  6. data/lib/dry/types/any.rb +32 -12
  7. data/lib/dry/types/array/constructor.rb +32 -0
  8. data/lib/dry/types/array/member.rb +75 -16
  9. data/lib/dry/types/array.rb +19 -6
  10. data/lib/dry/types/builder.rb +131 -15
  11. data/lib/dry/types/builder_methods.rb +49 -20
  12. data/lib/dry/types/coercions/json.rb +43 -7
  13. data/lib/dry/types/coercions/params.rb +118 -31
  14. data/lib/dry/types/coercions.rb +76 -22
  15. data/lib/dry/types/compat.rb +0 -2
  16. data/lib/dry/types/compiler.rb +56 -41
  17. data/lib/dry/types/constrained/coercible.rb +36 -6
  18. data/lib/dry/types/constrained.rb +81 -32
  19. data/lib/dry/types/constraints.rb +18 -4
  20. data/lib/dry/types/constructor/function.rb +216 -0
  21. data/lib/dry/types/constructor/wrapper.rb +94 -0
  22. data/lib/dry/types/constructor.rb +126 -56
  23. data/lib/dry/types/container.rb +7 -0
  24. data/lib/dry/types/core.rb +54 -21
  25. data/lib/dry/types/decorator.rb +38 -17
  26. data/lib/dry/types/default.rb +61 -16
  27. data/lib/dry/types/enum.rb +43 -20
  28. data/lib/dry/types/errors.rb +75 -9
  29. data/lib/dry/types/extensions/maybe.rb +74 -16
  30. data/lib/dry/types/extensions/monads.rb +29 -0
  31. data/lib/dry/types/extensions.rb +7 -1
  32. data/lib/dry/types/fn_container.rb +6 -1
  33. data/lib/dry/types/hash/constructor.rb +33 -0
  34. data/lib/dry/types/hash.rb +86 -67
  35. data/lib/dry/types/inflector.rb +3 -1
  36. data/lib/dry/types/json.rb +18 -16
  37. data/lib/dry/types/lax.rb +75 -0
  38. data/lib/dry/types/map.rb +76 -33
  39. data/lib/dry/types/meta.rb +51 -0
  40. data/lib/dry/types/module.rb +120 -0
  41. data/lib/dry/types/nominal.rb +210 -0
  42. data/lib/dry/types/options.rb +13 -26
  43. data/lib/dry/types/params.rb +39 -25
  44. data/lib/dry/types/predicate_inferrer.rb +238 -0
  45. data/lib/dry/types/predicate_registry.rb +34 -0
  46. data/lib/dry/types/primitive_inferrer.rb +97 -0
  47. data/lib/dry/types/printable.rb +16 -0
  48. data/lib/dry/types/printer.rb +315 -0
  49. data/lib/dry/types/result.rb +29 -3
  50. data/lib/dry/types/schema/key.rb +156 -0
  51. data/lib/dry/types/schema.rb +408 -0
  52. data/lib/dry/types/spec/types.rb +103 -33
  53. data/lib/dry/types/sum.rb +84 -35
  54. data/lib/dry/types/type.rb +49 -0
  55. data/lib/dry/types/version.rb +3 -1
  56. data/lib/dry/types.rb +156 -76
  57. data/lib/dry-types.rb +3 -1
  58. metadata +65 -78
  59. data/.gitignore +0 -10
  60. data/.rspec +0 -2
  61. data/.travis.yml +0 -27
  62. data/.yardopts +0 -5
  63. data/CONTRIBUTING.md +0 -29
  64. data/Gemfile +0 -24
  65. data/Rakefile +0 -20
  66. data/benchmarks/hash_schemas.rb +0 -51
  67. data/lib/dry/types/compat/form_types.rb +0 -27
  68. data/lib/dry/types/compat/int.rb +0 -14
  69. data/lib/dry/types/definition.rb +0 -113
  70. data/lib/dry/types/hash/schema.rb +0 -199
  71. data/lib/dry/types/hash/schema_builder.rb +0 -75
  72. data/lib/dry/types/safe.rb +0 -59
  73. data/log/.gitkeep +0 -0
@@ -1,5 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Types
5
+ # Common API for building type objects in a convenient way
6
+ #
7
+ #
8
+ # @api public
3
9
  module BuilderMethods
4
10
  # @api private
5
11
  def included(base)
@@ -8,7 +14,8 @@ module Dry
8
14
  end
9
15
 
10
16
  # Build an array type.
11
- # It is a shortcut for Array.of
17
+ #
18
+ # Shortcut for Array#of.
12
19
  #
13
20
  # @example
14
21
  # Types::Strings = Types.Array(Types::String)
@@ -17,18 +24,16 @@ module Dry
17
24
  #
18
25
  # @return [Dry::Types::Array]
19
26
  def Array(type)
20
- self::Array.of(type)
27
+ Strict(::Array).of(type)
21
28
  end
22
29
 
23
30
  # Build a hash schema
24
31
  #
25
- # @param [Symbol] schema Schema type
26
32
  # @param [Hash{Symbol => Dry::Types::Type}] type_map
27
33
  #
28
34
  # @return [Dry::Types::Array]
29
- # @api public
30
- def Hash(schema, type_map)
31
- self::Hash.public_send(schema, type_map)
35
+ def Hash(type_map)
36
+ Strict(::Hash).schema(type_map)
32
37
  end
33
38
 
34
39
  # Build a type which values are instances of a given class
@@ -42,9 +47,8 @@ module Dry
42
47
  # @param [Class,Module] klass Class or module
43
48
  #
44
49
  # @return [Dry::Types::Type]
45
- # @api public
46
50
  def Instance(klass)
47
- Definition.new(klass).constrained(type: klass)
51
+ Nominal(klass).constrained(type: klass)
48
52
  end
49
53
  alias_method :Strict, :Instance
50
54
 
@@ -54,9 +58,8 @@ module Dry
54
58
  # @param [Object] value
55
59
  #
56
60
  # @return [Dry::Types::Type]
57
- # @api public
58
61
  def Value(value)
59
- Definition.new(value.class).constrained(eql: value)
62
+ Nominal(value.class).constrained(eql: value)
60
63
  end
61
64
 
62
65
  # Build a type with a single value
@@ -65,9 +68,8 @@ module Dry
65
68
  # @param [Object] object
66
69
  #
67
70
  # @return [Dry::Types::Type]
68
- # @api public
69
71
  def Constant(object)
70
- Definition.new(object.class).constrained(is: object)
72
+ Nominal(object.class).constrained(is: object)
71
73
  end
72
74
 
73
75
  # Build a constructor type
@@ -78,19 +80,31 @@ module Dry
78
80
  # @param [#call,nil] block Value constructor
79
81
  #
80
82
  # @return [Dry::Types::Type]
81
- # @api public
82
83
  def Constructor(klass, cons = nil, &block)
83
- Definition.new(klass).constructor(cons || block || klass.method(:new))
84
+ if klass.is_a?(Type)
85
+ if cons || block
86
+ klass.constructor(cons || block)
87
+ else
88
+ klass
89
+ end
90
+ else
91
+ Nominal(klass).constructor(cons || block || klass.method(:new))
92
+ end
84
93
  end
85
94
 
86
- # Build a definition type
95
+ # Build a nominal type
87
96
  #
88
97
  # @param [Class] klass
89
98
  #
90
99
  # @return [Dry::Types::Type]
91
- # @api public
92
- def Definition(klass)
93
- Definition.new(klass)
100
+ def Nominal(klass)
101
+ if klass <= ::Array
102
+ Array.new(klass)
103
+ elsif klass <= ::Hash
104
+ Hash.new(klass)
105
+ else
106
+ Nominal.new(klass)
107
+ end
94
108
  end
95
109
 
96
110
  # Build a map type
@@ -103,9 +117,24 @@ module Dry
103
117
  # @param [Type] value_type Value type
104
118
  #
105
119
  # @return [Dry::Types::Map]
106
- # @api public
107
120
  def Map(key_type, value_type)
108
- Types['hash'].map(key_type, value_type)
121
+ Nominal(::Hash).map(key_type, value_type)
122
+ end
123
+
124
+ # Builds a constrained nominal type accepting any value that
125
+ # responds to given methods
126
+ #
127
+ # @example
128
+ # Types::Callable = Types.Interface(:call)
129
+ # Types::Contact = Types.Interface(:name, :address)
130
+ #
131
+ # @param methods [Array<String, Symbol>] Method names
132
+ #
133
+ # @return [Dry::Types::Contrained]
134
+ def Interface(*methods)
135
+ methods.reduce(Types["nominal.any"]) do |type, method|
136
+ type.constrained(respond_to: method)
137
+ end
109
138
  end
110
139
  end
111
140
  end
@@ -1,19 +1,55 @@
1
- require 'date'
2
- require 'bigdecimal'
3
- require 'bigdecimal/util'
4
- require 'time'
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "bigdecimal"
5
+ require "bigdecimal/util"
6
+ require "time"
5
7
 
6
8
  module Dry
7
9
  module Types
8
10
  module Coercions
11
+ # JSON-specific coercions
12
+ #
13
+ # @api public
9
14
  module JSON
10
15
  extend Coercions
11
16
 
17
+ # @param [Object] input
18
+ #
19
+ # @return [nil] if the input is nil
20
+ #
21
+ # @raise CoercionError
22
+ #
23
+ # @api public
24
+ def self.to_nil(input, &_block)
25
+ if input.nil?
26
+ nil
27
+ elsif block_given?
28
+ yield
29
+ else
30
+ raise CoercionError, "#{input.inspect} is not nil"
31
+ end
32
+ end
33
+
12
34
  # @param [#to_d, Object] input
35
+ #
13
36
  # @return [BigDecimal,nil]
14
- def self.to_decimal(input)
15
- return if input.nil?
16
- input.to_d unless empty_str?(input)
37
+ #
38
+ # @raise CoercionError
39
+ #
40
+ # @api public
41
+ def self.to_decimal(input, &_block)
42
+ if input.is_a?(::Float)
43
+ input.to_d
44
+ else
45
+ BigDecimal(input)
46
+ end
47
+ rescue ArgumentError, TypeError
48
+ if block_given?
49
+ yield
50
+ else
51
+ raise CoercionError, "#{input} cannot be coerced to decimal"
52
+ end
17
53
  end
18
54
  end
19
55
  end
@@ -1,78 +1,165 @@
1
- require 'bigdecimal'
2
- require 'bigdecimal/util'
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+ require "bigdecimal/util"
3
5
 
4
6
  module Dry
5
7
  module Types
6
8
  module Coercions
9
+ # Params-specific coercions
10
+ #
11
+ # @api public
7
12
  module Params
8
13
  TRUE_VALUES = %w[1 on On ON t true True TRUE T y yes Yes YES Y].freeze
9
14
  FALSE_VALUES = %w[0 off Off OFF f false False FALSE F n no No NO N].freeze
10
- BOOLEAN_MAP = ::Hash[TRUE_VALUES.product([true]) + FALSE_VALUES.product([false])].freeze
15
+ BOOLEAN_MAP = ::Hash[
16
+ TRUE_VALUES.product([true]) + FALSE_VALUES.product([false])
17
+ ].merge(true => true, false => false).freeze
11
18
 
12
19
  extend Coercions
13
20
 
21
+ # @param [Object] input
22
+ #
23
+ # @return [nil] if the input is an empty string or nil
24
+ #
25
+ # @raise CoercionError
26
+ #
27
+ # @api public
28
+ def self.to_nil(input, &_block)
29
+ if input.nil? || empty_str?(input)
30
+ nil
31
+ elsif block_given?
32
+ yield
33
+ else
34
+ raise CoercionError, "#{input.inspect} is not nil"
35
+ end
36
+ end
37
+
14
38
  # @param [String, Object] input
39
+ #
15
40
  # @return [Boolean,Object]
41
+ #
16
42
  # @see TRUE_VALUES
17
43
  # @see FALSE_VALUES
18
- def self.to_true(input)
19
- BOOLEAN_MAP.fetch(input.to_s, input)
44
+ #
45
+ # @raise CoercionError
46
+ #
47
+ # @api public
48
+ def self.to_true(input, &_block)
49
+ BOOLEAN_MAP.fetch(input.to_s) do
50
+ if block_given?
51
+ yield
52
+ else
53
+ raise CoercionError, "#{input} cannot be coerced to true"
54
+ end
55
+ end
20
56
  end
21
57
 
22
58
  # @param [String, Object] input
59
+ #
23
60
  # @return [Boolean,Object]
61
+ #
24
62
  # @see TRUE_VALUES
25
63
  # @see FALSE_VALUES
26
- def self.to_false(input)
27
- BOOLEAN_MAP.fetch(input.to_s, input)
64
+ #
65
+ # @raise CoercionError
66
+ #
67
+ # @api public
68
+ def self.to_false(input, &_block)
69
+ BOOLEAN_MAP.fetch(input.to_s) do
70
+ if block_given?
71
+ yield
72
+ else
73
+ raise CoercionError, "#{input} cannot be coerced to false"
74
+ end
75
+ end
28
76
  end
29
77
 
30
78
  # @param [#to_int, #to_i, Object] input
79
+ #
31
80
  # @return [Integer, nil, Object]
32
- def self.to_int(input)
33
- if empty_str?(input)
34
- nil
81
+ #
82
+ # @raise CoercionError
83
+ #
84
+ # @api public
85
+ def self.to_int(input, &block)
86
+ if input.is_a? String
87
+ Integer(input, 10)
35
88
  else
36
89
  Integer(input)
37
90
  end
38
- rescue ArgumentError, TypeError
39
- input
91
+ rescue ArgumentError, TypeError => e
92
+ CoercionError.handle(e, &block)
40
93
  end
41
94
 
42
95
  # @param [#to_f, Object] input
96
+ #
43
97
  # @return [Float, nil, Object]
44
- def self.to_float(input)
45
- if empty_str?(input)
46
- nil
47
- else
48
- Float(input)
49
- end
50
- rescue ArgumentError, TypeError
51
- input
98
+ #
99
+ # @raise CoercionError
100
+ #
101
+ # @api public
102
+ def self.to_float(input, &block)
103
+ Float(input)
104
+ rescue ArgumentError, TypeError => e
105
+ CoercionError.handle(e, &block)
52
106
  end
53
107
 
54
108
  # @param [#to_d, Object] input
109
+ #
55
110
  # @return [BigDecimal, nil, Object]
56
- def self.to_decimal(input)
57
- result = to_float(input)
58
-
59
- if result.instance_of?(Float)
60
- input.to_d
61
- else
62
- result
111
+ #
112
+ # @raise CoercionError
113
+ #
114
+ # @api public
115
+ def self.to_decimal(input, &_block)
116
+ to_float(input) do
117
+ if block_given?
118
+ return yield
119
+ else
120
+ raise CoercionError, "#{input.inspect} cannot be coerced to decimal"
121
+ end
63
122
  end
123
+
124
+ input.to_d
64
125
  end
65
126
 
66
127
  # @param [Array, String, Object] input
128
+ #
67
129
  # @return [Array, Object]
68
- def self.to_ary(input)
69
- empty_str?(input) ? [] : input
130
+ #
131
+ # @raise CoercionError
132
+ #
133
+ # @api public
134
+ def self.to_ary(input, &_block)
135
+ if empty_str?(input)
136
+ []
137
+ elsif input.is_a?(::Array)
138
+ input
139
+ elsif block_given?
140
+ yield
141
+ else
142
+ raise CoercionError, "#{input.inspect} cannot be coerced to array"
143
+ end
70
144
  end
71
145
 
72
146
  # @param [Hash, String, Object] input
147
+ #
73
148
  # @return [Hash, Object]
74
- def self.to_hash(input)
75
- empty_str?(input) ? {} : input
149
+ #
150
+ # @raise CoercionError
151
+ #
152
+ # @api public
153
+ def self.to_hash(input, &_block)
154
+ if empty_str?(input)
155
+ {}
156
+ elsif input.is_a?(::Hash)
157
+ input
158
+ elsif block_given?
159
+ yield
160
+ else
161
+ raise CoercionError, "#{input.inspect} cannot be coerced to hash"
162
+ end
76
163
  end
77
164
  end
78
165
  end
@@ -1,50 +1,104 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Types
5
+ # Common coercion functions used by the built-in `Params` and `JSON` types
6
+ #
7
+ # @api public
3
8
  module Coercions
4
9
  include Dry::Core::Constants
5
10
 
6
- # @param [String, Object] input
7
- # @return [nil] if the input is an empty string
8
- # @return [Object] otherwise the input object is returned
9
- def to_nil(input)
10
- input unless empty_str?(input)
11
- end
12
-
13
11
  # @param [#to_str, Object] input
12
+ #
14
13
  # @return [Date, Object]
14
+ #
15
15
  # @see Date.parse
16
- def to_date(input)
17
- return input unless input.respond_to?(:to_str)
18
- Date.parse(input)
19
- rescue ArgumentError, RangeError
20
- input
16
+ #
17
+ # @api public
18
+ def to_date(input, &block)
19
+ if input.respond_to?(:to_str)
20
+ begin
21
+ ::Date.parse(input)
22
+ rescue ArgumentError, RangeError => e
23
+ CoercionError.handle(e, &block)
24
+ end
25
+ elsif input.is_a?(::Date)
26
+ input
27
+ elsif block_given?
28
+ yield
29
+ else
30
+ raise CoercionError, "#{input.inspect} is not a string"
31
+ end
21
32
  end
22
33
 
23
34
  # @param [#to_str, Object] input
35
+ #
24
36
  # @return [DateTime, Object]
37
+ #
25
38
  # @see DateTime.parse
26
- def to_date_time(input)
27
- return input unless input.respond_to?(:to_str)
28
- DateTime.parse(input)
29
- rescue ArgumentError
30
- input
39
+ #
40
+ # @api public
41
+ def to_date_time(input, &block)
42
+ if input.respond_to?(:to_str)
43
+ begin
44
+ ::DateTime.parse(input)
45
+ rescue ArgumentError => e
46
+ CoercionError.handle(e, &block)
47
+ end
48
+ elsif input.is_a?(::DateTime)
49
+ input
50
+ elsif block_given?
51
+ yield
52
+ else
53
+ raise CoercionError, "#{input.inspect} is not a string"
54
+ end
31
55
  end
32
56
 
33
57
  # @param [#to_str, Object] input
58
+ #
34
59
  # @return [Time, Object]
60
+ #
35
61
  # @see Time.parse
36
- def to_time(input)
37
- return input unless input.respond_to?(:to_str)
38
- Time.parse(input)
39
- rescue ArgumentError
40
- input
62
+ #
63
+ # @api public
64
+ def to_time(input, &block)
65
+ if input.respond_to?(:to_str)
66
+ begin
67
+ ::Time.parse(input)
68
+ rescue ArgumentError => e
69
+ CoercionError.handle(e, &block)
70
+ end
71
+ elsif input.is_a?(::Time)
72
+ input
73
+ elsif block_given?
74
+ yield
75
+ else
76
+ raise CoercionError, "#{input.inspect} is not a string"
77
+ end
78
+ end
79
+
80
+ # @param [#to_sym, Object] input
81
+ #
82
+ # @return [Symbol, Object]
83
+ #
84
+ # @raise CoercionError
85
+ #
86
+ # @api public
87
+ def to_symbol(input, &block)
88
+ input.to_sym
89
+ rescue NoMethodError => e
90
+ CoercionError.handle(e, &block)
41
91
  end
42
92
 
43
93
  private
44
94
 
45
95
  # Checks whether String is empty
96
+ #
46
97
  # @param [String, Object] value
98
+ #
47
99
  # @return [Boolean]
100
+ #
101
+ # @api private
48
102
  def empty_str?(value)
49
103
  EMPTY_STRING.eql?(value)
50
104
  end
@@ -1,2 +0,0 @@
1
- require 'dry/types/compat/int'
2
- require 'dry/types/compat/form_types'
@@ -1,6 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/deprecations"
4
+
1
5
  module Dry
2
6
  module Types
7
+ # @api private
3
8
  class Compiler
9
+ extend ::Dry::Core::Deprecations[:'dry-types']
10
+
4
11
  attr_reader :registry
5
12
 
6
13
  def initialize(registry)
@@ -13,33 +20,34 @@ module Dry
13
20
 
14
21
  def visit(node)
15
22
  type, body = node
16
- send(:"visit_#{ type }", body)
23
+ send(:"visit_#{type}", body)
17
24
  end
18
25
 
19
26
  def visit_constrained(node)
20
- definition, rule, meta = node
21
- Types::Constrained.new(visit(definition), rule: visit_rule(rule)).meta(meta)
27
+ nominal, rule = node
28
+ type = visit(nominal)
29
+ type.constrained_type.new(type, rule: visit_rule(rule))
22
30
  end
23
31
 
24
32
  def visit_constructor(node)
25
- definition, fn_register_name, meta = node
26
- fn = Dry::Types::FnContainer[fn_register_name]
27
- primitive = visit(definition)
28
- Types::Constructor.new(primitive, meta: meta, fn: fn)
33
+ nominal, fn = node
34
+ primitive = visit(nominal)
35
+ primitive.constructor(compile_fn(fn))
29
36
  end
30
37
 
31
- def visit_safe(node)
32
- ast, meta = node
33
- Types::Safe.new(visit(ast), meta: meta)
38
+ def visit_lax(node)
39
+ Types::Lax.new(visit(node))
34
40
  end
41
+ deprecate(:visit_safe, :visit_lax)
35
42
 
36
- def visit_definition(node)
43
+ def visit_nominal(node)
37
44
  type, meta = node
45
+ nominal_name = "nominal.#{Types.identifier(type)}"
38
46
 
39
- if registry.registered?(type)
40
- registry[type].meta(meta)
47
+ if registry.registered?(nominal_name)
48
+ registry[nominal_name].meta(meta)
41
49
  else
42
- Definition.new(type, meta: meta)
50
+ Nominal.new(type, meta: meta)
43
51
  end
44
52
  end
45
53
 
@@ -55,65 +63,72 @@ module Dry
55
63
  def visit_array(node)
56
64
  member, meta = node
57
65
  member = member.is_a?(Class) ? member : visit(member)
58
- registry['array'].of(member).meta(meta)
66
+ registry["nominal.array"].of(member).meta(meta)
59
67
  end
60
68
 
61
69
  def visit_hash(node)
62
- constructor, schema, meta = node
63
- merge_with('hash', constructor, schema).meta(meta)
70
+ opts, meta = node
71
+ registry["nominal.hash"].with(**opts, meta: meta)
64
72
  end
65
73
 
66
- def visit_hash_schema(node)
67
- schema, meta = node
68
- merge_with_schema('hash', schema).meta(meta)
74
+ def visit_schema(node)
75
+ keys, options, meta = node
76
+ registry["nominal.hash"].schema(keys.map { |key| visit(key) }).with(**options, meta: meta)
69
77
  end
70
78
 
71
79
  def visit_json_hash(node)
72
- schema, meta = node
73
- merge_with('json.hash', :symbolized, schema).meta(meta)
80
+ keys, meta = node
81
+ registry["json.hash"].schema(keys.map { |key| visit(key) }, meta)
74
82
  end
75
83
 
76
84
  def visit_json_array(node)
77
85
  member, meta = node
78
- registry['json.array'].of(visit(member)).meta(meta)
86
+ registry["json.array"].of(visit(member)).meta(meta)
79
87
  end
80
88
 
81
89
  def visit_params_hash(node)
82
- schema, meta = node
83
- merge_with('params.hash', :symbolized, schema).meta(meta)
90
+ keys, meta = node
91
+ registry["params.hash"].schema(keys.map { |key| visit(key) }, meta)
84
92
  end
85
93
 
86
94
  def visit_params_array(node)
87
95
  member, meta = node
88
- registry['params.array'].of(visit(member)).meta(meta)
96
+ registry["params.array"].of(visit(member)).meta(meta)
89
97
  end
90
98
 
91
- def visit_member(node)
92
- name, type = node
93
- { name => visit(type) }
99
+ def visit_key(node)
100
+ name, required, type = node
101
+ Schema::Key.new(visit(type), name, required: required)
94
102
  end
95
103
 
96
104
  def visit_enum(node)
97
- type, mapping, meta = node
98
- Enum.new(visit(type), mapping: mapping, meta: meta)
105
+ type, mapping = node
106
+ Enum.new(visit(type), mapping: mapping)
99
107
  end
100
108
 
101
109
  def visit_map(node)
102
110
  key_type, value_type, meta = node
103
- registry['hash'].map(visit(key_type), visit(value_type)).meta(meta)
111
+ registry["nominal.hash"].map(visit(key_type), visit(value_type)).meta(meta)
104
112
  end
105
113
 
106
- def merge_with(hash_id, constructor, schema)
107
- registry[hash_id].schema(
108
- schema.map { |key| visit(key) }.reduce({}, :update),
109
- constructor
110
- )
114
+ def visit_any(meta)
115
+ registry["any"].meta(meta)
111
116
  end
112
117
 
113
- def merge_with_schema(hash_id, schema)
114
- registry[hash_id].instantiate(
115
- schema.map { |key| visit(key) }.reduce({}, :update)
116
- )
118
+ def compile_fn(fn)
119
+ type, *node = fn
120
+
121
+ case type
122
+ when :id
123
+ Dry::Types::FnContainer[node.fetch(0)]
124
+ when :callable
125
+ node.fetch(0)
126
+ when :method
127
+ target, method = node
128
+ target.method(method)
129
+ else
130
+ raise ArgumentError, "Cannot build callable from #{fn.inspect}"
131
+ end
117
132
  end
118
133
  end
119
134
  end