From 353d67d753f55ebded28b4fcd232324db22148bf Mon Sep 17 00:00:00 2001 From: Mike Roberts Date: Thu, 28 Mar 2019 11:23:19 -0400 Subject: [PATCH 1/5] code import --- .gitignore | 1 + lib/tree_reject.rb | 53 ++++++++++++++++++++- spec/tree_reject_spec.rb | 99 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0cb6eeb..f48b45c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /pkg/ /spec/reports/ /tmp/ +*.iml \ No newline at end of file diff --git a/lib/tree_reject.rb b/lib/tree_reject.rb index 7690603..1758ad5 100644 --- a/lib/tree_reject.rb +++ b/lib/tree_reject.rb @@ -2,6 +2,57 @@ require 'tree_reject/version' -# Top-level gem module module TreeReject + extend self + + module TreeRejectHash + def tree_reject + TreeReject.tree_reject(self) + end + end + + # returns a new map with the ignored keys removed + # ignored_keys must be an array of IKItems + # IKItem is the type | Hash> + # 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_hash.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_hash, 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) diff --git a/spec/tree_reject_spec.rb b/spec/tree_reject_spec.rb index 529c2c7..a154126 100644 --- a/spec/tree_reject_spec.rb +++ b/spec/tree_reject_spec.rb @@ -4,4 +4,103 @@ 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 do + actual = TreeReject.tree_reject(original, 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 From d9922bd96d7fb04f28147a1ac0cd0f96ee144594 Mon Sep 17 00:00:00 2001 From: Mike Roberts Date: Thu, 28 Mar 2019 11:31:17 -0400 Subject: [PATCH 2/5] extension tests --- lib/tree_reject.rb | 4 ++-- spec/tree_reject_spec.rb | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/tree_reject.rb b/lib/tree_reject.rb index 1758ad5..74197e3 100644 --- a/lib/tree_reject.rb +++ b/lib/tree_reject.rb @@ -6,8 +6,8 @@ module TreeReject extend self module TreeRejectHash - def tree_reject - TreeReject.tree_reject(self) + def tree_reject(ignorned_keys) + TreeReject.tree_reject(self, ignorned_keys) end end diff --git a/spec/tree_reject_spec.rb b/spec/tree_reject_spec.rb index a154126..f803330 100644 --- a/spec/tree_reject_spec.rb +++ b/spec/tree_reject_spec.rb @@ -8,10 +8,15 @@ context "TreeReject.tree_reject" do shared_examples "ignored keys in hash were removed" do - specify 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 From 1c30a2791cb3847286d063e55505586610e2c369 Mon Sep 17 00:00:00 2001 From: Mike Roberts Date: Thu, 28 Mar 2019 14:13:56 -0400 Subject: [PATCH 3/5] readme --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++------ lib/tree_reject.rb | 4 +-- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 257cd3b..783315b 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,48 @@ -# 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 + +`tree_reject` is a Ruby gem that removes deeply nested keys from Ruby Hashes or hash-like objects. + +For example if you have the Hash: + +```ruby +hash = { + a: { + aa: { + aaa: 'aaa', + aab: 'aab' + } + }, + b: { + ba: 'ba' + } +} +``` + +and `tree_reject` the Hash: +```ruby +{ + a: { + aa: :aaa + } +} +``` + +your new hash will be: +```ruby +{ + a: { + aa: { + aab: 'aab' + } + }, + b: { + ba: 'ba' + } +} +``` ## Installation @@ -25,7 +62,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 @@ -48,4 +95,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). - diff --git a/lib/tree_reject.rb b/lib/tree_reject.rb index 74197e3..539f84d 100644 --- a/lib/tree_reject.rb +++ b/lib/tree_reject.rb @@ -19,11 +19,11 @@ def tree_reject(map, ignored_keys) ignored_leaves = extract_leaves(ignored_keys) ignored_subtrees = extract_subtrees(ignored_keys, ignored_leaves) - map.to_hash.each_with_object({}) do |(k, v), cleaned| + 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_hash, ignored_subtrees[k]) + cleaned_v = tree_reject(v.to_h, ignored_subtrees[k]) cleaned[k] = cleaned_v unless cleaned_v == {} elsif !v.nil? cleaned[k] = v From c09dff64ed0a26a9895b3a31755d96e01fd13856 Mon Sep 17 00:00:00 2001 From: Mike Roberts Date: Thu, 28 Mar 2019 14:16:25 -0400 Subject: [PATCH 4/5] version --- CHANGELOG.md | 5 +---- README.md | 1 + lib/tree_reject/version.rb | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b7a132..2eef70b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 783315b..e061423 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://travis-ci.org/salsify/tree_reject.svg?branch=master)](https://travis-ci.org/salsify/tree_reject) +[![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. diff --git a/lib/tree_reject/version.rb b/lib/tree_reject/version.rb index baf0cbc..96aec6c 100644 --- a/lib/tree_reject/version.rb +++ b/lib/tree_reject/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module TreeReject - VERSION = '0.1.0' + VERSION = '1.0.0' end From a8820e251f1513e31263ef8c50fb18d22aa6d972 Mon Sep 17 00:00:00 2001 From: Mike Roberts Date: Thu, 28 Mar 2019 15:46:15 -0400 Subject: [PATCH 5/5] make readme clearer --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e061423..2ca6db2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ `tree_reject` is a Ruby gem that removes deeply nested keys from Ruby Hashes or hash-like objects. -For example if you have the Hash: +For example: ```ruby hash = { @@ -20,18 +20,15 @@ hash = { ba: 'ba' } } -``` -and `tree_reject` the Hash: -```ruby -{ +hash.tree_reject({ a: { aa: :aaa } -} +}) ``` -your new hash will be: +results in: ```ruby { a: {