Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Evaluator#schema_error? and Evaluator#rule_error? #617

Merged
merged 6 commits into from
Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
solnic marked this conversation as resolved.
Show resolved Hide resolved

# @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