Skip to content

Commit

Permalink
Merge pull request #1 from salsify/feature/import-from-salsify-kafka
Browse files Browse the repository at this point in the history
Import tree_reject from salsify_kafka
  • Loading branch information
ecopoesis committed Mar 28, 2019
2 parents efa583c + a8820e2 commit a3d44d1
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/pkg/
/spec/reports/
/tmp/
*.iml
5 changes: 1 addition & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,5 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).

## Unreleased

## 0.1.0 - 2019-03-28
### Added
## 1.0.0 - 2019-03-28
- Initial version
60 changes: 52 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,46 @@
# TreeReject
# tree_reject

Welcome to your new gem! In this directory, you'll find the files you need to be
able to package up your Ruby library into a gem. Put your Ruby code in the file
`lib/tree_reject`. To experiment with that code, run
`bin/console` for an interactive prompt.
[![Build Status](https://travis-ci.org/salsify/tree_reject.svg?branch=master)](https://travis-ci.org/salsify/tree_reject)

TODO: Delete this and the text above, and describe your gem
[![Gem Version](https://badge.fury.io/rb/tree_reject.svg)](https://badge.fury.io/rb/tree_reject)

`tree_reject` is a Ruby gem that removes deeply nested keys from Ruby Hashes or hash-like objects.

For example:

```ruby
hash = {
a: {
aa: {
aaa: 'aaa',
aab: 'aab'
}
},
b: {
ba: 'ba'
}
}

hash.tree_reject({
a: {
aa: :aaa
}
})
```

results in:
```ruby
{
a: {
aa: {
aab: 'aab'
}
},
b: {
ba: 'ba'
}
}
```

## Installation

Expand All @@ -25,7 +60,17 @@ Or install it yourself as:

## Usage

TODO: Write usage instructions here
`tree_reject` extends Ruby's built-in `Hash`:

```ruby
my_hash.tree_reject(ignored_keys)
```

It is also available for objects that support `to_h`.

```ruby
TreeReject.tree_reject(my_hash_like, ignored_keys)
```

## Development

Expand All @@ -48,4 +93,3 @@ https://github.com/salsify/tree_reject.## License

The gem is available as open source under the terms of the
[MIT License](http://opensource.org/licenses/MIT).

53 changes: 52 additions & 1 deletion lib/tree_reject.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,57 @@

require 'tree_reject/version'

# Top-level gem module
module TreeReject
extend self

module TreeRejectHash
def tree_reject(ignorned_keys)
TreeReject.tree_reject(self, ignorned_keys)
end
end

# returns a new map with the ignored keys removed
# ignored_keys must be an array of IKItems
# IKItem is the type <Symbol | Array<IkItem> | Hash<Symbol, IKItem>>
# keys are only removed if their path in the trees match
def tree_reject(map, ignored_keys)
ignored_leaves = extract_leaves(ignored_keys)
ignored_subtrees = extract_subtrees(ignored_keys, ignored_leaves)

map.to_h.each_with_object({}) do |(k, v), cleaned|
if ignored_leaves.include?(k)
next
elsif v.is_a?(Hash) || v.respond_to?(:attributes)
cleaned_v = tree_reject(v.to_h, ignored_subtrees[k])
cleaned[k] = cleaned_v unless cleaned_v == {}
elsif !v.nil?
cleaned[k] = v
end
end
end

private

# extract the top level leaves from the ignored_keys tree structure
def extract_leaves(ignored_keys)
[ignored_keys].flatten.reject { |ignored_key| ignored_key.is_a?(Hash) }
end

# extract the top level subtrees from the ignored_keys tree structure, skipping ones included ignored_leaves
def extract_subtrees(ignored_keys, ignored_leaves)
[ignored_keys].flatten.select { |ignored_key| ignored_key.is_a?(Hash) }.inject({}) do |h, ignored_subtree|
ignored_subtree.each_pair do |k, v|
next if ignored_leaves.include?(k)

if h.key?(k)
h[k] << v
else
h[k] = [v]
end
end
h
end
end
end

Hash.send(:include, TreeReject::TreeRejectHash)
2 changes: 1 addition & 1 deletion lib/tree_reject/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module TreeReject
VERSION = '0.1.0'
VERSION = '1.0.0'
end
104 changes: 104 additions & 0 deletions spec/tree_reject_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,108 @@
it "has a version number" do
expect(TreeReject::VERSION).not_to be nil
end

context "TreeReject.tree_reject" do

shared_examples "ignored keys in hash were removed" do
specify "hash as object" do
actual = TreeReject.tree_reject(original, ignored)
expect(actual).to eq expected
end

specify "hash extension" do
actual = original.tree_reject(ignored)
expect(actual).to eq expected
end
end

shared_examples "ignored keys in Virtus-like models were removed" do
specify do
actual = TreeReject.tree_reject(TestModel.new(original), ignored)
expect(actual).to eq expected
end
end

class TestModel < Struct.new(:a, :h, :v)
def initialize(**attributes)
attributes.each do |key, value|
send("#{key}=", value)
end
end

alias_method :to_hash, :to_h
end

context "removes top level key" do
let(:original) { { a: 1 } }
let(:expected) { {} }
let(:ignored) { :a }

it_behaves_like "ignored keys in hash were removed"
it_behaves_like "ignored keys in Virtus-like models were removed"
end

context "removed top level key with children" do
let(:original) { { h: { b: 1 } } }
let(:expected) { {} }
let(:ignored) { :h }

it_behaves_like "ignored keys in hash were removed"
it_behaves_like "ignored keys in Virtus-like models were removed"
end

context "removed child key" do
let(:original) { { h: { b: 1, c: 2 } } }
let(:expected) { { h: { c: 2 } } }
let(:ignored) { { h: :b } }

it_behaves_like "ignored keys in hash were removed"
it_behaves_like "ignored keys in Virtus-like models were removed"
end

context "non-existent top-level key ignored" do
let(:original) { { a: 1 } }
let(:expected) { { a: 1 } }
let(:ignored) { :b }

it_behaves_like "ignored keys in hash were removed"
it_behaves_like "ignored keys in Virtus-like models were removed"
end

context "non-existent child key ignored" do
let(:original) { { a: 1 } }
let(:expected) { { a: 1 } }
let(:ignored) { { b: :c } }

it_behaves_like "ignored keys in hash were removed"
it_behaves_like "ignored keys in Virtus-like models were removed"
end

context "sibling keys removed, array of hashes" do
let(:original) { { h: { b: 1, c: 2, d: 3 } } }
let(:expected) { { h: { d: 3 } } }
let(:ignored) { [{ h: :b }, { h: :c }] }

it_behaves_like "ignored keys in hash were removed"
it_behaves_like "ignored keys in Virtus-like models were removed"
end

context "sibling keys removed, hash of arrays" do
let(:original) { { h: { b: 1, c: 2, d: 3 } } }
let(:expected) { { h: { d: 3 } } }
let(:ignored) { { h: [:b, :c] } }

it_behaves_like "ignored keys in hash were removed"
it_behaves_like "ignored keys in Virtus-like models were removed"
end


context "removed Virtus object child key" do
let(:original) { { v: { a: 1 } } }
let(:expected) { {} }
let(:ignored) { { v: :a } }

it_behaves_like "ignored keys in Virtus-like models were removed"
end
end
end

0 comments on commit a3d44d1

Please sign in to comment.