dry-schema 1.4.2 → 1.5.3

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +217 -78
  3. data/LICENSE +1 -1
  4. data/README.md +4 -4
  5. data/config/errors.yml +4 -0
  6. data/dry-schema.gemspec +46 -0
  7. data/lib/dry-schema.rb +1 -1
  8. data/lib/dry/schema.rb +19 -6
  9. data/lib/dry/schema/compiler.rb +5 -5
  10. data/lib/dry/schema/config.rb +15 -6
  11. data/lib/dry/schema/constants.rb +16 -7
  12. data/lib/dry/schema/dsl.rb +87 -27
  13. data/lib/dry/schema/extensions.rb +10 -2
  14. data/lib/dry/schema/extensions/hints.rb +15 -8
  15. data/lib/dry/schema/extensions/hints/message_compiler_methods.rb +1 -1
  16. data/lib/dry/schema/extensions/hints/message_set_methods.rb +0 -47
  17. data/lib/dry/schema/extensions/info.rb +27 -0
  18. data/lib/dry/schema/extensions/info/schema_compiler.rb +105 -0
  19. data/lib/dry/schema/extensions/monads.rb +1 -1
  20. data/lib/dry/schema/extensions/struct.rb +32 -0
  21. data/lib/dry/schema/json.rb +1 -1
  22. data/lib/dry/schema/key.rb +16 -1
  23. data/lib/dry/schema/key_coercer.rb +4 -4
  24. data/lib/dry/schema/key_map.rb +9 -4
  25. data/lib/dry/schema/key_validator.rb +67 -0
  26. data/lib/dry/schema/macros.rb +8 -8
  27. data/lib/dry/schema/macros/array.rb +17 -4
  28. data/lib/dry/schema/macros/core.rb +9 -4
  29. data/lib/dry/schema/macros/dsl.rb +34 -19
  30. data/lib/dry/schema/macros/each.rb +4 -4
  31. data/lib/dry/schema/macros/filled.rb +5 -5
  32. data/lib/dry/schema/macros/hash.rb +21 -3
  33. data/lib/dry/schema/macros/key.rb +9 -9
  34. data/lib/dry/schema/macros/maybe.rb +4 -5
  35. data/lib/dry/schema/macros/optional.rb +1 -1
  36. data/lib/dry/schema/macros/required.rb +1 -1
  37. data/lib/dry/schema/macros/schema.rb +23 -2
  38. data/lib/dry/schema/macros/value.rb +34 -7
  39. data/lib/dry/schema/message.rb +35 -9
  40. data/lib/dry/schema/message/or.rb +18 -39
  41. data/lib/dry/schema/message/or/abstract.rb +28 -0
  42. data/lib/dry/schema/message/or/multi_path.rb +37 -0
  43. data/lib/dry/schema/message/or/single_path.rb +64 -0
  44. data/lib/dry/schema/message_compiler.rb +55 -19
  45. data/lib/dry/schema/message_compiler/visitor_opts.rb +2 -2
  46. data/lib/dry/schema/message_set.rb +26 -37
  47. data/lib/dry/schema/messages.rb +6 -6
  48. data/lib/dry/schema/messages/abstract.rb +54 -56
  49. data/lib/dry/schema/messages/i18n.rb +29 -27
  50. data/lib/dry/schema/messages/namespaced.rb +12 -2
  51. data/lib/dry/schema/messages/template.rb +19 -44
  52. data/lib/dry/schema/messages/yaml.rb +61 -14
  53. data/lib/dry/schema/params.rb +1 -1
  54. data/lib/dry/schema/path.rb +44 -5
  55. data/lib/dry/schema/predicate.rb +4 -2
  56. data/lib/dry/schema/predicate_inferrer.rb +4 -184
  57. data/lib/dry/schema/predicate_registry.rb +2 -2
  58. data/lib/dry/schema/primitive_inferrer.rb +16 -0
  59. data/lib/dry/schema/processor.rb +49 -28
  60. data/lib/dry/schema/processor_steps.rb +50 -27
  61. data/lib/dry/schema/result.rb +52 -5
  62. data/lib/dry/schema/rule_applier.rb +7 -7
  63. data/lib/dry/schema/step.rb +79 -0
  64. data/lib/dry/schema/trace.rb +5 -4
  65. data/lib/dry/schema/type_container.rb +3 -3
  66. data/lib/dry/schema/type_registry.rb +2 -2
  67. data/lib/dry/schema/types.rb +1 -1
  68. data/lib/dry/schema/value_coercer.rb +2 -2
  69. data/lib/dry/schema/version.rb +1 -1
  70. metadata +21 -7
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/initializer'
3
+ require "dry/initializer"
4
+
5
+ require "dry/schema/constants"
6
+ require "dry/schema/step"
4
7
 
5
8
  module Dry
6
9
  module Schema
@@ -20,8 +23,6 @@ module Dry
20
23
  class ProcessorSteps
21
24
  extend Dry::Initializer
22
25
 
23
- STEPS_IN_ORDER = %i[key_coercer filter_schema value_coercer rule_applier].freeze
24
-
25
26
  option :steps, default: -> { EMPTY_HASH.dup }
26
27
  option :before_steps, default: -> { EMPTY_HASH.dup }
27
28
  option :after_steps, default: -> { EMPTY_HASH.dup }
@@ -35,13 +36,29 @@ module Dry
35
36
  # @api public
36
37
  def call(result)
37
38
  STEPS_IN_ORDER.each do |name|
38
- before_steps[name]&.each { |step| process_step(step, result) }
39
- process_step(steps[name], result)
40
- after_steps[name]&.each { |step| process_step(step, result) }
39
+ before_steps[name]&.each { |step| step&.(result) }
40
+ steps[name]&.(result)
41
+ after_steps[name]&.each { |step| step&.(result) }
41
42
  end
43
+
42
44
  result
43
45
  end
44
46
 
47
+ # @api private
48
+ def rule_applier
49
+ @rule_applier ||= steps[:rule_applier].executor
50
+ end
51
+
52
+ # @api private
53
+ def key_map
54
+ @key_map ||= self[:key_coercer].executor.key_map
55
+ end
56
+
57
+ # @api private
58
+ def type_schema
59
+ @type_schema ||= steps[:value_coercer].executor.type_schema
60
+ end
61
+
45
62
  # Returns step by name
46
63
  #
47
64
  # @param [Symbol] name The step name
@@ -57,8 +74,7 @@ module Dry
57
74
  #
58
75
  # @api public
59
76
  def []=(name, value)
60
- validate_step_name(name)
61
- steps[name] = value
77
+ steps[name] = Step.new(type: :core, name: name, executor: value)
62
78
  end
63
79
 
64
80
  # Add passed block before mentioned step
@@ -69,9 +85,8 @@ module Dry
69
85
  #
70
86
  # @api public
71
87
  def after(name, &block)
72
- validate_step_name(name)
73
88
  after_steps[name] ||= EMPTY_ARRAY.dup
74
- after_steps[name] << block.to_proc
89
+ after_steps[name] << Step.new(type: :after, name: name, executor: block)
75
90
  self
76
91
  end
77
92
 
@@ -83,33 +98,41 @@ module Dry
83
98
  #
84
99
  # @api public
85
100
  def before(name, &block)
86
- validate_step_name(name)
87
101
  before_steps[name] ||= EMPTY_ARRAY.dup
88
- before_steps[name] << block.to_proc
102
+ before_steps[name] << Step.new(type: :before, name: name, executor: block)
89
103
  self
90
104
  end
91
105
 
92
- # @api private
93
- def process_step(step, result)
94
- return unless step
95
-
96
- output = step.(result)
97
- result.replace(output) if output.is_a?(::Hash)
106
+ # Stacks callback steps and returns new ProcessorSteps instance
107
+ #
108
+ # @param [ProcessorSteps] other
109
+ #
110
+ # @return [ProcessorSteps]
111
+ #
112
+ # @api public
113
+ def merge(other)
114
+ ProcessorSteps.new(
115
+ before_steps: merge_callbacks(before_steps, other.before_steps),
116
+ after_steps: merge_callbacks(after_steps, other.after_steps)
117
+ )
98
118
  end
99
119
 
100
120
  # @api private
101
- def validate_step_name(name)
102
- return if STEPS_IN_ORDER.include?(name)
103
-
104
- raise ArgumentError, "Undefined step name #{name}. Available names: #{STEPS_IN_ORDER}"
121
+ def merge_callbacks(left, right)
122
+ left.merge(right) do |_key, oldval, newval|
123
+ oldval + newval
124
+ end
105
125
  end
106
126
 
107
127
  # @api private
108
- def initialize_copy(source)
109
- super
110
- @steps = source.steps.dup
111
- @before_steps = source.before_steps.dup
112
- @after_steps = source.after_steps.dup
128
+ def import_callbacks(path, other)
129
+ other.before_steps.each do |name, steps|
130
+ (before_steps[name] ||= []).concat(steps.map { |step| step.scoped(path) })
131
+ end
132
+
133
+ other.after_steps.each do |name, steps|
134
+ (after_steps[name] ||= []).concat(steps.map { |step| step.scoped(path) })
135
+ end
113
136
  end
114
137
  end
115
138
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/initializer'
4
- require 'dry/equalizer'
3
+ require "dry/initializer"
4
+ require "dry/equalizer"
5
5
 
6
- require 'dry/schema/path'
6
+ require "dry/schema/path"
7
7
 
8
8
  module Dry
9
9
  module Schema
@@ -31,13 +31,44 @@ module Dry
31
31
  # @api private
32
32
  option :message_compiler
33
33
 
34
+ # @api private
35
+ option :parent, default: -> { nil }
36
+
34
37
  # @api private
35
38
  def self.new(*, **)
36
39
  result = super
37
- yield(result)
40
+ yield(result) if block_given?
38
41
  result.freeze
39
42
  end
40
43
 
44
+ # Return a new result scoped to a specific path
45
+ #
46
+ # @param path [Symbol, Array, Path]
47
+ #
48
+ # @return [Result]
49
+ #
50
+ # @api private
51
+ def at(path, &block)
52
+ new(Path[path].reduce(output) { |a, e| a[e] }, parent: self, &block)
53
+ end
54
+
55
+ # @api private
56
+ def new(output, **opts, &block)
57
+ self.class.new(
58
+ output,
59
+ message_compiler: message_compiler,
60
+ results: results,
61
+ **opts,
62
+ &block
63
+ )
64
+ end
65
+
66
+ # @api private
67
+ def update(hash)
68
+ output.update(hash)
69
+ self
70
+ end
71
+
41
72
  # @api private
42
73
  def replace(hash)
43
74
  @output = hash
@@ -90,7 +121,7 @@ module Dry
90
121
  #
91
122
  # @api public
92
123
  def success?
93
- results.empty?
124
+ result_ast.empty?
94
125
  end
95
126
 
96
127
  # Check if the result is not successful
@@ -136,6 +167,22 @@ module Dry
136
167
  "#<#{self.class}#{to_h.inspect} errors=#{errors.to_h.inspect}>"
137
168
  end
138
169
 
170
+ if RUBY_VERSION >= "2.7"
171
+ # Pattern matching support
172
+ #
173
+ # @api private
174
+ def deconstruct_keys(_)
175
+ output
176
+ end
177
+ end
178
+
179
+ # Add a new error AST node
180
+ #
181
+ # @api private
182
+ def add_error(node)
183
+ result_ast << node
184
+ end
185
+
139
186
  private
140
187
 
141
188
  # A list of failure ASTs produced by rule result objects
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/initializer'
3
+ require "dry/initializer"
4
4
 
5
- require 'dry/schema/constants'
6
- require 'dry/schema/config'
7
- require 'dry/schema/result'
8
- require 'dry/schema/messages'
9
- require 'dry/schema/message_compiler'
5
+ require "dry/schema/constants"
6
+ require "dry/schema/config"
7
+ require "dry/schema/result"
8
+ require "dry/schema/messages"
9
+ require "dry/schema/message_compiler"
10
10
 
11
11
  module Dry
12
12
  module Schema
@@ -20,7 +20,7 @@ module Dry
20
20
  param :rules
21
21
 
22
22
  # @api private
23
- option :config, default: -> { Config.new }
23
+ option :config, default: -> { Schema.config.dup }
24
24
 
25
25
  # @api private
26
26
  option :message_compiler, default: -> { MessageCompiler.new(Messages.setup(config.messages)) }
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/schema/constants"
4
+ require "dry/schema/path"
5
+
6
+ module Dry
7
+ module Schema
8
+ # @api private
9
+ class Step
10
+ # @api private
11
+ attr_reader :name
12
+
13
+ # @api private
14
+ attr_reader :type
15
+
16
+ # @api private
17
+ attr_reader :executor
18
+
19
+ # @api private
20
+ class Scoped
21
+ # @api private
22
+ attr_reader :path
23
+
24
+ # @api private
25
+ attr_reader :step
26
+
27
+ # @api private
28
+ def initialize(path, step)
29
+ @path = Path[path]
30
+ @step = step
31
+ end
32
+
33
+ # @api private
34
+ def scoped(new_path)
35
+ self.class.new(Path[[*new_path, *path]], step)
36
+ end
37
+
38
+ # @api private
39
+ def call(result)
40
+ result.at(path) do |scoped_result|
41
+ output = step.(scoped_result).to_h
42
+ target = Array(path)[0..-2].reduce(result) { |a, e| a[e] }
43
+
44
+ target.update(path.last => output)
45
+ end
46
+ end
47
+ end
48
+
49
+ # @api private
50
+ def initialize(type:, name:, executor:)
51
+ @type = type
52
+ @name = name
53
+ @executor = executor
54
+ validate_name(name)
55
+ end
56
+
57
+ # @api private
58
+ def call(result)
59
+ output = executor.(result)
60
+ result.replace(output) if output.is_a?(Hash)
61
+ output
62
+ end
63
+
64
+ # @api private
65
+ def scoped(path)
66
+ Scoped.new(path, self)
67
+ end
68
+
69
+ private
70
+
71
+ # @api private
72
+ def validate_name(name)
73
+ return if STEPS_IN_ORDER.include?(name)
74
+
75
+ raise ArgumentError, "Undefined step name #{name}. Available names: #{STEPS_IN_ORDER}"
76
+ end
77
+ end
78
+ end
79
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/constants'
4
- require 'dry/schema/compiler'
5
- require 'dry/schema/predicate'
3
+ require "dry/schema/constants"
4
+ require "dry/schema/compiler"
5
+ require "dry/schema/predicate"
6
6
 
7
7
  module Dry
8
8
  module Schema
@@ -29,8 +29,9 @@ module Dry
29
29
  end
30
30
 
31
31
  # @api private
32
- def evaluate(*args, type_spec: ::Dry::Schema::Undefined, **opts)
32
+ def evaluate(*args, **opts)
33
33
  predicates = opts.empty? ? args : args.push(opts)
34
+
34
35
  evaluate_predicates(predicates).each do |rule|
35
36
  append(rule)
36
37
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/container'
4
- require 'dry/types'
3
+ require "dry/container"
4
+ require "dry/types"
5
5
 
6
6
  module Dry
7
7
  module Schema
@@ -21,7 +21,7 @@ module Dry
21
21
  merge(types_container)
22
22
  end
23
23
 
24
- alias registered? key?
24
+ alias_method :registered?, :key?
25
25
  end
26
26
  end
27
27
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/constants'
4
- require 'dry/schema/types'
3
+ require "dry/schema/constants"
4
+ require "dry/schema/types"
5
5
 
6
6
  module Dry
7
7
  module Schema
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/types'
3
+ require "dry/types"
4
4
 
5
5
  module Dry
6
6
  module Schema
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/equalizer'
4
- require 'dry/initializer'
3
+ require "dry/equalizer"
4
+ require "dry/initializer"
5
5
 
6
6
  module Dry
7
7
  module Schema
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module Schema
5
- VERSION = '1.4.2'
5
+ VERSION = "1.5.3"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-19 00:00:00.000000000 Z
11
+ date: 2020-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -106,14 +106,14 @@ dependencies:
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '1.2'
109
+ version: '1.4'
110
110
  type: :runtime
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: '1.2'
116
+ version: '1.4'
117
117
  - !ruby/object:Gem::Dependency
118
118
  name: bundler
119
119
  requirement: !ruby/object:Gem::Requirement
@@ -170,6 +170,7 @@ files:
170
170
  - LICENSE
171
171
  - README.md
172
172
  - config/errors.yml
173
+ - dry-schema.gemspec
173
174
  - lib/dry-schema.rb
174
175
  - lib/dry/schema.rb
175
176
  - lib/dry/schema/compiler.rb
@@ -182,11 +183,15 @@ files:
182
183
  - lib/dry/schema/extensions/hints/message_compiler_methods.rb
183
184
  - lib/dry/schema/extensions/hints/message_set_methods.rb
184
185
  - lib/dry/schema/extensions/hints/result_methods.rb
186
+ - lib/dry/schema/extensions/info.rb
187
+ - lib/dry/schema/extensions/info/schema_compiler.rb
185
188
  - lib/dry/schema/extensions/monads.rb
189
+ - lib/dry/schema/extensions/struct.rb
186
190
  - lib/dry/schema/json.rb
187
191
  - lib/dry/schema/key.rb
188
192
  - lib/dry/schema/key_coercer.rb
189
193
  - lib/dry/schema/key_map.rb
194
+ - lib/dry/schema/key_validator.rb
190
195
  - lib/dry/schema/macros.rb
191
196
  - lib/dry/schema/macros/array.rb
192
197
  - lib/dry/schema/macros/core.rb
@@ -202,6 +207,9 @@ files:
202
207
  - lib/dry/schema/macros/value.rb
203
208
  - lib/dry/schema/message.rb
204
209
  - lib/dry/schema/message/or.rb
210
+ - lib/dry/schema/message/or/abstract.rb
211
+ - lib/dry/schema/message/or/multi_path.rb
212
+ - lib/dry/schema/message/or/single_path.rb
205
213
  - lib/dry/schema/message_compiler.rb
206
214
  - lib/dry/schema/message_compiler/visitor_opts.rb
207
215
  - lib/dry/schema/message_set.rb
@@ -217,20 +225,26 @@ files:
217
225
  - lib/dry/schema/predicate.rb
218
226
  - lib/dry/schema/predicate_inferrer.rb
219
227
  - lib/dry/schema/predicate_registry.rb
228
+ - lib/dry/schema/primitive_inferrer.rb
220
229
  - lib/dry/schema/processor.rb
221
230
  - lib/dry/schema/processor_steps.rb
222
231
  - lib/dry/schema/result.rb
223
232
  - lib/dry/schema/rule_applier.rb
233
+ - lib/dry/schema/step.rb
224
234
  - lib/dry/schema/trace.rb
225
235
  - lib/dry/schema/type_container.rb
226
236
  - lib/dry/schema/type_registry.rb
227
237
  - lib/dry/schema/types.rb
228
238
  - lib/dry/schema/value_coercer.rb
229
239
  - lib/dry/schema/version.rb
230
- homepage: https://github.com/dry-rb/dry-schema
240
+ homepage: https://dry-rb.org/gems/dry-schema
231
241
  licenses:
232
242
  - MIT
233
- metadata: {}
243
+ metadata:
244
+ allowed_push_host: https://rubygems.org
245
+ changelog_uri: https://github.com/dry-rb/dry-schema/blob/master/CHANGELOG.md
246
+ source_code_uri: https://github.com/dry-rb/dry-schema
247
+ bug_tracker_uri: https://github.com/dry-rb/dry-schema/issues
234
248
  post_install_message:
235
249
  rdoc_options: []
236
250
  require_paths:
@@ -239,7 +253,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
239
253
  requirements:
240
254
  - - ">="
241
255
  - !ruby/object:Gem::Version
242
- version: '2.4'
256
+ version: 2.4.0
243
257
  required_rubygems_version: !ruby/object:Gem::Requirement
244
258
  requirements:
245
259
  - - ">="