From 19604701caa0adf50dd3eba98bbf42387adf2058 Mon Sep 17 00:00:00 2001 From: "zachary.zhang" Date: Tue, 8 Nov 2022 11:40:32 +0800 Subject: [PATCH 1/6] fix evaluate given in array --- lib/grape/dsl/inside_route.rb | 2 +- lib/grape/validations/params_scope.rb | 6 ----- spec/grape/validations/params_scope_spec.rb | 26 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/grape/dsl/inside_route.rb b/lib/grape/dsl/inside_route.rb index c8697ee7d1..1fb25fdaef 100644 --- a/lib/grape/dsl/inside_route.rb +++ b/lib/grape/dsl/inside_route.rb @@ -48,7 +48,7 @@ def declared_array(passed_params, options, declared_params, params_nested_path) def declared_hash(passed_params, options, declared_params, params_nested_path) declared_params.each_with_object(passed_params.class.new) do |declared_param_attr, memo| - next if options[:evaluate_given] && !declared_param_attr.meets_dependency?(options[:request_params]) + next if options[:evaluate_given] && !declared_param_attr.scope.meets_dependency?(passed_params, options[:request_params]) declared_hash_attr(passed_params, options, declared_param_attr.key, params_nested_path, memo) end diff --git a/lib/grape/validations/params_scope.rb b/lib/grape/validations/params_scope.rb index 5eb28cf7fc..519a87b4a3 100644 --- a/lib/grape/validations/params_scope.rb +++ b/lib/grape/validations/params_scope.rb @@ -21,12 +21,6 @@ def initialize(key, scope) @scope = scope end - def meets_dependency?(request_params) - return true if scope.nil? - - scope.meets_dependency?(scope.params(request_params), request_params) - end - # @return Array[Symbol, Hash[Symbol => Array]] declared_params with symbol instead of Attr def self.attrs_keys(declared_params) declared_params.map do |declared_param_attr| diff --git a/spec/grape/validations/params_scope_spec.rb b/spec/grape/validations/params_scope_spec.rb index ed46b9ee94..4e739a370e 100644 --- a/spec/grape/validations/params_scope_spec.rb +++ b/spec/grape/validations/params_scope_spec.rb @@ -690,6 +690,32 @@ def initialize(value) end end + context 'lateral parameter within an array param' do + before do + [true, false].each do |evaluate_given| + subject.params do + optional :array, type: Array do + optional :a + given :a do + optional :b + end + end + end + subject.get("/evaluate_given_#{evaluate_given}") { declared(params, evaluate_given: evaluate_given).to_json } + end + end + + it 'evaluate_given_false' do + get '/evaluate_given_false', array: [{ b: 'b'}, { a: 'a', b: 'b'}] + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil, 'b' => 'b' }, { 'a' => 'a', 'b' => 'b' }]) + end + + it 'evaluate_given_true' do + get '/evaluate_given_true', array: [{ b: 'b'}, { a: 'a', b: 'b'}] + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil}, { 'a' => 'a', 'b' => 'b' }]) + end + end + context 'nested parameter' do before do [true, false].each do |evaluate_given| From 45dd4d92c68617dcfcd9251904f17c1cd40c539c Mon Sep 17 00:00:00 2001 From: "zachary.zhang" Date: Tue, 8 Nov 2022 13:25:42 +0800 Subject: [PATCH 2/6] changelog --- CHANGELOG.md | 1 + spec/grape/validations/params_scope_spec.rb | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 046bb8566d..f4c87b42a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ * [#2256](https://github.com/ruby-grape/grape/pull/2256): Raise `Grape::Exceptions::MultipartPartLimitError` from Rack when too many files are uploaded - [@bschmeck](https://github.com/bschmeck). * [#2266](https://github.com/ruby-grape/grape/pull/2266): Fix code coverage - [@duffn](https://github.com/duffn). * [#2284](https://github.com/ruby-grape/grape/pull/2284): Fix an unexpected backtick - [@zysend](https://github.com/zysend). +* [#2287](https://github.com/ruby-grape/grape/pull/2287): Fix evaluate given in array - [@zysend](https://github.com/zysend). * Your contribution here. ### 1.6.2 (2021/12/30) diff --git a/spec/grape/validations/params_scope_spec.rb b/spec/grape/validations/params_scope_spec.rb index 4e739a370e..0964927ba9 100644 --- a/spec/grape/validations/params_scope_spec.rb +++ b/spec/grape/validations/params_scope_spec.rb @@ -701,18 +701,18 @@ def initialize(value) end end end - subject.get("/evaluate_given_#{evaluate_given}") { declared(params, evaluate_given: evaluate_given).to_json } + subject.post("/evaluate_given_#{evaluate_given}") { declared(params, evaluate_given: evaluate_given).to_json } end end it 'evaluate_given_false' do - get '/evaluate_given_false', array: [{ b: 'b'}, { a: 'a', b: 'b'}] + post '/evaluate_given_false', array: [{ b: 'b' }, { a: 'a', b: 'b' }] expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil, 'b' => 'b' }, { 'a' => 'a', 'b' => 'b' }]) end it 'evaluate_given_true' do - get '/evaluate_given_true', array: [{ b: 'b'}, { a: 'a', b: 'b'}] - expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil}, { 'a' => 'a', 'b' => 'b' }]) + post '/evaluate_given_true', array: [{ b: 'b' }, { a: 'a', b: 'b' }] + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil }, { 'a' => 'a', 'b' => 'b' }]) end end From 19b038a74f37f3fac9b4b468929b7bdd98d9deb9 Mon Sep 17 00:00:00 2001 From: "zachary.zhang" Date: Tue, 8 Nov 2022 17:26:07 +0800 Subject: [PATCH 3/6] avoid a bug that may be caused by rack-test by modifying spec --- spec/grape/validations/params_scope_spec.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/spec/grape/validations/params_scope_spec.rb b/spec/grape/validations/params_scope_spec.rb index 0964927ba9..eed64de57d 100644 --- a/spec/grape/validations/params_scope_spec.rb +++ b/spec/grape/validations/params_scope_spec.rb @@ -696,23 +696,25 @@ def initialize(value) subject.params do optional :array, type: Array do optional :a - given :a do + given a: ->(a) { a == 'a' } do optional :b end end end - subject.post("/evaluate_given_#{evaluate_given}") { declared(params, evaluate_given: evaluate_given).to_json } + subject.get("/evaluate_given_#{evaluate_given}") do + declared(params, evaluate_given: evaluate_given).to_json + end end end it 'evaluate_given_false' do - post '/evaluate_given_false', array: [{ b: 'b' }, { a: 'a', b: 'b' }] - expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil, 'b' => 'b' }, { 'a' => 'a', 'b' => 'b' }]) + get '/evaluate_given_false', array: [{ a: 'c', b: 'b' }, { a: 'a', b: 'b' }] + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => 'c', 'b' => 'b' }, { 'a' => 'a', 'b' => 'b' }]) end it 'evaluate_given_true' do - post '/evaluate_given_true', array: [{ b: 'b' }, { a: 'a', b: 'b' }] - expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil }, { 'a' => 'a', 'b' => 'b' }]) + get '/evaluate_given_true', array: [{ a: 'c', b: 'b' }, { a: 'a', b: 'b' }] + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => 'c' }, { 'a' => 'a', 'b' => 'b' }]) end end From 785b7c6ebb4358970926de082b76c86424d7580b Mon Sep 17 00:00:00 2001 From: "zachary.zhang" Date: Wed, 9 Nov 2022 11:18:46 +0800 Subject: [PATCH 4/6] changelog, spec --- CHANGELOG.md | 3 +-- spec/grape/validations/params_scope_spec.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4c87b42a1..670f15d3a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ * [#2272](https://github.com/ruby-grape/grape/pull/2272): Added error on param init when provided type does not have `[]` coercion method, previously validation silently failed for any value - [@vasfed](https://github.com/Vasfed). * [#2274](https://github.com/ruby-grape/grape/pull/2274): Error middleware support using rack util's symbols as status - [@dhruvCW](https://github.com/dhruvCW). * [#2276](https://github.com/ruby-grape/grape/pull/2276): Fix exception super - [@ericproulx](https://github.com/ericproulx). -* [#2285](https://github.com/ruby-grape/grape/pull/2285): Added :evaluate_given to declared(params) - [@zysend](https://github.com/zysend). +* [#2285](https://github.com/ruby-grape/grape/pull/2285), [#2287](https://github.com/ruby-grape/grape/pull/2287): Added :evaluate_given to declared(params) - [@zysend](https://github.com/zysend). * Your contribution here. #### Fixes @@ -28,7 +28,6 @@ * [#2256](https://github.com/ruby-grape/grape/pull/2256): Raise `Grape::Exceptions::MultipartPartLimitError` from Rack when too many files are uploaded - [@bschmeck](https://github.com/bschmeck). * [#2266](https://github.com/ruby-grape/grape/pull/2266): Fix code coverage - [@duffn](https://github.com/duffn). * [#2284](https://github.com/ruby-grape/grape/pull/2284): Fix an unexpected backtick - [@zysend](https://github.com/zysend). -* [#2287](https://github.com/ruby-grape/grape/pull/2287): Fix evaluate given in array - [@zysend](https://github.com/zysend). * Your contribution here. ### 1.6.2 (2021/12/30) diff --git a/spec/grape/validations/params_scope_spec.rb b/spec/grape/validations/params_scope_spec.rb index eed64de57d..90eb2b5870 100644 --- a/spec/grape/validations/params_scope_spec.rb +++ b/spec/grape/validations/params_scope_spec.rb @@ -696,25 +696,25 @@ def initialize(value) subject.params do optional :array, type: Array do optional :a - given a: ->(a) { a == 'a' } do + given :a do optional :b end end end - subject.get("/evaluate_given_#{evaluate_given}") do + subject.post("/evaluate_given_#{evaluate_given}") do declared(params, evaluate_given: evaluate_given).to_json end end end it 'evaluate_given_false' do - get '/evaluate_given_false', array: [{ a: 'c', b: 'b' }, { a: 'a', b: 'b' }] - expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => 'c', 'b' => 'b' }, { 'a' => 'a', 'b' => 'b' }]) + post '/evaluate_given_false', { array: [{ b: 'b' }, { a: 'a', b: 'b' }] }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil, 'b' => 'b' }, { 'a' => 'a', 'b' => 'b' }]) end it 'evaluate_given_true' do - get '/evaluate_given_true', array: [{ a: 'c', b: 'b' }, { a: 'a', b: 'b' }] - expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => 'c' }, { 'a' => 'a', 'b' => 'b' }]) + post '/evaluate_given_true', { array: [{ b: 'b' }, { a: 'a', b: 'b' }] }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil }, { 'a' => 'a', 'b' => 'b' }]) end end From 1affae0b602c4620ee42f85e822c499bf61f50c9 Mon Sep 17 00:00:00 2001 From: "zachary.zhang" Date: Fri, 11 Nov 2022 15:45:17 +0800 Subject: [PATCH 5/6] meets_dependency will use params directly instead of @parent.params(request_params) when request_params is not given --- lib/grape/dsl/inside_route.rb | 4 +- lib/grape/validations/params_scope.rb | 6 +- spec/grape/validations/params_scope_spec.rb | 217 +++++++++++++++++--- 3 files changed, 192 insertions(+), 35 deletions(-) diff --git a/lib/grape/dsl/inside_route.rb b/lib/grape/dsl/inside_route.rb index 1fb25fdaef..7929bc9f68 100644 --- a/lib/grape/dsl/inside_route.rb +++ b/lib/grape/dsl/inside_route.rb @@ -28,7 +28,7 @@ def self.post_filter_methods(type) # has completed module PostBeforeFilter def declared(passed_params, options = {}, declared_params = nil, params_nested_path = []) - options = options.reverse_merge(include_missing: true, include_parent_namespaces: true, evaluate_given: false, request_params: passed_params) + options = options.reverse_merge(include_missing: true, include_parent_namespaces: true, evaluate_given: false) declared_params ||= optioned_declared_params(**options) if passed_params.is_a?(Array) @@ -48,7 +48,7 @@ def declared_array(passed_params, options, declared_params, params_nested_path) def declared_hash(passed_params, options, declared_params, params_nested_path) declared_params.each_with_object(passed_params.class.new) do |declared_param_attr, memo| - next if options[:evaluate_given] && !declared_param_attr.scope.meets_dependency?(passed_params, options[:request_params]) + next if options[:evaluate_given] && !declared_param_attr.scope.meets_dependency?(passed_params) declared_hash_attr(passed_params, options, declared_param_attr.key, params_nested_path, memo) end diff --git a/lib/grape/validations/params_scope.rb b/lib/grape/validations/params_scope.rb index 519a87b4a3..ebc9650258 100644 --- a/lib/grape/validations/params_scope.rb +++ b/lib/grape/validations/params_scope.rb @@ -88,12 +88,12 @@ def should_validate?(parameters) parent.should_validate?(parameters) end - def meets_dependency?(params, request_params) + def meets_dependency?(params, *request_params) return true unless @dependent_on - return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params) + return false if @parent.present? && !@parent.meets_dependency?(request_params.present? ? @parent.params(*request_params) : params, *request_params) - return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array) + return params.any? { |param| meets_dependency?(param, *request_params) } if params.is_a?(Array) # params might be anything what looks like a hash, so it must implement a `key?` method return false unless params.respond_to?(:key?) diff --git a/spec/grape/validations/params_scope_spec.rb b/spec/grape/validations/params_scope_spec.rb index 90eb2b5870..05fa9c265a 100644 --- a/spec/grape/validations/params_scope_spec.rb +++ b/spec/grape/validations/params_scope_spec.rb @@ -690,35 +690,7 @@ def initialize(value) end end - context 'lateral parameter within an array param' do - before do - [true, false].each do |evaluate_given| - subject.params do - optional :array, type: Array do - optional :a - given :a do - optional :b - end - end - end - subject.post("/evaluate_given_#{evaluate_given}") do - declared(params, evaluate_given: evaluate_given).to_json - end - end - end - - it 'evaluate_given_false' do - post '/evaluate_given_false', { array: [{ b: 'b' }, { a: 'a', b: 'b' }] }.to_json, 'CONTENT_TYPE' => 'application/json' - expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil, 'b' => 'b' }, { 'a' => 'a', 'b' => 'b' }]) - end - - it 'evaluate_given_true' do - post '/evaluate_given_true', { array: [{ b: 'b' }, { a: 'a', b: 'b' }] }.to_json, 'CONTENT_TYPE' => 'application/json' - expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil }, { 'a' => 'a', 'b' => 'b' }]) - end - end - - context 'nested parameter' do + context 'lateral hash parameter' do before do [true, false].each do |evaluate_given| subject.params do @@ -757,7 +729,7 @@ def initialize(value) end end - context 'nested given parameter' do + context 'lateral parameter within lateral hash parameter' do before do [true, false].each do |evaluate_given| subject.params do @@ -817,6 +789,191 @@ def initialize(value) expect(JSON.parse(last_response.body)).to eq('a' => 'y', 'b' => { 'd' => 'd', 'f' => nil, 'e' => { 'i' => nil } }) end end + + context 'lateral parameter within an array param' do + before do + [true, false].each do |evaluate_given| + subject.params do + optional :array, type: Array do + optional :a + given :a do + optional :b + end + end + end + subject.post("/evaluate_given_#{evaluate_given}") do + declared(params, evaluate_given: evaluate_given).to_json + end + end + end + + it 'evaluate_given_false' do + post '/evaluate_given_false', { array: [{ b: 'b' }, { a: 'a', b: 'b' }] }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil, 'b' => 'b' }, { 'a' => 'a', 'b' => 'b' }]) + end + + it 'evaluate_given_true' do + post '/evaluate_given_true', { array: [{ b: 'b' }, { a: 'a', b: 'b' }] }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil }, { 'a' => 'a', 'b' => 'b' }]) + end + end + + context 'nested given parameter' do + before do + [true, false].each do |evaluate_given| + subject.params do + optional :a + optional :c + given :a do + given :c do + optional :b + end + end + end + subject.post("/evaluate_given_#{evaluate_given}") do + declared(params, evaluate_given: evaluate_given).to_json + end + end + end + + it 'evaluate_given_false' do + post '/evaluate_given_false', { a: 'a', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('a' => 'a', 'b' => 'b', 'c' => nil) + + post '/evaluate_given_false', { c: 'c', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('a' => nil, 'b' => 'b', 'c' => 'c') + + post '/evaluate_given_false', { a: 'a', c: 'c', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('a' => 'a', 'b' => 'b', 'c' => 'c') + end + + it 'evaluate_given_true' do + post '/evaluate_given_true', { a: 'a', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('a' => 'a', 'c' => nil) + + post '/evaluate_given_true', { c: 'c', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('a' => nil, 'c' => 'c') + + post '/evaluate_given_true', { a: 'a', c: 'c', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('a' => 'a', 'b' => 'b', 'c' => 'c') + end + end + + context 'nested given parameter within an array param' do + before do + [true, false].each do |evaluate_given| + subject.params do + optional :array, type: Array do + optional :a + optional :c + given :a do + given :c do + optional :b + end + end + end + end + subject.post("/evaluate_given_#{evaluate_given}") do + declared(params, evaluate_given: evaluate_given).to_json + end + end + end + + let :evaluate_given_params do + { + array: [ + { a: 'a', b: 'b' }, + { c: 'c', b: 'b' }, + { a: 'a', c: 'c', b: 'b' } + ] + } + end + + it 'evaluate_given_false' do + post '/evaluate_given_false', evaluate_given_params.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => 'a', 'b' => 'b', 'c' => nil }, { 'a' => nil, 'b' => 'b', 'c' => 'c' }, { 'a' => 'a', 'b' => 'b', 'c' => 'c' }]) + end + + it 'evaluate_given_true' do + post '/evaluate_given_true', evaluate_given_params.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => 'a', 'c' => nil }, { 'a' => nil, 'c' => 'c' }, { 'a' => 'a', 'b' => 'b', 'c' => 'c' }]) + end + end + + context 'nested given parameter within a nested given parameter within an array param' do + before do + [true, false].each do |evaluate_given| + subject.params do + optional :array, type: Array do + optional :a + optional :c + given :a do + given :c do + optional :array, type: Array do + optional :a + optional :c + given :a do + given :c do + optional :b + end + end + end + end + end + end + end + subject.post("/evaluate_given_#{evaluate_given}") do + declared(params, evaluate_given: evaluate_given).to_json + end + end + end + + let :evaluate_given_params do + { + array: [{ + a: 'a', + c: 'c', + array: [ + { a: 'a', b: 'b' }, + { c: 'c', b: 'b' }, + { a: 'a', c: 'c', b: 'b' } + ] + }] + } + end + + it 'evaluate_given_false' do + expected_response_hash = { + 'array' => [{ + 'a' => 'a', + 'c' => 'c', + 'array' => [ + { 'a' => 'a', 'b' => 'b', 'c' => nil }, + { 'a' => nil, 'c' => 'c', 'b' => 'b' }, + { 'a' => 'a', 'c' => 'c', 'b' => 'b' } + ] + }] + } + post '/evaluate_given_false', evaluate_given_params.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq(expected_response_hash) + end + + it 'evaluate_given_true' do + expected_response_hash = { + 'array' => [{ + 'a' => 'a', + 'c' => 'c', + 'array' => [ + { 'a' => 'a', 'c' => nil }, + { 'a' => nil, 'c' => 'c' }, + { 'a' => 'a', 'b' => 'b', 'c' => 'c' } + ] + }] + } + post '/evaluate_given_true', evaluate_given_params.to_json, 'CONTENT_TYPE' => 'application/json' + expect(JSON.parse(last_response.body)).to eq(expected_response_hash) + end + end end end From 4cd8cfb32df143b1519946c46520f451643872e3 Mon Sep 17 00:00:00 2001 From: "zachary.zhang" Date: Tue, 22 Nov 2022 10:06:40 +0800 Subject: [PATCH 6/6] create attr_meets_dependency instand of changing meets_dependency to more complex --- lib/grape/dsl/inside_route.rb | 2 +- lib/grape/validations/params_scope.rb | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/grape/dsl/inside_route.rb b/lib/grape/dsl/inside_route.rb index 7929bc9f68..fb72d0898b 100644 --- a/lib/grape/dsl/inside_route.rb +++ b/lib/grape/dsl/inside_route.rb @@ -48,7 +48,7 @@ def declared_array(passed_params, options, declared_params, params_nested_path) def declared_hash(passed_params, options, declared_params, params_nested_path) declared_params.each_with_object(passed_params.class.new) do |declared_param_attr, memo| - next if options[:evaluate_given] && !declared_param_attr.scope.meets_dependency?(passed_params) + next if options[:evaluate_given] && !declared_param_attr.scope.attr_meets_dependency?(passed_params) declared_hash_attr(passed_params, options, declared_param_attr.key, params_nested_path, memo) end diff --git a/lib/grape/validations/params_scope.rb b/lib/grape/validations/params_scope.rb index ebc9650258..ae4d91d18a 100644 --- a/lib/grape/validations/params_scope.rb +++ b/lib/grape/validations/params_scope.rb @@ -88,13 +88,25 @@ def should_validate?(parameters) parent.should_validate?(parameters) end - def meets_dependency?(params, *request_params) + def meets_dependency?(params, request_params) return true unless @dependent_on - return false if @parent.present? && !@parent.meets_dependency?(request_params.present? ? @parent.params(*request_params) : params, *request_params) + return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params) - return params.any? { |param| meets_dependency?(param, *request_params) } if params.is_a?(Array) + return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array) + meets_hash_dependency?(params) + end + + def attr_meets_dependency?(params) + return true unless @dependent_on + + return false if @parent.present? && !@parent.attr_meets_dependency?(params) + + meets_hash_dependency?(params) + end + + def meets_hash_dependency?(params) # params might be anything what looks like a hash, so it must implement a `key?` method return false unless params.respond_to?(:key?)