summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Herold <michael.j.herold@gmail.com>2015-10-25 13:42:36 -0500
committerMichael Herold <michael.j.herold@gmail.com>2015-10-25 14:54:13 -0500
commit262a6728604a5e75128e295177fb80aceaa651bc (patch)
treec1cf9fc727cb00647d00307db8b56f93658b7143
parent9298241eb2d500fa573b1c2aa22ecd91c877d812 (diff)
downloadhashie-bugfix-deep-find.tar.gz
Make DeepLocate indifference-aware by defaultbugfix-deep-find
`Hashie::Extensions::DeepLocate.deep_locate`, when passed a key, did not take into account indifferent access. This lead to a regression in v3.4.1 when `Hashie::Extensions::DeepFind` was modified to be backed by `DeepLocate`. This change makes the default comparator aware of indifferent access by inspecting the object that it is called on. It works with both `ActiveSupport::HashWithIndifferentAccess` and `Hashie::Extensions::IndifferentAccess`. Fixes #309
-rw-r--r--CHANGELOG.md1
-rw-r--r--lib/hashie/extensions/deep_locate.rb36
-rw-r--r--spec/hashie/extensions/deep_find_spec.rb68
-rw-r--r--spec/hashie/extensions/deep_locate_spec.rb13
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