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