dry-schema 0.1.0 → 1.0.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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +166 -0
  3. data/LICENSE +1 -1
  4. data/README.md +13 -4
  5. data/config/errors.yml +59 -54
  6. data/lib/dry/schema/compiler.rb +24 -0
  7. data/lib/dry/schema/config.rb +23 -27
  8. data/lib/dry/schema/constants.rb +27 -3
  9. data/lib/dry/schema/dsl.rb +36 -49
  10. data/lib/dry/schema/extensions/hints/compiler_methods.rb +19 -0
  11. data/lib/dry/schema/extensions/hints/message_compiler_methods.rb +84 -0
  12. data/lib/dry/schema/extensions/hints/message_set_methods.rb +44 -0
  13. data/lib/dry/schema/extensions/hints/result_methods.rb +47 -0
  14. data/lib/dry/schema/extensions/hints.rb +56 -0
  15. data/lib/dry/schema/extensions/monads.rb +15 -4
  16. data/lib/dry/schema/extensions.rb +6 -0
  17. data/lib/dry/schema/json.rb +4 -2
  18. data/lib/dry/schema/key.rb +8 -10
  19. data/lib/dry/schema/key_coercer.rb +6 -2
  20. data/lib/dry/schema/key_map.rb +5 -4
  21. data/lib/dry/schema/macros/array.rb +39 -0
  22. data/lib/dry/schema/macros/core.rb +2 -0
  23. data/lib/dry/schema/macros/dsl.rb +149 -4
  24. data/lib/dry/schema/macros/each.rb +12 -1
  25. data/lib/dry/schema/macros/filled.rb +14 -7
  26. data/lib/dry/schema/macros/hash.rb +6 -32
  27. data/lib/dry/schema/macros/key.rb +38 -60
  28. data/lib/dry/schema/macros/maybe.rb +5 -3
  29. data/lib/dry/schema/macros/optional.rb +8 -1
  30. data/lib/dry/schema/macros/required.rb +3 -1
  31. data/lib/dry/schema/macros/schema.rb +46 -0
  32. data/lib/dry/schema/macros/value.rb +18 -2
  33. data/lib/dry/schema/macros.rb +4 -0
  34. data/lib/dry/schema/message/or.rb +54 -0
  35. data/lib/dry/schema/message.rb +56 -72
  36. data/lib/dry/schema/message_compiler/visitor_opts.rb +28 -1
  37. data/lib/dry/schema/message_compiler.rb +85 -74
  38. data/lib/dry/schema/message_set.rb +82 -50
  39. data/lib/dry/schema/messages/abstract.rb +84 -44
  40. data/lib/dry/schema/messages/i18n.rb +45 -9
  41. data/lib/dry/schema/messages/namespaced.rb +22 -5
  42. data/lib/dry/schema/messages/template.rb +68 -0
  43. data/lib/dry/schema/messages/yaml.rb +76 -22
  44. data/lib/dry/schema/messages.rb +19 -24
  45. data/lib/dry/schema/namespaced_rule.rb +37 -0
  46. data/lib/dry/schema/params.rb +4 -2
  47. data/lib/dry/schema/path.rb +102 -0
  48. data/lib/dry/schema/predicate.rb +15 -0
  49. data/lib/dry/schema/predicate_inferrer.rb +116 -23
  50. data/lib/dry/schema/predicate_registry.rb +2 -0
  51. data/lib/dry/schema/processor.rb +70 -39
  52. data/lib/dry/schema/result.rb +16 -31
  53. data/lib/dry/schema/rule_applier.rb +10 -3
  54. data/lib/dry/schema/trace.rb +16 -3
  55. data/lib/dry/schema/type_registry.rb +5 -3
  56. data/lib/dry/schema/types.rb +3 -1
  57. data/lib/dry/schema/value_coercer.rb +10 -4
  58. data/lib/dry/schema/version.rb +3 -1
  59. data/lib/dry/schema.rb +39 -13
  60. data/lib/dry-schema.rb +2 -0
  61. metadata +43 -47
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/schema/processor'
2
4
 
3
5
  module Dry
@@ -5,7 +7,7 @@ module Dry
5
7
  # Params schema type
6
8
  #
7
9
  # @see Processor
8
- # @see Schema.Params
10
+ # @see Schema#Params
9
11
  #
10
12
  # @api public
11
13
  class Params < Processor
@@ -13,4 +15,4 @@ module Dry
13
15
  config.type_registry = config.type_registry.namespaced(:params)
14
16
  end
15
17
  end
16
- end
18
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/schema/constants'
4
+
5
+ module Dry
6
+ module Schema
7
+ # Path represents a list of keys in a hash
8
+ #
9
+ # @api private
10
+ class Path
11
+ include Comparable
12
+ include Enumerable
13
+
14
+ # @return [Array<Symbol>]
15
+ attr_reader :keys
16
+
17
+ alias_method :root, :first
18
+
19
+ # Coerce a spec into a path object
20
+ #
21
+ # @param [Path, Symbol, String, Hash, Array<Symbol>] spec
22
+ #
23
+ # @return [Path]
24
+ #
25
+ # @api private
26
+ def self.[](spec)
27
+ case spec
28
+ when Symbol, Array
29
+ new(Array[*spec])
30
+ when String
31
+ new(spec.split(DOT).map(&:to_sym))
32
+ when Hash
33
+ new(keys_from_hash(spec))
34
+ when Path
35
+ spec
36
+ else
37
+ raise ArgumentError, '+spec+ must be either a Symbol, Array, Hash or a Path'
38
+ end
39
+ end
40
+
41
+ # Extract a list of keys from a hash
42
+ #
43
+ # @api private
44
+ def self.keys_from_hash(hash)
45
+ hash.inject([]) { |a, (k, v)|
46
+ v.is_a?(Hash) ? a.concat([k, *keys_from_hash(v)]) : a.concat([k, v])
47
+ }
48
+ end
49
+
50
+ # @api private
51
+ def initialize(keys)
52
+ @keys = keys
53
+ end
54
+
55
+ # @api private
56
+ def each(&block)
57
+ keys.each(&block)
58
+ end
59
+
60
+ # @api private
61
+ def index(key)
62
+ keys.index(key)
63
+ end
64
+
65
+ # @api private
66
+ def include?(other)
67
+ return false unless same_root?(other)
68
+ return false if index? && other.index? && !last.equal?(other.last)
69
+ self >= other
70
+ end
71
+
72
+ # @api private
73
+ def <=>(other)
74
+ raise ArgumentError, "Can't compare paths from different branches" unless same_root?(other)
75
+
76
+ return 0 if keys.eql?(other.keys)
77
+
78
+ res =
79
+ map { |key| (idx = other.index(key)) && keys[idx].equal?(key) }
80
+ .compact
81
+ .reject { |value| value.equal?(false) }
82
+
83
+ res.size < count ? 1 : -1
84
+ end
85
+
86
+ # @api private
87
+ def last
88
+ keys.last
89
+ end
90
+
91
+ # @api private
92
+ def same_root?(other)
93
+ root.equal?(other.root)
94
+ end
95
+
96
+ # @api private
97
+ def index?
98
+ last.is_a?(Integer)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/equalizer'
2
4
  require 'dry/logic/operators'
3
5
 
@@ -19,6 +21,10 @@ module Dry
19
21
  @predicate = predicate
20
22
  end
21
23
 
24
+ # Dump negated predicate to an AST
25
+ #
26
+ # @return [Array]
27
+ #
22
28
  # @api private
23
29
  def to_ast(*args)
24
30
  [:not, predicate.to_ast(*args)]
@@ -51,6 +57,9 @@ module Dry
51
57
 
52
58
  # Negate a predicate
53
59
  #
60
+ # @example
61
+ # required(:name).value(:string) { !empty? }
62
+ #
54
63
  # @return [Negation]
55
64
  #
56
65
  # @api public
@@ -65,11 +74,17 @@ module Dry
65
74
  end
66
75
  end
67
76
 
77
+ # Compile predicate to a rule object
78
+ #
68
79
  # @api private
69
80
  def to_rule
70
81
  compiler.visit(to_ast)
71
82
  end
72
83
 
84
+ # Dump predicate to an AST
85
+ #
86
+ # @return [Array]
87
+ #
73
88
  # @api private
74
89
  def to_ast(*)
75
90
  [:predicate, [name, compiler.predicates.arg_list(name, *args)]]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/core/cache'
2
4
 
3
5
  module Dry
@@ -9,40 +11,131 @@ module Dry
9
11
  class PredicateInferrer
10
12
  extend Dry::Core::Cache
11
13
 
12
- TYPE_TO_PREDICATE = Hash.new do |hash, type|
13
- primitive = type.meta[:maybe] ? type.right.primitive : type.primitive
14
-
15
- if hash.key?(primitive)
16
- hash[primitive]
17
- else
18
- :"#{primitive.name.split('::').last.downcase}?"
19
- end
20
- end
21
-
22
- TYPE_TO_PREDICATE.update(
14
+ TYPE_TO_PREDICATE = {
15
+ DateTime => :date_time?,
23
16
  FalseClass => :false?,
24
17
  Integer => :int?,
25
18
  NilClass => :nil?,
26
19
  String => :str?,
27
20
  TrueClass => :true?
28
- ).freeze
21
+ }.freeze
22
+
23
+ REDUCED_TYPES = {
24
+ %i[true? false?] => :bool?
25
+ }.freeze
26
+
27
+ # Compiler reduces type AST into a list of predicates
28
+ #
29
+ # @api private
30
+ class Compiler
31
+ # @return [PredicateRegistry]
32
+ # @api private
33
+ attr_reader :registry
34
+
35
+ # @api private
36
+ def initialize(registry)
37
+ @registry = registry
38
+ end
39
+
40
+ # @api private
41
+ def infer_predicate(type)
42
+ TYPE_TO_PREDICATE.fetch(type) { :"#{type.name.split('::').last.downcase}?" }
43
+ end
44
+
45
+ # @api private
46
+ def visit(node)
47
+ meth, rest = node
48
+ public_send(:"visit_#{meth}", rest)
49
+ end
50
+
51
+ # @api private
52
+ def visit_nominal(node)
53
+ type = node[0]
54
+ predicate = infer_predicate(type)
55
+
56
+ if registry.key?(predicate)
57
+ predicate
58
+ else
59
+ { type?: type }
60
+ end
61
+ end
62
+
63
+ # @api private
64
+ def visit_hash(_)
65
+ :hash?
66
+ end
67
+
68
+ # @api private
69
+ def visit_array(_)
70
+ :array?
71
+ end
72
+
73
+ # @api private
74
+ def visit_lax(node)
75
+ visit(node)
76
+ end
77
+
78
+ # @api private
79
+ def visit_constructor(node)
80
+ other, * = node
81
+ visit(other)
82
+ end
83
+
84
+ # @api private
85
+ def visit_enum(node)
86
+ other, * = node
87
+ visit(other)
88
+ end
89
+
90
+ # @api private
91
+ def visit_sum(node)
92
+ left, right = node
93
+
94
+ predicates = [visit(left), visit(right)]
95
+
96
+ if predicates.first == :nil?
97
+ predicates[1..predicates.size - 1]
98
+ else
99
+ predicates
100
+ end
101
+ end
102
+
103
+ # @api private
104
+ def visit_constrained(node)
105
+ other, * = node
106
+ visit(other)
107
+ end
108
+
109
+ # @api private
110
+ def visit_any(_)
111
+ nil
112
+ end
113
+ end
114
+
115
+ # @return [Compiler]
116
+ # @api private
117
+ attr_reader :compiler
118
+
119
+ # @api private
120
+ def initialize(registry)
121
+ @compiler = Compiler.new(registry)
122
+ end
29
123
 
30
124
  # Infer predicate identifier from the provided type
31
125
  #
32
126
  # @return [Symbol]
33
127
  #
34
128
  # @api private
35
- def self.[](type)
36
- fetch_or_store(type.hash) {
37
- predicates =
38
- if type.is_a?(Dry::Types::Sum) && !type.meta[:maybe]
39
- [self[type.left], self[type.right]]
40
- else
41
- TYPE_TO_PREDICATE[type]
42
- end
43
-
44
- Array(predicates).flatten
45
- }
129
+ def [](type)
130
+ self.class.fetch_or_store(type.hash) do
131
+ predicates = compiler.visit(type.to_ast)
132
+
133
+ if predicates.is_a?(Hash)
134
+ predicates
135
+ else
136
+ Array(REDUCED_TYPES[predicates] || predicates).flatten
137
+ end
138
+ end
46
139
  end
47
140
  end
48
141
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/predicates'
2
4
 
3
5
  module Dry
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/configurable'
2
4
  require 'dry/initializer'
3
5
 
@@ -15,7 +17,7 @@ module Dry
15
17
  # 1. Prepare input hash using a key map
16
18
  # 2. Apply pre-coercion filtering rules (optional step, used only when `filter` was used)
17
19
  # 3. Apply value coercions based on type specifications
18
- # 4. Apply rules
20
+ # 4. Apply rules
19
21
  #
20
22
  # @see Params
21
23
  # @see JSON
@@ -30,42 +32,42 @@ module Dry
30
32
 
31
33
  param :steps, default: -> { EMPTY_ARRAY.dup }
32
34
 
33
- # Define a schema for your processor class
34
- #
35
- # @see Params
36
- # @see JSON
37
- #
38
- # @return [Class]
39
- #
40
- # @api public
41
- def self.define(&block)
42
- @__definition__ ||= DSL.new(
43
- processor_type: self, parent: superclass.definition, **config, &block
44
- )
45
- self
46
- end
35
+ class << self
36
+ # Return DSL configured via #define
37
+ #
38
+ # @return [DSL]
39
+ # @api private
40
+ attr_reader :definition
47
41
 
48
- # Return DSL configured via #define
49
- #
50
- # @return [DSL]
51
- #
52
- # @api private
53
- def self.definition
54
- @__definition__
55
- end
42
+ # Define a schema for your processor class
43
+ #
44
+ # @see Schema#define
45
+ # @see Schema#Params
46
+ # @see Schema#JSON
47
+ #
48
+ # @return [Class]
49
+ #
50
+ # @api public
51
+ def define(&block)
52
+ @definition ||= DSL.new(
53
+ processor_type: self, parent: superclass.definition, **config, &block
54
+ )
55
+ self
56
+ end
56
57
 
57
- # Build a new processor object
58
- #
59
- # @return [Processor]
60
- #
61
- # @api public
62
- def self.new(&block)
63
- if block
64
- super.tap(&block)
65
- elsif definition
66
- definition.call
67
- else
68
- raise ArgumentError, 'Cannot create a schema without a definition'
58
+ # Build a new processor object
59
+ #
60
+ # @return [Processor]
61
+ #
62
+ # @api public
63
+ def new(&block)
64
+ if block
65
+ super.tap(&block)
66
+ elsif definition
67
+ definition.call
68
+ else
69
+ raise ArgumentError, 'Cannot create a schema without a definition'
70
+ end
69
71
  end
70
72
  end
71
73
 
@@ -94,6 +96,16 @@ module Dry
94
96
  end
95
97
  end
96
98
  end
99
+ alias_method :[], :call
100
+
101
+ # Return a proc that acts like a schema object
102
+ #
103
+ # @return [Proc]
104
+ #
105
+ # @api public
106
+ def to_proc
107
+ ->(input) { call(input) }
108
+ end
97
109
 
98
110
  # Return the key map
99
111
  #
@@ -101,7 +113,18 @@ module Dry
101
113
  #
102
114
  # @api public
103
115
  def key_map
104
- @__key_map__ ||= steps.detect { |s| s.is_a?(KeyCoercer) }.key_map
116
+ @key_map ||= steps.detect { |s| s.is_a?(KeyCoercer) }.key_map
117
+ end
118
+
119
+ # Return string represntation
120
+ #
121
+ # @return [String]
122
+ #
123
+ # @api public
124
+ def inspect
125
+ <<~STR.strip
126
+ #<#{self.class.name} keys=#{key_map.map(&:dump)} rules=#{rules.map { |k, v| [k, v.to_s] }.to_h}>
127
+ STR
105
128
  end
106
129
 
107
130
  # Return the type schema
@@ -110,7 +133,16 @@ module Dry
110
133
  #
111
134
  # @api private
112
135
  def type_schema
113
- @__type_schema__ ||= steps.detect { |s| s.is_a?(ValueCoercer) }.type_schema
136
+ @type_schema ||= steps.detect { |s| s.is_a?(ValueCoercer) }.type_schema
137
+ end
138
+
139
+ # Return the rules config
140
+ #
141
+ # @return [Dry::Types::Config]
142
+ #
143
+ # @api private
144
+ def config
145
+ @config ||= rule_applier.config
114
146
  end
115
147
 
116
148
  # Return AST representation of the rules
@@ -142,8 +174,7 @@ module Dry
142
174
  #
143
175
  # @api private
144
176
  def rule_applier
145
- # TODO: make this more explicit through class types
146
- @__rule_applier__ ||= steps.last
177
+ @rule_applier ||= steps.last
147
178
  end
148
179
  alias_method :to_rule, :rule_applier
149
180
  end
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/initializer'
2
4
  require 'dry/equalizer'
3
5
 
6
+ require 'dry/schema/path'
7
+
4
8
  module Dry
5
9
  module Schema
6
10
  # Processing result
@@ -15,8 +19,11 @@ module Dry
15
19
 
16
20
  # @api private
17
21
  param :output
22
+
23
+ # Dump result to a hash returning processed and validated data
24
+ #
25
+ # @return [Hash]
18
26
  alias_method :to_h, :output
19
- alias_method :to_hash, :output
20
27
 
21
28
  # @api private
22
29
  param :results, default: -> { EMPTY_ARRAY.dup }
@@ -66,15 +73,15 @@ module Dry
66
73
  output.key?(name)
67
74
  end
68
75
 
69
- # Check if a given key resulted in an error
76
+ # Check if there's an error for the provided spec
70
77
  #
71
- # @param [Symbol] name
78
+ # @param [Symbol, Hash<Symbol=>Symbol>] spec
72
79
  #
73
80
  # @return [Boolean]
74
81
  #
75
82
  # @api public
76
- def error?(name)
77
- errors.key?(name)
83
+ def error?(spec)
84
+ message_set.any? { |msg| Path[msg.path].include?(Path[spec]) }
78
85
  end
79
86
 
80
87
  # Check if the result is successful
@@ -99,33 +106,11 @@ module Dry
99
106
  #
100
107
  # @see #message_set
101
108
  #
102
- # @return [Hash<Symbol=>Array>]
109
+ # @return [MessageSet]
103
110
  #
104
111
  # @api public
105
112
  def errors(options = EMPTY_HASH)
106
- message_set(options.merge(hints: false)).dump
107
- end
108
-
109
- # Get all messages including hints
110
- #
111
- # @see #message_set
112
- #
113
- # @return [Hash<Symbol=>Array>]
114
- #
115
- # @api public
116
- def messages(options = EMPTY_HASH)
117
- message_set(options.merge(hints: true)).dump
118
- end
119
-
120
- # Get hints exclusively without errors
121
- #
122
- # @see #message_set
123
- #
124
- # @return [Hash<Symbol=>Array>]
125
- #
126
- # @api public
127
- def hints(options = EMPTY_HASH)
128
- message_set(options.merge(failures: false)).dump
113
+ message_set(options)
129
114
  end
130
115
 
131
116
  # Return the message set
@@ -148,7 +133,7 @@ module Dry
148
133
  #
149
134
  # @api public
150
135
  def inspect
151
- "#<#{self.class}#{to_h.inspect} errors=#{errors.inspect}>"
136
+ "#<#{self.class}#{to_h.inspect} errors=#{errors.to_h.inspect}>"
152
137
  end
153
138
 
154
139
  private
@@ -157,7 +142,7 @@ module Dry
157
142
  #
158
143
  # @api private
159
144
  def result_ast
160
- @__result__ast ||= results.map(&:to_ast)
145
+ @result_ast ||= results.map(&:to_ast)
161
146
  end
162
147
  end
163
148
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/initializer'
2
4
 
3
5
  require 'dry/schema/constants'
@@ -18,10 +20,10 @@ module Dry
18
20
  param :rules
19
21
 
20
22
  # @api private
21
- option :config, default: proc { Config.new }
23
+ option :config, default: -> { Config.new }
22
24
 
23
25
  # @api private
24
- option :message_compiler, default: proc { MessageCompiler.new(Messages.setup(config)) }
26
+ option :message_compiler, default: -> { MessageCompiler.new(Messages.setup(config.messages)) }
25
27
 
26
28
  # @api private
27
29
  def call(input)
@@ -29,6 +31,7 @@ module Dry
29
31
 
30
32
  rules.each do |name, rule|
31
33
  next if input.error?(name)
34
+
32
35
  result = rule.(input)
33
36
  results << result if result.failure?
34
37
  end
@@ -38,7 +41,11 @@ module Dry
38
41
 
39
42
  # @api private
40
43
  def to_ast
41
- [:set, rules.values.map(&:to_ast)]
44
+ if config.messages.namespace
45
+ [:namespace, [config.messages.namespace, [:set, rules.values.map(&:to_ast)]]]
46
+ else
47
+ [:set, rules.values.map(&:to_ast)]
48
+ end
42
49
  end
43
50
  end
44
51
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/schema/constants'
2
4
  require 'dry/schema/compiler'
3
5
  require 'dry/schema/predicate'
@@ -27,10 +29,15 @@ module Dry
27
29
  end
28
30
 
29
31
  # @api private
30
- def evaluate(*predicates, **opts, &block)
32
+ def evaluate(*predicates, **opts)
33
+ pred_opts = opts.dup
34
+ pred_opts.delete(:type_spec)
35
+
31
36
  predicates.each do |predicate|
32
37
  if predicate.respond_to?(:call)
33
38
  append(predicate)
39
+ elsif predicate.is_a?(::Hash)
40
+ evaluate_hash_predicates(predicate)
34
41
  elsif predicate.is_a?(::Array)
35
42
  append(predicate.map { |pred| __send__(pred) }.reduce(:|))
36
43
  else
@@ -38,10 +45,16 @@ module Dry
38
45
  end
39
46
  end
40
47
 
41
- opts.each do |predicate, *args|
48
+ evaluate_hash_predicates(pred_opts)
49
+
50
+ self
51
+ end
52
+
53
+ # @api private
54
+ def evaluate_hash_predicates(predicates)
55
+ predicates.each do |predicate, *args|
42
56
  append(__send__(predicate, *args))
43
57
  end
44
-
45
58
  self
46
59
  end
47
60
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/schema/constants'
2
4
  require 'dry/schema/types'
3
5
 
@@ -16,12 +18,12 @@ module Dry
16
18
  attr_reader :namespace
17
19
 
18
20
  # @api private
19
- def self.new(types = Dry::Types, namespace = nil)
21
+ def self.new(types = Dry::Types, namespace = :nominal)
20
22
  super
21
23
  end
22
24
 
23
25
  # @api private
24
- def initialize(types, namespace = nil)
26
+ def initialize(types, namespace = :nominal)
25
27
  @types = types
26
28
  @namespace = namespace
27
29
  end
@@ -39,4 +41,4 @@ module Dry
39
41
  end
40
42
  end
41
43
  end
42
- end
44
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/types'
2
4
 
3
5
  module Dry
@@ -6,7 +8,7 @@ module Dry
6
8
  #
7
9
  # @api public
8
10
  module Types
9
- include Dry::Types.module
11
+ include Dry.Types
10
12
  end
11
13
  end
12
14
  end