diff --git a/docsite/source/rules.html.md b/docsite/source/rules.html.md
index 5ff6125e..137458a0 100644
--- a/docsite/source/rules.html.md
+++ b/docsite/source/rules.html.md
@@ -181,6 +181,46 @@ contract.call(email: 'jane@doe.org', login: 'jane', password: "").errors.to_h
# => {:password=>["password is required"]}
```
+### Checking for previous errors
+
+Sometimes you may be interested in adding an error when some other error has happened.
+
+With `#schema_error?(key)` you can check whether the schema has an error for a given key:
+
+```ruby
+class PersonContract < Dry::Validation::Contract
+ schema do
+ required(:email).filled(:string)
+ required(:name).filled(:string)
+ end
+
+ rule(:name) do
+ key.failure('first introduce a valid email') if schema_error?(:email)
+ end
+end
+
+PersonContract.new.(email: nil, name: 'foo').errors.to_h
+# { email: ['must be a string'], name: ['first introduce a valid email'] }
+```
+
+In complex rules you may be interested to know whether the current rule already had an error. For that, you can use `#rule_error?`
+
+```ruby
+class FooContract < Dry::Validation::Contract
+ schema do
+ required(:foo).filled(:string)
+ end
+
+ rule(:foo) do
+ key.failure('failure added')
+ key.failure('failure added after checking') if rule_error?
+ end
+end
+
+FooContract.new.(foo: 'foo').errors.to_h
+# { foo: ['failure added', 'failure added after checking'] }
+```
+
### Defining a rule for each element of an array
To check each element of an array you can simply use `Rule#each` shortcut. It works just like a normal rule, which means it's only applied when a value passed schema checks and supports setting failure messages in the standard way.
diff --git a/lib/dry/validation/evaluator.rb b/lib/dry/validation/evaluator.rb
index 2ff46738..d68a3026 100644
--- a/lib/dry/validation/evaluator.rb
+++ b/lib/dry/validation/evaluator.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'dry/initializer'
+require 'dry/core/deprecations'
require 'dry/validation/constants'
require 'dry/validation/failures'
@@ -16,6 +17,9 @@ module Validation
# @api public
class Evaluator
extend Dry::Initializer
+ extend Dry::Core::Deprecations[:'dry-validation']
+
+ deprecate :error?, :schema_error?
# @!attribute [r] _contract
# @return [Contract]
@@ -163,17 +167,26 @@ def key?
values.key?(key_name)
end
- # Check if there are any errors under the provided path
+ # Check if there are any errors on the schema under the provided path
#
# @param [Symbol, String, Array] A Path-compatible spec
#
# @return [Boolean]
#
# @api public
- def error?(path)
+ def schema_error?(path)
result.error?(path)
end
+ # Check if there are any errors on the current rule
+ #
+ # @return [Boolean]
+ #
+ # @api public
+ def rule_error?
+ !key(path).empty?
+ end
+
# @api private
def respond_to_missing?(meth, include_private = false)
super || _contract.respond_to?(meth, true)
diff --git a/lib/dry/validation/failures.rb b/lib/dry/validation/failures.rb
index 50479ff9..a3a0003c 100644
--- a/lib/dry/validation/failures.rb
+++ b/lib/dry/validation/failures.rb
@@ -60,6 +60,11 @@ def failure(message, tokens = EMPTY_HASH)
opts << { message: message, tokens: tokens, path: path }
self
end
+
+ # @api private
+ def empty?
+ opts.empty?
+ end
end
end
end
diff --git a/spec/integration/contract/evaluator/errors_spec.rb b/spec/integration/contract/evaluator/errors_spec.rb
new file mode 100644
index 00000000..fa4171fe
--- /dev/null
+++ b/spec/integration/contract/evaluator/errors_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+RSpec.describe Dry::Validation::Evaluator do
+ describe '#schema_error?' do
+ let(:contract) do
+ Class.new(Dry::Validation::Contract) do
+ schema do
+ required(:email).filled(:string)
+ required(:name).filled(:string)
+ end
+
+ rule(:name) do
+ key.failure('first introduce a valid email') if schema_error?(:email)
+ end
+ end
+ end
+
+ it 'checks for errors in given key' do
+ expect(contract.new.(email: nil, name: 'foo').errors.to_h).to eql(
+ email: ['must be a string'],
+ name: ['first introduce a valid email']
+ )
+ end
+ end
+
+ describe '#rule_error?' do
+ let(:contract) do
+ Class.new(Dry::Validation::Contract) do
+ schema do
+ required(:foo).filled(:string)
+ end
+
+ rule(:foo) do
+ key.failure('failure added')
+ key.failure('failure added after checking') if rule_error?
+ end
+ end
+ end
+
+ it 'checks for errors in current rule' do
+ expect(contract.new.(foo: 'some@email.com').errors.to_h).to eql(
+ foo: ['failure added', 'failure added after checking']
+ )
+ end
+ end
+end