Skip to content

Commit

Permalink
Merge pull request #617 from dry-rb/rule_error_default
Browse files Browse the repository at this point in the history
Add `Evaluator#schema_error?` and `Evaluator#rule_error?`

[changelog]

version: unreleased
added:
  - "`schema_error?` rule helper (@waiting-for-dev)"
  - "`rule_error?` rule helper (@waiting-for-dev)"
  • Loading branch information
solnic authored Feb 11, 2020
2 parents 229a763 + ef2c5e7 commit 3d1421b
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 2 deletions.
40 changes: 40 additions & 0 deletions docsite/source/rules.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 15 additions & 2 deletions lib/dry/validation/evaluator.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'dry/initializer'
require 'dry/core/deprecations'

require 'dry/validation/constants'
require 'dry/validation/failures'
Expand All @@ -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]
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions lib/dry/validation/failures.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
46 changes: 46 additions & 0 deletions spec/integration/contract/evaluator/errors_spec.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 3d1421b

Please sign in to comment.