dry-validation 1.4.2 → 1.5.4

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