diff --git a/docsite/source/rules.html.md b/docsite/source/rules.html.md
index 33b8e3b1..77081856 100644
--- a/docsite/source/rules.html.md
+++ b/docsite/source/rules.html.md
@@ -181,6 +181,23 @@ contract.call(email: 'jane@doe.org', login: 'jane', password: "").errors.to_h
# => {:password=>["password is required"]}
```
+The `key?` method supports passing an explicit key name for rules that have multiple keys.
+
+```ruby
+class DistanceContract < Dry::Validation::Contract
+ schema do
+ optional(:kilometers).filled(:integer)
+ optional(:miles).filled(:integer)
+ end
+
+ rule(:kilometers, :miles) do
+ if key?(:kilometers) ^ key?(:miles)
+ base.failure("must only contain one of: kilometers, miles")
+ end
+ end
+end
+```
+
### Checking for previous errors
Sometimes you may be interested in adding an error when some other error has happened.
diff --git a/lib/dry/validation/evaluator.rb b/lib/dry/validation/evaluator.rb
index 31984053..ccaeb83b 100644
--- a/lib/dry/validation/evaluator.rb
+++ b/lib/dry/validation/evaluator.rb
@@ -155,16 +155,23 @@ def value
#
# This is useful when dealing with rules for optional keys
#
- # @example
+ # @example use the default key name
# rule(:age) do
# key.failure(:invalid) if key? && value < 18
# end
#
+ # @example specify the key name
+ # rule(:start_date, :end_date) do
+ # if key?(:start_date) && !key?(:end_date)
+ # key(:end_date).failure("must provide an end_date with start_date")
+ # end
+ # end
+ #
# @return [Boolean]
#
# @api public
- def key?
- values.key?(key_name)
+ def key?(name = key_name)
+ values.key?(name)
end
# Check if there are any errors on the schema under the provided path
diff --git a/spec/integration/evaluator_spec.rb b/spec/integration/evaluator_spec.rb
index 32ccbca9..8b89d30c 100644
--- a/spec/integration/evaluator_spec.rb
+++ b/spec/integration/evaluator_spec.rb
@@ -12,7 +12,7 @@
end
let(:options) do
- {keys: [:email], result: {}, values: values, _context: {}}
+ {keys: [:email, :name], result: {}, values: values, _context: {}}
end
let(:values) do
@@ -46,4 +46,20 @@
end
end
end
+
+ describe "#key?" do
+ subject(:evaluator) do
+ Dry::Validation::Evaluator.new(contract, **options)
+ end
+
+ it "delegates to #values" do
+ expect(values).to receive(:key?).with(:name)
+ evaluator.key?(:name)
+ end
+
+ it "uses #key_name as the default argument" do
+ expect(values).to receive(:key?).with(:email)
+ evaluator.key?
+ end
+ end
end