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

adds support of explict argument for #rule_error? method #673

Merged
merged 2 commits into from
Nov 11, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 22 additions & 0 deletions docsite/source/rules.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,28 @@ FooContract.new.(foo: 'foo').errors.to_h
# { foo: ['failure added', 'failure added after checking'] }
```

Also it is possible for checking other rule error by passing explicit argument to `rule_error?` method

```ruby
class PersonContract < Dry::Validation::Contract
schema do
required(:email).filled(:string)
required(:name).filled(:string)
end

rule(:name) do
key.failure('name rule error')
end

rule(:email) do
key.failure('email rule error') if rule_error?(:name)
end
end

PersonContract.new.call(email: 'bar', name: 'foo').errors.to_h
# {name: ['name rule error'], email: ['email rule error']}
```

### 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
10 changes: 8 additions & 2 deletions lib/dry/validation/evaluator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,17 @@ def schema_error?(path)

# Check if there are any errors on the current rule
#
# @param path [Symbol, String, Array] A Path-compatible spec
#
# @return [Boolean]
#
# @api public
def rule_error?
!key(path).empty?
def rule_error?(path = nil)
if path.nil?
!key(self.path).empty?
else
result.error?(path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This checks all errors, not just rule errors, so I'm afraid this doesn't do what we want. One option to make this work would be to add Result#rule_error? that would filter out schema errors and look only at rule errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem here that schema and rule errors are mixed in Result#errors set.
So I see two possible solutions:

  1. Filter schema errors keys from errors set and searching in that subset.
  2. Check that there are no schema error with provided key and then look up Result#errors.

The first approach seems to me is a little more straightforward.
Because in second one you need to know that rule dependent on schema and does not evaluate if there are some error.
But the second one was easier to implement 😄

If second approach is not ok for you I will rewrite it.

end
end

# @api private
Expand Down
51 changes: 39 additions & 12 deletions spec/integration/contract/evaluator/errors_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,50 @@
end

describe "#rule_error?" do
let(:contract) do
Class.new(Dry::Validation::Contract) do
schema do
required(:foo).filled(:string)
end
context "without argument" 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?
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

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"]
)
context "with argument" do
let(:contract) do
Class.new(Dry::Validation::Contract) do
schema do
required(:name).filled(:string)
required(:email).filled(:string)
end

rule(:name) do
key.failure("expected")
end

rule(:email) do
key.failure("also expected") if rule_error?(:name)
end
end
end

it "checks for error in rule with name provided in argument" do
expect(contract.new.(name: "John", email: "some@email.com").errors.to_h).to eql(
{name: ["expected"], email: ["also expected"]}
)
end
end
end
end