dry-validation 1.4.2 → 1.5.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea571eab29d2c8f50457cd8472a8f0d66da98bfda7f7fd1b697776532193b12d
4
- data.tar.gz: 0e45ff77064b269b25e4c9dbc84219dcc78e07390bb14e75dd2fa0ab24593096
3
+ metadata.gz: e10a66994158c6865c81439277cbbe57a629189cd20257c500658821dd520431
4
+ data.tar.gz: 00017e8bc8bf56b4890f5ff441f303556276892681e2c4d732a5ffda52bd54b2
5
5
  SHA512:
6
- metadata.gz: ddc760c72d530ceada83071591ebb7d214ed1c1fe7ec56486120170cd8b32a50916468dc086018820d3505a1eb14df1cbfe07a80b212da7c5f67c262dc47847b
7
- data.tar.gz: d06df105ce8e8a9ef8720dd6df9748585c9621a39bca8a73067e337ad153edbdbd052581be687a6df9bc0c62e16b1ad9fb8866014a0d4ba9a2c411c31d892610
6
+ metadata.gz: 4dcb6dba84761e057a2bb72e2b4fbd47bba9c568ee0b1bc52e6b5bd48bbd6e7dc804f418746b7d61b9a98fbaede7be075fe6f2b6610209ec934ca8857a1898c4
7
+ data.tar.gz: 4a5b3cdd5a6afea0b99a368dc2ce8b244474897692f753e00231b0c77ef4fa7f9ff363b538b1e968ca0a1aa54294efcf0ec44108acaddbcdb1fa267a02ab6b16
@@ -1,3 +1,68 @@
1
+ ## 1.5.4 2020-08-21
2
+
3
+
4
+ ### Fixed
5
+
6
+ - Full messages work correctly with rule failures now (issue #661 fixed via #662) (@stind)
7
+ - Providing a custom message template for array errors works correctly (issue #663 fixed via #665) (@tadeusz-niemiec)
8
+
9
+
10
+ [Compare v1.5.3...v1.5.4](https://github.com/dry-rb/dry-validation/compare/v1.5.3...v1.5.4)
11
+
12
+ ## 1.5.3 2020-07-27
13
+
14
+
15
+ ### Added
16
+
17
+ - You can now access current value's index via `rule(:foo).each do |index:|` (issue #606 done via #657) (@mrbongiolo)
18
+
19
+ ### Fixed
20
+
21
+ - Using `.each(:foo)` works as expected when there are errors related to other keys (issue #659 fixed via #660) (@solnic)
22
+
23
+ ### Changed
24
+
25
+ - `Result#error?` is now a public API and it takes into consideration both schema and rule errors (issue #655 fixed via #656) (@PragTob)
26
+
27
+ [Compare v1.5.2...v1.5.3](https://github.com/dry-rb/dry-validation/compare/v1.5.2...v1.5.3)
28
+
29
+ ## 1.5.2 2020-07-14
30
+
31
+
32
+ ### Fixed
33
+
34
+ - `key?` predicate in rules no longer crashes when the rule path points to a non-existent array value (issue #653 fixed via #654) (@solnic)
35
+
36
+
37
+ [Compare v1.5.1...v1.5.2](https://github.com/dry-rb/dry-validation/compare/v1.5.1...v1.5.2)
38
+
39
+ ## 1.5.1 2020-06-18
40
+
41
+
42
+ ### Fixed
43
+
44
+ - dry-monads no longer required for the `:hints` extension (@schokomarie)
45
+ - Using `full: true` option works as expected with custom rule messages (issue #618 fixed via #651) (@sirfilip)
46
+ - Using `locale: ...` option works as expected with hints (issue #589 fixed via 652) (@sirfilip)
47
+
48
+
49
+ [Compare v1.5.0...v1.5.1](https://github.com/dry-rb/dry-validation/compare/v1.5.0...v1.5.1)
50
+
51
+ ## 1.5.0 2020-03-11
52
+
53
+
54
+ ### Added
55
+
56
+ - `schema_error?` rule helper (@waiting-for-dev)
57
+ - `rule_error?` rule helper (@waiting-for-dev)
58
+
59
+ ### Changed
60
+
61
+ - dry-schema dependency was bumped to `~> 1.5` (@solnic)
62
+ - [internal] `KeyMap` patches have been removed since dry-schema now provides required functionality (@solnic)
63
+
64
+ [Compare v1.4.2...v1.5.0](https://github.com/dry-rb/dry-validation/compare/v1.4.2...v1.5.0)
65
+
1
66
  ## 1.4.2 2020-01-18
2
67
 
3
68
 
@@ -16,6 +16,8 @@ Gem::Specification.new do |spec|
16
16
  spec.description = spec.summary
17
17
  spec.homepage = 'https://dry-rb.org/gems/dry-validation'
18
18
  spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-validation.gemspec", "lib/**/*", "config/*.yml"]
19
+ spec.bindir = 'bin'
20
+ spec.executables = []
19
21
  spec.require_paths = ['lib']
20
22
 
21
23
  spec.metadata['allowed_push_host'] = 'https://rubygems.org'
@@ -23,7 +25,7 @@ Gem::Specification.new do |spec|
23
25
  spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-validation'
24
26
  spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-validation/issues'
25
27
 
26
- spec.required_ruby_version = '>= 2.4.0'
28
+ spec.required_ruby_version = ">= 2.4.0"
27
29
 
28
30
  # to update dependencies edit project.yml
29
31
  spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
@@ -31,7 +33,7 @@ Gem::Specification.new do |spec|
31
33
  spec.add_runtime_dependency "dry-core", "~> 0.4"
32
34
  spec.add_runtime_dependency "dry-equalizer", "~> 0.2"
33
35
  spec.add_runtime_dependency "dry-initializer", "~> 3.0"
34
- spec.add_runtime_dependency "dry-schema", "~> 1.4", ">= 1.4.3"
36
+ spec.add_runtime_dependency "dry-schema", "~> 1.5"
35
37
 
36
38
  spec.add_development_dependency "bundler"
37
39
  spec.add_development_dependency "rake"
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/validation'
3
+ require "dry/validation"
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/validation/constants'
4
- require 'dry/validation/contract'
5
- require 'dry/validation/macros'
3
+ require "dry/validation/constants"
4
+ require "dry/validation/contract"
5
+ require "dry/validation/macros"
6
6
 
7
7
  # Main namespace
8
8
  #
@@ -16,15 +16,15 @@ module Dry
16
16
  extend Macros::Registrar
17
17
 
18
18
  register_extension(:monads) do
19
- require 'dry/validation/extensions/monads'
19
+ require "dry/validation/extensions/monads"
20
20
  end
21
21
 
22
22
  register_extension(:hints) do
23
- require 'dry/validation/extensions/hints'
23
+ require "dry/validation/extensions/hints"
24
24
  end
25
25
 
26
26
  register_extension(:predicates_as_macros) do
27
- require 'dry/validation/extensions/predicates_as_macros'
27
+ require "dry/validation/extensions/predicates_as_macros"
28
28
  end
29
29
 
30
30
  # Define a contract and build its instance
@@ -46,11 +46,9 @@ module Dry
46
46
  #
47
47
  # @api public
48
48
  #
49
- # rubocop:disable Naming/MethodName
50
49
  def self.Contract(options = EMPTY_HASH, &block)
51
50
  Contract.build(options, &block)
52
51
  end
53
- # rubocop:enable Naming/MethodName
54
52
 
55
53
  # This is needed by Macros::Registrar
56
54
  #
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/config'
4
- require 'dry/validation/macros'
3
+ require "dry/schema/config"
4
+ require "dry/validation/macros"
5
5
 
6
6
  module Dry
7
7
  module Validation
@@ -1,22 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pathname'
4
- require 'dry/core/constants'
3
+ require "pathname"
4
+ require "dry/core/constants"
5
5
 
6
6
  module Dry
7
7
  module Validation
8
8
  include Dry::Core::Constants
9
9
 
10
- DOT = '.'
10
+ DOT = "."
11
11
 
12
12
  # Root path is used for base errors in hash representation of error messages
13
13
  ROOT_PATH = [nil].freeze
14
14
 
15
15
  # Path to the default errors locale file
16
- DEFAULT_ERRORS_NAMESPACE = 'dry_validation'
16
+ DEFAULT_ERRORS_NAMESPACE = "dry_validation"
17
17
 
18
18
  # Path to the default errors locale file
19
- DEFAULT_ERRORS_PATH = Pathname(__FILE__).join('../../../../config/errors.yml').realpath.freeze
19
+ DEFAULT_ERRORS_PATH = Pathname(__FILE__).join("../../../../config/errors.yml").realpath.freeze
20
20
 
21
21
  # Mapping for block kwarg options used by block_options
22
22
  #
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/map'
3
+ require "concurrent/map"
4
4
 
5
- require 'dry/equalizer'
6
- require 'dry/initializer'
7
- require 'dry/schema/path'
5
+ require "dry/equalizer"
6
+ require "dry/initializer"
7
+ require "dry/schema/path"
8
8
 
9
- require 'dry/validation/config'
10
- require 'dry/validation/constants'
11
- require 'dry/validation/rule'
12
- require 'dry/validation/evaluator'
13
- require 'dry/validation/messages/resolver'
14
- require 'dry/validation/result'
15
- require 'dry/validation/contract/class_interface'
9
+ require "dry/validation/config"
10
+ require "dry/validation/constants"
11
+ require "dry/validation/rule"
12
+ require "dry/validation/evaluator"
13
+ require "dry/validation/messages/resolver"
14
+ require "dry/validation/result"
15
+ require "dry/validation/contract/class_interface"
16
16
 
17
17
  module Dry
18
18
  module Validation
@@ -123,14 +123,14 @@ module Dry
123
123
  return path.expand.any? { |nested_path| error?(result, nested_path) }
124
124
  end
125
125
 
126
- return true if result.error?(path)
126
+ return true if result.schema_error?(path)
127
127
 
128
128
  path
129
129
  .to_a[0..-2]
130
130
  .any? { |key|
131
131
  curr_path = Schema::Path[path.keys[0..path.keys.index(key)]]
132
132
 
133
- return false unless result.error?(curr_path)
133
+ return false unless result.schema_error?(curr_path)
134
134
 
135
135
  result.errors.any? { |err|
136
136
  (other = Schema::Path[err.path]).same_root?(curr_path) && other == curr_path
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema'
4
- require 'dry/schema/messages'
5
- require 'dry/schema/path'
6
- require 'dry/schema/key_map'
3
+ require "dry/schema"
4
+ require "dry/schema/messages"
5
+ require "dry/schema/path"
6
+ require "dry/schema/key_map"
7
7
 
8
- require 'dry/validation/constants'
9
- require 'dry/validation/macros'
10
- require 'dry/validation/schema_ext'
8
+ require "dry/validation/constants"
9
+ require "dry/validation/macros"
10
+ require "dry/validation/schema_ext"
11
11
 
12
12
  module Dry
13
13
  module Validation
@@ -23,7 +23,7 @@ module Dry
23
23
  # @api private
24
24
  def inherited(klass)
25
25
  super
26
- klass.instance_variable_set('@config', config.dup)
26
+ klass.instance_variable_set("@config", config.dup)
27
27
  end
28
28
 
29
29
  # Configuration
@@ -154,28 +154,15 @@ module Dry
154
154
  private
155
155
 
156
156
  # @api private
157
- # rubocop:disable Metrics/AbcSize
158
157
  def ensure_valid_keys(*keys)
159
- valid_paths = key_map.to_dot_notation.map { |value| Schema::Path[value] }
160
-
161
- invalid_keys = keys
162
- .map { |key|
163
- [key, Schema::Path[key]]
164
- }
165
- .flat_map { |(key, path)|
166
- if (last = path.last).is_a?(Array)
167
- last.map { |last_key|
168
- path_key = [*path.to_a[0..-2], last_key]
169
- [path_key, Schema::Path[path_key]]
170
- }
171
- else
172
- [[key, path]]
173
- end
174
- }
175
- .reject { |(_, path)|
176
- valid_paths.any? { |valid_path| valid_path.include?(path) }
177
- }
178
- .map(&:first)
158
+ valid_paths = key_map.to_dot_notation
159
+ key_paths = key_paths(keys)
160
+
161
+ invalid_keys = key_paths.map { |(key, path)|
162
+ unless valid_paths.any? { |vp| vp.include?(path) || vp.include?("#{path}[]") }
163
+ key
164
+ end
165
+ }.compact.uniq
179
166
 
180
167
  return if invalid_keys.empty?
181
168
 
@@ -183,7 +170,28 @@ module Dry
183
170
  #{name}.rule specifies keys that are not defined by the schema: #{invalid_keys.inspect}
184
171
  STR
185
172
  end
186
- # rubocop:enable Metrics/AbcSize
173
+
174
+ # @api private
175
+ def key_paths(keys)
176
+ keys.map { |key|
177
+ case key
178
+ when Hash
179
+ path = Schema::Path[key]
180
+ if path.multi_value?
181
+ *head, tail = Array(path)
182
+ [key].product(
183
+ tail.map { |el| [*head, *el] }.map { |parts| parts.join(DOT) }
184
+ )
185
+ else
186
+ [[key, path.to_a.join(DOT)]]
187
+ end
188
+ when Array
189
+ [[key, Schema::Path[key].to_a.join(DOT)]]
190
+ else
191
+ [[key, key.to_s]]
192
+ end
193
+ }.flatten(1)
194
+ end
187
195
 
188
196
  # @api private
189
197
  def key_map
@@ -192,7 +200,7 @@ module Dry
192
200
 
193
201
  # @api private
194
202
  def core_schema_opts
195
- { parent: superclass&.__schema__, config: config }
203
+ {parent: superclass&.__schema__, config: config}
196
204
  end
197
205
 
198
206
  # @api private
@@ -200,7 +208,7 @@ module Dry
200
208
  return __schema__ if external_schemas.empty? && block.nil?
201
209
 
202
210
  unless __schema__.nil?
203
- raise ::Dry::Validation::DuplicateSchemaError, 'Schema has already been defined'
211
+ raise ::Dry::Validation::DuplicateSchemaError, "Schema has already been defined"
204
212
  end
205
213
 
206
214
  schema_opts = core_schema_opts
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/initializer'
3
+ require "dry/initializer"
4
+ require "dry/core/deprecations"
4
5
 
5
- require 'dry/validation/constants'
6
- require 'dry/validation/failures'
6
+ require "dry/validation/constants"
7
+ require "dry/validation/failures"
7
8
 
8
9
  module Dry
9
10
  module Validation
@@ -16,6 +17,9 @@ module Dry
16
17
  # @api public
17
18
  class Evaluator
18
19
  extend Dry::Initializer
20
+ extend Dry::Core::Deprecations[:'dry-validation']
21
+
22
+ deprecate :error?, :schema_error?
19
23
 
20
24
  # @!attribute [r] _contract
21
25
  # @return [Contract]
@@ -142,7 +146,7 @@ module Dry
142
146
  #
143
147
  # @return [Object]
144
148
  #
145
- # @public
149
+ # @api public
146
150
  def value
147
151
  values[key_name]
148
152
  end
@@ -151,27 +155,43 @@ module Dry
151
155
  #
152
156
  # This is useful when dealing with rules for optional keys
153
157
  #
154
- # @example
158
+ # @example use the default key name
155
159
  # rule(:age) do
156
160
  # key.failure(:invalid) if key? && value < 18
157
161
  # end
158
162
  #
163
+ # @example specify the key name
164
+ # rule(:start_date, :end_date) do
165
+ # if key?(:start_date) && !key?(:end_date)
166
+ # key(:end_date).failure("must provide an end_date with start_date")
167
+ # end
168
+ # end
169
+ #
159
170
  # @return [Boolean]
160
171
  #
161
172
  # @api public
162
- def key?
163
- values.key?(key_name)
173
+ def key?(name = key_name)
174
+ values.key?(name)
164
175
  end
165
176
 
166
- # Check if there are any errors under the provided path
177
+ # Check if there are any errors on the schema under the provided path
178
+ #
179
+ # @param path [Symbol, String, Array] A Path-compatible spec
167
180
  #
168
- # @param [Symbol, String, Array] A Path-compatible spec
181
+ # @return [Boolean]
182
+ #
183
+ # @api public
184
+ def schema_error?(path)
185
+ result.schema_error?(path)
186
+ end
187
+
188
+ # Check if there are any errors on the current rule
169
189
  #
170
190
  # @return [Boolean]
171
191
  #
172
192
  # @api public
173
- def error?(path)
174
- result.error?(path)
193
+ def rule_error?
194
+ !key(path).empty?
175
195
  end
176
196
 
177
197
  # @api private
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/result'
4
-
5
3
  module Dry
6
4
  module Validation
7
5
  # Hints extension
@@ -46,7 +44,7 @@ module Dry
46
44
  #
47
45
  # @api public
48
46
  def messages(new_options = EMPTY_HASH)
49
- errors.with(hints.to_a, options.merge(**new_options))
47
+ errors.with(hints(new_options).to_a, options.merge(**new_options))
50
48
  end
51
49
 
52
50
  # Return hint messages
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/result'
3
+ require "dry/monads/result"
4
4
 
5
5
  module Dry
6
6
  module Validation
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/predicate_registry'
4
- require 'dry/validation/contract'
3
+ require "dry/schema/predicate_registry"
4
+ require "dry/validation/contract"
5
5
 
6
6
  module Dry
7
7
  module Validation
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/path'
4
- require 'dry/validation/constants'
3
+ require "dry/schema/path"
4
+ require "dry/validation/constants"
5
5
 
6
6
  module Dry
7
7
  module Validation
@@ -57,9 +57,14 @@ module Dry
57
57
  #
58
58
  # @api public
59
59
  def failure(message, tokens = EMPTY_HASH)
60
- opts << { message: message, tokens: tokens, path: path }
60
+ opts << {message: message, tokens: tokens, path: path}
61
61
  self
62
62
  end
63
+
64
+ # @api private
65
+ def empty?
66
+ opts.empty?
67
+ end
63
68
  end
64
69
  end
65
70
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/initializer'
4
- require 'dry/validation/constants'
3
+ require "dry/initializer"
4
+ require "dry/validation/constants"
5
5
 
6
6
  module Dry
7
7
  module Validation
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/validation/constants'
4
- require 'dry/validation/function'
3
+ require "dry/validation/constants"
4
+ require "dry/validation/function"
5
5
 
6
6
  module Dry
7
7
  module Validation
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/container'
4
- require 'dry/validation/macro'
3
+ require "dry/container"
4
+ require "dry/validation/macro"
5
5
 
6
6
  module Dry
7
7
  module Validation
@@ -25,7 +25,7 @@ module Dry
25
25
  # end
26
26
  #
27
27
  # @param [Symbol] name The name of the macro
28
- # @param [Array] *args Optional default arguments for the macro
28
+ # @param [Array] args Optional default positional arguments for the macro
29
29
  #
30
30
  # @return [self]
31
31
  #
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/equalizer'
3
+ require "dry/equalizer"
4
4
 
5
- require 'dry/schema/constants'
6
- require 'dry/schema/message'
5
+ require "dry/schema/constants"
6
+ require "dry/schema/message"
7
7
 
8
8
  module Dry
9
9
  module Validation
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/message_set'
3
+ require "dry/schema/message_set"
4
4
 
5
- require 'dry/validation/constants'
6
- require 'dry/validation/message'
5
+ require "dry/validation/constants"
6
+ require "dry/validation/message"
7
7
 
8
8
  module Dry
9
9
  module Validation
@@ -57,7 +57,6 @@ module Dry
57
57
  @empty = nil
58
58
  source_messages << message
59
59
  messages << message
60
- initialize_placeholders!
61
60
  self
62
61
  end
63
62
 
@@ -86,58 +85,13 @@ module Dry
86
85
  # @api private
87
86
  def freeze
88
87
  source_messages.select { |err| err.respond_to?(:evaluate) }.each do |err|
89
- idx = source_messages.index(err)
88
+ idx = messages.index(err) || source_messages.index(err)
90
89
  msg = err.evaluate(locale: locale, full: options[:full])
91
90
  messages[idx] = msg
92
91
  end
93
92
  to_h
94
93
  self
95
94
  end
96
-
97
- private
98
-
99
- # @api private
100
- def unique_paths
101
- source_messages.uniq(&:path).map(&:path)
102
- end
103
-
104
- # @api private
105
- def messages_map
106
- @messages_map ||= reduce(placeholders) { |hash, msg|
107
- node = msg.path.reduce(hash) { |a, e| a.is_a?(Hash) ? a[e] : a.last[e] }
108
- (node[0].is_a?(::Array) ? node[0] : node) << msg.dump
109
- hash
110
- }
111
- end
112
-
113
- # @api private
114
- #
115
- # rubocop:disable Metrics/AbcSize
116
- # rubocop:disable Metrics/PerceivedComplexity
117
- def initialize_placeholders!
118
- @placeholders = unique_paths.sort_by(&:size).each_with_object(EMPTY_HASH.dup) { |path, hash|
119
- curr_idx = 0
120
- last_idx = path.size - 1
121
- node = hash
122
-
123
- while curr_idx <= last_idx
124
- key = path[curr_idx]
125
-
126
- next_node =
127
- if node.is_a?(Array) && key.is_a?(Symbol)
128
- node_hash = (node << [] << {}).last
129
- node_hash[key] || (node_hash[key] = curr_idx < last_idx ? {} : [])
130
- else
131
- node[key] || (node[key] = curr_idx < last_idx ? {} : [])
132
- end
133
-
134
- node = next_node
135
- curr_idx += 1
136
- end
137
- }
138
- end
139
- # rubocop:enable Metrics/AbcSize
140
- # rubocop:enable Metrics/PerceivedComplexity
141
95
  end
142
96
  end
143
97
  end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/validation/message'
3
+ require "dry/validation/message"
4
+ require "dry/schema/message_compiler"
4
5
 
5
6
  module Dry
6
7
  module Validation
7
8
  module Messages
9
+ FULL_MESSAGE_WHITESPACE = Dry::Schema::MessageCompiler::FULL_MESSAGE_WHITESPACE
10
+
8
11
  # Resolve translated messages from failure arguments
9
12
  #
10
13
  # @api public
@@ -33,7 +36,7 @@ module Dry
33
36
  when Symbol
34
37
  Message[->(**opts) { message(message, path: path, tokens: tokens, **opts) }, path, meta]
35
38
  when String
36
- Message[message, path, meta]
39
+ Message[->(**opts) { [message_text(message, path: path, **opts), meta] }, path, meta]
37
40
  when Hash
38
41
  meta = message.dup
39
42
  text = meta.delete(:text) { |key|
@@ -69,6 +72,11 @@ module Dry
69
72
  template, meta = messages[rule, msg_opts.merge(path: keys.last)] unless template
70
73
  end
71
74
 
75
+ if !template && keys.size > 1
76
+ non_index_keys = keys.reject { |k| k.is_a?(Integer) }
77
+ template, meta = messages[rule, msg_opts.merge(path: non_index_keys.join(DOT))]
78
+ end
79
+
72
80
  unless template
73
81
  raise MissingMessageError, <<~STR
74
82
  Message template for #{rule.inspect} under #{keys.join(DOT).inspect} was not found
@@ -78,12 +86,29 @@ module Dry
78
86
  parsed_tokens = parse_tokens(tokens)
79
87
  text = template.(template.data(parsed_tokens))
80
88
 
81
- [full ? "#{messages.rule(keys.last, msg_opts)} #{text}" : text, meta]
89
+ [message_text(text, path: path, locale: locale, full: full), meta]
82
90
  end
83
91
  # rubocop:enable Metrics/AbcSize
84
92
 
85
93
  private
86
94
 
95
+ def message_text(text, path:, locale: nil, full: false)
96
+ return text unless full
97
+
98
+ key = key_text(path: path, locale: locale)
99
+
100
+ [key, text].compact.join(FULL_MESSAGE_WHITESPACE[locale])
101
+ end
102
+
103
+ def key_text(path:, locale: nil)
104
+ locale ||= messages.default_locale
105
+
106
+ keys = path.to_a.compact
107
+ msg_opts = {path: keys, locale: locale}
108
+
109
+ messages.rule(keys.last, msg_opts) || keys.last
110
+ end
111
+
87
112
  def parse_tokens(tokens)
88
113
  Hash[
89
114
  tokens.map do |key, token|
@@ -95,7 +120,7 @@ module Dry
95
120
  def parse_token(token)
96
121
  case token
97
122
  when Array
98
- token.join(', ')
123
+ token.join(", ")
99
124
  else
100
125
  token
101
126
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/map'
4
- require 'dry/equalizer'
3
+ require "concurrent/map"
4
+ require "dry/equalizer"
5
5
 
6
- require 'dry/validation/constants'
7
- require 'dry/validation/message_set'
8
- require 'dry/validation/values'
6
+ require "dry/validation/constants"
7
+ require "dry/validation/message_set"
8
+ require "dry/validation/values"
9
9
 
10
10
  module Dry
11
11
  module Validation
@@ -101,8 +101,15 @@ module Dry
101
101
 
102
102
  # Check if values include an error for the provided key
103
103
  #
104
- # @api private
104
+ # @api public
105
105
  def error?(key)
106
+ errors.any? { |msg| Schema::Path[msg.path].include?(Schema::Path[key]) }
107
+ end
108
+
109
+ # Check if the base schema (without rules) includes an error for the provided key
110
+ #
111
+ # @api private
112
+ def schema_error?(key)
106
113
  schema_result.error?(key)
107
114
  end
108
115
 
@@ -116,7 +123,7 @@ module Dry
116
123
  key_path = Schema::Path[key]
117
124
  err_path = Schema::Path[error.path]
118
125
 
119
- return false unless key_path.same_root?(err_path)
126
+ next unless key_path.same_root?(err_path)
120
127
 
121
128
  key_path == err_path
122
129
  }
@@ -179,7 +186,7 @@ module Dry
179
186
  super
180
187
  end
181
188
 
182
- if RUBY_VERSION >= '2.7'
189
+ if RUBY_VERSION >= "2.7"
183
190
  # Pattern matching
184
191
  #
185
192
  # @api private
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/equalizer'
3
+ require "dry/equalizer"
4
4
 
5
- require 'dry/validation/constants'
6
- require 'dry/validation/function'
5
+ require "dry/validation/constants"
6
+ require "dry/validation/function"
7
7
 
8
8
  module Dry
9
9
  module Validation
@@ -65,8 +65,8 @@ module Dry
65
65
  # for a given array item.
66
66
  #
67
67
  # @example
68
- # rule(:nums).each do
69
- # key.failure("must be greater than 0") if value < 0
68
+ # rule(:nums).each do |index:|
69
+ # key([:number, index]).failure("must be greater than 0") if value < 0
70
70
  # end
71
71
  # rule(:nums).each(min: 3)
72
72
  # rule(address: :city) do
@@ -86,9 +86,9 @@ module Dry
86
86
  values[root].each_with_index do |_, idx|
87
87
  path = [*Schema::Path[root].to_a, idx]
88
88
 
89
- next if result.error?(path)
89
+ next if result.schema_error?(path)
90
90
 
91
- evaluator = with(macros: macros, keys: [path], &block)
91
+ evaluator = with(macros: macros, keys: [path], index: idx, &block)
92
92
 
93
93
  failures.concat(evaluator.failures)
94
94
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/key'
4
- require 'dry/schema/key_map'
3
+ require "dry/schema/path"
5
4
 
6
5
  module Dry
7
6
  module Schema
@@ -16,31 +15,5 @@ module Dry
16
15
  to_a[0..-2].product(last).map { |spec| self.class[spec] }
17
16
  end
18
17
  end
19
-
20
- # @api private
21
- #
22
- # TODO: this should be moved to dry-schema at some point
23
- class Key
24
- # @api private
25
- def to_dot_notation
26
- [name.to_s]
27
- end
28
-
29
- # @api private
30
- class Hash < Key
31
- # @api private
32
- def to_dot_notation
33
- [name].product(members.flat_map(&:to_dot_notation)).map { |e| e.join(DOT) }
34
- end
35
- end
36
- end
37
-
38
- # @api private
39
- class KeyMap
40
- # @api private
41
- def to_dot_notation
42
- @to_dot_notation ||= map(&:to_dot_notation).flatten
43
- end
44
- end
45
18
  end
46
19
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/equalizer'
4
- require 'dry/schema/path'
5
- require 'dry/validation/constants'
3
+ require "dry/equalizer"
4
+ require "dry/schema/path"
5
+ require "dry/validation/constants"
6
6
 
7
7
  module Dry
8
8
  module Validation
@@ -35,7 +35,10 @@ module Dry
35
35
  # key.failure('must be > 18') if values[:age] <= 18
36
36
  # end
37
37
  #
38
- # @param [Symbol] key
38
+ # @param args [Symbol, String, Hash, Array<Symbol>] If given as a single
39
+ # Symbol, String, Array or Hash, build a key array using
40
+ # {Dry::Schema::Path} digging for data. If given as positional
41
+ # arguments, use these with Hash#dig on the data directly.
39
42
  #
40
43
  # @return [Object]
41
44
  #
@@ -53,7 +56,7 @@ module Dry
53
56
  vals = self.class.new(data.dig(*keys))
54
57
  vals.fetch_values(*last) { nil }
55
58
  else
56
- raise ArgumentError, '+key+ must be a valid path specification'
59
+ raise ArgumentError, "+key+ must be a valid path specification"
57
60
  end
58
61
  end
59
62
 
@@ -65,6 +68,8 @@ module Dry
65
68
  if e.is_a?(Array)
66
69
  result = e.all? { |k| key?(k, a) }
67
70
  return result
71
+ elsif e.is_a?(Symbol) && a.is_a?(Array)
72
+ return false
68
73
  else
69
74
  return false unless a.is_a?(Array) ? (e >= 0 && e < a.size) : a.key?(e)
70
75
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module Validation
5
- VERSION = '1.4.2'
5
+ VERSION = "1.5.4"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-validation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-18 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
@@ -92,20 +92,14 @@ dependencies:
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '1.4'
96
- - - ">="
97
- - !ruby/object:Gem::Version
98
- version: 1.4.3
95
+ version: '1.5'
99
96
  type: :runtime
100
97
  prerelease: false
101
98
  version_requirements: !ruby/object:Gem::Requirement
102
99
  requirements:
103
100
  - - "~>"
104
101
  - !ruby/object:Gem::Version
105
- version: '1.4'
106
- - - ">="
107
- - !ruby/object:Gem::Version
108
- version: 1.4.3
102
+ version: '1.5'
109
103
  - !ruby/object:Gem::Dependency
110
104
  name: bundler
111
105
  requirement: !ruby/object:Gem::Requirement