From 7781fe4acf664e31993e2cb65fdc748b146011ee Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 13:49:03 -0400 Subject: [PATCH 01/10] Initial commit. --- .../core_ext/enumerable.rb | 1 + .../core_ext/enumerable/deep_clone.rb | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 lib/more_core_extensions/core_ext/enumerable.rb create mode 100644 lib/more_core_extensions/core_ext/enumerable/deep_clone.rb diff --git a/lib/more_core_extensions/core_ext/enumerable.rb b/lib/more_core_extensions/core_ext/enumerable.rb new file mode 100644 index 0000000..ce5d5ce --- /dev/null +++ b/lib/more_core_extensions/core_ext/enumerable.rb @@ -0,0 +1 @@ +require 'more_core_extensions/core_ext/enumerable/deep_clone' diff --git a/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb b/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb new file mode 100644 index 0000000..55120c2 --- /dev/null +++ b/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb @@ -0,0 +1,20 @@ +module MoreCoreExtensions + module EnumerableDeepClone + # Create a deep clone of the enumerable object. This is similar to deep_dup + # but uses a Marshal based approach instead. + # + # h1 = {:a => "hello"} + # h2 = h1.deep_clone + # + # h1[:a] << " world" + # + # h1[:a] # "hello world" + # h2[:a] # "hello" + # + def deep_clone + Marshal.load(Marshal.dump(self)) + end + end +end + +Enumerable.send(:include, MoreCoreExtensions::EnumerableDeepClone) From 9c3f268297d167db8aa44969bddd2898a9517af1 Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 14:05:44 -0400 Subject: [PATCH 02/10] Add deep_clone method and specs. --- .../core_ext/enumerable/deep_clone.rb | 3 +- spec/core_ext/enumerable/deep_clone.rb | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 spec/core_ext/enumerable/deep_clone.rb diff --git a/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb b/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb index 55120c2..7c8d9b4 100644 --- a/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb +++ b/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb @@ -17,4 +17,5 @@ def deep_clone end end -Enumerable.send(:include, MoreCoreExtensions::EnumerableDeepClone) +Array.send(:include, MoreCoreExtensions::EnumerableDeepClone) +Hash.send(:include, MoreCoreExtensions::EnumerableDeepClone) diff --git a/spec/core_ext/enumerable/deep_clone.rb b/spec/core_ext/enumerable/deep_clone.rb new file mode 100644 index 0000000..80b5d40 --- /dev/null +++ b/spec/core_ext/enumerable/deep_clone.rb @@ -0,0 +1,34 @@ +describe Enumerable do + before do + @nested_hash = {'outer' => {:name => 'test', :value => [1,2], :token => 1}} + @nested_array = [{'outer' => {:name => 'test', :value => [1,2], :token => 1}}] + end + + context "Hash#deep_clone" do + it "deep clones a hash as expected" do + expect(@nested_hash.deep_clone).to eq(@nested_hash) + end + + it "doesn't modify the clone if the original is modified" do + clone = @nested_hash.deep_clone + @nested_hash['outer'][:name] << 'stuff' + + expect(@nested_hash['outer'][:name]).to eq('teststuff') + expect(clone['outer'][:name]).to eq('test') + end + end + + context "Array#deep_clone" do + it "deep clones an array as expected" do + expect(@nested_array.deep_clone).to eq(@nested_array) + end + + it "doesn't modify the clone if the original is modified" do + clone = @nested_array.deep_clone + @nested_array[0]['outer'][:name] << 'stuff' + + expect(@nested_array[0]['outer'][:name]).to eq('teststuff') + expect(clone[0]['outer'][:name]).to eq('test') + end + end +end From 0a3b7b6084baebd0feec56cd12f96d942c74ec78 Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 14:20:38 -0400 Subject: [PATCH 03/10] Add Hash#deep_delete and specs. --- lib/more_core_extensions/core_ext/hash/deletes.rb | 10 ++++++++++ spec/core_ext/hash/deletes_spec.rb | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/lib/more_core_extensions/core_ext/hash/deletes.rb b/lib/more_core_extensions/core_ext/hash/deletes.rb index 5b6bcb1..184aa81 100644 --- a/lib/more_core_extensions/core_ext/hash/deletes.rb +++ b/lib/more_core_extensions/core_ext/hash/deletes.rb @@ -15,6 +15,16 @@ def delete_nils def delete_blanks delete_if { |k, v| v.blank? } end + + # Deletes all keys and subkeys that match +key+. + # + # {:a => {:b => 2, :c => 3}}.deep_delete(:b) # => {:a => {:c => 3}} + # + def deep_delete(key) + key = [key] unless key.kind_of?(Array) + key.each { |k| delete(k) } + each_value { |v| v.deep_delete(key) if v.respond_to?(:deep_delete) } + end end end diff --git a/spec/core_ext/hash/deletes_spec.rb b/spec/core_ext/hash/deletes_spec.rb index a8a7803..5e82b8b 100644 --- a/spec/core_ext/hash/deletes_spec.rb +++ b/spec/core_ext/hash/deletes_spec.rb @@ -12,4 +12,10 @@ expect({:c => nil}.delete_blanks).to eq({}) expect({:a => 1, :b => [], :c => nil}.delete_blanks).to eq({:a => 1}) end + + it "#deep_delete" do + expect({}.deep_delete(:c)).to eq({}) + expect({:a => {:b => [1,2], :c => 3}}.deep_delete(:c)).to eq({:a => {:b => [1,2]}}) + expect({:a => {:b => {:c => 1}, :c => 3}}.deep_delete(:c)).to eq({:a => {:b => {}}}) + end end From 1345e71027f53d62a64e4d014923e53ccff35402 Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 14:27:15 -0400 Subject: [PATCH 04/10] Add deep_delete to Array. --- lib/more_core_extensions/core_ext/array/deletes.rb | 9 +++++++++ spec/core_ext/array/deletes_spec.rb | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/lib/more_core_extensions/core_ext/array/deletes.rb b/lib/more_core_extensions/core_ext/array/deletes.rb index fd995b2..5db3366 100644 --- a/lib/more_core_extensions/core_ext/array/deletes.rb +++ b/lib/more_core_extensions/core_ext/array/deletes.rb @@ -15,6 +15,15 @@ def delete_nils def delete_blanks delete_if { |i| i.blank? } end + + # Deletes all keys and subkeys that match +key+. + # + # [{:a => {:b => 2, :c => 3}}].deep_delete(:b) # => [{:a => {:c => 3}}] + # + def deep_delete(key) + each { |i| i.deep_delete(key) if i.respond_to?(:deep_delete) } + self + end end end diff --git a/spec/core_ext/array/deletes_spec.rb b/spec/core_ext/array/deletes_spec.rb index d69db62..47b6a02 100644 --- a/spec/core_ext/array/deletes_spec.rb +++ b/spec/core_ext/array/deletes_spec.rb @@ -12,4 +12,10 @@ expect([nil].delete_blanks).to eq([]) expect([1, [], nil].delete_blanks).to eq([1]) end + + it "#deep_delete" do + expect([{}].deep_delete(:c)).to eq([{}]) + expect([{:a => {:b => [1,2], :c => 3}}].deep_delete(:c)).to eq([{:a => {:b => [1,2]}}]) + expect([{:a => {:b => {:c => 1}, :c => 3}}].deep_delete(:c)).to eq([{:a => {:b => {}}}]) + end end From 5350247f9aac2f8d633f01ed8065aeb4ecc6c99b Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 15:34:15 -0400 Subject: [PATCH 05/10] Minor cleanups. --- lib/more_core_extensions/core_ext/array/deletes.rb | 2 +- spec/core_ext/array/deletes_spec.rb | 2 +- spec/core_ext/enumerable/deep_clone.rb | 4 ++-- spec/core_ext/hash/deletes_spec.rb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/more_core_extensions/core_ext/array/deletes.rb b/lib/more_core_extensions/core_ext/array/deletes.rb index 5db3366..5a4ee67 100644 --- a/lib/more_core_extensions/core_ext/array/deletes.rb +++ b/lib/more_core_extensions/core_ext/array/deletes.rb @@ -18,7 +18,7 @@ def delete_blanks # Deletes all keys and subkeys that match +key+. # - # [{:a => {:b => 2, :c => 3}}].deep_delete(:b) # => [{:a => {:c => 3}}] + # [{:a => {:b => 2, :c => 3}}].deep_delete(:b) # => [{:a => {:c => 3}}] # def deep_delete(key) each { |i| i.deep_delete(key) if i.respond_to?(:deep_delete) } diff --git a/spec/core_ext/array/deletes_spec.rb b/spec/core_ext/array/deletes_spec.rb index 47b6a02..5db88a2 100644 --- a/spec/core_ext/array/deletes_spec.rb +++ b/spec/core_ext/array/deletes_spec.rb @@ -15,7 +15,7 @@ it "#deep_delete" do expect([{}].deep_delete(:c)).to eq([{}]) - expect([{:a => {:b => [1,2], :c => 3}}].deep_delete(:c)).to eq([{:a => {:b => [1,2]}}]) + expect([{:a => {:b => [1, 2], :c => 3}}].deep_delete(:c)).to eq([{:a => {:b => [1, 2]}}]) expect([{:a => {:b => {:c => 1}, :c => 3}}].deep_delete(:c)).to eq([{:a => {:b => {}}}]) end end diff --git a/spec/core_ext/enumerable/deep_clone.rb b/spec/core_ext/enumerable/deep_clone.rb index 80b5d40..77bf5ec 100644 --- a/spec/core_ext/enumerable/deep_clone.rb +++ b/spec/core_ext/enumerable/deep_clone.rb @@ -1,7 +1,7 @@ describe Enumerable do before do - @nested_hash = {'outer' => {:name => 'test', :value => [1,2], :token => 1}} - @nested_array = [{'outer' => {:name => 'test', :value => [1,2], :token => 1}}] + @nested_hash = {'outer' => {:name => 'test', :value => [1, 2], :token => 1}} + @nested_array = [{'outer' => {:name => 'test', :value => [1, 2], :token => 1}}] end context "Hash#deep_clone" do diff --git a/spec/core_ext/hash/deletes_spec.rb b/spec/core_ext/hash/deletes_spec.rb index 5e82b8b..959025e 100644 --- a/spec/core_ext/hash/deletes_spec.rb +++ b/spec/core_ext/hash/deletes_spec.rb @@ -15,7 +15,7 @@ it "#deep_delete" do expect({}.deep_delete(:c)).to eq({}) - expect({:a => {:b => [1,2], :c => 3}}.deep_delete(:c)).to eq({:a => {:b => [1,2]}}) + expect({:a => {:b => [1, 2], :c => 3}}.deep_delete(:c)).to eq({:a => {:b => [1, 2]}}) expect({:a => {:b => {:c => 1}, :c => 3}}.deep_delete(:c)).to eq({:a => {:b => {}}}) end end From 1cc4d731277abb260e333421e2b936965a922030 Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 15:41:14 -0400 Subject: [PATCH 06/10] Update readme. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index cacfb41..1cd5f15 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS * core_ext/array/deletes.rb * `#delete_blanks` - Deletes all items where the value is blank * `#delete_nils` - Deletes all items where the value is nil + * `#deep_delete` - Deletes nested hash key elements * core_ext/array/duplicates.rb * `#duplicates` - Returns an Array of the duplicates found * core_ext/array/element_counts.rb @@ -56,11 +57,16 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS * `#lineage` - Returns an Array of all superclasses. * `#leaf_subclasses` - Returns an Array of all descendants which have no subclasses. +#### Enumerable +* core_ext/enumerable/deep_clone.rb + * `#deep_clone` - Performs a Marshal based deep clone (Array, Hash). + #### Hash * core_ext/hash/deletes.rb * `#delete_blanks` - Deletes all keys where the value is blank * `#delete_nils` - Deletes all keys where the value is nil + * `#deep_delete` - Deletes nested hash key elements * core_ext/hash/nested.rb (see [Shared](#shared)) * `#delete_blank_paths` - Deletes all paths where the value is blank * core_ext/hash/sorting.rb (see [Shared](#shared)) From 54fc0ddab1763742fc4e4fa2874c3f50c40b1909 Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 18:57:06 -0400 Subject: [PATCH 07/10] Move deep_clone to shared module, update readme and specs. --- README.md | 2 +- .../core_ext/enumerable/deep_clone.rb | 21 ----------- .../core_ext/shared/nested.rb | 15 ++++++++ spec/core_ext/shared/nested_spec.rb | 35 +++++++++++++++++++ 4 files changed, 51 insertions(+), 22 deletions(-) delete mode 100644 lib/more_core_extensions/core_ext/enumerable/deep_clone.rb diff --git a/README.md b/README.md index 1cd5f15..c9901d0 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,6 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS #### Enumerable * core_ext/enumerable/deep_clone.rb - * `#deep_clone` - Performs a Marshal based deep clone (Array, Hash). #### Hash @@ -135,6 +134,7 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS #### Shared * core_ext/shared/nested.rb + * `#deep_clone` - Performs a Marshal based deep clone * `#delete_path` - Delete the value at the specified nesting * `#fetch_path` - Fetch the value at the specified nesting * `#find_path` - Detect which nesting holds the specified value diff --git a/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb b/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb deleted file mode 100644 index 7c8d9b4..0000000 --- a/lib/more_core_extensions/core_ext/enumerable/deep_clone.rb +++ /dev/null @@ -1,21 +0,0 @@ -module MoreCoreExtensions - module EnumerableDeepClone - # Create a deep clone of the enumerable object. This is similar to deep_dup - # but uses a Marshal based approach instead. - # - # h1 = {:a => "hello"} - # h2 = h1.deep_clone - # - # h1[:a] << " world" - # - # h1[:a] # "hello world" - # h2[:a] # "hello" - # - def deep_clone - Marshal.load(Marshal.dump(self)) - end - end -end - -Array.send(:include, MoreCoreExtensions::EnumerableDeepClone) -Hash.send(:include, MoreCoreExtensions::EnumerableDeepClone) diff --git a/lib/more_core_extensions/core_ext/shared/nested.rb b/lib/more_core_extensions/core_ext/shared/nested.rb index 778467a..01e5603 100644 --- a/lib/more_core_extensions/core_ext/shared/nested.rb +++ b/lib/more_core_extensions/core_ext/shared/nested.rb @@ -100,6 +100,21 @@ def find_path(val) end [] end + + # Create a deep clone of the object. This is similar to deep_dup + # but uses a Marshal based approach instead. + # + # h1 = {:a => "hello"} + # h2 = h1.deep_clone + # + # h1[:a] << " world" + # + # h1[:a] # "hello world" + # h2[:a] # "hello" + # + def deep_clone + Marshal.load(Marshal.dump(self)) + end end end end diff --git a/spec/core_ext/shared/nested_spec.rb b/spec/core_ext/shared/nested_spec.rb index 8dc6d3a..3c07e14 100644 --- a/spec/core_ext/shared/nested_spec.rb +++ b/spec/core_ext/shared/nested_spec.rb @@ -145,4 +145,39 @@ end end end + + describe "#deep_clone" do + before do + @nested_hash = {'outer' => {:name => 'test', :value => [1, 2], :token => 1}} + @nested_array = [{'outer' => {:name => 'test', :value => [1, 2], :token => 1}}] + end + + context "Hash#deep_clone" do + it "deep clones a hash as expected" do + expect(@nested_hash.deep_clone).to eq(@nested_hash) + end + + it "doesn't modify the clone if the original is modified" do + clone = @nested_hash.deep_clone + @nested_hash['outer'][:name] << 'stuff' + + expect(@nested_hash['outer'][:name]).to eq('teststuff') + expect(clone['outer'][:name]).to eq('test') + end + end + + context "Array#deep_clone" do + it "deep clones an array as expected" do + expect(@nested_array.deep_clone).to eq(@nested_array) + end + + it "doesn't modify the clone if the original is modified" do + clone = @nested_array.deep_clone + @nested_array[0]['outer'][:name] << 'stuff' + + expect(@nested_array[0]['outer'][:name]).to eq('teststuff') + expect(clone[0]['outer'][:name]).to eq('test') + end + end + end end From 6fcb4d6e8246ca02abea67baad2b6d317a2ac884 Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 18:57:34 -0400 Subject: [PATCH 08/10] Remove old comment. --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index c9901d0..fd4b2d5 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,6 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS * `#lineage` - Returns an Array of all superclasses. * `#leaf_subclasses` - Returns an Array of all descendants which have no subclasses. -#### Enumerable -* core_ext/enumerable/deep_clone.rb - #### Hash * core_ext/hash/deletes.rb From 4a9a7ec9fa37af02aa2ddaf5717ba71eb79959e8 Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 18:58:19 -0400 Subject: [PATCH 09/10] Remove enumerable.rb. --- lib/more_core_extensions/core_ext/enumerable.rb | 1 - 1 file changed, 1 deletion(-) delete mode 100644 lib/more_core_extensions/core_ext/enumerable.rb diff --git a/lib/more_core_extensions/core_ext/enumerable.rb b/lib/more_core_extensions/core_ext/enumerable.rb deleted file mode 100644 index ce5d5ce..0000000 --- a/lib/more_core_extensions/core_ext/enumerable.rb +++ /dev/null @@ -1 +0,0 @@ -require 'more_core_extensions/core_ext/enumerable/deep_clone' From d4f8eaef0e8cbab360d350dad05686c5a735d37c Mon Sep 17 00:00:00 2001 From: Daniel Berger Date: Fri, 17 Jul 2020 19:00:04 -0400 Subject: [PATCH 10/10] Remove enumerable spec file. --- spec/core_ext/enumerable/deep_clone.rb | 34 -------------------------- 1 file changed, 34 deletions(-) delete mode 100644 spec/core_ext/enumerable/deep_clone.rb diff --git a/spec/core_ext/enumerable/deep_clone.rb b/spec/core_ext/enumerable/deep_clone.rb deleted file mode 100644 index 77bf5ec..0000000 --- a/spec/core_ext/enumerable/deep_clone.rb +++ /dev/null @@ -1,34 +0,0 @@ -describe Enumerable do - before do - @nested_hash = {'outer' => {:name => 'test', :value => [1, 2], :token => 1}} - @nested_array = [{'outer' => {:name => 'test', :value => [1, 2], :token => 1}}] - end - - context "Hash#deep_clone" do - it "deep clones a hash as expected" do - expect(@nested_hash.deep_clone).to eq(@nested_hash) - end - - it "doesn't modify the clone if the original is modified" do - clone = @nested_hash.deep_clone - @nested_hash['outer'][:name] << 'stuff' - - expect(@nested_hash['outer'][:name]).to eq('teststuff') - expect(clone['outer'][:name]).to eq('test') - end - end - - context "Array#deep_clone" do - it "deep clones an array as expected" do - expect(@nested_array.deep_clone).to eq(@nested_array) - end - - it "doesn't modify the clone if the original is modified" do - clone = @nested_array.deep_clone - @nested_array[0]['outer'][:name] << 'stuff' - - expect(@nested_array[0]['outer'][:name]).to eq('teststuff') - expect(clone[0]['outer'][:name]).to eq('test') - end - end -end