diff options
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | lib/hashie/extensions/deep_locate.rb | 36 | ||||
-rw-r--r-- | spec/hashie/extensions/deep_find_spec.rb | 68 | ||||
-rw-r--r-- | spec/hashie/extensions/deep_locate_spec.rb | 13 |
4 files changed, 103 insertions, 15 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 12fa6ad..31a47ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 3.4.4 (Next) * [#317](https://github.com/intridea/hashie/pull/317): Ensure `Hashie::Extensions::MethodQuery` methods return boolean values - [@michaelherold](https://github.com/michaelherold). +* [#319](https://github.com/intridea/hashie/pull/319): Fix a regression from 3.4.1 where `Hashie::Extensions::DeepFind` is no longer indifference-aware - [@michaelherold](https://github.com/michaelherold). * Your contribution here. ## 3.4.3 (10/25/2015) diff --git a/lib/hashie/extensions/deep_locate.rb b/lib/hashie/extensions/deep_locate.rb index bf17c83..feb5282 100644 --- a/lib/hashie/extensions/deep_locate.rb +++ b/lib/hashie/extensions/deep_locate.rb @@ -17,12 +17,7 @@ module Hashie # Hashie::Extensions::DeepLocate.deep_locate -> (key, value, object) { key == :title }, books # # => [{:title=>"Ruby for beginners", :pages=>120}, ...] def self.deep_locate(comparator, object) - # ensure comparator is a callable - unless comparator.respond_to?(:call) - comparator = lambda do |non_callable_object| - ->(key, _, _) { key == non_callable_object } - end.call(comparator) - end + comparator = _construct_key_comparator(comparator, object) unless comparator.respond_to?(:call) _deep_locate(comparator, object) end @@ -68,17 +63,18 @@ module Hashie private + def self._construct_key_comparator(search_key, object) + search_key = search_key.to_s if defined?(::ActiveSupport) && object.is_a?(::ActiveSupport::HashWithIndifferentAccess) + search_key = search_key.to_s if object.respond_to?(:indifferent_access?) && object.indifferent_access? + + lambda do |non_callable_object| + ->(key, _, _) { key == non_callable_object } + end.call(search_key) + end + def self._deep_locate(comparator, object, result = []) if object.is_a?(::Enumerable) - if object.any? do |value| - if object.is_a?(::Hash) - key, value = value - else - key = nil - end - - comparator.call(key, value, object) - end + if object.any? { |value| _match_comparator?(value, comparator, object) } result.push object else (object.respond_to?(:values) ? object.values : object.entries).each do |value| @@ -89,6 +85,16 @@ module Hashie result end + + def self._match_comparator?(value, comparator, object) + if object.is_a?(::Hash) + key, value = value + else + key = nil + end + + comparator.call(key, value, object) + end end end end diff --git a/spec/hashie/extensions/deep_find_spec.rb b/spec/hashie/extensions/deep_find_spec.rb index 2fc0bf2..0b1401c 100644 --- a/spec/hashie/extensions/deep_find_spec.rb +++ b/spec/hashie/extensions/deep_find_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'active_support/core_ext/hash/indifferent_access' describe Hashie::Extensions::DeepFind do subject { Class.new(Hash) { include Hashie::Extensions::DeepFind } } @@ -42,4 +43,71 @@ describe Hashie::Extensions::DeepFind do expect(instance.deep_find_all(:wahoo)).to be_nil end end + + context 'on an ActiveSupport::HashWithIndifferentAccess' do + subject(:instance) { hash.with_indifferent_access.extend(Hashie::Extensions::DeepFind) } + + describe '#deep_find' do + it 'indifferently detects a value from a nested hash' do + expect(instance.deep_find(:address)).to eq('123 Library St.') + expect(instance.deep_find('address')).to eq('123 Library St.') + end + + it 'indifferently detects a value from a nested array' do + expect(instance.deep_find(:title)).to eq('Call of the Wild') + expect(instance.deep_find('title')).to eq('Call of the Wild') + end + + it 'indifferently returns nil if it does not find a match' do + expect(instance.deep_find(:wahoo)).to be_nil + expect(instance.deep_find('wahoo')).to be_nil + end + end + + describe '#deep_find_all' do + it 'indifferently detects all values from a nested hash' do + expect(instance.deep_find_all(:title)).to eq(['Call of the Wild', 'Moby Dick', 'Main Library']) + expect(instance.deep_find_all('title')).to eq(['Call of the Wild', 'Moby Dick', 'Main Library']) + end + + it 'indifferently returns nil if it does not find any matches' do + expect(instance.deep_find_all(:wahoo)).to be_nil + expect(instance.deep_find_all('wahoo')).to be_nil + end + end + end + + context 'on a Hash including Hashie::Extensions::IndifferentAccess' do + let(:klass) { Class.new(Hash) { include Hashie::Extensions::IndifferentAccess } } + subject(:instance) { klass[hash.dup].extend(Hashie::Extensions::DeepFind) } + + describe '#deep_find' do + it 'indifferently detects a value from a nested hash' do + expect(instance.deep_find(:address)).to eq('123 Library St.') + expect(instance.deep_find('address')).to eq('123 Library St.') + end + + it 'indifferently detects a value from a nested array' do + expect(instance.deep_find(:title)).to eq('Call of the Wild') + expect(instance.deep_find('title')).to eq('Call of the Wild') + end + + it 'indifferently returns nil if it does not find a match' do + expect(instance.deep_find(:wahoo)).to be_nil + expect(instance.deep_find('wahoo')).to be_nil + end + end + + describe '#deep_find_all' do + it 'indifferently detects all values from a nested hash' do + expect(instance.deep_find_all(:title)).to eq(['Call of the Wild', 'Moby Dick', 'Main Library']) + expect(instance.deep_find_all('title')).to eq(['Call of the Wild', 'Moby Dick', 'Main Library']) + end + + it 'indifferently returns nil if it does not find any matches' do + expect(instance.deep_find_all(:wahoo)).to be_nil + expect(instance.deep_find_all('wahoo')).to be_nil + end + end + end end diff --git a/spec/hashie/extensions/deep_locate_spec.rb b/spec/hashie/extensions/deep_locate_spec.rb index 3391b27..d174f3c 100644 --- a/spec/hashie/extensions/deep_locate_spec.rb +++ b/spec/hashie/extensions/deep_locate_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'active_support/core_ext/hash/indifferent_access' describe Hashie::Extensions::DeepLocate do let(:hash) do @@ -121,4 +122,16 @@ describe Hashie::Extensions::DeepLocate do expect(instance.deep_locate(:bool)).to eq([hash[:query]]) end end + + context 'on an ActiveSupport::HashWithIndifferentAccess' do + let(:instance) { hash.dup.with_indifferent_access } + + it 'can locate symbolic keys' do + expect(described_class.deep_locate(:lsr10, instance)).to eq ['lsr10' => { 'gte' => 2014 }] + end + + it 'can locate string keys' do + expect(described_class.deep_locate('lsr10', instance)).to eq ['lsr10' => { 'gte' => 2014 }] + end + end end |