From 074a48a299494f810a724ad2698740425235b493 Mon Sep 17 00:00:00 2001 From: Piotr Solnica Date: Mon, 4 Mar 2019 12:03:25 +0100 Subject: [PATCH] Add Result#[]= and Result#key? Closes #460 Closes #461 --- lib/dry/validation/result.rb | 34 ++++++++++++- .../contract/evaluator/setting_values_spec.rb | 49 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 spec/integration/contract/evaluator/setting_values_spec.rb diff --git a/lib/dry/validation/result.rb b/lib/dry/validation/result.rb index 3abf6fae..82183bdc 100644 --- a/lib/dry/validation/result.rb +++ b/lib/dry/validation/result.rb @@ -79,7 +79,32 @@ def add_error(key, message) # # @api public def [](key) - values[key] + values.key?(key) && values[key] || storage.key?(key) && storage[key] + end + + # Store value under specified key + # + # @param [Symbol] key + # @param [Object] value + # + # @return [Object] + # + # @api public + def []=(key, value) + raise ArgumentError, "Key +#{key}+ was already set" if key?(key) + + storage[key] = value + end + + # Check if a key was set + # + # @param [Symbol] key + # + # @return [Bool] + # + # @api public + def key?(key) + values.key?(key) || storage.key?(key) end # Coerce to a hash @@ -103,6 +128,13 @@ def update(new_errors) def inspect "#<#{self.class}#{to_h.inspect} errors=#{errors.inspect}>" end + + private + + # @api private + def storage + @storage ||= EMPTY_HASH.dup + end end end end diff --git a/spec/integration/contract/evaluator/setting_values_spec.rb b/spec/integration/contract/evaluator/setting_values_spec.rb new file mode 100644 index 00000000..0c17d707 --- /dev/null +++ b/spec/integration/contract/evaluator/setting_values_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +RSpec.describe Dry::Validation::Evaluator, 'values writer' do + context 'when key does not exist' do + subject(:contract) do + Dry::Validation::Contract.build do + schema do + required(:email).filled(:string) + required(:user_id).filled(:integer) + end + + rule(:user_id) do + if values[:user_id].equal?(312) + values[:user] = 'jane' + else + failure(:user, 'must be jane') + end + end + + rule(:email) do + failure('is invalid') if values[:user] == 'jane' && values[:email] != 'jane@doe.org' + end + end + end + + it 'stores new values between rule execution' do + expect(contract.(user_id: 3, email: 'john@doe.org').errors).to eql(user: ['must be jane']) + expect(contract.(user_id: 312, email: 'john@doe.org').errors).to eql(email: ['is invalid']) + end + end + + context 'when key already exists' do + subject(:contract) do + Dry::Validation::Contract.build do + schema do + required(:email).filled(:string) + end + + rule(:email) do + values[:email] = 'foo' + end + end + end + + it 'raises error' do + expect { contract.(email: 'jane@doe.org') }.to raise_error(ArgumentError, /email/) + end + end +end