Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hashie::Extensions::DeepGrep #528

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,7 @@ Any violations of this scheme are considered to be bugs.

* [#523](https://github.com/hashie/hashie/pull/523): Added TOC, ensure a keep-a-changelog formatted CHANGELOG - [@dblock](https://github.com/dblock).
* [#522](https://github.com/hashie/hashie/pull/522): Added eierlegende Wollmilchsau mascot graphic - [@carolineartz](https://github.com/carolineartz).
* [#528](https://github.com/hashie/hashie/pull/528): Hashie::extensions::grep - [@vmarutha](https://github.com/vmarutha).
vmarutha marked this conversation as resolved.
Show resolved Hide resolved
* Your contribution here.

### Changed
Expand Down
1 change: 1 addition & 0 deletions lib/hashie.rb
Expand Up @@ -26,6 +26,7 @@ module Extensions
autoload :DeepFetch, 'hashie/extensions/deep_fetch'
autoload :DeepFind, 'hashie/extensions/deep_find'
autoload :DeepLocate, 'hashie/extensions/deep_locate'
autoload :Grep, 'hashie/extensions/grep'
autoload :PrettyInspect, 'hashie/extensions/pretty_inspect'
autoload :KeyConversion, 'hashie/extensions/key_conversion'
autoload :MethodAccessWithOverride, 'hashie/extensions/method_access'
Expand Down
51 changes: 51 additions & 0 deletions lib/hashie/extensions/grep.rb
@@ -0,0 +1,51 @@
# frozen_string_literal: true

require 'hashie/extensions/deep_locate'
module Hashie
module Extensions
module Grep
# Performs a depth-first search on deeply nested data structures for
# a key and returns all occurrences of the key.
vmarutha marked this conversation as resolved.
Show resolved Hide resolved
#
# options = {
# users: [
# { location: {address: '123 Street'} },
# { location: {address: '234 Street'}}
vmarutha marked this conversation as resolved.
Show resolved Hide resolved
# ]
# }
# options.extend(Hashie::Extensions::Grep)
# options.grep(/Street/) # => [{:address: '123 Street'}, {:address: '234 Street'}]
#
# class MyHash < Hash
# include Hashie::Extensions::Grep
# end
#
# my_hash = MyHash.new
# my_hash[:users] = [
# {location: {address: '123 Street'}},
# {location: {address: '234 Street'}}
# ]
# my_hash.grep(/Street/) # => [{:address: '123 Street'}, {:address: '234 Street'}]
def grep(pattern)
matches = _grep(pattern)
matches.empty? ? nil : matches
end

private

def _match_text?(pattern, text)
return false unless text.instance_of?(String) || text.instance_of?(Symbol)

!pattern.match(text).nil?
end

def _grep(pattern, object = self, matches = [])
grep_result = DeepLocate.deep_locate(lambda do |key, value, _object|
_match_text?(pattern, key) || _match_text?(pattern, value)
end, object)

matches.concat(grep_result)
end
end
end
end
49 changes: 49 additions & 0 deletions spec/hashie/extensions/grep_spec.rb
@@ -0,0 +1,49 @@
# frozen_string_literal: true

require 'spec_helper'

describe Hashie::Extensions::Grep do
subject { Class.new(Hash) { include Hashie::Extensions::Grep } }
let(:hash) do
{
library: {
books: [
{ title: 'Call of the Wild' },
{ title: 'Moby Dick' }
],
authors: ['Herman Melville', 'Jack London'],
shelves: nil,
location: {
address: '123 Library St.',
title: 'Main Library'
}
}
}
end
let(:instance) { subject.new.update(hash) }

describe '#grep' do
it 'greps a key from a nested hash' do
expect(instance.grep(/^t/)).to eq([
{ title: 'Call of the Wild' },
{ title: 'Moby Dick' },
{ address: '123 Library St.', title: 'Main Library' }
])
end

it 'greps a value from a nested hash' do
expect(instance.grep(/^M/)).to eq([
{ title: 'Moby Dick' },
{ address: '123 Library St.', title: 'Main Library' }
])
end

it 'greps from an array' do
expect(instance.grep(/Jack/)).to eq([['Herman Melville', 'Jack London']])
end

it 'returns nil if it does not find a match' do
expect(instance.grep(/wahoo/)).to be_nil
end
end
end